From bd9da8ecf02f3c0060b74f56a69c0e2c8207fa2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= <49535803+damacaa@users.noreply.github.com> Date: Wed, 16 Jul 2025 20:13:41 +0200 Subject: [PATCH 01/17] WIP New material color shader with terrible blur --- include/weird-renderer/Renderer.h | 31 ++- src/weird-engine/Scene.cpp | 10 +- src/weird-renderer/Renderer.cpp | 95 ++++---- .../shaders/2DLightingShader.frag | 203 ++++++++++++++++++ .../shaders/2DMaterialColorShader.frag | 82 +++++++ ...rching2d.frag => 2DSDFDistanceShader.frag} | 72 +++---- ...tProcess2d.frag => PostProcessShader.frag} | 7 + 7 files changed, 416 insertions(+), 84 deletions(-) create mode 100644 src/weird-renderer/shaders/2DLightingShader.frag create mode 100644 src/weird-renderer/shaders/2DMaterialColorShader.frag rename src/weird-renderer/shaders/{raymarching2d.frag => 2DSDFDistanceShader.frag} (71%) rename src/weird-renderer/shaders/{postProcess2d.frag => PostProcessShader.frag} (96%) diff --git a/include/weird-renderer/Renderer.h b/include/weird-renderer/Renderer.h index 0cc458d..3851529 100644 --- a/include/weird-renderer/Renderer.h +++ b/include/weird-renderer/Renderer.h @@ -42,7 +42,6 @@ namespace WeirdEngine Renderer(const unsigned int width, const unsigned int height); ~Renderer(); void render(Scene& scene, const double time); - bool checkWindowClosed() const; void setWindowTitle(const char* name); SDL_Window* getWindow(); @@ -59,8 +58,10 @@ namespace WeirdEngine Shader m_geometryShaderProgram; Shader m_instancedGeometryShaderProgram; - Shader m_2DsdfShaderProgram; - Shader m_postProcessShaderProgram; + Shader m_2DDistanceShader; + Shader m_2DMaterialColorShader; + Shader m_2DLightingShader; + Shader m_postProcessingShader; Shader m_3DsdfShaderProgram; Shader m_combineScenesShaderProgram; Shader m_outputShaderProgram; @@ -69,6 +70,7 @@ namespace WeirdEngine RenderTarget m_3DSceneRender; RenderTarget m_2DSceneRender; + RenderTarget m_2DColorRender; RenderTarget m_2DPostProcessRender; RenderTarget m_combinationRender; @@ -85,6 +87,7 @@ namespace WeirdEngine Texture m_distanceTexture; + Texture m_2dColorTexture; Texture m_lit2DSceneTexture; Texture m_combineResultTexture; @@ -92,9 +95,27 @@ namespace WeirdEngine WeirdRenderer::Dot2D* m_2DData = nullptr; uint32_t m_2DDataSize = 0; - void renderFire(Scene& scene, Camera& camera, float time); - void renderGeometry(Scene& scene, Camera& camera); + void output(Scene& scene, Texture& texture); + + glm::vec3 m_colorPalette[16] = { + vec3(0.025f, 0.025f, 0.05f), // Black + vec3(1.0f, 1.0f, 1.0f), // White + vec3(0.484f, 0.484f, 0.584f), // Dark Gray + vec3(0.752f, 0.762f, 0.74f), // Light Gray + vec3(.95f, 0.1f, 0.1f), // Red + vec3(0.1f, .95f, 0.1f), // Green + vec3(0.15f, 0.25f, .85f), // Blue + vec3(1.0f, .9f, 0.2f), // Yellow + vec3(.95f, 0.4f, 0.1f), // Orange + vec3(0.5f, 0.0f, 1.0f), // Purple + vec3(0.0f, .9f, .9f), // Cyan + vec3(1.0f, 0.3f, .6f), // Magenta + vec3(0.5f, 1.0f, 0.5f), // Light Green + vec3(1.0f, 0.5f, 0.5f), // Pink + vec3(0.5f, 0.5f, 1.0f), // Light Blue + vec3(0.4f, 0.25f, 0.1f) // Brown + }; }; diff --git a/src/weird-engine/Scene.cpp b/src/weird-engine/Scene.cpp index 7729e80..942c0fb 100644 --- a/src/weird-engine/Scene.cpp +++ b/src/weird-engine/Scene.cpp @@ -181,8 +181,7 @@ namespace WeirdEngine // Shape distance calculation oss << "float dist = " << fragmentCode << ";" << std::endl; - // Scale negative distances - oss << "dist = dist > 0 ? dist : 0.1 * dist;" << std::endl; + // Apply globalEffect logic oss << "float currentMinDistance = " << (globalEffect ? "minDist" : groupDistanceVariable) << ";" << std::endl; @@ -226,6 +225,11 @@ namespace WeirdEngine oss << "col = getMaterial(p," << 3 << ");}\n"; } + // Scale negative distances + oss << "minDist = minDist > 0 ? minDist : 0.1 * minDist;" << std::endl; + + oss << "col.x = minDist; col.y = 5;" << std::endl; + // oss << "minDist -= 1.5;\n"; // Get string @@ -249,7 +253,7 @@ namespace WeirdEngine void Scene::updateRayMarchingShader(WeirdRenderer::Shader &shader) { - m_sdfRenderSystem2D.updatePalette(shader); + // m_sdfRenderSystem2D.updatePalette(shader); updateCustomShapesShader(shader); } diff --git a/src/weird-renderer/Renderer.cpp b/src/weird-renderer/Renderer.cpp index 037e8f0..a1a98f9 100644 --- a/src/weird-renderer/Renderer.cpp +++ b/src/weird-renderer/Renderer.cpp @@ -82,8 +82,8 @@ namespace WeirdEngine , m_windowWidth(width) , m_windowHeight(height) , m_renderScale(1.0f) - , m_renderWidth(width* m_renderScale) - , m_renderHeight(height* m_renderScale) + , m_renderWidth(width * m_renderScale) + , m_renderHeight(height * m_renderScale) , m_vSyncEnabled(true) { Screen::width = m_windowWidth; @@ -98,9 +98,13 @@ namespace WeirdEngine m_3DsdfShaderProgram = Shader(SHADERS_PATH "renderPlane.vert", SHADERS_PATH "raymarching.frag"); - m_2DsdfShaderProgram = Shader(SHADERS_PATH "renderPlane.vert", SHADERS_PATH "raymarching2d.frag"); + m_2DDistanceShader = Shader(SHADERS_PATH "renderPlane.vert", SHADERS_PATH "2DSDFDistanceShader.frag"); - m_postProcessShaderProgram = Shader(SHADERS_PATH "renderPlane.vert", SHADERS_PATH "postProcess2d.frag"); + m_2DMaterialColorShader = Shader(SHADERS_PATH "renderPlane.vert", SHADERS_PATH "2DMaterialColorShader.frag"); + + m_2DLightingShader = Shader(SHADERS_PATH "renderPlane.vert", SHADERS_PATH "2DLigh" + "tingShader.frag"); + m_postProcessingShader = Shader(SHADERS_PATH "renderPlane.vert", SHADERS_PATH "PostProcessShader.frag"); m_combineScenesShaderProgram = Shader(SHADERS_PATH "renderPlane.vert", SHADERS_PATH "combineScenes.frag"); @@ -126,6 +130,10 @@ namespace WeirdEngine m_2DSceneRender = RenderTarget(false); m_2DSceneRender.bindColorTextureToFrameBuffer(m_distanceTexture); + m_2dColorTexture = Texture(m_renderWidth, m_renderHeight, Texture::TextureType::Data); + m_2DColorRender = RenderTarget(false); + m_2DColorRender.bindColorTextureToFrameBuffer(m_2dColorTexture); + m_lit2DSceneTexture = Texture(m_renderWidth, m_renderHeight, Texture::TextureType::Data); m_2DPostProcessRender = RenderTarget(false); m_2DPostProcessRender.bindColorTextureToFrameBuffer(m_lit2DSceneTexture); @@ -150,8 +158,8 @@ namespace WeirdEngine // Delete all the objects we've created m_geometryShaderProgram.free(); m_instancedGeometryShaderProgram.free(); - m_2DsdfShaderProgram.free(); - m_postProcessShaderProgram.free(); + m_2DDistanceShader.free(); + m_2DLightingShader.free(); m_3DsdfShaderProgram.free(); m_combineScenesShaderProgram.free(); m_outputShaderProgram.free(); @@ -214,29 +222,55 @@ namespace WeirdEngine m_2DSceneRender.bind(); // Draw ray marching stuff - m_2DsdfShaderProgram.use(); + m_2DDistanceShader.use(); - scene.updateRayMarchingShader(m_2DsdfShaderProgram); + scene.updateRayMarchingShader(m_2DDistanceShader); // Set uniforms - m_2DsdfShaderProgram.setUniform("u_camMatrix", sceneCamera.view); - m_2DsdfShaderProgram.setUniform("u_time", scene.getTime()); - m_2DsdfShaderProgram.setUniform("u_resolution", glm::vec2(m_renderWidth, m_renderHeight)); + m_2DDistanceShader.setUniform("u_camMatrix", sceneCamera.view); + m_2DDistanceShader.setUniform("u_time", scene.getTime()); + m_2DDistanceShader.setUniform("u_resolution", glm::vec2(m_renderWidth, m_renderHeight)); - m_2DsdfShaderProgram.setUniform("u_blendIterations", 1); + m_2DDistanceShader.setUniform("u_blendIterations", 1); - m_2DsdfShaderProgram.setUniform("t_colorTexture", 0); + m_2DDistanceShader.setUniform("t_colorTexture", 0); m_distanceTexture.bind(0); // Shape data scene.get2DShapesData(m_2DData, m_2DDataSize); - m_2DsdfShaderProgram.setUniform("u_loadedObjects", (int)m_2DDataSize); + m_2DDistanceShader.setUniform("u_loadedObjects", (int)m_2DDataSize); - m_2DsdfShaderProgram.setUniform("t_shapeBuffer", 1); + m_2DDistanceShader.setUniform("t_shapeBuffer", 1); m_shapes2D.uploadData(m_2DData, m_2DDataSize); m_shapes2D.bind(1); - m_renderPlane.draw(m_2DsdfShaderProgram); + m_renderPlane.draw(m_2DDistanceShader); + + m_distanceTexture.unbind(); + m_shapes2D.unbind(); + } + + { + // Bind the framebuffer you want to render to + m_2DColorRender.bind(); + + // Draw ray marching stuff + m_2DMaterialColorShader.use(); + + // Get materials ? + // scene.updateRayMarchingShader(m_2DcolorShaderProgram); + + // Set uniforms + m_2DMaterialColorShader.setUniform("u_camMatrix", sceneCamera.view); + m_2DMaterialColorShader.setUniform("u_time", scene.getTime()); + m_2DMaterialColorShader.setUniform("u_resolution", glm::vec2(m_renderWidth, m_renderHeight)); + m_2DMaterialColorShader.setUniform("u_staticColors", m_colorPalette, 16); + + m_2DMaterialColorShader.setUniform("t_colorTexture", 0); + m_distanceTexture.bind(0); + + + m_renderPlane.draw(m_2DMaterialColorShader); m_distanceTexture.unbind(); m_shapes2D.unbind(); @@ -246,17 +280,18 @@ namespace WeirdEngine { m_2DPostProcessRender.bind(); - m_postProcessShaderProgram.use(); - m_postProcessShaderProgram.setUniform("u_time", scene.getTime()); - m_postProcessShaderProgram.setUniform("u_resolution", glm::vec2(m_renderWidth, m_renderHeight)); + m_2DLightingShader.use(); + m_2DLightingShader.setUniform("u_camMatrix", sceneCamera.view); + m_2DLightingShader.setUniform("u_time", scene.getTime()); + m_2DLightingShader.setUniform("u_resolution", glm::vec2(m_renderWidth, m_renderHeight)); - GLuint u_colorTextureLocation = glGetUniformLocation(m_postProcessShaderProgram.ID, "t_colorTexture"); + GLuint u_colorTextureLocation = glGetUniformLocation(m_2DLightingShader.ID, "t_colorTexture"); glUniform1i(u_colorTextureLocation, 0); - m_distanceTexture.bind(0); + m_2dColorTexture.bind(0); - m_renderPlane.draw(m_postProcessShaderProgram); - m_distanceTexture.unbind(); + m_renderPlane.draw(m_2DLightingShader); + m_2dColorTexture.unbind(); } } @@ -370,7 +405,7 @@ namespace WeirdEngine glUniform1i(u_colorTextureLocation3d, 1); m_3DSceneTexture.bind(1); - m_renderPlane.draw(m_postProcessShaderProgram); + m_renderPlane.draw(m_2DLightingShader); m_lit2DSceneTexture.unbind(); m_3DSceneTexture.unbind(); @@ -378,19 +413,7 @@ namespace WeirdEngine output(scene, m_combineResultTexture); } - void Renderer::renderFire(Scene& scene, Camera& camera, float time) - { - } - - void Renderer::renderGeometry(Scene& scene, Camera& camera) - { - } - bool Renderer::checkWindowClosed() const - { - - return false; - } void Renderer::setWindowTitle(const char* name) { diff --git a/src/weird-renderer/shaders/2DLightingShader.frag b/src/weird-renderer/shaders/2DLightingShader.frag new file mode 100644 index 0000000..6a0d7a6 --- /dev/null +++ b/src/weird-renderer/shaders/2DLightingShader.frag @@ -0,0 +1,203 @@ +#version 330 core + +#define SHADOWS_ENABLED +// #define SOFT_SHADOWS +#define DITHERING + +// #define DEBUG_SHOW_DISTANCE + +// Constants +const int MAX_STEPS = 1000; +const float EPSILON = 0.0; +const float NEAR = 0.1f; +const float FAR = 1.2f; + +// Outputs u_staticColors in RGBA +layout(location = 0) out vec4 FragColor; + +// Inputs from vertex shader +in vec3 v_worldPos; +in vec3 v_normal; +in vec3 v_color; +in vec2 v_texCoord; + +uniform mat4 u_camMatrix; +uniform vec2 u_resolution; +uniform float u_time; + +uniform sampler2D t_colorTexture; + +uniform vec2 u_directionalLightDirection = vec2(0.7071f, 0.7071f); + +#ifdef DITHERING + +uniform float u_spread = .025; +uniform int u_colorCount = 16; + +// Dithering and posterizing +uniform int u_bayer2[2 * 2] = int[2 * 2]( +0, 2, +3, 1); + +uniform int u_bayer4[4 * 4] = int[4 * 4]( +0, 8, 2, 10, +12, 4, 14, 6, +3, 11, 1, 9, +15, 7, 13, 5); + +uniform int u_bayer8[8 * 8] = int[8 * 8]( +0, 32, 8, 40, 2, 34, 10, 42, +48, 16, 56, 24, 50, 18, 58, 26, +12, 44, 4, 36, 14, 46, 6, 38, +60, 28, 52, 20, 62, 30, 54, 22, +3, 35, 11, 43, 1, 33, 9, 41, +51, 19, 59, 27, 49, 17, 57, 25, +15, 47, 7, 39, 13, 45, 5, 37, +63, 31, 55, 23, 61, 29, 53, 21); + +float Getu_bayer2(int x, int y) +{ + return float(u_bayer2[(x % 2) + (y % 2) * 2]) * (1.0f / 4.0f) - 0.5f; +} + +float Getu_bayer4(int x, int y) +{ + return float(u_bayer4[(x % 4) + (y % 4) * 4]) * (1.0f / 16.0f) - 0.5f; +} + +float Getu_bayer8(int x, int y) +{ + return float(u_bayer8[(x % 8) + (y % 8) * 8]) * (1.0f / 64.0f) - 0.5f; +} + +#endif + +float map(vec2 p) +{ + return texture(t_colorTexture, p).w; +} + +float rayMarch(vec2 ro, vec2 rd, out float minDistance) +{ + float d; + minDistance = 10000.0; + + float traveled = 0.0; + + for (int i = 0; i < MAX_STEPS; i++) + { + vec2 p = ro + (traveled * rd); + + d = map(p); + + minDistance = min(d, minDistance); + + if (d <= EPSILON) + break; + + if (p.x <= 0.0 || p.x >= 1.0 || p.y <= 0.0 || p.y >= 1.0) + return FAR; + + // traveled += 0.01; + traveled += d; + // traveled += min(d, 0.01); + + if (traveled >= FAR) + { + return FAR; + } + } + + return traveled; +} + +float render(vec2 uv) +{ + +#ifdef SHADOWS_ENABLED + + // Point light + vec2 rd = normalize(vec2(1.0) - uv); + + // Directional light + // vec2 rd = u_directionalLightDirection.xy; + + float d = map(uv); + float minD; + vec2 offsetPosition = uv + (2.0 / u_resolution) * rd; + if (d <= 0.0) + { + + // float dd = -max(-0.05, d) - 0.01; + // dd = max(0.0, dd); + float distanceFallof = 1.25; + float maxDistance = 0.05; + + float dd = mix(0.0, 1.0, -(distanceFallof * d)); // - 0.005; + dd = min(maxDistance, dd); + + // Get distance at offset position + d = rayMarch(offsetPosition, rd, minD); + return d < FAR ? 1.0 + (-1.5f * dd) : 2.0; // * dot(n, rd); + } + + d = rayMarch(uv, rd, minD); + + // return (10.0 * minD)+0.5; + +#ifdef SOFT_SHADOWS + return mix(0.85, 1.0, d / FAR); +#else + return d < FAR ? 0.85 : 1.0; +#endif + +#else + + return 1.0; + +#endif +} + +void main() +{ + vec2 screenUV = (gl_FragCoord.xy / u_resolution.xy); + vec4 color = texture(t_colorTexture, screenUV); + float distance = color.w; + +#ifdef DEBUG_SHOW_DISTANCE + + if (abs(distance) < (0.5 / u_resolution.x)) + { + FragColor = vec4(vec3(0.0), 1.0); + } + else + { + float value = 0.5 * (cos(500.0 * distance) + 1.0); + value = value * value * value; + vec3 debugColor = distance > 0 ? mix(vec3(1), vec3(0.2), value) : // outside + (distance + 1.0) * mix(vec3(1.0, 0.2, 0.2), vec3(0.1), value); // inside + + FragColor = vec4(debugColor, 1.0); + } + + return; +#endif + + float light = render(screenUV); + vec3 col = light * color.xyz; + + #ifdef DITHERING + + int x = int(gl_FragCoord.x); + int y = int(gl_FragCoord.y); + col = col + u_spread * Getu_bayer4(x, y); + + col.r = floor((u_colorCount - 1.0f) * col.r + 0.5) / (u_colorCount - 1.0f); + col.g = floor((u_colorCount - 1.0f) * col.g + 0.5) / (u_colorCount - 1.0f); + col.b = floor((u_colorCount - 1.0f) * col.b + 0.5) / (u_colorCount - 1.0f); + + #endif + + + FragColor = vec4(col.xyz, 1.0); +} diff --git a/src/weird-renderer/shaders/2DMaterialColorShader.frag b/src/weird-renderer/shaders/2DMaterialColorShader.frag new file mode 100644 index 0000000..33364db --- /dev/null +++ b/src/weird-renderer/shaders/2DMaterialColorShader.frag @@ -0,0 +1,82 @@ +#version 330 core + +#define DEBUG_SHOW_DISTANCE 0 + +// Constants + + +// Outputs u_staticColors in RGBA +layout(location = 0) out vec4 FragColor; + +// Inputs from vertex shader +in vec3 v_worldPos; +in vec3 v_normal; +in vec3 v_color; +in vec2 v_texCoord; + +uniform mat4 u_camMatrix; +uniform vec2 u_resolution; +uniform float u_time; + +uniform sampler2D t_colorTexture; +uniform vec3 u_staticColors[16]; + +float map(vec2 p) +{ + return texture(t_colorTexture, p).w; +} + + +vec3 randomColor(int index) { + float seed = float(index) * 43758.5453; + float r = fract(sin(seed) * 43758.5453); + float g = fract(sin(seed + 1.0) * 43758.5453); + float b = fract(sin(seed + 2.0) * 43758.5453); + return vec3(r, g, b); +} + + +void main() +{ + vec2 screenUV = gl_FragCoord.xy / u_resolution.xy; + vec4 color = texture(t_colorTexture, screenUV); + float distance = color.x; + float mask = color.z; + vec3 c = u_staticColors[int(color.y)]; + + vec2 uv = (2.0 * v_texCoord) - 1.0; + float zoom = -u_camMatrix[3].z; + vec2 pos = (zoom * uv) - u_camMatrix[3].xy; + float aspectRatio = u_resolution.x / u_resolution.y; + vec2 zoomVec = vec2((zoom * aspectRatio) - 1.0, zoom); + + float pixel = 0.2 / u_resolution.y; + vec3 background = mix(u_staticColors[3], u_staticColors[2], + min(fract(0.1 * pos.x), fract(0.1 * pos.y)) > pixel * zoom ? 1.0 : 0.0); + + // Accumulate colors of neighboring cells + vec3 blendedColor = vec3(0.0); + float totalWeight = 0.0; + + for (int x = -10; x <= 10; ++x) { + for (int y = -10; y <= 10; ++y) { + vec2 offset = vec2(float(x), float(y)) / u_resolution; + vec4 neighbor = texture(t_colorTexture, screenUV + offset); + float w = 1.0; + if (x == 0 && y == 0) { + w = 2.0; // Weight center more if desired + } + vec3 neighborColor = u_staticColors[int(neighbor.y)]; + blendedColor += neighborColor * w; + totalWeight += w; + } + } + + blendedColor /= totalWeight; + + // Decide whether to use blended or background + c = distance <= 0.0 ? mix(c, blendedColor, mask) : background; + + FragColor = vec4(c, distance); +} + diff --git a/src/weird-renderer/shaders/raymarching2d.frag b/src/weird-renderer/shaders/2DSDFDistanceShader.frag similarity index 71% rename from src/weird-renderer/shaders/raymarching2d.frag rename to src/weird-renderer/shaders/2DSDFDistanceShader.frag index 9a4eb52..25e97b7 100644 --- a/src/weird-renderer/shaders/raymarching2d.frag +++ b/src/weird-renderer/shaders/2DSDFDistanceShader.frag @@ -21,10 +21,10 @@ uniform float u_time; // Custom -#define BLEND_SHAPES 0 -#define MOTION_BLUR 1 +#define BLEND_SHAPES 1 +#define MOTION_BLUR 0 -uniform float u_k = 0.25; +uniform float u_k = 1.0; // Uniforms uniform sampler2D t_colorTexture; @@ -138,68 +138,57 @@ vec3 getMaterial(vec2 p, int materialId) return u_staticColors[materialId]; } -vec4 getColor(vec2 p, vec2 uv) +vec3 getColor(vec2 p, vec2 uv) { float minDist = 100000.0; + int finalMaterialId = 3; + float mask = 1.0; - vec3 col = vec3(0.0); - float minZ = 1000.0f; - - float zoom = -u_camMatrix[3].z; - - float aspectRatio = u_resolution.x / u_resolution.y; - vec2 zoomVec = vec2((zoom * aspectRatio) - 1.0, zoom); - - bool bestIsScreenSpace = false; + vec3 col; /*ADD_SHAPES_HERE*/ if(minDist < EPSILON) { - return vec4(col, minDist); + return vec3(minDist, finalMaterialId, 0.0); } + float shapeDist = minDist; + minDist = 100000.0; + for (int i = 0; i < u_loadedObjects - (2 * u_customShapeCount); i++) { vec4 positionSizeMaterial = texelFetch(t_shapeBuffer, i); int materialId = int(positionSizeMaterial.w); // vec4 extraParameters = texelFetch(t_shapeBuffer, (2 * i) + 1); - float z = positionSizeMaterial.z; - float objectDist = shape_circle(p - positionSizeMaterial.xy); + // Inside ball mask is set to 0 + mask = objectDist <= 0 ? 0.0 : mask; + + finalMaterialId = objectDist <= minDist || objectDist < 0.0 ? materialId : finalMaterialId; + #if BLEND_SHAPES minDist = fOpUnionSoft(objectDist, minDist, u_k); - float delta = 1 - (max(u_k - abs(objectDist - minDist), 0.0) / u_k); // After new d is calculated - col = mix(getMaterial(p, materialId), col, delta); #else - if(objectDist < minDist) + minDist = min(minDist, objectDist); + + if(minDist < EPSILON) { - minDist = objectDist; - col = getMaterial(positionSizeMaterial.xy, materialId); - minZ = z; - - if(minDist < EPSILON) - { - break; - } + break; } #endif } - // Set bacu_kground color - // vec3 bacu_kground = mix(u_staticColors[2], u_staticColors[3], mod(floor(.1 * p.x) + floor(.1 * p.y), 2.0)); - float pixel = 0.2 / u_resolution.y; - vec3 background = mix(u_staticColors[3], u_staticColors[2], min(fract(0.1 * p.x), fract(0.1 * p.y)) > pixel * zoom ? 1.0 : 0.0); - col = minDist > 0.0 ? background : col; + minDist = min(minDist, shapeDist); - return vec4(col, minDist); + return vec3(minDist, finalMaterialId, mask); } void main() @@ -212,24 +201,27 @@ void main() float zoom = -u_camMatrix[3].z; vec2 pos = (zoom * uv) - u_camMatrix[3].xy; - vec4 color = getColor(pos, v_texCoord); // Same as uv but (0, 0) is bottom left corner - float distance = color.w; + vec3 result = getColor(pos, v_texCoord); // Same as uv but (0, 0) is bottom left corner + float distance = result.x; float finalDistance = 0.6667 * 0.5 * distance / zoom; + + #if MOTION_BLUR vec2 screenUV = v_texCoord; vec4 previousColor = texture(t_colorTexture, screenUV.xy); - float previousDistance = previousColor.w; + float previousDistance = previousColor.x; + int previousMaterial = int(previousColor.y); previousDistance += u_blendIterations * 0.00035; previousDistance = mix(finalDistance, previousDistance, 0.95); // previousDistance = min(previousDistance + (u_blendIterations * 0.00035), mix(finalDistance, previousDistance, 0.9)); - FragColor = previousDistance < finalDistance ? vec4(previousColor.xyz, previousDistance) : vec4(color.xyz, finalDistance); - if (FragColor.w > 0.0) - FragColor = vec4(color.xyz, FragColor.w); + FragColor = previousDistance < finalDistance ? vec4(previousDistance, previousMaterial, result.z, 0) : vec4(finalDistance, result.y, result.z, 0); + if (FragColor.x > 0.0) // Distance + FragColor = vec4(finalDistance, previousMaterial, result.z, 0); // FragColor = previousColor; @@ -237,7 +229,7 @@ void main() #else - FragColor = vec4(color.xyz, finalDistance); + FragColor = vec4(finalDistance, result.y, result.z, 0); #endif diff --git a/src/weird-renderer/shaders/postProcess2d.frag b/src/weird-renderer/shaders/PostProcessShader.frag similarity index 96% rename from src/weird-renderer/shaders/postProcess2d.frag rename to src/weird-renderer/shaders/PostProcessShader.frag index 0879171..d65948f 100644 --- a/src/weird-renderer/shaders/postProcess2d.frag +++ b/src/weird-renderer/shaders/PostProcessShader.frag @@ -15,6 +15,13 @@ const float FAR = 1.2f; // Outputs u_staticColors in RGBA layout(location = 0) out vec4 FragColor; +// Inputs from vertex shader +in vec3 v_worldPos; +in vec3 v_normal; +in vec3 v_color; +in vec2 v_texCoord; + +uniform mat4 u_camMatrix; uniform vec2 u_resolution; uniform float u_time; From f9f4e72d64f12d751f206d5fd35ed7d6b2b112a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= <49535803+damacaa@users.noreply.github.com> Date: Fri, 18 Jul 2025 22:18:11 +0200 Subject: [PATCH 02/17] [WIP] Started blending materials --- include/weird-renderer/Renderer.h | 11 ++++- src/weird-renderer/Renderer.cpp | 45 +++++++++++++++++++ src/weird-renderer/shaders/2DBackground.frag | 0 .../shaders/2DMaterialBlendShader.frag | 29 ++++++++++++ .../shaders/2DMaterialColorShader.frag | 3 +- .../shaders/2DSDFDistanceShader.frag | 26 +++++------ 6 files changed, 98 insertions(+), 16 deletions(-) create mode 100644 src/weird-renderer/shaders/2DBackground.frag create mode 100644 src/weird-renderer/shaders/2DMaterialBlendShader.frag diff --git a/include/weird-renderer/Renderer.h b/include/weird-renderer/Renderer.h index 3851529..9b5eb7b 100644 --- a/include/weird-renderer/Renderer.h +++ b/include/weird-renderer/Renderer.h @@ -60,6 +60,7 @@ namespace WeirdEngine Shader m_instancedGeometryShaderProgram; Shader m_2DDistanceShader; Shader m_2DMaterialColorShader; + Shader m_2DMaterialBlendShader; Shader m_2DLightingShader; Shader m_postProcessingShader; Shader m_3DsdfShaderProgram; @@ -83,11 +84,19 @@ namespace WeirdEngine Texture m_geometryTexture; Texture m_geometryDepthTexture; Texture m_3DSceneTexture; - Texture m_3DDepthSceneTexture; + Texture m_3DDepthSceneTexture; Texture m_distanceTexture; Texture m_2dColorTexture; + + + Texture m_postProcessTextureFront; + Texture m_postProcessTextureBack; + RenderTarget m_postProcessRenderFront; + RenderTarget m_postProcessRenderBack; + RenderTarget *m_postProcessDoubleBuffer[2]; + Texture m_lit2DSceneTexture; Texture m_combineResultTexture; diff --git a/src/weird-renderer/Renderer.cpp b/src/weird-renderer/Renderer.cpp index a1a98f9..a773d83 100644 --- a/src/weird-renderer/Renderer.cpp +++ b/src/weird-renderer/Renderer.cpp @@ -102,6 +102,8 @@ namespace WeirdEngine m_2DMaterialColorShader = Shader(SHADERS_PATH "renderPlane.vert", SHADERS_PATH "2DMaterialColorShader.frag"); + m_2DMaterialBlendShader = Shader(SHADERS_PATH "renderPlane.vert", SHADERS_PATH "2DMaterialBlendShader.frag"); + m_2DLightingShader = Shader(SHADERS_PATH "renderPlane.vert", SHADERS_PATH "2DLigh" "tingShader.frag"); m_postProcessingShader = Shader(SHADERS_PATH "renderPlane.vert", SHADERS_PATH "PostProcessShader.frag"); @@ -134,6 +136,15 @@ namespace WeirdEngine m_2DColorRender = RenderTarget(false); m_2DColorRender.bindColorTextureToFrameBuffer(m_2dColorTexture); + m_postProcessRenderFront = RenderTarget(false); + m_postProcessRenderFront.bindColorTextureToFrameBuffer(m_postProcessTextureFront); + + m_postProcessRenderBack = RenderTarget(false); + m_postProcessRenderBack.bindColorTextureToFrameBuffer(m_postProcessTextureBack); + + m_postProcessDoubleBuffer[0] = &m_postProcessRenderFront; + m_postProcessDoubleBuffer[1] = &m_postProcessRenderBack; + m_lit2DSceneTexture = Texture(m_renderWidth, m_renderHeight, Texture::TextureType::Data); m_2DPostProcessRender = RenderTarget(false); m_2DPostProcessRender.bindColorTextureToFrameBuffer(m_lit2DSceneTexture); @@ -276,6 +287,40 @@ namespace WeirdEngine m_shapes2D.unbind(); } + { + m_bloomRenderTarget->bind(); + m_brightFilterShader.use(); + m_brightFilterShader.setUniform("t_colorTexture", 0); + renderTarget.getColorAttachment()->bind(0); + + m_renderPlane.draw(m_brightFilterShader); + + m_blurShader.use(); + m_blurShader.setUniform("t_colorTexture", 0); + + bool horizontal = true; + static int amount = 10; + + for (unsigned int i = 0; i < amount; i++) + { + m_postProcessDoubleBuffer[horizontal]->bind(); + + m_blurShader.setUniform("u_horizontal", horizontal); + if (i == 0) + { + m_brightPassTexture->bind(0); + } + else + { + m_postProcessDoubleBuffer[!horizontal]->getColorAttachment()->bind(0); + } + + m_renderPlane.draw(m_blurShader); + + horizontal = !horizontal; + } + } + // 2D Lighting { m_2DPostProcessRender.bind(); diff --git a/src/weird-renderer/shaders/2DBackground.frag b/src/weird-renderer/shaders/2DBackground.frag new file mode 100644 index 0000000..e69de29 diff --git a/src/weird-renderer/shaders/2DMaterialBlendShader.frag b/src/weird-renderer/shaders/2DMaterialBlendShader.frag new file mode 100644 index 0000000..f33944f --- /dev/null +++ b/src/weird-renderer/shaders/2DMaterialBlendShader.frag @@ -0,0 +1,29 @@ +#version 330 core + +// Outputs colors in RGBA +out vec4 FragColor; + +in vec2 v_texCoord; + +uniform sampler2D t_colorTexture; + +uniform bool u_horizontal; +uniform float u_weight[5] = float[] (0.227027, 0.1945946, 0.1216216, 0.054054, 0.016216); + +// Taken form learnopengl.com and optimized it to reduce branching +void main() +{ + vec2 tex_offset = 1.0 / textureSize(t_colorTexture, 0); // gets size of single texel + vec4 data = texture(t_colorTexture, v_texCoord); + float mask = data.w; + vec3 result = data.rgb * max(mask, u_weight[0]); // current fragment's contribution + + for(int i = 1; i < mask * 5; ++i) + { + vec2 offset = u_horizontal ? vec2(tex_offset.x * i, 0.0) : vec2(0.0, tex_offset.y * i); + result += texture(t_colorTexture, v_texCoord + offset).rgb * u_weight[i]; + result += texture(t_colorTexture, v_texCoord - offset).rgb * u_weight[i]; + } + + FragColor = vec4(result, 1.0); +} diff --git a/src/weird-renderer/shaders/2DMaterialColorShader.frag b/src/weird-renderer/shaders/2DMaterialColorShader.frag index 33364db..da2775d 100644 --- a/src/weird-renderer/shaders/2DMaterialColorShader.frag +++ b/src/weird-renderer/shaders/2DMaterialColorShader.frag @@ -4,7 +4,6 @@ // Constants - // Outputs u_staticColors in RGBA layout(location = 0) out vec4 FragColor; @@ -75,7 +74,7 @@ void main() blendedColor /= totalWeight; // Decide whether to use blended or background - c = distance <= 0.0 ? mix(c, blendedColor, mask) : background; + c = false || distance <= 0.0 ? mix(c, blendedColor, mask) : background; FragColor = vec4(c, distance); } diff --git a/src/weird-renderer/shaders/2DSDFDistanceShader.frag b/src/weird-renderer/shaders/2DSDFDistanceShader.frag index 25e97b7..fa8fea6 100644 --- a/src/weird-renderer/shaders/2DSDFDistanceShader.frag +++ b/src/weird-renderer/shaders/2DSDFDistanceShader.frag @@ -1,5 +1,7 @@ #version 330 core +#define BLEND_SHAPES 1 +#define MOTION_BLUR 1 out vec4 FragColor; @@ -18,14 +20,7 @@ uniform vec3 u_lightPos; uniform vec3 u_camPos; uniform float u_time; - -// Custom - -#define BLEND_SHAPES 1 -#define MOTION_BLUR 0 - uniform float u_k = 1.0; -// Uniforms uniform sampler2D t_colorTexture; uniform int u_loadedObjects; @@ -156,6 +151,8 @@ vec3 getColor(vec2 p, vec2 uv) float shapeDist = minDist; minDist = 100000.0; + float inv_k = 1.0 / u_k; + for (int i = 0; i < u_loadedObjects - (2 * u_customShapeCount); i++) { vec4 positionSizeMaterial = texelFetch(t_shapeBuffer, i); @@ -171,7 +168,7 @@ vec3 getColor(vec2 p, vec2 uv) #if BLEND_SHAPES - minDist = fOpUnionSoft(objectDist, minDist, u_k); + minDist = fOpUnionSoft(objectDist, minDist, u_k, inv_k); #else @@ -195,7 +192,7 @@ void main() { // FragColor = vec4(u_customShapeCount); // return; - + vec2 uv = (2.0f * v_texCoord) - 1.0f; float zoom = -u_camMatrix[3].z; @@ -212,6 +209,7 @@ void main() vec2 screenUV = v_texCoord; vec4 previousColor = texture(t_colorTexture, screenUV.xy); + float previousDistance = previousColor.x; int previousMaterial = int(previousColor.y); @@ -219,9 +217,9 @@ void main() previousDistance = mix(finalDistance, previousDistance, 0.95); // previousDistance = min(previousDistance + (u_blendIterations * 0.00035), mix(finalDistance, previousDistance, 0.9)); - FragColor = previousDistance < finalDistance ? vec4(previousDistance, previousMaterial, result.z, 0) : vec4(finalDistance, result.y, result.z, 0); - if (FragColor.x > 0.0) // Distance - FragColor = vec4(finalDistance, previousMaterial, result.z, 0); + finalDistance = min(previousDistance, finalDistance); + + // FragColor = previousColor; @@ -229,8 +227,10 @@ void main() #else - FragColor = vec4(finalDistance, result.y, result.z, 0); + #endif + FragColor = vec4(finalDistance, result.y, result.z, 0); + } From 57a9dca27af21e9829bc57d854c40c99100ed15b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= <49535803+damacaa@users.noreply.github.com> Date: Sun, 20 Jul 2025 23:44:07 +0200 Subject: [PATCH 03/17] Faulty integration of blend shader --- src/weird-renderer/Renderer.cpp | 32 +++---- .../shaders/2DLightingShader.frag | 11 ++- .../shaders/2DMaterialBlendShader.frag | 85 +++++++++++++++++-- .../shaders/2DMaterialColorShader.frag | 25 +----- 4 files changed, 105 insertions(+), 48 deletions(-) diff --git a/src/weird-renderer/Renderer.cpp b/src/weird-renderer/Renderer.cpp index a773d83..213de31 100644 --- a/src/weird-renderer/Renderer.cpp +++ b/src/weird-renderer/Renderer.cpp @@ -136,9 +136,11 @@ namespace WeirdEngine m_2DColorRender = RenderTarget(false); m_2DColorRender.bindColorTextureToFrameBuffer(m_2dColorTexture); + m_postProcessTextureFront = Texture(m_renderWidth, m_renderHeight, Texture::TextureType::Data); m_postProcessRenderFront = RenderTarget(false); m_postProcessRenderFront.bindColorTextureToFrameBuffer(m_postProcessTextureFront); + m_postProcessTextureBack = Texture(m_renderWidth, m_renderHeight, Texture::TextureType::Data); m_postProcessRenderBack = RenderTarget(false); m_postProcessRenderBack.bindColorTextureToFrameBuffer(m_postProcessTextureBack); @@ -287,35 +289,31 @@ namespace WeirdEngine m_shapes2D.unbind(); } + bool horizontal = true; { - m_bloomRenderTarget->bind(); - m_brightFilterShader.use(); - m_brightFilterShader.setUniform("t_colorTexture", 0); - renderTarget.getColorAttachment()->bind(0); + m_2DMaterialBlendShader.use(); + m_2DMaterialBlendShader.setUniform("t_colorTexture", 0); + - m_renderPlane.draw(m_brightFilterShader); - m_blurShader.use(); - m_blurShader.setUniform("t_colorTexture", 0); - bool horizontal = true; static int amount = 10; for (unsigned int i = 0; i < amount; i++) { m_postProcessDoubleBuffer[horizontal]->bind(); - m_blurShader.setUniform("u_horizontal", horizontal); + m_2DMaterialBlendShader.setUniform("u_horizontal", horizontal); if (i == 0) { - m_brightPassTexture->bind(0); + m_2dColorTexture.bind(0); } else { m_postProcessDoubleBuffer[!horizontal]->getColorAttachment()->bind(0); } - m_renderPlane.draw(m_blurShader); + m_renderPlane.draw(m_2DMaterialBlendShader); horizontal = !horizontal; } @@ -330,13 +328,17 @@ namespace WeirdEngine m_2DLightingShader.setUniform("u_time", scene.getTime()); m_2DLightingShader.setUniform("u_resolution", glm::vec2(m_renderWidth, m_renderHeight)); - GLuint u_colorTextureLocation = glGetUniformLocation(m_2DLightingShader.ID, "t_colorTexture"); - glUniform1i(u_colorTextureLocation, 0); + GLuint colorTextureLocation = glGetUniformLocation(m_2DLightingShader.ID, "t_colorTexture"); + glUniform1i(colorTextureLocation, 0); + + m_postProcessDoubleBuffer[!horizontal]->getColorAttachment()->bind(0); - m_2dColorTexture.bind(0); + GLuint distanceTextureLocation = glGetUniformLocation(m_2DLightingShader.ID, "t_distanceTexture"); + glUniform1i(distanceTextureLocation, 1); + m_distanceTexture.bind(1); m_renderPlane.draw(m_2DLightingShader); - m_2dColorTexture.unbind(); + m_postProcessTextureFront.unbind(); } } diff --git a/src/weird-renderer/shaders/2DLightingShader.frag b/src/weird-renderer/shaders/2DLightingShader.frag index 6a0d7a6..63b5773 100644 --- a/src/weird-renderer/shaders/2DLightingShader.frag +++ b/src/weird-renderer/shaders/2DLightingShader.frag @@ -26,6 +26,8 @@ uniform vec2 u_resolution; uniform float u_time; uniform sampler2D t_colorTexture; +uniform sampler2D t_distanceTexture; +uniform sampler2D t_backgroundTexture; uniform vec2 u_directionalLightDirection = vec2(0.7071f, 0.7071f); @@ -74,7 +76,7 @@ float Getu_bayer8(int x, int y) float map(vec2 p) { - return texture(t_colorTexture, p).w; + return texture(t_distanceTexture, p).x; } float rayMarch(vec2 ro, vec2 rd, out float minDistance) @@ -161,8 +163,11 @@ float render(vec2 uv) void main() { vec2 screenUV = (gl_FragCoord.xy / u_resolution.xy); - vec4 color = texture(t_colorTexture, screenUV); - float distance = color.w; + vec3 color = texture(t_colorTexture, screenUV).rgb; + vec4 data = texture(t_distanceTexture, screenUV); + float distance = data.x; + + color = false || distance <= 0.0 ? color : vec3(0.3); #ifdef DEBUG_SHOW_DISTANCE diff --git a/src/weird-renderer/shaders/2DMaterialBlendShader.frag b/src/weird-renderer/shaders/2DMaterialBlendShader.frag index f33944f..431adaa 100644 --- a/src/weird-renderer/shaders/2DMaterialBlendShader.frag +++ b/src/weird-renderer/shaders/2DMaterialBlendShader.frag @@ -10,20 +10,87 @@ uniform sampler2D t_colorTexture; uniform bool u_horizontal; uniform float u_weight[5] = float[] (0.227027, 0.1945946, 0.1216216, 0.054054, 0.016216); +vec3 rgb2hsl(vec3 c) { + float r = c.r, g = c.g, b = c.b; + + float maxC = max(r, max(g, b)); + float minC = min(r, min(g, b)); + float delta = maxC - minC; + + float h = 0.0; + float s = 0.0; + float l = (maxC + minC) * 0.5; + + if (delta > 0.00001) { + s = delta / (1.0 - abs(2.0 * l - 1.0)); + + if (maxC == r) { + h = mod((g - b) / delta, 6.0); + } else if (maxC == g) { + h = (b - r) / delta + 2.0; + } else { + h = (r - g) / delta + 4.0; + } + + h /= 6.0; + if (h < 0.0) h += 1.0; + } + + return vec3(h, s, l); // H ∈ [0,1], S ∈ [0,1], L ∈ [0,1] +} + +float hue2rgb(float p, float q, float t) { + if (t < 0.0) t += 1.0; + if (t > 1.0) t -= 1.0; + if (t < 1.0/6.0) return p + (q - p) * 6.0 * t; + if (t < 1.0/2.0) return q; + if (t < 2.0/3.0) return p + (q - p) * (2.0/3.0 - t) * 6.0; + return p; +} + +vec3 hsl2rgb(vec3 hsl) { + float h = hsl.x; + float s = hsl.y; + float l = hsl.z; + + float r, g, b; + + if (s == 0.0) { + r = g = b = l; // achromatic + } else { + float q = l < 0.5 ? l * (1.0 + s) : l + s - l * s; + float p = 2.0 * l - q; + r = hue2rgb(p, q, h + 1.0/3.0); + g = hue2rgb(p, q, h); + b = hue2rgb(p, q, h - 1.0/3.0); + } + + return vec3(r, g, b); +} + + // Taken form learnopengl.com and optimized it to reduce branching void main() { - vec2 tex_offset = 1.0 / textureSize(t_colorTexture, 0); // gets size of single texel + vec2 tex_offset = 1.0 / textureSize(t_colorTexture, 0); vec4 data = texture(t_colorTexture, v_texCoord); - float mask = data.w; - vec3 result = data.rgb * max(mask, u_weight[0]); // current fragment's contribution - - for(int i = 1; i < mask * 5; ++i) + float mask = data.x; + + vec3 baseHSL = rgb2hsl(data.rgb); + vec3 resultHSL = baseHSL * u_weight[0]; + + for (int i = 1; i < 5; ++i) { - vec2 offset = u_horizontal ? vec2(tex_offset.x * i, 0.0) : vec2(0.0, tex_offset.y * i); - result += texture(t_colorTexture, v_texCoord + offset).rgb * u_weight[i]; - result += texture(t_colorTexture, v_texCoord - offset).rgb * u_weight[i]; + vec2 offset = u_horizontal ? vec2(tex_offset.x * i, 0.0) : vec2(0.0, tex_offset.y * i); + + vec3 hsl1 = rgb2hsl(texture(t_colorTexture, v_texCoord + offset).rgb); + vec3 hsl2 = rgb2hsl(texture(t_colorTexture, v_texCoord - offset).rgb); + + resultHSL += hsl1 * u_weight[i]; + resultHSL += hsl2 * u_weight[i]; } - FragColor = vec4(result, 1.0); + vec3 finalRGB = hsl2rgb(resultHSL); + FragColor = vec4(mix(data.xyz, finalRGB, mask), data.a); + // FragColor = vec4(vec3(mask), data.w); } diff --git a/src/weird-renderer/shaders/2DMaterialColorShader.frag b/src/weird-renderer/shaders/2DMaterialColorShader.frag index da2775d..706afe2 100644 --- a/src/weird-renderer/shaders/2DMaterialColorShader.frag +++ b/src/weird-renderer/shaders/2DMaterialColorShader.frag @@ -53,29 +53,12 @@ void main() vec3 background = mix(u_staticColors[3], u_staticColors[2], min(fract(0.1 * pos.x), fract(0.1 * pos.y)) > pixel * zoom ? 1.0 : 0.0); - // Accumulate colors of neighboring cells - vec3 blendedColor = vec3(0.0); - float totalWeight = 0.0; - - for (int x = -10; x <= 10; ++x) { - for (int y = -10; y <= 10; ++y) { - vec2 offset = vec2(float(x), float(y)) / u_resolution; - vec4 neighbor = texture(t_colorTexture, screenUV + offset); - float w = 1.0; - if (x == 0 && y == 0) { - w = 2.0; // Weight center more if desired - } - vec3 neighborColor = u_staticColors[int(neighbor.y)]; - blendedColor += neighborColor * w; - totalWeight += w; - } - } - - blendedColor /= totalWeight; + // Decide whether to use blended or background - c = false || distance <= 0.0 ? mix(c, blendedColor, mask) : background; + // c = distance <= 0.0 ? c : background; - FragColor = vec4(c, distance); + // FragColor = vec4(c, distance); + FragColor = vec4(c, mask); } From a3309b3bfdc879751b6d4d31ee05f1efad32b832 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= <49535803+damacaa@users.noreply.github.com> Date: Mon, 21 Jul 2025 12:28:21 +0200 Subject: [PATCH 04/17] Linear rgb blending --- .../ecs/Systems/PlayerMovementSystem.h | 4 +-- src/weird-renderer/Renderer.cpp | 2 +- .../shaders/2DLightingShader.frag | 19 +++++++++----- .../shaders/2DMaterialBlendShader.frag | 26 ++++++++++++------- .../shaders/2DSDFDistanceShader.frag | 6 ++--- 5 files changed, 34 insertions(+), 23 deletions(-) diff --git a/include/weird-engine/ecs/Systems/PlayerMovementSystem.h b/include/weird-engine/ecs/Systems/PlayerMovementSystem.h index 7004eef..33937c6 100644 --- a/include/weird-engine/ecs/Systems/PlayerMovementSystem.h +++ b/include/weird-engine/ecs/Systems/PlayerMovementSystem.h @@ -130,9 +130,9 @@ namespace WeirdEngine targetPosition += flyComponent.scrollSpeed * flyComponent.speed * vec3(travel.x / abs(t.position.z), travel.y / abs(t.position.z), 0); } - if (targetPosition.z < 20.0f) + if (targetPosition.z < 5.0f) { - targetPosition.z = 20.0f; + targetPosition.z = 5.0f; } if (targetPosition.y < 0.1f) diff --git a/src/weird-renderer/Renderer.cpp b/src/weird-renderer/Renderer.cpp index 213de31..088b599 100644 --- a/src/weird-renderer/Renderer.cpp +++ b/src/weird-renderer/Renderer.cpp @@ -297,7 +297,7 @@ namespace WeirdEngine - static int amount = 10; + static int amount = 5; for (unsigned int i = 0; i < amount; i++) { diff --git a/src/weird-renderer/shaders/2DLightingShader.frag b/src/weird-renderer/shaders/2DLightingShader.frag index 63b5773..19e10c2 100644 --- a/src/weird-renderer/shaders/2DLightingShader.frag +++ b/src/weird-renderer/shaders/2DLightingShader.frag @@ -127,6 +127,7 @@ float render(vec2 uv) float d = map(uv); float minD; vec2 offsetPosition = uv + (2.0 / u_resolution) * rd; + if (d <= 0.0) { @@ -147,13 +148,13 @@ float render(vec2 uv) // return (10.0 * minD)+0.5; -#ifdef SOFT_SHADOWS - return mix(0.85, 1.0, d / FAR); -#else - return d < FAR ? 0.85 : 1.0; -#endif + #ifdef SOFT_SHADOWS + return mix(0.85, 1.0, d / FAR); + #else + return d < FAR ? 0.85 : 1.0; + #endif -#else +#else // No shadows return 1.0; @@ -167,7 +168,11 @@ void main() vec4 data = texture(t_distanceTexture, screenUV); float distance = data.x; - color = false || distance <= 0.0 ? color : vec3(0.3); + vec3 backgroundColor = vec3(0.35); + + float aaWidth = 0.001; + float edge = smoothstep(0.0, aaWidth, distance); // aaWidth controls softness + color = mix(color, backgroundColor, edge); #ifdef DEBUG_SHOW_DISTANCE diff --git a/src/weird-renderer/shaders/2DMaterialBlendShader.frag b/src/weird-renderer/shaders/2DMaterialBlendShader.frag index 431adaa..95feaf0 100644 --- a/src/weird-renderer/shaders/2DMaterialBlendShader.frag +++ b/src/weird-renderer/shaders/2DMaterialBlendShader.frag @@ -68,29 +68,35 @@ vec3 hsl2rgb(vec3 hsl) { return vec3(r, g, b); } +vec3 toLinear(vec3 srgb) { + return pow(srgb, vec3(2.2)); // or use inverse gamma +} +vec3 toSRGB(vec3 linear) { + return pow(linear, vec3(1.0 / 2.2)); +} + // Taken form learnopengl.com and optimized it to reduce branching void main() { vec2 tex_offset = 1.0 / textureSize(t_colorTexture, 0); vec4 data = texture(t_colorTexture, v_texCoord); - float mask = data.x; + float mask = data.w; - vec3 baseHSL = rgb2hsl(data.rgb); - vec3 resultHSL = baseHSL * u_weight[0]; + vec3 result = toLinear(data.rgb) * u_weight[0]; // TODO: precompute toLinear before this shader for (int i = 1; i < 5; ++i) { - vec2 offset = u_horizontal ? vec2(tex_offset.x * i, 0.0) : vec2(0.0, tex_offset.y * i); + vec2 offset = u_horizontal ? vec2(tex_offset.x * i, 0.0) : vec2(0.0, tex_offset.y * i); - vec3 hsl1 = rgb2hsl(texture(t_colorTexture, v_texCoord + offset).rgb); - vec3 hsl2 = rgb2hsl(texture(t_colorTexture, v_texCoord - offset).rgb); + vec4 colRight = texture(t_colorTexture, v_texCoord + offset); + result += toLinear(colRight.rgb) * u_weight[i]; - resultHSL += hsl1 * u_weight[i]; - resultHSL += hsl2 * u_weight[i]; + vec4 colLeft = texture(t_colorTexture, v_texCoord - offset); + result += toLinear(colLeft.rgb) * u_weight[i]; } - vec3 finalRGB = hsl2rgb(resultHSL); - FragColor = vec4(mix(data.xyz, finalRGB, mask), data.a); + result = toSRGB(result); + FragColor = vec4(mix(data.xyz, result, mask), data.a); // FragColor = vec4(vec3(mask), data.w); } diff --git a/src/weird-renderer/shaders/2DSDFDistanceShader.frag b/src/weird-renderer/shaders/2DSDFDistanceShader.frag index fa8fea6..3e94468 100644 --- a/src/weird-renderer/shaders/2DSDFDistanceShader.frag +++ b/src/weird-renderer/shaders/2DSDFDistanceShader.frag @@ -20,7 +20,7 @@ uniform vec3 u_lightPos; uniform vec3 u_camPos; uniform float u_time; -uniform float u_k = 1.0; +uniform float u_k = 0.25; uniform sampler2D t_colorTexture; uniform int u_loadedObjects; @@ -149,7 +149,7 @@ vec3 getColor(vec2 p, vec2 uv) } float shapeDist = minDist; - minDist = 100000.0; + // minDist = 100000.0; float inv_k = 1.0 / u_k; @@ -162,7 +162,7 @@ vec3 getColor(vec2 p, vec2 uv) float objectDist = shape_circle(p - positionSizeMaterial.xy); // Inside ball mask is set to 0 - mask = objectDist <= 0 ? 0.0 : mask; + mask = objectDist <= 0 ? 1.5 : mask; finalMaterialId = objectDist <= minDist || objectDist < 0.0 ? materialId : finalMaterialId; From 3f2af68482fb0ce55084d7c389bb093ccfc6584a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= <49535803+damacaa@users.noreply.github.com> Date: Mon, 21 Jul 2025 18:53:13 +0200 Subject: [PATCH 05/17] Shape materials --- include/weird-engine/Scene.h | 2 +- .../weird-engine/ecs/Components/CustomShape.h | 1 + include/weird-renderer/Renderer.h | 3 ++ src/weird-engine/Scene.cpp | 31 +++++++++------ src/weird-renderer/Renderer.cpp | 27 +++++++++++-- src/weird-renderer/shaders/2DBackground.frag | 39 +++++++++++++++++++ .../shaders/2DLightingShader.frag | 12 +++++- .../shaders/2DMaterialColorShader.frag | 4 +- .../shaders/2DSDFDistanceShader.frag | 15 ++++--- 9 files changed, 106 insertions(+), 28 deletions(-) diff --git a/include/weird-engine/Scene.h b/include/weird-engine/Scene.h index 941b16b..0975044 100644 --- a/include/weird-engine/Scene.h +++ b/include/weird-engine/Scene.h @@ -64,7 +64,7 @@ namespace WeirdEngine std::vector> m_sdfs; - Entity addShape(ShapeId shapeId, float* variables, CombinationType combination = CombinationType::Addition, bool hasCollision = true, int group = 0); + Entity addShape(ShapeId shapeId, float* variables, uint16_t material, CombinationType combination = CombinationType::Addition, bool hasCollision = true, int group = 0); void lookAt(Entity entity); diff --git a/include/weird-engine/ecs/Components/CustomShape.h b/include/weird-engine/ecs/Components/CustomShape.h index 48f3d2e..8004579 100644 --- a/include/weird-engine/ecs/Components/CustomShape.h +++ b/include/weird-engine/ecs/Components/CustomShape.h @@ -25,6 +25,7 @@ namespace WeirdEngine bool m_screenSpace; bool m_hasCollision; uint16_t m_groupId; + uint16_t m_material; CustomShape() : m_distanceFieldId(0), m_isDirty(true), m_screenSpace(false) { diff --git a/include/weird-renderer/Renderer.h b/include/weird-renderer/Renderer.h index 9b5eb7b..54ab975 100644 --- a/include/weird-renderer/Renderer.h +++ b/include/weird-renderer/Renderer.h @@ -61,6 +61,7 @@ namespace WeirdEngine Shader m_2DDistanceShader; Shader m_2DMaterialColorShader; Shader m_2DMaterialBlendShader; + Shader m_2DGridShader; Shader m_2DLightingShader; Shader m_postProcessingShader; Shader m_3DsdfShaderProgram; @@ -73,6 +74,7 @@ namespace WeirdEngine RenderTarget m_2DSceneRender; RenderTarget m_2DColorRender; RenderTarget m_2DPostProcessRender; + RenderTarget m_2DBackgroundRender; RenderTarget m_combinationRender; RenderTarget m_outputResolutionRender; @@ -98,6 +100,7 @@ namespace WeirdEngine RenderTarget *m_postProcessDoubleBuffer[2]; Texture m_lit2DSceneTexture; + Texture m_2DBackgroundTexture; Texture m_combineResultTexture; diff --git a/src/weird-engine/Scene.cpp b/src/weird-engine/Scene.cpp index 942c0fb..e81e811 100644 --- a/src/weird-engine/Scene.cpp +++ b/src/weird-engine/Scene.cpp @@ -103,9 +103,9 @@ namespace WeirdEngine void Scene::updateCustomShapesShader(WeirdRenderer::Shader &shader) { - auto sdfBalls = m_ecs.getComponentManager()->getComponentArray(); + const auto sdfBalls = m_ecs.getComponentManager()->getComponentArray(); int32_t ballsCount = sdfBalls->getSize(); - auto componentArray = m_ecs.getComponentManager()->getComponentArray(); + const auto componentArray = m_ecs.getComponentManager()->getComponentArray(); shader.setUniform("u_customShapeCount", componentArray->getSize()); if (!m_sdfRenderSystem2D.shaderNeedsUpdate()) @@ -117,7 +117,7 @@ namespace WeirdEngine std::string str = shader.getFragmentCode(); - std::string toReplace("/*ADD_SHAPES_HERE*/"); + const std::string toReplace("/*ADD_SHAPES_HERE*/"); std::ostringstream oss; @@ -133,10 +133,10 @@ namespace WeirdEngine for (size_t i = 0; i < componentArray->getSize(); i++) { // Get shape - auto &shape = componentArray->getDataAtIdx(i); + const auto &shape = componentArray->getDataAtIdx(i); // Get group - int group = shape.m_groupId; + const int group = shape.m_groupId; // Start new group if necessary if(group != currentGroup) @@ -144,8 +144,7 @@ namespace WeirdEngine // If this is not the first group, combine current group distance with global minDistance if(currentGroup != -1) { - oss << "if(minDist >"<< groupDistanceVariable <<"){ minDist = "<< groupDistanceVariable <<";\n"; - oss << "col = getMaterial(p," << 3 << ");}\n"; + oss << "if(minDist >"<< groupDistanceVariable <<"){ minDist = "<< groupDistanceVariable <<";}\n"; } // Next group @@ -183,6 +182,7 @@ namespace WeirdEngine + // Apply globalEffect logic oss << "float currentMinDistance = " << (globalEffect ? "minDist" : groupDistanceVariable) << ";" << std::endl; @@ -192,6 +192,7 @@ namespace WeirdEngine case CombinationType::Addition: { oss << "currentMinDistance = min(currentMinDistance, dist);\n"; + oss << "finalMaterialId = dist <= min(minDist, currentMinDistance) ? " << shape.m_material << ": finalMaterialId;" << std::endl; break; } case CombinationType::Subtraction: @@ -207,6 +208,7 @@ namespace WeirdEngine case CombinationType::SmoothAddition: { oss << "currentMinDistance = fOpUnionSoft(currentMinDistance, dist, 1.0);\n"; + oss << "finalMaterialId = dist <= min(minDist, currentMinDistance) ? " << shape.m_material << ": finalMaterialId;" << std::endl; break; } default: @@ -221,14 +223,13 @@ namespace WeirdEngine // Combine last group if (componentArray->getSize() > 0) { - oss << "if(minDist >" << groupDistanceVariable << "){ minDist = " << groupDistanceVariable << ";\n"; - oss << "col = getMaterial(p," << 3 << ");}\n"; + oss << "if(minDist >" << groupDistanceVariable << "){ minDist = " << groupDistanceVariable << ";}\n"; } // Scale negative distances oss << "minDist = minDist > 0 ? minDist : 0.1 * minDist;" << std::endl; - oss << "col.x = minDist; col.y = 5;" << std::endl; + // oss << "col.x = minDist; col.y = 5;" << std::endl; // oss << "minDist -= 1.5;\n"; @@ -249,6 +250,13 @@ namespace WeirdEngine // Set new source code and recompile shader shader.setFragmentCode(str); + + // TODO: only debug + std::ofstream outFile("generated_shader.frag"); + if (outFile.is_open()) { + outFile << str; + outFile.close(); + } } void Scene::updateRayMarchingShader(WeirdRenderer::Shader &shader) @@ -318,7 +326,7 @@ namespace WeirdEngine return m_renderMode; } - Entity Scene::addShape(ShapeId shapeId, float* variables, CombinationType combination, bool hasCollision, int group) + Entity Scene::addShape(ShapeId shapeId, float* variables, uint16_t material, CombinationType combination, bool hasCollision, int group) { Entity entity = m_ecs.createEntity(); CustomShape &shape = m_ecs.addComponent(entity); @@ -326,6 +334,7 @@ namespace WeirdEngine shape.m_combination = combination; shape.m_hasCollision = hasCollision; shape.m_groupId = group; + shape.m_material = material; std::copy(variables, variables + 8, shape.m_parameters); // CustomShape shape(shapeId, variables); // check old constructor for references diff --git a/src/weird-renderer/Renderer.cpp b/src/weird-renderer/Renderer.cpp index 088b599..f4a6bba 100644 --- a/src/weird-renderer/Renderer.cpp +++ b/src/weird-renderer/Renderer.cpp @@ -104,8 +104,10 @@ namespace WeirdEngine m_2DMaterialBlendShader = Shader(SHADERS_PATH "renderPlane.vert", SHADERS_PATH "2DMaterialBlendShader.frag"); - m_2DLightingShader = Shader(SHADERS_PATH "renderPlane.vert", SHADERS_PATH "2DLigh" - "tingShader.frag"); + m_2DLightingShader = Shader(SHADERS_PATH "renderPlane.vert", SHADERS_PATH "2DLightingShader.frag"); + + m_2DGridShader = Shader(SHADERS_PATH "renderPlane.vert", SHADERS_PATH "2DBackground.frag"); + m_postProcessingShader = Shader(SHADERS_PATH "renderPlane.vert", SHADERS_PATH "PostProcessShader.frag"); m_combineScenesShaderProgram = Shader(SHADERS_PATH "renderPlane.vert", SHADERS_PATH "combineScenes.frag"); @@ -151,6 +153,10 @@ namespace WeirdEngine m_2DPostProcessRender = RenderTarget(false); m_2DPostProcessRender.bindColorTextureToFrameBuffer(m_lit2DSceneTexture); + m_2DBackgroundTexture = Texture(m_renderWidth, m_renderHeight, Texture::TextureType::Data); + m_2DBackgroundRender = RenderTarget(false); + m_2DBackgroundRender.bindColorTextureToFrameBuffer(m_2DBackgroundTexture); + m_combineResultTexture = Texture(m_renderWidth, m_renderHeight, Texture::TextureType::Data); m_combinationRender = RenderTarget(false); m_combinationRender.bindColorTextureToFrameBuffer(m_combineResultTexture); @@ -297,7 +303,7 @@ namespace WeirdEngine - static int amount = 5; + static int amount = 10; for (unsigned int i = 0; i < amount; i++) { @@ -319,6 +325,17 @@ namespace WeirdEngine } } + { + m_2DBackgroundRender.bind(); + + m_2DGridShader.use(); + m_2DGridShader.setUniform("u_camMatrix", sceneCamera.view); + m_2DGridShader.setUniform("u_time", scene.getTime()); + m_2DGridShader.setUniform("u_resolution", glm::vec2(m_renderWidth, m_renderHeight)); + + m_renderPlane.draw(m_2DLightingShader); + } + // 2D Lighting { m_2DPostProcessRender.bind(); @@ -337,6 +354,10 @@ namespace WeirdEngine glUniform1i(distanceTextureLocation, 1); m_distanceTexture.bind(1); + GLuint backgroundTextureLocation = glGetUniformLocation(m_2DLightingShader.ID, "t_backgroundTexture"); + glUniform1i(backgroundTextureLocation, 2); + m_2DBackgroundTexture.bind(2); + m_renderPlane.draw(m_2DLightingShader); m_postProcessTextureFront.unbind(); } diff --git a/src/weird-renderer/shaders/2DBackground.frag b/src/weird-renderer/shaders/2DBackground.frag index e69de29..fe8d418 100644 --- a/src/weird-renderer/shaders/2DBackground.frag +++ b/src/weird-renderer/shaders/2DBackground.frag @@ -0,0 +1,39 @@ +#version 330 core + +// Constants + +// Outputs u_staticColors in RGBA +layout(location = 0) out vec4 FragColor; + +// Inputs from vertex shader +in vec3 v_worldPos; +in vec3 v_normal; +in vec3 v_color; +in vec2 v_texCoord; + +uniform mat4 u_camMatrix; +uniform vec2 u_resolution; +uniform float u_time; + +void main() +{ + vec2 screenUV = gl_FragCoord.xy / u_resolution.xy; + + vec2 uv = (2.0 * v_texCoord) - 1.0; + float zoom = -u_camMatrix[3].z; + vec2 pos = (zoom * uv) - u_camMatrix[3].xy; + float aspectRatio = u_resolution.x / u_resolution.y; + vec2 zoomVec = vec2((zoom * aspectRatio) - 1.0, zoom); + + float pixel = 0.2 / u_resolution.y; + vec3 background = mix(vec3(0.55), vec3(0.7), + min(fract(0.1 * pos.x), fract(0.1 * pos.y)) > pixel * zoom ? 1.0 : 0.0); + + + + // Decide whether to use blended or background + // c = distance <= 0.0 ? c : background; + + // FragColor = vec4(c, distance); + FragColor = vec4(background, 1.0); +} \ No newline at end of file diff --git a/src/weird-renderer/shaders/2DLightingShader.frag b/src/weird-renderer/shaders/2DLightingShader.frag index 19e10c2..fd70c88 100644 --- a/src/weird-renderer/shaders/2DLightingShader.frag +++ b/src/weird-renderer/shaders/2DLightingShader.frag @@ -5,6 +5,7 @@ #define DITHERING // #define DEBUG_SHOW_DISTANCE +// #define DEBUG_SHOW_COLORS // Constants const int MAX_STEPS = 1000; @@ -164,14 +165,20 @@ float render(vec2 uv) void main() { vec2 screenUV = (gl_FragCoord.xy / u_resolution.xy); - vec3 color = texture(t_colorTexture, screenUV).rgb; + vec4 colorSample = texture(t_colorTexture, screenUV); + vec3 color = colorSample.rgb; // + (0.25 * floor(colorSample.a)); vec4 data = texture(t_distanceTexture, screenUV); float distance = data.x; - vec3 backgroundColor = vec3(0.35); + vec3 backgroundColor = texture(t_backgroundTexture, screenUV).rgb;// vec3(0.35); float aaWidth = 0.001; float edge = smoothstep(0.0, aaWidth, distance); // aaWidth controls softness + + #ifdef DEBUG_SHOW_COLORS + edge = 0.0; + #endif + color = mix(color, backgroundColor, edge); #ifdef DEBUG_SHOW_DISTANCE @@ -193,6 +200,7 @@ void main() return; #endif + float light = render(screenUV); vec3 col = light * color.xyz; diff --git a/src/weird-renderer/shaders/2DMaterialColorShader.frag b/src/weird-renderer/shaders/2DMaterialColorShader.frag index 706afe2..d7546fa 100644 --- a/src/weird-renderer/shaders/2DMaterialColorShader.frag +++ b/src/weird-renderer/shaders/2DMaterialColorShader.frag @@ -49,9 +49,7 @@ void main() float aspectRatio = u_resolution.x / u_resolution.y; vec2 zoomVec = vec2((zoom * aspectRatio) - 1.0, zoom); - float pixel = 0.2 / u_resolution.y; - vec3 background = mix(u_staticColors[3], u_staticColors[2], - min(fract(0.1 * pos.x), fract(0.1 * pos.y)) > pixel * zoom ? 1.0 : 0.0); + diff --git a/src/weird-renderer/shaders/2DSDFDistanceShader.frag b/src/weird-renderer/shaders/2DSDFDistanceShader.frag index 3e94468..9fe49e2 100644 --- a/src/weird-renderer/shaders/2DSDFDistanceShader.frag +++ b/src/weird-renderer/shaders/2DSDFDistanceShader.frag @@ -20,7 +20,7 @@ uniform vec3 u_lightPos; uniform vec3 u_camPos; uniform float u_time; -uniform float u_k = 0.25; +uniform float u_k = 1.25; uniform sampler2D t_colorTexture; uniform int u_loadedObjects; @@ -136,16 +136,14 @@ vec3 getMaterial(vec2 p, int materialId) vec3 getColor(vec2 p, vec2 uv) { float minDist = 100000.0; - int finalMaterialId = 3; + int finalMaterialId = 0; float mask = 1.0; - vec3 col; - /*ADD_SHAPES_HERE*/ if(minDist < EPSILON) { - return vec3(minDist, finalMaterialId, 0.0); + return vec3(minDist, finalMaterialId, mask); } float shapeDist = minDist; @@ -157,14 +155,13 @@ vec3 getColor(vec2 p, vec2 uv) { vec4 positionSizeMaterial = texelFetch(t_shapeBuffer, i); int materialId = int(positionSizeMaterial.w); - // vec4 extraParameters = texelFetch(t_shapeBuffer, (2 * i) + 1); float objectDist = shape_circle(p - positionSizeMaterial.xy); // Inside ball mask is set to 0 - mask = objectDist <= 0 ? 1.5 : mask; + mask = objectDist <= 0 ? 0.5 : mask; - finalMaterialId = objectDist <= minDist || objectDist < 0.0 ? materialId : finalMaterialId; + finalMaterialId = objectDist <= minDist ? materialId : finalMaterialId; #if BLEND_SHAPES @@ -176,6 +173,8 @@ vec3 getColor(vec2 p, vec2 uv) if(minDist < EPSILON) { + + break; } From a7f4caa3a20eafca5d2e2fd60a58b77d53ef5aa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= <49535803+damacaa@users.noreply.github.com> Date: Wed, 23 Jul 2025 12:10:51 +0200 Subject: [PATCH 06/17] Fixed artifacts --- src/weird-renderer/shaders/2DLightingShader.frag | 13 ++++--------- src/weird-renderer/shaders/2DSDFDistanceShader.frag | 10 +++++----- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/weird-renderer/shaders/2DLightingShader.frag b/src/weird-renderer/shaders/2DLightingShader.frag index fd70c88..f6740f4 100644 --- a/src/weird-renderer/shaders/2DLightingShader.frag +++ b/src/weird-renderer/shaders/2DLightingShader.frag @@ -127,7 +127,7 @@ float render(vec2 uv) float d = map(uv); float minD; - vec2 offsetPosition = uv + (2.0 / u_resolution) * rd; + vec2 offsetPosition = uv + (2.0 / u_resolution) * rd; // 2 pixels towards the light if (d <= 0.0) { @@ -172,7 +172,7 @@ void main() vec3 backgroundColor = texture(t_backgroundTexture, screenUV).rgb;// vec3(0.35); - float aaWidth = 0.001; + float aaWidth = 0.0; float edge = smoothstep(0.0, aaWidth, distance); // aaWidth controls softness #ifdef DEBUG_SHOW_COLORS @@ -183,19 +183,14 @@ void main() #ifdef DEBUG_SHOW_DISTANCE - if (abs(distance) < (0.5 / u_resolution.x)) - { - FragColor = vec4(vec3(0.0), 1.0); - } - else - { + float value = 0.5 * (cos(500.0 * distance) + 1.0); value = value * value * value; vec3 debugColor = distance > 0 ? mix(vec3(1), vec3(0.2), value) : // outside (distance + 1.0) * mix(vec3(1.0, 0.2, 0.2), vec3(0.1), value); // inside FragColor = vec4(debugColor, 1.0); - } + return; #endif diff --git a/src/weird-renderer/shaders/2DSDFDistanceShader.frag b/src/weird-renderer/shaders/2DSDFDistanceShader.frag index 9fe49e2..b9eaa90 100644 --- a/src/weird-renderer/shaders/2DSDFDistanceShader.frag +++ b/src/weird-renderer/shaders/2DSDFDistanceShader.frag @@ -20,7 +20,7 @@ uniform vec3 u_lightPos; uniform vec3 u_camPos; uniform float u_time; -uniform float u_k = 1.25; +uniform float u_k = 0.25; uniform sampler2D t_colorTexture; uniform int u_loadedObjects; @@ -141,13 +141,13 @@ vec3 getColor(vec2 p, vec2 uv) /*ADD_SHAPES_HERE*/ - if(minDist < EPSILON) + if(minDist <= 0.0) { - return vec3(minDist, finalMaterialId, mask); + return vec3(minDist, finalMaterialId, 0.0); } float shapeDist = minDist; - // minDist = 100000.0; + // minDist = 1.0; // Disable blending between balls and shapes float inv_k = 1.0 / u_k; @@ -159,7 +159,7 @@ vec3 getColor(vec2 p, vec2 uv) float objectDist = shape_circle(p - positionSizeMaterial.xy); // Inside ball mask is set to 0 - mask = objectDist <= 0 ? 0.5 : mask; + mask = objectDist <= 0 ? 0.25 : mask; finalMaterialId = objectDist <= minDist ? materialId : finalMaterialId; From a733bb44bf5dd930a3e02e1d1b5b27d36fb5de7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= <49535803+damacaa@users.noreply.github.com> Date: Wed, 23 Jul 2025 12:11:04 +0200 Subject: [PATCH 07/17] Making it look good --- include/weird-renderer/Renderer.h | 4 +- src/weird-renderer/Renderer.cpp | 8 +-- src/weird-renderer/shaders/2DBackground.frag | 2 +- .../shaders/2DMaterialBlendShader.frag | 60 +------------------ 4 files changed, 7 insertions(+), 67 deletions(-) diff --git a/include/weird-renderer/Renderer.h b/include/weird-renderer/Renderer.h index 54ab975..5ad18ad 100644 --- a/include/weird-renderer/Renderer.h +++ b/include/weird-renderer/Renderer.h @@ -107,6 +107,8 @@ namespace WeirdEngine WeirdRenderer::Dot2D* m_2DData = nullptr; uint32_t m_2DDataSize = 0; + uint32_t m_materialBlendIterations; + void output(Scene& scene, Texture& texture); @@ -115,7 +117,7 @@ namespace WeirdEngine vec3(1.0f, 1.0f, 1.0f), // White vec3(0.484f, 0.484f, 0.584f), // Dark Gray vec3(0.752f, 0.762f, 0.74f), // Light Gray - vec3(.95f, 0.1f, 0.1f), // Red + vec3(.8f, 0.1f, 0.1f), // Red vec3(0.1f, .95f, 0.1f), // Green vec3(0.15f, 0.25f, .85f), // Blue vec3(1.0f, .9f, 0.2f), // Yellow diff --git a/src/weird-renderer/Renderer.cpp b/src/weird-renderer/Renderer.cpp index f4a6bba..e2e2b39 100644 --- a/src/weird-renderer/Renderer.cpp +++ b/src/weird-renderer/Renderer.cpp @@ -85,6 +85,7 @@ namespace WeirdEngine , m_renderWidth(width * m_renderScale) , m_renderHeight(height * m_renderScale) , m_vSyncEnabled(true) + , m_materialBlendIterations(10) { Screen::width = m_windowWidth; Screen::height = m_windowHeight; @@ -300,12 +301,7 @@ namespace WeirdEngine m_2DMaterialBlendShader.use(); m_2DMaterialBlendShader.setUniform("t_colorTexture", 0); - - - - static int amount = 10; - - for (unsigned int i = 0; i < amount; i++) + for (unsigned int i = 0; i < m_materialBlendIterations; i++) { m_postProcessDoubleBuffer[horizontal]->bind(); diff --git a/src/weird-renderer/shaders/2DBackground.frag b/src/weird-renderer/shaders/2DBackground.frag index fe8d418..1b82a59 100644 --- a/src/weird-renderer/shaders/2DBackground.frag +++ b/src/weird-renderer/shaders/2DBackground.frag @@ -20,7 +20,7 @@ void main() vec2 screenUV = gl_FragCoord.xy / u_resolution.xy; vec2 uv = (2.0 * v_texCoord) - 1.0; - float zoom = -u_camMatrix[3].z; + float zoom = -2.0 * u_camMatrix[3].z; vec2 pos = (zoom * uv) - u_camMatrix[3].xy; float aspectRatio = u_resolution.x / u_resolution.y; vec2 zoomVec = vec2((zoom * aspectRatio) - 1.0, zoom); diff --git a/src/weird-renderer/shaders/2DMaterialBlendShader.frag b/src/weird-renderer/shaders/2DMaterialBlendShader.frag index 95feaf0..71d4865 100644 --- a/src/weird-renderer/shaders/2DMaterialBlendShader.frag +++ b/src/weird-renderer/shaders/2DMaterialBlendShader.frag @@ -10,63 +10,7 @@ uniform sampler2D t_colorTexture; uniform bool u_horizontal; uniform float u_weight[5] = float[] (0.227027, 0.1945946, 0.1216216, 0.054054, 0.016216); -vec3 rgb2hsl(vec3 c) { - float r = c.r, g = c.g, b = c.b; - float maxC = max(r, max(g, b)); - float minC = min(r, min(g, b)); - float delta = maxC - minC; - - float h = 0.0; - float s = 0.0; - float l = (maxC + minC) * 0.5; - - if (delta > 0.00001) { - s = delta / (1.0 - abs(2.0 * l - 1.0)); - - if (maxC == r) { - h = mod((g - b) / delta, 6.0); - } else if (maxC == g) { - h = (b - r) / delta + 2.0; - } else { - h = (r - g) / delta + 4.0; - } - - h /= 6.0; - if (h < 0.0) h += 1.0; - } - - return vec3(h, s, l); // H ∈ [0,1], S ∈ [0,1], L ∈ [0,1] -} - -float hue2rgb(float p, float q, float t) { - if (t < 0.0) t += 1.0; - if (t > 1.0) t -= 1.0; - if (t < 1.0/6.0) return p + (q - p) * 6.0 * t; - if (t < 1.0/2.0) return q; - if (t < 2.0/3.0) return p + (q - p) * (2.0/3.0 - t) * 6.0; - return p; -} - -vec3 hsl2rgb(vec3 hsl) { - float h = hsl.x; - float s = hsl.y; - float l = hsl.z; - - float r, g, b; - - if (s == 0.0) { - r = g = b = l; // achromatic - } else { - float q = l < 0.5 ? l * (1.0 + s) : l + s - l * s; - float p = 2.0 * l - q; - r = hue2rgb(p, q, h + 1.0/3.0); - g = hue2rgb(p, q, h); - b = hue2rgb(p, q, h - 1.0/3.0); - } - - return vec3(r, g, b); -} vec3 toLinear(vec3 srgb) { return pow(srgb, vec3(2.2)); // or use inverse gamma @@ -75,11 +19,9 @@ vec3 toSRGB(vec3 linear) { return pow(linear, vec3(1.0 / 2.2)); } - -// Taken form learnopengl.com and optimized it to reduce branching void main() { - vec2 tex_offset = 1.0 / textureSize(t_colorTexture, 0); + vec2 tex_offset = 2.0 / textureSize(t_colorTexture, 0); vec4 data = texture(t_colorTexture, v_texCoord); float mask = data.w; From cc2807920e45383f9eef3d06fc530fa5e8620c67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= <49535803+damacaa@users.noreply.github.com> Date: Wed, 23 Jul 2025 18:38:09 +0200 Subject: [PATCH 08/17] Bad fix for non 1:1 aspect ratios --- src/weird-renderer/shaders/2DBackground.frag | 16 +++++++--------- src/weird-renderer/shaders/2DLightingShader.frag | 2 +- .../shaders/2DSDFDistanceShader.frag | 12 ++++++++---- src/weird-renderer/shaders/backgroundGrid.frag | 4 ++++ 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/weird-renderer/shaders/2DBackground.frag b/src/weird-renderer/shaders/2DBackground.frag index 1b82a59..4cbfa9c 100644 --- a/src/weird-renderer/shaders/2DBackground.frag +++ b/src/weird-renderer/shaders/2DBackground.frag @@ -20,20 +20,18 @@ void main() vec2 screenUV = gl_FragCoord.xy / u_resolution.xy; vec2 uv = (2.0 * v_texCoord) - 1.0; - float zoom = -2.0 * u_camMatrix[3].z; + uv.x *= (u_resolution.x / u_resolution.y); + + float zoom = -1.5 * u_camMatrix[3].z; vec2 pos = (zoom * uv) - u_camMatrix[3].xy; float aspectRatio = u_resolution.x / u_resolution.y; vec2 zoomVec = vec2((zoom * aspectRatio) - 1.0, zoom); - float pixel = 0.2 / u_resolution.y; + // vec2 pixel = zoom / u_resolution; + // pixel.x *= aspectRatio; + vec3 background = mix(vec3(0.55), vec3(0.7), - min(fract(0.1 * pos.x), fract(0.1 * pos.y)) > pixel * zoom ? 1.0 : 0.0); + (fract(0.1 * pos.x) > 0.01 && fract(0.1 * pos.y) > 0.01) ? 1.0 : 0.0); - - - // Decide whether to use blended or background - // c = distance <= 0.0 ? c : background; - - // FragColor = vec4(c, distance); FragColor = vec4(background, 1.0); } \ No newline at end of file diff --git a/src/weird-renderer/shaders/2DLightingShader.frag b/src/weird-renderer/shaders/2DLightingShader.frag index f6740f4..62cd53c 100644 --- a/src/weird-renderer/shaders/2DLightingShader.frag +++ b/src/weird-renderer/shaders/2DLightingShader.frag @@ -164,7 +164,7 @@ float render(vec2 uv) void main() { - vec2 screenUV = (gl_FragCoord.xy / u_resolution.xy); + vec2 screenUV = v_texCoord; vec4 colorSample = texture(t_colorTexture, screenUV); vec3 color = colorSample.rgb; // + (0.25 * floor(colorSample.a)); vec4 data = texture(t_distanceTexture, screenUV); diff --git a/src/weird-renderer/shaders/2DSDFDistanceShader.frag b/src/weird-renderer/shaders/2DSDFDistanceShader.frag index b9eaa90..9a1f49f 100644 --- a/src/weird-renderer/shaders/2DSDFDistanceShader.frag +++ b/src/weird-renderer/shaders/2DSDFDistanceShader.frag @@ -1,7 +1,7 @@ #version 330 core #define BLEND_SHAPES 1 -#define MOTION_BLUR 1 +// #define MOTION_BLUR 1 out vec4 FragColor; @@ -193,14 +193,18 @@ void main() // return; vec2 uv = (2.0f * v_texCoord) - 1.0f; + float aspectRatio = u_resolution.x / u_resolution.y; // TODO: uniform + uv.x *= aspectRatio; float zoom = -u_camMatrix[3].z; vec2 pos = (zoom * uv) - u_camMatrix[3].xy; + vec3 result = getColor(pos, v_texCoord); // Same as uv but (0, 0) is bottom left corner float distance = result.x; - float finalDistance = 0.6667 * 0.5 * distance / zoom; + float finalDistance = 0.5 * distance / zoom; + finalDistance *= 0.5 / aspectRatio; @@ -210,13 +214,13 @@ void main() vec4 previousColor = texture(t_colorTexture, screenUV.xy); float previousDistance = previousColor.x; - int previousMaterial = int(previousColor.y); + int previousMaterial = int(previousColor.y); previousDistance += u_blendIterations * 0.00035; previousDistance = mix(finalDistance, previousDistance, 0.95); // previousDistance = min(previousDistance + (u_blendIterations * 0.00035), mix(finalDistance, previousDistance, 0.9)); - finalDistance = min(previousDistance, finalDistance); + finalDistance = min(previousDistance, finalDistance); diff --git a/src/weird-renderer/shaders/backgroundGrid.frag b/src/weird-renderer/shaders/backgroundGrid.frag index b329821..e5809d3 100644 --- a/src/weird-renderer/shaders/backgroundGrid.frag +++ b/src/weird-renderer/shaders/backgroundGrid.frag @@ -7,6 +7,7 @@ in vec2 v_texCoord; uniform mat4 u_camMatrix; uniform float u_fov; +uniform vec2 u_resolution; uniform float gridScale = 1.0; // Size of each grid cell in world units uniform vec3 gridColor = vec3(1); // Color of the grid lines @@ -41,6 +42,9 @@ void main() { // Bring 0,0 to the center of the screen vec2 uv = (2.0 * v_texCoord) - 1.0; + float aspectRatio = u_resolution.x / u_resolution.y; + uv.x *= 2.0 * aspectRatio; + // Calculate the direction of a ray that goes from origin towards the frag coord // This formula is often used in ray marching, I don't know where I got it for the first // time at this point From fb3ec6e355ae3990decae616fe957b6ac70a4049 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= <49535803+damacaa@users.noreply.github.com> Date: Thu, 24 Jul 2025 18:58:52 +0200 Subject: [PATCH 09/17] Fixed aspect ratio issues --- src/weird-renderer/shaders/2DBackground.frag | 8 ++++---- src/weird-renderer/shaders/2DMaterialBlendShader.frag | 2 +- src/weird-renderer/shaders/2DSDFDistanceShader.frag | 2 +- src/weird-renderer/shaders/raymarching.frag | 6 ++---- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/weird-renderer/shaders/2DBackground.frag b/src/weird-renderer/shaders/2DBackground.frag index 4cbfa9c..993535c 100644 --- a/src/weird-renderer/shaders/2DBackground.frag +++ b/src/weird-renderer/shaders/2DBackground.frag @@ -27,11 +27,11 @@ void main() float aspectRatio = u_resolution.x / u_resolution.y; vec2 zoomVec = vec2((zoom * aspectRatio) - 1.0, zoom); - // vec2 pixel = zoom / u_resolution; - // pixel.x *= aspectRatio; - + vec2 pixel = 0.2 * zoom / u_resolution; + pixel.x *= aspectRatio; + vec3 background = mix(vec3(0.55), vec3(0.7), - (fract(0.1 * pos.x) > 0.01 && fract(0.1 * pos.y) > 0.01) ? 1.0 : 0.0); + (fract(0.1 * pos.x) > pixel.x && fract(0.1 * pos.y) > pixel.y) ? 1.0 : 0.0); FragColor = vec4(background, 1.0); } \ No newline at end of file diff --git a/src/weird-renderer/shaders/2DMaterialBlendShader.frag b/src/weird-renderer/shaders/2DMaterialBlendShader.frag index 71d4865..ca86234 100644 --- a/src/weird-renderer/shaders/2DMaterialBlendShader.frag +++ b/src/weird-renderer/shaders/2DMaterialBlendShader.frag @@ -21,7 +21,7 @@ vec3 toSRGB(vec3 linear) { void main() { - vec2 tex_offset = 2.0 / textureSize(t_colorTexture, 0); + vec2 tex_offset = 1.0 / textureSize(t_colorTexture, 0); vec4 data = texture(t_colorTexture, v_texCoord); float mask = data.w; diff --git a/src/weird-renderer/shaders/2DSDFDistanceShader.frag b/src/weird-renderer/shaders/2DSDFDistanceShader.frag index 9a1f49f..d235a93 100644 --- a/src/weird-renderer/shaders/2DSDFDistanceShader.frag +++ b/src/weird-renderer/shaders/2DSDFDistanceShader.frag @@ -1,7 +1,7 @@ #version 330 core #define BLEND_SHAPES 1 -// #define MOTION_BLUR 1 +#define MOTION_BLUR 1 out vec4 FragColor; diff --git a/src/weird-renderer/shaders/raymarching.frag b/src/weird-renderer/shaders/raymarching.frag index 7761112..215c3f5 100644 --- a/src/weird-renderer/shaders/raymarching.frag +++ b/src/weird-renderer/shaders/raymarching.frag @@ -397,13 +397,11 @@ float rand(vec2 co){ void main() { - - float aspectRatio = u_resolution.x / u_resolution.y; - vec2 pixelScale = vec2(aspectRatio * 0.2, 0.2); - vec2 screenUV = v_texCoord; vec2 uv = (2.0f * v_texCoord) - 1.0f; + float aspectRatio = u_resolution.x / u_resolution.y; // TODO: uniform + uv.x *= aspectRatio; // Calculate true z value from the depth buffer: https://stackoverflow.com/questions/6652253/getting-the-true-z-value-from-the-depth-buffer float depth = texture(t_depthTexture, screenUV).r; From c4889c03cfd97ae0f55d6e32d815f322cbbe1acd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= <49535803+damacaa@users.noreply.github.com> Date: Thu, 24 Jul 2025 18:59:14 +0200 Subject: [PATCH 10/17] Imrproved quality control --- include/weird-renderer/Renderer.h | 2 ++ src/weird-renderer/Renderer.cpp | 15 ++++++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/include/weird-renderer/Renderer.h b/include/weird-renderer/Renderer.h index 5ad18ad..c0cc5ec 100644 --- a/include/weird-renderer/Renderer.h +++ b/include/weird-renderer/Renderer.h @@ -51,7 +51,9 @@ namespace WeirdEngine SDL_Window* m_window; unsigned int m_windowWidth, m_windowHeight; + float m_distanceSampleScale; float m_renderScale; + unsigned int m_distanceSampleWidth, m_distanceSampleHeight; unsigned int m_renderWidth, m_renderHeight; bool m_vSyncEnabled; diff --git a/src/weird-renderer/Renderer.cpp b/src/weird-renderer/Renderer.cpp index e2e2b39..23b0679 100644 --- a/src/weird-renderer/Renderer.cpp +++ b/src/weird-renderer/Renderer.cpp @@ -81,6 +81,9 @@ namespace WeirdEngine : m_initializer(width, height, m_window) , m_windowWidth(width) , m_windowHeight(height) + , m_distanceSampleScale(1.0f) + , m_distanceSampleWidth(width * m_distanceSampleScale) + , m_distanceSampleHeight(height * m_distanceSampleScale) , m_renderScale(1.0f) , m_renderWidth(width * m_renderScale) , m_renderHeight(height * m_renderScale) @@ -131,7 +134,7 @@ namespace WeirdEngine m_3DSceneRender.bindDepthTextureToFrameBuffer(m_3DDepthSceneTexture); - m_distanceTexture = Texture(m_renderWidth, m_renderHeight, Texture::TextureType::Data); + m_distanceTexture = Texture(m_distanceSampleWidth, m_distanceSampleHeight, Texture::TextureType::Data); m_2DSceneRender = RenderTarget(false); m_2DSceneRender.bindColorTextureToFrameBuffer(m_distanceTexture); @@ -150,7 +153,7 @@ namespace WeirdEngine m_postProcessDoubleBuffer[0] = &m_postProcessRenderFront; m_postProcessDoubleBuffer[1] = &m_postProcessRenderBack; - m_lit2DSceneTexture = Texture(m_renderWidth, m_renderHeight, Texture::TextureType::Data); + m_lit2DSceneTexture = Texture(m_renderWidth, m_renderHeight, m_renderScale <= 0.5f ? Texture::TextureType::RetroColor : Texture::TextureType::Data); m_2DPostProcessRender = RenderTarget(false); m_2DPostProcessRender.bindColorTextureToFrameBuffer(m_lit2DSceneTexture); @@ -238,6 +241,8 @@ namespace WeirdEngine if (enable2D) { { + glViewport(0, 0, m_distanceSampleWidth, m_distanceSampleHeight); + // Bind the framebuffer you want to render to m_2DSceneRender.bind(); @@ -249,7 +254,7 @@ namespace WeirdEngine // Set uniforms m_2DDistanceShader.setUniform("u_camMatrix", sceneCamera.view); m_2DDistanceShader.setUniform("u_time", scene.getTime()); - m_2DDistanceShader.setUniform("u_resolution", glm::vec2(m_renderWidth, m_renderHeight)); + m_2DDistanceShader.setUniform("u_resolution", glm::vec2( m_distanceSampleWidth, m_distanceSampleHeight)); m_2DDistanceShader.setUniform("u_blendIterations", 1); @@ -270,6 +275,8 @@ namespace WeirdEngine m_shapes2D.unbind(); } + glViewport(0, 0, m_renderWidth, m_renderHeight); + { // Bind the framebuffer you want to render to m_2DColorRender.bind(); @@ -334,6 +341,8 @@ namespace WeirdEngine // 2D Lighting { + // glViewport(0, 0, m_windowWidth, m_windowHeight); + m_2DPostProcessRender.bind(); m_2DLightingShader.use(); From 5f197fba52ea643b840ae84813b89e0dcac18667 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= <49535803+damacaa@users.noreply.github.com> Date: Thu, 24 Jul 2025 19:43:54 +0200 Subject: [PATCH 11/17] Correct formatting --- src/weird-renderer/shaders/2DBackground.frag | 2 +- .../shaders/2DLightingShader.frag | 160 +++++++++--------- .../shaders/2DMaterialBlendShader.frag | 4 +- .../shaders/2DMaterialColorShader.frag | 3 - .../shaders/2DSDFDistanceShader.frag | 97 ++++++----- 5 files changed, 131 insertions(+), 135 deletions(-) diff --git a/src/weird-renderer/shaders/2DBackground.frag b/src/weird-renderer/shaders/2DBackground.frag index 993535c..2238d10 100644 --- a/src/weird-renderer/shaders/2DBackground.frag +++ b/src/weird-renderer/shaders/2DBackground.frag @@ -21,7 +21,7 @@ void main() vec2 uv = (2.0 * v_texCoord) - 1.0; uv.x *= (u_resolution.x / u_resolution.y); - + float zoom = -1.5 * u_camMatrix[3].z; vec2 pos = (zoom * uv) - u_camMatrix[3].xy; float aspectRatio = u_resolution.x / u_resolution.y; diff --git a/src/weird-renderer/shaders/2DLightingShader.frag b/src/weird-renderer/shaders/2DLightingShader.frag index 62cd53c..323cd62 100644 --- a/src/weird-renderer/shaders/2DLightingShader.frag +++ b/src/weird-renderer/shaders/2DLightingShader.frag @@ -60,157 +60,157 @@ uniform int u_bayer8[8 * 8] = int[8 * 8]( float Getu_bayer2(int x, int y) { - return float(u_bayer2[(x % 2) + (y % 2) * 2]) * (1.0f / 4.0f) - 0.5f; + return float(u_bayer2[(x % 2) + (y % 2) * 2]) * (1.0f / 4.0f) - 0.5f; } float Getu_bayer4(int x, int y) { - return float(u_bayer4[(x % 4) + (y % 4) * 4]) * (1.0f / 16.0f) - 0.5f; + return float(u_bayer4[(x % 4) + (y % 4) * 4]) * (1.0f / 16.0f) - 0.5f; } float Getu_bayer8(int x, int y) { - return float(u_bayer8[(x % 8) + (y % 8) * 8]) * (1.0f / 64.0f) - 0.5f; + return float(u_bayer8[(x % 8) + (y % 8) * 8]) * (1.0f / 64.0f) - 0.5f; } #endif float map(vec2 p) { - return texture(t_distanceTexture, p).x; + return texture(t_distanceTexture, p).x; } float rayMarch(vec2 ro, vec2 rd, out float minDistance) { - float d; - minDistance = 10000.0; + float d; + minDistance = 10000.0; - float traveled = 0.0; + float traveled = 0.0; - for (int i = 0; i < MAX_STEPS; i++) - { - vec2 p = ro + (traveled * rd); + for (int i = 0; i < MAX_STEPS; i++) + { + vec2 p = ro + (traveled * rd); - d = map(p); + d = map(p); - minDistance = min(d, minDistance); + minDistance = min(d, minDistance); - if (d <= EPSILON) - break; + if (d <= EPSILON) + break; - if (p.x <= 0.0 || p.x >= 1.0 || p.y <= 0.0 || p.y >= 1.0) - return FAR; + if (p.x <= 0.0 || p.x >= 1.0 || p.y <= 0.0 || p.y >= 1.0) + return FAR; - // traveled += 0.01; - traveled += d; - // traveled += min(d, 0.01); + // traveled += 0.01; + traveled += d; + // traveled += min(d, 0.01); - if (traveled >= FAR) - { - return FAR; + if (traveled >= FAR) + { + return FAR; + } } - } - return traveled; + return traveled; } float render(vec2 uv) { -#ifdef SHADOWS_ENABLED + #ifdef SHADOWS_ENABLED - // Point light - vec2 rd = normalize(vec2(1.0) - uv); + // Point light + vec2 rd = normalize(vec2(1.0) - uv); - // Directional light - // vec2 rd = u_directionalLightDirection.xy; + // Directional light + // vec2 rd = u_directionalLightDirection.xy; - float d = map(uv); - float minD; - vec2 offsetPosition = uv + (2.0 / u_resolution) * rd; // 2 pixels towards the light + float d = map(uv); + float minD; + vec2 offsetPosition = uv + (2.0 / u_resolution) * rd;// 2 pixels towards the light - if (d <= 0.0) - { + if (d <= 0.0) + { - // float dd = -max(-0.05, d) - 0.01; - // dd = max(0.0, dd); - float distanceFallof = 1.25; - float maxDistance = 0.05; + // float dd = -max(-0.05, d) - 0.01; + // dd = max(0.0, dd); + float distanceFallof = 1.25; + float maxDistance = 0.05; - float dd = mix(0.0, 1.0, -(distanceFallof * d)); // - 0.005; - dd = min(maxDistance, dd); + float dd = mix(0.0, 1.0, -(distanceFallof * d));// - 0.005; + dd = min(maxDistance, dd); - // Get distance at offset position - d = rayMarch(offsetPosition, rd, minD); - return d < FAR ? 1.0 + (-1.5f * dd) : 2.0; // * dot(n, rd); - } + // Get distance at offset position + d = rayMarch(offsetPosition, rd, minD); + return d < FAR ? 1.0 + (-1.5f * dd) : 2.0;// * dot(n, rd); + } - d = rayMarch(uv, rd, minD); + d = rayMarch(uv, rd, minD); - // return (10.0 * minD)+0.5; + // return (10.0 * minD)+0.5; - #ifdef SOFT_SHADOWS + #ifdef SOFT_SHADOWS return mix(0.85, 1.0, d / FAR); - #else + #else return d < FAR ? 0.85 : 1.0; - #endif + #endif -#else // No shadows + #else// No shadows - return 1.0; + return 1.0; -#endif + #endif } void main() { - vec2 screenUV = v_texCoord; - vec4 colorSample = texture(t_colorTexture, screenUV); - vec3 color = colorSample.rgb; // + (0.25 * floor(colorSample.a)); - vec4 data = texture(t_distanceTexture, screenUV); - float distance = data.x; + vec2 screenUV = v_texCoord; + vec4 colorSample = texture(t_colorTexture, screenUV); + vec3 color = colorSample.rgb;// + (0.25 * floor(colorSample.a)); + vec4 data = texture(t_distanceTexture, screenUV); + float distance = data.x; - vec3 backgroundColor = texture(t_backgroundTexture, screenUV).rgb;// vec3(0.35); + vec3 backgroundColor = texture(t_backgroundTexture, screenUV).rgb;// vec3(0.35); - float aaWidth = 0.0; - float edge = smoothstep(0.0, aaWidth, distance); // aaWidth controls softness + float aaWidth = 0.0; + float edge = smoothstep(0.0, aaWidth, distance);// aaWidth controls softness - #ifdef DEBUG_SHOW_COLORS - edge = 0.0; - #endif + #ifdef DEBUG_SHOW_COLORS + edge = 0.0; + #endif - color = mix(color, backgroundColor, edge); + color = mix(color, backgroundColor, edge); -#ifdef DEBUG_SHOW_DISTANCE + #ifdef DEBUG_SHOW_DISTANCE float value = 0.5 * (cos(500.0 * distance) + 1.0); value = value * value * value; - vec3 debugColor = distance > 0 ? mix(vec3(1), vec3(0.2), value) : // outside - (distance + 1.0) * mix(vec3(1.0, 0.2, 0.2), vec3(0.1), value); // inside + vec3 debugColor = distance > 0 ? mix(vec3(1), vec3(0.2), value) :// outside + (distance + 1.0) * mix(vec3(1.0, 0.2, 0.2), vec3(0.1), value);// inside FragColor = vec4(debugColor, 1.0); - return; -#endif + return; + #endif - float light = render(screenUV); - vec3 col = light * color.xyz; + float light = render(screenUV); + vec3 col = light * color.xyz; - #ifdef DITHERING + #ifdef DITHERING - int x = int(gl_FragCoord.x); - int y = int(gl_FragCoord.y); - col = col + u_spread * Getu_bayer4(x, y); + int x = int(gl_FragCoord.x); + int y = int(gl_FragCoord.y); + col = col + u_spread * Getu_bayer4(x, y); - col.r = floor((u_colorCount - 1.0f) * col.r + 0.5) / (u_colorCount - 1.0f); - col.g = floor((u_colorCount - 1.0f) * col.g + 0.5) / (u_colorCount - 1.0f); - col.b = floor((u_colorCount - 1.0f) * col.b + 0.5) / (u_colorCount - 1.0f); + col.r = floor((u_colorCount - 1.0f) * col.r + 0.5) / (u_colorCount - 1.0f); + col.g = floor((u_colorCount - 1.0f) * col.g + 0.5) / (u_colorCount - 1.0f); + col.b = floor((u_colorCount - 1.0f) * col.b + 0.5) / (u_colorCount - 1.0f); - #endif + #endif - FragColor = vec4(col.xyz, 1.0); + FragColor = vec4(col.xyz, 1.0); } diff --git a/src/weird-renderer/shaders/2DMaterialBlendShader.frag b/src/weird-renderer/shaders/2DMaterialBlendShader.frag index ca86234..1bbde72 100644 --- a/src/weird-renderer/shaders/2DMaterialBlendShader.frag +++ b/src/weird-renderer/shaders/2DMaterialBlendShader.frag @@ -13,7 +13,7 @@ uniform float u_weight[5] = float[] (0.227027, 0.1945946, 0.1216216, 0.054054, 0 vec3 toLinear(vec3 srgb) { - return pow(srgb, vec3(2.2)); // or use inverse gamma + return pow(srgb, vec3(2.2));// or use inverse gamma } vec3 toSRGB(vec3 linear) { return pow(linear, vec3(1.0 / 2.2)); @@ -25,7 +25,7 @@ void main() vec4 data = texture(t_colorTexture, v_texCoord); float mask = data.w; - vec3 result = toLinear(data.rgb) * u_weight[0]; // TODO: precompute toLinear before this shader + vec3 result = toLinear(data.rgb) * u_weight[0];// TODO: precompute toLinear before this shader for (int i = 1; i < 5; ++i) { diff --git a/src/weird-renderer/shaders/2DMaterialColorShader.frag b/src/weird-renderer/shaders/2DMaterialColorShader.frag index d7546fa..5856fae 100644 --- a/src/weird-renderer/shaders/2DMaterialColorShader.frag +++ b/src/weird-renderer/shaders/2DMaterialColorShader.frag @@ -50,9 +50,6 @@ void main() vec2 zoomVec = vec2((zoom * aspectRatio) - 1.0, zoom); - - - // Decide whether to use blended or background // c = distance <= 0.0 ? c : background; diff --git a/src/weird-renderer/shaders/2DSDFDistanceShader.frag b/src/weird-renderer/shaders/2DSDFDistanceShader.frag index d235a93..9e21f40 100644 --- a/src/weird-renderer/shaders/2DSDFDistanceShader.frag +++ b/src/weird-renderer/shaders/2DSDFDistanceShader.frag @@ -63,74 +63,74 @@ const float FAR = 100.0f; // Operations float fOpUnionSoft(float a, float b, float r) { - float e = max(r - abs(a - b), 0); - return min(a, b) - e * e * 0.25 / r; + float e = max(r - abs(a - b), 0); + return min(a, b) - e * e * 0.25 / r; } float fOpUnionSoft(float a, float b, float r, float invR) { - float e = max(r - abs(a - b), 0); - return min(a, b) - e * e * 0.25 * invR; + float e = max(r - abs(a - b), 0); + return min(a, b) - e * e * 0.25 * invR; } float smin(float a, float b, float u_k) { - float h = clamp(0.5 + 0.5 * (b - a) / u_k, 0.0, 1.0); - return mix(b, a, h) - u_k * h * (1.0 - h); + float h = clamp(0.5 + 0.5 * (b - a) / u_k, 0.0, 1.0); + return mix(b, a, h) - u_k * h * (1.0 - h); } float shape_circle(vec2 p) { - return length(p) - 0.5; + return length(p) - 0.5; } // y = sin(5x + t) / 5 // 0 = sin(5x + t) / 5 - y float shape_sine(vec2 p) { - return p.y - sin(p.x * 5.0 + u_time) * 0.2; + return p.y - sin(p.x * 5.0 + u_time) * 0.2; } float shape_box2d(vec2 p, vec2 b) { - vec2 d = abs(p) - b; - return min(max(d.x, d.y), 0.0) + length(max(d, 0.0)); + vec2 d = abs(p) - b; + return min(max(d.x, d.y), 0.0) + length(max(d, 0.0)); } float shape_line(vec2 p, vec2 a, vec2 b) { - vec2 dir = b - a; - return abs(dot(normalize(vec2(dir.y, -dir.x)), a - p)); + vec2 dir = b - a; + return abs(dot(normalize(vec2(dir.y, -dir.x)), a - p)); } float shape_segment(vec2 p, vec2 a, vec2 b) { - float d = shape_line(p, a, b); - float d0 = dot(p - b, b - a); - float d1 = dot(p - a, b - a); - return d1 < 0.0 ? length(a - p) : d0 > 0.0 ? length(b - p) - : d; + float d = shape_line(p, a, b); + float d0 = dot(p - b, b - a); + float d1 = dot(p - a, b - a); + return d1 < 0.0 ? length(a - p) : d0 > 0.0 ? length(b - p) + : d; } float shape_circles_smin(vec2 p, float t) { - return smin(shape_circle(p - vec2(cos(t))), shape_circle(p + vec2(sin(t), 0)), 0.8); + return smin(shape_circle(p - vec2(cos(t))), shape_circle(p + vec2(sin(t), 0)), 0.8); } vec3 draw_line(float d, float thicu_kness) { - const float aa = 3.0; - return vec3(smoothstep(0.0, aa / u_resolution.y, max(0.0, abs(d) - thicu_kness))); + const float aa = 3.0; + return vec3(smoothstep(0.0, aa / u_resolution.y, max(0.0, abs(d) - thicu_kness))); } vec3 draw_line(float d) { - return draw_line(d, 0.0025); + return draw_line(d, 0.0025); } vec3 getMaterial(vec2 p, int materialId) { - return u_staticColors[materialId]; + return u_staticColors[materialId]; } vec3 getColor(vec2 p, vec2 uv) @@ -141,7 +141,7 @@ vec3 getColor(vec2 p, vec2 uv) /*ADD_SHAPES_HERE*/ - if(minDist <= 0.0) + if (minDist <= 0.0) { return vec3(minDist, finalMaterialId, 0.0); } @@ -171,7 +171,7 @@ vec3 getColor(vec2 p, vec2 uv) minDist = min(minDist, objectDist); - if(minDist < EPSILON) + if (minDist < EPSILON) { @@ -189,50 +189,49 @@ vec3 getColor(vec2 p, vec2 uv) void main() { - // FragColor = vec4(u_customShapeCount); - // return; + // FragColor = vec4(u_customShapeCount); + // return; - vec2 uv = (2.0f * v_texCoord) - 1.0f; - float aspectRatio = u_resolution.x / u_resolution.y; // TODO: uniform - uv.x *= aspectRatio; + vec2 uv = (2.0f * v_texCoord) - 1.0f; + float aspectRatio = u_resolution.x / u_resolution.y;// TODO: uniform + uv.x *= aspectRatio; - float zoom = -u_camMatrix[3].z; - vec2 pos = (zoom * uv) - u_camMatrix[3].xy; - + float zoom = -u_camMatrix[3].z; + vec2 pos = (zoom * uv) - u_camMatrix[3].xy; - vec3 result = getColor(pos, v_texCoord); // Same as uv but (0, 0) is bottom left corner - float distance = result.x; - float finalDistance = 0.5 * distance / zoom; - finalDistance *= 0.5 / aspectRatio; + vec3 result = getColor(pos, v_texCoord);// Same as uv but (0, 0) is bottom left corner + float distance = result.x; + float finalDistance = 0.5 * distance / zoom; + finalDistance *= 0.5 / aspectRatio; -#if MOTION_BLUR - vec2 screenUV = v_texCoord; - vec4 previousColor = texture(t_colorTexture, screenUV.xy); + #if MOTION_BLUR - float previousDistance = previousColor.x; - int previousMaterial = int(previousColor.y); + vec2 screenUV = v_texCoord; + vec4 previousColor = texture(t_colorTexture, screenUV.xy); - previousDistance += u_blendIterations * 0.00035; - previousDistance = mix(finalDistance, previousDistance, 0.95); - // previousDistance = min(previousDistance + (u_blendIterations * 0.00035), mix(finalDistance, previousDistance, 0.9)); + float previousDistance = previousColor.x; + int previousMaterial = int(previousColor.y); - finalDistance = min(previousDistance, finalDistance); + previousDistance += u_blendIterations * 0.00035; + previousDistance = mix(finalDistance, previousDistance, 0.95); + // previousDistance = min(previousDistance + (u_blendIterations * 0.00035), mix(finalDistance, previousDistance, 0.9)); + finalDistance = min(previousDistance, finalDistance); - // FragColor = previousColor; + // FragColor = previousColor; - // FragColor = mix(vec4(color.xyz, finalDistance), previousColor, 0.9); + // FragColor = mix(vec4(color.xyz, finalDistance), previousColor, 0.9); -#else + #else -#endif + #endif FragColor = vec4(finalDistance, result.y, result.z, 0); From 5dc4bcd44fa52f0fdc23627357209f013b0ef502 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= <49535803+damacaa@users.noreply.github.com> Date: Sat, 26 Jul 2025 22:01:33 +0200 Subject: [PATCH 12/17] Blend over time to reduce blend iterations Introduced some artifacts --- src/weird-renderer/Renderer.cpp | 6 +++++- src/weird-renderer/shaders/2DLightingShader.frag | 6 +++--- src/weird-renderer/shaders/2DMaterialBlendShader.frag | 4 +++- src/weird-renderer/shaders/2DMaterialColorShader.frag | 5 +++++ src/weird-renderer/shaders/2DSDFDistanceShader.frag | 2 +- 5 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/weird-renderer/Renderer.cpp b/src/weird-renderer/Renderer.cpp index 23b0679..62b10b8 100644 --- a/src/weird-renderer/Renderer.cpp +++ b/src/weird-renderer/Renderer.cpp @@ -88,7 +88,7 @@ namespace WeirdEngine , m_renderWidth(width * m_renderScale) , m_renderHeight(height * m_renderScale) , m_vSyncEnabled(true) - , m_materialBlendIterations(10) + , m_materialBlendIterations(2) { Screen::width = m_windowWidth; Screen::height = m_windowHeight; @@ -296,6 +296,9 @@ namespace WeirdEngine m_2DMaterialColorShader.setUniform("t_colorTexture", 0); m_distanceTexture.bind(0); + m_2DMaterialColorShader.setUniform("t_currentColorTexture", 1); + m_postProcessDoubleBuffer[0]->getColorAttachment()->bind(1); + m_renderPlane.draw(m_2DMaterialColorShader); @@ -307,6 +310,7 @@ namespace WeirdEngine { m_2DMaterialBlendShader.use(); m_2DMaterialBlendShader.setUniform("t_colorTexture", 0); + m_2DMaterialBlendShader.setUniform("u_time", scene.getTime()); for (unsigned int i = 0; i < m_materialBlendIterations; i++) { diff --git a/src/weird-renderer/shaders/2DLightingShader.frag b/src/weird-renderer/shaders/2DLightingShader.frag index 323cd62..f9a3dcb 100644 --- a/src/weird-renderer/shaders/2DLightingShader.frag +++ b/src/weird-renderer/shaders/2DLightingShader.frag @@ -2,7 +2,7 @@ #define SHADOWS_ENABLED // #define SOFT_SHADOWS -#define DITHERING +// #define DITHERING // #define DEBUG_SHOW_DISTANCE // #define DEBUG_SHOW_COLORS @@ -34,8 +34,8 @@ uniform vec2 u_directionalLightDirection = vec2(0.7071f, 0.7071f); #ifdef DITHERING -uniform float u_spread = .025; -uniform int u_colorCount = 16; +uniform float u_spread = .5; +uniform int u_colorCount = 4; // Dithering and posterizing uniform int u_bayer2[2 * 2] = int[2 * 2]( diff --git a/src/weird-renderer/shaders/2DMaterialBlendShader.frag b/src/weird-renderer/shaders/2DMaterialBlendShader.frag index 1bbde72..51f6950 100644 --- a/src/weird-renderer/shaders/2DMaterialBlendShader.frag +++ b/src/weird-renderer/shaders/2DMaterialBlendShader.frag @@ -9,6 +9,7 @@ uniform sampler2D t_colorTexture; uniform bool u_horizontal; uniform float u_weight[5] = float[] (0.227027, 0.1945946, 0.1216216, 0.054054, 0.016216); +uniform float u_time; @@ -21,7 +22,8 @@ vec3 toSRGB(vec3 linear) { void main() { - vec2 tex_offset = 1.0 / textureSize(t_colorTexture, 0); + vec2 tex_offset = 5.0 / textureSize(t_colorTexture, 0); + vec4 data = texture(t_colorTexture, v_texCoord); float mask = data.w; diff --git a/src/weird-renderer/shaders/2DMaterialColorShader.frag b/src/weird-renderer/shaders/2DMaterialColorShader.frag index 5856fae..4a71d48 100644 --- a/src/weird-renderer/shaders/2DMaterialColorShader.frag +++ b/src/weird-renderer/shaders/2DMaterialColorShader.frag @@ -18,6 +18,7 @@ uniform vec2 u_resolution; uniform float u_time; uniform sampler2D t_colorTexture; +uniform sampler2D t_currentColorTexture; uniform vec3 u_staticColors[16]; float map(vec2 p) @@ -53,6 +54,10 @@ void main() // Decide whether to use blended or background // c = distance <= 0.0 ? c : background; + vec3 currentColor = texture(t_currentColorTexture, screenUV).xyz; + + c = mix(c, currentColor, 0.99 * mask); + // FragColor = vec4(c, distance); FragColor = vec4(c, mask); } diff --git a/src/weird-renderer/shaders/2DSDFDistanceShader.frag b/src/weird-renderer/shaders/2DSDFDistanceShader.frag index 9e21f40..28f5da4 100644 --- a/src/weird-renderer/shaders/2DSDFDistanceShader.frag +++ b/src/weird-renderer/shaders/2DSDFDistanceShader.frag @@ -20,7 +20,7 @@ uniform vec3 u_lightPos; uniform vec3 u_camPos; uniform float u_time; -uniform float u_k = 0.25; +uniform float u_k = 0.75; uniform sampler2D t_colorTexture; uniform int u_loadedObjects; From d1b5f1ad5693fa9d4188dd1cf769b8471e98f23d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= <49535803+damacaa@users.noreply.github.com> Date: Sun, 27 Jul 2025 19:28:45 +0200 Subject: [PATCH 13/17] Fixed blending artifacts --- .../shaders/2DMaterialBlendShader.frag | 6 +++++- .../shaders/2DSDFDistanceShader.frag | 21 ++++++++++++------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/weird-renderer/shaders/2DMaterialBlendShader.frag b/src/weird-renderer/shaders/2DMaterialBlendShader.frag index 51f6950..56411dc 100644 --- a/src/weird-renderer/shaders/2DMaterialBlendShader.frag +++ b/src/weird-renderer/shaders/2DMaterialBlendShader.frag @@ -20,9 +20,13 @@ vec3 toSRGB(vec3 linear) { return pow(linear, vec3(1.0 / 2.2)); } +float hash(vec2 p) { + return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); +} + void main() { - vec2 tex_offset = 5.0 / textureSize(t_colorTexture, 0); + vec2 tex_offset = 1.0 / textureSize(t_colorTexture, 0); vec4 data = texture(t_colorTexture, v_texCoord); float mask = data.w; diff --git a/src/weird-renderer/shaders/2DSDFDistanceShader.frag b/src/weird-renderer/shaders/2DSDFDistanceShader.frag index 28f5da4..0b46042 100644 --- a/src/weird-renderer/shaders/2DSDFDistanceShader.frag +++ b/src/weird-renderer/shaders/2DSDFDistanceShader.frag @@ -20,7 +20,7 @@ uniform vec3 u_lightPos; uniform vec3 u_camPos; uniform float u_time; -uniform float u_k = 0.75; +uniform float u_k = 0.5; uniform sampler2D t_colorTexture; uniform int u_loadedObjects; @@ -136,6 +136,8 @@ vec3 getMaterial(vec2 p, int materialId) vec3 getColor(vec2 p, vec2 uv) { float minDist = 100000.0; + float minColorDist = minDist; + int finalMaterialId = 0; float mask = 1.0; @@ -147,7 +149,7 @@ vec3 getColor(vec2 p, vec2 uv) } float shapeDist = minDist; - // minDist = 1.0; // Disable blending between balls and shapes + minDist = 1.0; // Disable blending between balls and shapes float inv_k = 1.0 / u_k; @@ -161,22 +163,27 @@ vec3 getColor(vec2 p, vec2 uv) // Inside ball mask is set to 0 mask = objectDist <= 0 ? 0.25 : mask; + #if BLEND_SHAPES + + finalMaterialId = objectDist <= minColorDist ? materialId : finalMaterialId; + + #else + finalMaterialId = objectDist <= minDist ? materialId : finalMaterialId; + #endif + + #if BLEND_SHAPES minDist = fOpUnionSoft(objectDist, minDist, u_k, inv_k); + minColorDist = min(minColorDist, objectDist); #else minDist = min(minDist, objectDist); - if (minDist < EPSILON) - { - - break; - } #endif From 187d666d7c91432b392360aee56697df9f6448a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= <49535803+damacaa@users.noreply.github.com> Date: Sat, 2 Aug 2025 10:44:48 +0200 Subject: [PATCH 14/17] Improved blending --- src/weird-renderer/Renderer.cpp | 2 +- src/weird-renderer/shaders/2DMaterialBlendShader.frag | 10 ++++++---- src/weird-renderer/shaders/2DSDFDistanceShader.frag | 3 ++- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/weird-renderer/Renderer.cpp b/src/weird-renderer/Renderer.cpp index 62b10b8..fa4518d 100644 --- a/src/weird-renderer/Renderer.cpp +++ b/src/weird-renderer/Renderer.cpp @@ -306,7 +306,7 @@ namespace WeirdEngine m_shapes2D.unbind(); } - bool horizontal = true; + static bool horizontal = true; { m_2DMaterialBlendShader.use(); m_2DMaterialBlendShader.setUniform("t_colorTexture", 0); diff --git a/src/weird-renderer/shaders/2DMaterialBlendShader.frag b/src/weird-renderer/shaders/2DMaterialBlendShader.frag index 56411dc..44175a3 100644 --- a/src/weird-renderer/shaders/2DMaterialBlendShader.frag +++ b/src/weird-renderer/shaders/2DMaterialBlendShader.frag @@ -27,21 +27,23 @@ float hash(vec2 p) { void main() { vec2 tex_offset = 1.0 / textureSize(t_colorTexture, 0); + vec2 uv = tex_offset + vec2(hash(gl_FragCoord.xy + u_time)); vec4 data = texture(t_colorTexture, v_texCoord); float mask = data.w; - vec3 result = toLinear(data.rgb) * u_weight[0];// TODO: precompute toLinear before this shader + vec3 originalColor = toLinear(data.rgb); + vec3 result = originalColor * u_weight[0];// TODO: precompute toLinear before this shader for (int i = 1; i < 5; ++i) { - vec2 offset = u_horizontal ? vec2(tex_offset.x * i, 0.0) : vec2(0.0, tex_offset.y * i); + vec2 offset = u_horizontal ? vec2((tex_offset.x * i), 0.0) : vec2(0.0, (tex_offset.y * i)); // (tex_offset.x * i) + hash(gl_FragCoord.xy + u_time) vec4 colRight = texture(t_colorTexture, v_texCoord + offset); - result += toLinear(colRight.rgb) * u_weight[i]; + result += mix(toLinear(colRight.rgb), originalColor, 1.0 - colRight.a) * u_weight[i]; vec4 colLeft = texture(t_colorTexture, v_texCoord - offset); - result += toLinear(colLeft.rgb) * u_weight[i]; + result += mix(toLinear(colLeft.rgb), originalColor, 1.0 - colLeft.a) * u_weight[i]; } result = toSRGB(result); diff --git a/src/weird-renderer/shaders/2DSDFDistanceShader.frag b/src/weird-renderer/shaders/2DSDFDistanceShader.frag index 0b46042..e9df460 100644 --- a/src/weird-renderer/shaders/2DSDFDistanceShader.frag +++ b/src/weird-renderer/shaders/2DSDFDistanceShader.frag @@ -161,7 +161,7 @@ vec3 getColor(vec2 p, vec2 uv) float objectDist = shape_circle(p - positionSizeMaterial.xy); // Inside ball mask is set to 0 - mask = objectDist <= 0 ? 0.25 : mask; + mask = objectDist <= 0 ? 0.95 : mask; #if BLEND_SHAPES @@ -222,6 +222,7 @@ void main() float previousDistance = previousColor.x; int previousMaterial = int(previousColor.y); + result.y = finalDistance > 0.0 && previousDistance < 0.0 ? previousColor.y : result.y; previousDistance += u_blendIterations * 0.00035; previousDistance = mix(finalDistance, previousDistance, 0.95); From 8a573ad52bc921d3255829c431825c97f597be13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= <49535803+damacaa@users.noreply.github.com> Date: Sat, 2 Aug 2025 10:45:52 +0200 Subject: [PATCH 15/17] Better collision detection with custom shapes Smooth addition still kinda janky --- src/weird-physics/Simulation2D.cpp | 32 +++++++++++++++++------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/weird-physics/Simulation2D.cpp b/src/weird-physics/Simulation2D.cpp index 5c1178c..ec00749 100644 --- a/src/weird-physics/Simulation2D.cpp +++ b/src/weird-physics/Simulation2D.cpp @@ -531,6 +531,7 @@ namespace WeirdEngine float d = map(p); if (d < m_radious) { + float penetration = (m_radious - d); // Collision normal calculation @@ -541,25 +542,28 @@ namespace WeirdEngine vec2 normal = vec2(d - d1, d - d2); normal = normalize(normal); - // Position - p += penetration * normal; + if (map(p - (m_radious * normal)) <= EPSILON) // Bad solution? + { + // Position + p += penetration * normal; - // Impulse - float restitution = 0.5f; - vec2 vRel = -m_velocities[i]; - float velocityAlongNormal = glm::dot(normal, vRel); - float impulseMagnitude = -(1 + restitution) * velocityAlongNormal; // * m_mass[i]; -> cancels out later - vec2 impulse = impulseMagnitude * normal; + // Impulse + float restitution = 0.5f; + vec2 vRel = -m_velocities[i]; + float velocityAlongNormal = glm::dot(normal, vRel); + float impulseMagnitude = -(1 + restitution) * velocityAlongNormal; // * m_mass[i]; -> cancels out later + vec2 impulse = impulseMagnitude * normal; - // m_velocities[i] -= impulse; // * m_invMass[i] + // m_velocities[i] -= impulse; // * m_invMass[i] - // Penalty - vec2 v = penetration * normal; - vec2 force = m_mass[i] * m_push * v; + // Penalty + vec2 v = penetration * normal; + vec2 force = m_mass[i] * m_push * v; - // force -= (10000.0f * m_damping * m_velocities[i]); // Drag ??? + // force -= (10000.0f * m_damping * m_velocities[i]); // Drag ??? - m_forces[i] += force; + m_forces[i] += force; + } } // Old walls From 7b726aa878b11446dca215c3758c84a712c68835 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= <49535803+damacaa@users.noreply.github.com> Date: Mon, 18 Aug 2025 16:11:24 +0200 Subject: [PATCH 16/17] Moved samples to main repo --- .../empty-project}/CMakeLists.txt | 0 .../empty-project}/src/main.cpp | 0 examples/sample-scenes/.idea/.gitignore | 8 + examples/sample-scenes/.idea/.name | 1 + examples/sample-scenes/.idea/editor.xml | 248 +++++++++ examples/sample-scenes/.idea/misc.xml | 7 + examples/sample-scenes/.idea/modules.xml | 8 + .../sample-scenes/.idea/sample-scenes.iml | 2 + examples/sample-scenes/.idea/vcs.xml | 7 + examples/sample-scenes/CMakeLists.txt | 101 ++++ examples/sample-scenes/assets/fire/fire.jpg | Bin 0 -> 38278 bytes examples/sample-scenes/assets/fire/flame.png | Bin 0 -> 16800 bytes examples/sample-scenes/assets/fire/flame.xcf | Bin 0 -> 103373 bytes examples/sample-scenes/assets/fire/flame2.png | Bin 0 -> 3320 bytes .../assets/fire/shaders/fireParticles.frag | 16 + .../assets/fire/shaders/fireParticles.vert | 50 ++ .../assets/fire/shaders/flame.frag | 72 +++ .../assets/fire/shaders/heatDistortion.frag | 53 ++ .../assets/fire/shaders/smokeParticles.frag | 25 + .../assets/fire/shaders/smokeParticles.vert | 61 +++ examples/sample-scenes/assets/jimmy.jpg | Bin 0 -> 161711 bytes .../assets/lines/combination.frag | 47 ++ .../sample-scenes/assets/lines/lines.frag | 38 ++ examples/sample-scenes/assets/monkey/demo.bin | Bin 0 -> 91104 bytes .../sample-scenes/assets/monkey/demo.gltf | 104 ++++ .../assets/water/shaders/water.frag | 45 ++ .../include/ApparentCircularMotionScene.h | 50 ++ examples/sample-scenes/include/Classic.h | 64 +++ .../sample-scenes/include/CollisionHandling.h | 83 +++ examples/sample-scenes/include/DestroyScene.h | 79 +++ examples/sample-scenes/include/Fire.h | 474 ++++++++++++++++++ .../sample-scenes/include/FireworksScene.h | 56 +++ examples/sample-scenes/include/ImageScene.h | 168 +++++++ examples/sample-scenes/include/Lines.h | 111 ++++ .../include/MouseCollisionScene.h | 74 +++ examples/sample-scenes/include/RopeScene.h | 162 ++++++ .../include/ShapesCombinations.h | 140 ++++++ examples/sample-scenes/include/SpaceScene.h | 85 ++++ examples/sample-scenes/include/Text.h | 24 + examples/sample-scenes/include/Water.h | 403 +++++++++++++++ examples/sample-scenes/src/main.cpp | 41 ++ 41 files changed, 2907 insertions(+) rename {cmake/create-project => examples/empty-project}/CMakeLists.txt (100%) rename {cmake/create-project => examples/empty-project}/src/main.cpp (100%) create mode 100644 examples/sample-scenes/.idea/.gitignore create mode 100644 examples/sample-scenes/.idea/.name create mode 100644 examples/sample-scenes/.idea/editor.xml create mode 100644 examples/sample-scenes/.idea/misc.xml create mode 100644 examples/sample-scenes/.idea/modules.xml create mode 100644 examples/sample-scenes/.idea/sample-scenes.iml create mode 100644 examples/sample-scenes/.idea/vcs.xml create mode 100644 examples/sample-scenes/CMakeLists.txt create mode 100644 examples/sample-scenes/assets/fire/fire.jpg create mode 100644 examples/sample-scenes/assets/fire/flame.png create mode 100644 examples/sample-scenes/assets/fire/flame.xcf create mode 100644 examples/sample-scenes/assets/fire/flame2.png create mode 100644 examples/sample-scenes/assets/fire/shaders/fireParticles.frag create mode 100644 examples/sample-scenes/assets/fire/shaders/fireParticles.vert create mode 100644 examples/sample-scenes/assets/fire/shaders/flame.frag create mode 100644 examples/sample-scenes/assets/fire/shaders/heatDistortion.frag create mode 100644 examples/sample-scenes/assets/fire/shaders/smokeParticles.frag create mode 100644 examples/sample-scenes/assets/fire/shaders/smokeParticles.vert create mode 100644 examples/sample-scenes/assets/jimmy.jpg create mode 100644 examples/sample-scenes/assets/lines/combination.frag create mode 100644 examples/sample-scenes/assets/lines/lines.frag create mode 100644 examples/sample-scenes/assets/monkey/demo.bin create mode 100644 examples/sample-scenes/assets/monkey/demo.gltf create mode 100644 examples/sample-scenes/assets/water/shaders/water.frag create mode 100644 examples/sample-scenes/include/ApparentCircularMotionScene.h create mode 100644 examples/sample-scenes/include/Classic.h create mode 100644 examples/sample-scenes/include/CollisionHandling.h create mode 100644 examples/sample-scenes/include/DestroyScene.h create mode 100644 examples/sample-scenes/include/Fire.h create mode 100644 examples/sample-scenes/include/FireworksScene.h create mode 100644 examples/sample-scenes/include/ImageScene.h create mode 100644 examples/sample-scenes/include/Lines.h create mode 100644 examples/sample-scenes/include/MouseCollisionScene.h create mode 100644 examples/sample-scenes/include/RopeScene.h create mode 100644 examples/sample-scenes/include/ShapesCombinations.h create mode 100644 examples/sample-scenes/include/SpaceScene.h create mode 100644 examples/sample-scenes/include/Text.h create mode 100644 examples/sample-scenes/include/Water.h create mode 100644 examples/sample-scenes/src/main.cpp diff --git a/cmake/create-project/CMakeLists.txt b/examples/empty-project/CMakeLists.txt similarity index 100% rename from cmake/create-project/CMakeLists.txt rename to examples/empty-project/CMakeLists.txt diff --git a/cmake/create-project/src/main.cpp b/examples/empty-project/src/main.cpp similarity index 100% rename from cmake/create-project/src/main.cpp rename to examples/empty-project/src/main.cpp diff --git a/examples/sample-scenes/.idea/.gitignore b/examples/sample-scenes/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/examples/sample-scenes/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/examples/sample-scenes/.idea/.name b/examples/sample-scenes/.idea/.name new file mode 100644 index 0000000..da9b741 --- /dev/null +++ b/examples/sample-scenes/.idea/.name @@ -0,0 +1 @@ +WeirdSamples \ No newline at end of file diff --git a/examples/sample-scenes/.idea/editor.xml b/examples/sample-scenes/.idea/editor.xml new file mode 100644 index 0000000..ead1d8a --- /dev/null +++ b/examples/sample-scenes/.idea/editor.xml @@ -0,0 +1,248 @@ + + + + + \ No newline at end of file diff --git a/examples/sample-scenes/.idea/misc.xml b/examples/sample-scenes/.idea/misc.xml new file mode 100644 index 0000000..0b76fe5 --- /dev/null +++ b/examples/sample-scenes/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/examples/sample-scenes/.idea/modules.xml b/examples/sample-scenes/.idea/modules.xml new file mode 100644 index 0000000..4e17cb7 --- /dev/null +++ b/examples/sample-scenes/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/examples/sample-scenes/.idea/sample-scenes.iml b/examples/sample-scenes/.idea/sample-scenes.iml new file mode 100644 index 0000000..f08604b --- /dev/null +++ b/examples/sample-scenes/.idea/sample-scenes.iml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/examples/sample-scenes/.idea/vcs.xml b/examples/sample-scenes/.idea/vcs.xml new file mode 100644 index 0000000..415e336 --- /dev/null +++ b/examples/sample-scenes/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/examples/sample-scenes/CMakeLists.txt b/examples/sample-scenes/CMakeLists.txt new file mode 100644 index 0000000..47267c5 --- /dev/null +++ b/examples/sample-scenes/CMakeLists.txt @@ -0,0 +1,101 @@ +cmake_minimum_required(VERSION 3.10) +project(WeirdSamples) + +# Create folders if they don't exist +file(MAKE_DIRECTORY "${CMAKE_SOURCE_DIR}/assets") +file(MAKE_DIRECTORY "${CMAKE_SOURCE_DIR}/include") +file(MAKE_DIRECTORY "${CMAKE_SOURCE_DIR}/src") + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# Option to switch between local and online version of WeirdEngine +option(USE_LOCAL_WEIRD_ENGINE "Use local version of WeirdEngine" ON) + +# Set paths for the local WeirdEngine +set(WEIRD_ENGINE_LOCAL_PATH "../../") + +if(USE_LOCAL_WEIRD_ENGINE) + # Use the local version of WeirdEngine + message(STATUS "Using local version of WeirdEngine") + add_subdirectory(${WEIRD_ENGINE_LOCAL_PATH} ${CMAKE_BINARY_DIR}/weird-engine) +else() + # Fetch WeirdEngine from GitHub + message(STATUS "Using online version of WeirdEngine") + include(FetchContent) + FetchContent_Declare( + WeirdEngine + GIT_REPOSITORY https://github.com/damacaa/weird-engine + GIT_TAG main + ) + FetchContent_MakeAvailable(WeirdEngine) +endif() + +# Glob source files and header files from the proper directories. +file(GLOB_RECURSE WEIRDGAME_SOURCES "${CMAKE_SOURCE_DIR}/src/*.cpp") +file(GLOB_RECURSE WEIRDGAME_HEADERS + "${CMAKE_SOURCE_DIR}/include/*.h" + "${CMAKE_SOURCE_DIR}/include/*.hpp" +) +file(GLOB_RECURSE WEIRDGAME_ASSETS + "${CMAKE_SOURCE_DIR}/assets/*.*" +) + +# Mark headers as header-only so that Visual Studio treats them appropriately. +set_source_files_properties(${WEIRDGAME_HEADERS} PROPERTIES HEADER_FILE_ONLY TRUE) + +# Add executable including both sources and headers. +add_executable(${PROJECT_NAME} ${WEIRDGAME_SOURCES} ${WEIRDGAME_HEADERS} ${WEIRDGAME_ASSETS}) + +# Set include directories. +target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/include) + +# Link to WeirdEngine. +target_link_libraries(${PROJECT_NAME} PRIVATE WeirdEngine) + +# Set Asset Path Macro +# Define the assets path variable +set(ASSETS_PATH "${CMAKE_CURRENT_SOURCE_DIR}/assets/") +# set(ASSETS_PATH "./assets") # Set the asset path macro in release mode to a relative path that assumes the assets folder is in the same directory as the game executable + +# Print the assets path for debugging or confirmation +message(STATUS "Assets path: ${ASSETS_PATH}") + +# Use the variable in target_compile_definitions +target_compile_definitions(${PROJECT_NAME} PUBLIC ASSETS_PATH="${ASSETS_PATH}") + +# Example for the future with more libraries +add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + # Command for SDL3 + COMMAND ${CMAKE_COMMAND} -E copy_if_different + $ + $ + + # Command for SDL3_image + # COMMAND ${CMAKE_COMMAND} -E copy_if_different + # $ #<-- Note the target name might be different + # $ + + COMMENT "Copying runtime DLLs to executable directory" +) + +# ---------------------------------------------------------- +# Helper function to assign source groups based on folder structure. +function(assign_source_groups TARGET) + foreach(source_file IN LISTS ARGN) + # Get the full path to the file's directory. + get_filename_component(FILE_PATH "${source_file}" PATH) + # Compute the path relative to the project's root. + file(RELATIVE_PATH REL_PATH "${CMAKE_SOURCE_DIR}" "${FILE_PATH}") + # Replace forward slashes with backslashes for Visual Studio filter naming. + string(REPLACE "/" "\\" FILTER_PATH "${REL_PATH}") + if(FILTER_PATH STREQUAL "") + set(FILTER_PATH "Root") + endif() + # Assign the file to the computed filter. + source_group("${FILTER_PATH}" FILES "${source_file}") + endforeach() +endfunction() + +# Assign source groups for all your files. +assign_source_groups(${PROJECT_NAME} ${WEIRDGAME_SOURCES} ${WEIRDGAME_HEADERS} ${WEIRDGAME_ASSETS}) diff --git a/examples/sample-scenes/assets/fire/fire.jpg b/examples/sample-scenes/assets/fire/fire.jpg new file mode 100644 index 0000000000000000000000000000000000000000..eb060b3b98922d1bc38e401a772ab297e2d2b883 GIT binary patch literal 38278 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 literal 0 HcmV?d00001 diff --git a/examples/sample-scenes/assets/fire/flame.png b/examples/sample-scenes/assets/fire/flame.png new file mode 100644 index 0000000000000000000000000000000000000000..dbb62a473009334a05e25bf0f8dc8d5ec86cdb68 GIT binary patch literal 16800 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 #QVC>i61;!!mpmA7(wvA)jQRBD< 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 stops[i] && t <= stops[i+1]) { + float localT = (t - stops[i]) / (stops[i+1] - stops[i]); + return mix(colors[i], colors[i+1], localT); + } + } + + return colors[NUM_STOPS - 1]; // fallback +} + + +void main() +{ + vec2 uv = v_texCoord; + + // Noise0 + float noise0 = texture(t_noise, fract((3.0f * uv) - vec2(0.0f, u_time))).x - 0.5f; + + // Noise1 + float noise1 = texture(t_noise, fract((2.0 * uv) - vec2(0.0f, 1.2f * u_time))).x - 0.5f; + + // Combine all noise + float sumNoise = noise0 + noise1; + + // Reduce noise at the bottom of the flame + float noiseMask = clamp(3.0f * (v_texCoord.y - 0.35f), 0, 1); + sumNoise *= noiseMask; + + // Reduce overall noise strength + float noiseIntensity = 0.1f; + sumNoise *= noiseIntensity; + + // Sample texture with noise applied to uv coordinates + float alpha = texture(t_flameShape, clamp(uv + sumNoise, 0, 1)).x; + + alpha = smoothstep(.3, 1.0, alpha); + alpha = alpha * (alpha + 0.01f); + alpha = clamp(alpha, 0.0, 1.0); + + FragColor = vec4(getGradientColor(alpha), alpha); + // FragColor = vec4(vec3(sumNoise / noiseIntensity), 1.0f); // Debug noise + // FragColor = vec4(vec3(alpha), 1.0f); // Debug alpha +} \ No newline at end of file diff --git a/examples/sample-scenes/assets/fire/shaders/heatDistortion.frag b/examples/sample-scenes/assets/fire/shaders/heatDistortion.frag new file mode 100644 index 0000000..0d2e956 --- /dev/null +++ b/examples/sample-scenes/assets/fire/shaders/heatDistortion.frag @@ -0,0 +1,53 @@ +#version 330 core + +out vec4 FragColor; + +// Inputs from vertex shader +in vec3 v_worldPos; +in vec3 v_normal; +in vec3 v_color; +in vec2 v_texCoord; + +uniform sampler2D t_texture; +uniform sampler2D t_noise; +uniform sampler2D t_flameShape; + +uniform float u_time = 0.1f; +uniform vec2 u_resolution; + +// Fog calculations used to modulate distortion with distance +float linearDepth(float z, float near, float far) +{ + return (2.0 * near) / (far + near - z * (far - near)); +} + +void main() +{ + // Mesh uv to sample mask texture + vec2 uv = v_texCoord; + // Screen uv to sample scene texture + vec2 screenUV = gl_FragCoord.xy / u_resolution; + + // Use the flame shape to mask the noise + float bottomFade = texture(t_flameShape, vec2(uv.x, min(uv.y, 0.5))).r; + bottomFade = smoothstep(0.5, 1.0, bottomFade); + + // Fade at the top + float topFade = 1.0 - uv.y; + bottomFade *= topFade; + + // Blur is less strong when its farther from the camera, same math as fog + float depthMask = max(1.0 - (linearDepth(gl_FragCoord.z, 0.1, 100.0) * 1.5), 0.0); + + // Reduce strength when the flame is looking up + float dotWorldY = 1.0f - abs(dot(normalize(v_normal), vec3(0, 1, 0))); + + // Combine all masks + float mask = bottomFade * depthMask * dotWorldY; + + // Calculate scene texture uv offset + vec2 offset = vec2(0, mask * 0.1f * ((texture(t_noise, (2.0f * uv) - (1.0f * u_time)).x) - 0.5)); + + FragColor = texture(t_texture, screenUV + offset); + // FragColor = vec4(vec3(mask), 1.0); // Debug mask +} \ No newline at end of file diff --git a/examples/sample-scenes/assets/fire/shaders/smokeParticles.frag b/examples/sample-scenes/assets/fire/shaders/smokeParticles.frag new file mode 100644 index 0000000..6b3639d --- /dev/null +++ b/examples/sample-scenes/assets/fire/shaders/smokeParticles.frag @@ -0,0 +1,25 @@ +#version 330 core + +out vec4 FragColor; + +// Inputs from vertex shader +in vec3 v_worldPos; +in vec3 v_normal; +in vec3 v_color; +in vec2 v_texCoord; +in float v_delta; + +uniform float u_time; +uniform vec3 u_smokeColor = vec3(0.12); + +void main() +{ + // Distance to center generates a circular gradient + float d = 2.0 * length(v_texCoord - vec2(0.5)); + float alpha = max(0.0, 1.0 - d); + + // Animate alpha + alpha *= pow(1.0 - v_delta, 2.0); + + FragColor = vec4(u_smokeColor * (1.0 - v_delta), 0.75 * alpha); +} \ No newline at end of file diff --git a/examples/sample-scenes/assets/fire/shaders/smokeParticles.vert b/examples/sample-scenes/assets/fire/shaders/smokeParticles.vert new file mode 100644 index 0000000..b79a26c --- /dev/null +++ b/examples/sample-scenes/assets/fire/shaders/smokeParticles.vert @@ -0,0 +1,61 @@ +#version 330 core + +// Vertex attributes +layout (location = 0) in vec3 in_position; +layout (location = 1) in vec3 in_normal; +layout (location = 2) in vec3 in_color; +layout (location = 3) in vec2 in_texCoord; + +// Outputs to fragment shader +out vec3 v_worldPos; +out vec3 v_normal; +out vec3 v_color; +out vec2 v_texCoord; + +out float v_delta; + +// Uniforms +uniform mat4 u_model; +uniform mat4 u_camMatrix; +uniform mat3 u_normalMatrix; + +uniform float u_time; + +// ChatGPT: I want a function that, for a given x, increases but then plateaus +float plateauFunction(float x, float maxValue, float growthRate) +{ + return maxValue * (1.0 - exp(-growthRate * x)); +} + +float rand(float x) +{ + return fract(sin(x) * 43758.5453123); +} + +void main() +{ + // Animation delta + float delta = fract((gl_InstanceID * 123.45678f) + (0.1f * u_time)); + + // Position + vec3 offset = vec3( + delta * 5.0f * (rand(gl_InstanceID * 1.2345) - 0.5f), + delta * (7.0 + (3.0 * rand(gl_InstanceID * 1.2345))), + delta * 5.0f * (rand(gl_InstanceID * 0.9876) - 0.5f) + ); + + // Scale + float scale = 1.0 + (3.0 * delta); + + v_worldPos = vec3(u_model * vec4(scale * in_position, 1.0)) + offset; + v_normal = u_normalMatrix * in_normal; + v_color = in_color; + v_texCoord = in_texCoord; + + // Pass delta to fragment shader + v_delta = delta; + + gl_Position = u_camMatrix * vec4(v_worldPos, 1.0); +} + + \ No newline at end of file diff --git a/examples/sample-scenes/assets/jimmy.jpg b/examples/sample-scenes/assets/jimmy.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9a4f1d202e82638a7667e2a904ed9cfcb8762517 GIT binary patch literal 161711 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
17(a@SO}yRg$W3w=2@O03X@h9VtVcwpOA ziRq2MLDd?K?qMjS#Qri`!w-S=t2>p2?sWMBVK>}wc@uvADc_F zu_auan{>F#F5DIvBj1(l?OxOIJ4hZI)3s|hkNr08(enqFxI(gEMTX88uw^40VDr%9 ziSd_)-@~3O@)9|59lT7YOoCOHAbT7hoknX(O6tZdbBCP>!8~zBpK1M}cCf1Mk+?yS zHy=!%^?zNp+D{lfpVp{p(zUhF!N%ZJ@H>om`qwy)zrlKpw%|t_35vL8QN)Ri;3?`s z@AUjf>sR~|3SC=Lv+)kAf2nGg(OcOxu8C@zg{RKZ1y+Q%;C^zYfo8|2CciB{4ft34 zTUdC_;##B;`Hw4cfIe*J2LmcHf1Q5G{=pv)FBRwJ6`UzKM|3oMkBL4gCAb*BwTQ%_CAYLqskhQdu0xeyPmuB6 zy!!yxKq$Y&KNH@{+`|>bO{UC)WKQBY3*Ayh1UFuVS02@c@n^-_rn#yi)F-*T(;=UC z+7e%$HX!xmZhb+=A6oOD9QZQo8!J1zZBSm?$U`X*v`#%i9Iip^7?Ixxiu@+O5@M%P ze$8`o`C0wNs9f1kiOMNX@Xh#^+IE6ivxin7pY!5=htTI0^N)>wHeYzPL6_|5CNNqURuKN>PzBG>3Z?MZ z;?3rTtPRAnGjo>U`&N}}R;5zSd#KIE+Mjgzc1Y4RWaxU;zY=MX>K1=8j&sd<-^0&| zHk$5}6UQjrdZ|2vTiTzE<6A-*_T!4Zmm@yC>W-tWz4Ra}kPqWted50nOKW!A7TNyRE8%lGQ%{*Av)iVudenY0Zzw2c z8275T-Y;|@?`)dkJSE~*)KC}ej{R$9^~{(Uu4PfqouoLcvsT~Y9LaaLb8u^-Pl!{M z7%g0uh2gU;!p`JicCNnO+Bo6^$V2#7l}942Rk>cnocUdhkN97aANhI}EBs%E<}$u+ zGw)vq*?fDJP1(8WirdmZXh|3djIH$szLD_Nctwla*!wyBbq&&jvXhGHybI#=S%D9p zYvgYS{8MdAsE&V_4w>s-zu>PLi4kAS+UwMM*Ue^FY)&2N9Zs56;U{K&QQ_~2-br1q zaC;Ndw=I4l{i;Gdz`t7gyH4?=$2LTtt#|$w@gSWyJA;muId(e}&1tiuNi)`U4N3J2 za0`Me_l505Qitww$E9&v4!8E{6%_H`n)Dq4KQ1*OSJ+{(PHxQRoKHmXmEt<6@5OBE zmZDoc;2e)yd@UGDoDe%#RjKGLdtkv= zaU(U06@tNcxj3%(!uQb6WFm9`jyGqmaNx41DLB>YdD(8)Ocf+_FS0A50jd30<@ab#$Tzu*e9AdifhL;i8SfFu}j4AZ>uX82KYSP5=rJ=qT z-kIL`gTs+t=*=Y3qaf~EJ8R3lbFFFq7}J#@@F#wQm;qcIsqk?pbZs zw#bu2NmU(RBxDTdf^*!SYnz+FJ{#Aio%C-Pr}mRdLbC*i%2U^42RljWxw!!GjOMtH z5jCB*sDUlvnNTaZR+JY$X4^Uo!+8$h6~y_K@4Q_ocd6YhIs-n*16OO=f; z5O^0-_-gO=$R>hv#78tJjuk)zGVQOt^oSYBOER0&VGq!Q-ArKEmxpd;xYTr+@2@S@3qFC^F-%ZS@0qPS1pXO4HH0gPuP93dYCT6OSR>UmPaNOx*DdZen}EfB*J#N5 zxMjQWvW*NuELE|F(7)rI2fdQof(s4kl5CObG)-5Yv>pm2FOte+69`XegbLo_a^ zHz;zsJ zFZqff{sI30O@PTC-vh2!=F0eG@YhiIjpFNlD$yQGn}*tvDsJEIPIv>k^v!m zWe9ynIW#R|S4D?f(jzih$su94&53d4F(CcU`0v;3@7X8zPq(nrbzd0x&sm4V_S&=y z6zc?QH~b_d25iBW`C3HBO|BS7LY>v4Kv(c4t>6!Y9srX`(e!cRJKY{nJ5fcPBwCc& z3R)K0Btnc7CftmYNFl4#JO}$?c>BU;+fUGRrnS;_7n<@dD@W4fjz|@qOoc}=yE6^a zLYq$ZknHVU--bRmd^+&9pltjV@c#hA+7#E;p=|H&nJ-SF-~sca^XCRCvSfk^f(95@ z8z1d~uj}`-!hAPpE~2+myl}@8#Wj-!U*0@0u48E3Kv_9r3CKJXijAGXW!L-^x5TY2z&rn%;Z|1pUsr!=78EVgAwo0Ps)mhn_F-3;37e z=ZmHIf8tnf%#kgn#LEI)gm=R@+R(9Ykhwgb)#rMz?1`)Cnmxt$!=D>z8m!jyCA_+R zsKqXo+aN|rV1rLGGs!jV(`Gc0PUTZ@jG(s9 znSdEaBO5SEcjGfD#N zl!By^PIH?2L*p>;AMJPW3fD(hw$yab5Zi;MmxZ7@HTtNDTHU$w+E7rKn{w@saL6XK zK0RqVZ^U1PFQa&aO@9^m9N2kl1e1v5vTrp>k!Bb%Nf7{Ks-yOUM$+puXyk9!*7r-jObAh=DFOPm8>IBVu zZ+W6y>DPXA(xtTXG$hI9Tm?{6%N*ed3)Vm~Ko!}19{7Ey>z@m3^@Anmkv;vUqjz@# zfg4A67?RFNPW-8n#FAqG?h|0}sB4V)eXm$}8tYZpZ0%vVyzv~3VQ|P1E+G3o%bm$1 z0=`=;xX8vfdOLimLr!=DfMnsGJOqoZm!DpnGIm<%dEuS#zm;AaVLPgtQNb{uh90A{!|+Tc?)ZYo?ZE z)OG`fwT@$zRc=JHg*YrxPAZPKV}0ZQ02hc)*)+RbXc1;N1I)XUNdtM&h=XuZur~mq zcCpFG&!+qd@ZFcfJqm9Y-T|w4vRE5YwFP%V(j>7ok$}MNayNiGVTVjtRq!|9?0Sdo zH$J^HHO{Gbt=wPgh{^;m^2Cy|Iu2wiccQNfyn~ElqSs)0U+jnQUVRV3(cEb&P!B)2{U0K~>3?K?sieEv~sMwndSb zcvd@34$|2QPp8=YC$;ddg{0{y5;rlu%r|?TOCseX03icSj>`Jzs)A(PypPH z<7%kfza|FIbLf66j_y0hk6(n@eX_|U`$Q=eakTO%^Gj{oRkE2;$03dZ=DQDsz7W(r zFMoF}m8wJK{hsRbTadnF0yv^BsATzMxKxbo82hF{#(Bniy9u7R@k8NdzlJ|$%i9s7 zSn7IhwY{kl+)3tYG5}B+_Ykcjx&em{6sn+Q4Vv+CQ}QG@a?!F_YYpR=!yV3S_bb$PTDb(FAccJ~KX(j3MTJD^*1s2i10<`%+(oZK#WAXn%mpNu2av}=gGMQ`D28(aAP z-C=oU6UHubRz}{@?Se+-$qFOE2PlklSbjD5L!tac@NwBHUC$iu&g3d6LzP(&Wk3WY zBVde%LJn#xQd5nYwKn4}WAe-4o}(zxB8a0wEKB7`jn!RvWj=%)it=yT)8IY!qpc>f zXBjFY#Go!q43nS3Cce-8q`nK!;NOZ$x_!Kmc#lzxG;1W0M`hw2fGZT-a}C0rC!xz3AGunoqk8a$>B{x-uxq4+$@8Qr7>Qy@SDPOTG$nxW{z01Bjv~Lmjs+=89WXv z=)GsbIz8F*(%vL^+j8;-2>dy&Zu`QTUApJ}NJR_skG);h__1POp$OXNgt$^v8*`P; zb6nD9@a~m7OytIlg`|nZkv|#ajyfOKyx-y%h9&V$!MsIzWVVEcEOYWI{G-?aFg*qb zYV^&1<4uNAE#;WHuwBvx2+8OG=ttw6*JtA2hWbClZ`tEf@kzWXd8{wl?5$=}(;R>q zfgOSA$9nI|>eaeddY?O*(}e`0w`a^h2z*xz`k$A1bL1}@p+TG5hbE4^1_ZwonxdCFHMDJ@63?z|q2KEFY&N1^5 z^{}STH=98|GpW|Ed?BSlqFzO7;hjA$^&5+*_T1jhD+P#1qy|5_L4-KrNgG9Zw~Mq3 z%|`XLYl#axd17<4h}GW%wnBlBI2k^b>^}_t8^Pj#6D97Wd8$PorKo3@8Ht0gr$F zy&e$oozIJP=q>H-nrn#;BS?@lO`ZyY>;8Q!8A`~5+CO8zfV$tsUx+$7Y1Verz4YlC zC@GkE+*z`~1mKcS0A%3ezh*ujcsf4~d_K1DppQ?wu+l9fjKH&_q`1yyNZ9QpoZ-_A z(+4><;GghLKiI+_+1J65>bmrD>E0>Utrkx%csIvm3k-;zkgyUr1oSJw=Qux6Ttb#M zv84J(+HKVS>h2g*%!v13>6Kyi!S&pDubs{7S}rfB&pq))otK2A^DK_2(_J7*Rww&C z&AOh0B(!s>2c*c#eS6mpb#Ww`joG#q%>~>>D~QFS+lzpD86#F@-Y^Jd=tf3473*5% zj6NW+DjhBn{_MEHC(w?dkLz5~@NKobMVJh+&N!U(!SnVDzER@&fS*O7(pr!DX@=hH=Gl z){H7f#<5m7i$4LagYHyZ^T$CN!QnekS?CW{vB`P-g7Yn6YDmhrY8Mphj~cVYdFIND=!J|S;`dZcj{ zjacpLyPC(l@GhmIHugfLdV;vm<6os0e*kp&qAnCRKiK~O>(`6?X!uEM1IkuEyPW*J zYu;ItkHn6CbLOuBYVzr~QHe__>Bz-)SVXgfQeiKgoEG6pyp;=3OW_^87e8T!{Zp?Ee~R0ZDI_pYkT z!mFpoyYZ1a{cFC_ zd~@~)!6cENUX|H_#Y!{O=#&>j+-7LS-n-uo-i=Ds z5CO;)`W6DLXH3M8$2U@3+qmD2+2^f%>G3kf;nX?#cRqf#^$w?^#c^oevF)1q_v7Az zW4d>Xoura6*1ktOq^D6Q+?KB!9&+xn9$53~Te=jA_WuAWCl$uQE6hoA@~=}{UKZEZ z9YrAWo5>^zB_|5KarN(B_5&+*Vyuo|X9RS*fQCOYCOn_Iao)MTQ{xOi6}N4$#!))3 z?^-@5y|B~mTI%WD;XEp-+FaL)c>BgyLIxgM?wFA5$2lIA_W5Q<%g_|LUWFfuUl}a* zeN6;5uX7~)7{?@^t$9z2ZX>?GXTG}oDn<)U7{LgA0S6UV#ojB_tu+~<(`_X-vJH$j z1GFFZx~TeBDdHLJRE59oq|S-o*6iqyG7?Gx099y4mMT>wAeoAtVvBQKFBpUcTc$N|RUct?s3DG`fAY z)}d<>?o^4v@`tN0ToaDfR?S7OqKl~M(!8;@6{QT8_IV`agTd!LO?s5+a*-!=WolYY z+PIQi?=hxdJNGi>fIr?|qor?nGs4=3jwipvz=kVtEL`eSEvyl);knHC`A8klBpeK! zR-Ut|TiDD#!1gyV$OMmi*6Xx`)*>4xaPPqD++f#D;-5_RjB+B@D%f@y;vF5T3WPewTB;$2T#uohO!<|65K zvD*l_#?h7Dyp;g;mPFELuOv>s z$OFDOXApoEWg06Q|r;>H3R7_P9i4Vci)G3M^zPR#3PAjx&+t-VgXcqql@~p{43K4-*Bw zj_OMzZ6}E2JBs<3-L^>3DC`MxK|JH4zwu{`^c#D7n~ibwn|NnCk!|FX;A0;sVs`9x zJd>UcW$iVwagEPz@Mnj-JMhCrv69C?y}$cy+|x~`y7~83@}i&@Q7}75G40+I0!B|! zgVSrD8HSCnY0aswlcrC*5X~}4G>;w&6eb`xNV`JHlr7Iq-qG5P&h$M(B+$C(48 z1$B=srw)uUiksmVj=Xo^TP-_I@padRd|9MR5NS0lBJ3yLyN8;D5~Z=uNWmD-72kN9 z;r{@KzBc%mM)22&1^vDI=@K0uRDvh_UAONMl*KaTCGyvhjB->j6|zhXtL;wT!k#3% zu#u#>@XJEiw;oin-z*{{^Rh_FB$gno$_tYuYw}6DG8uel^$l82NM0bR?u9#op>rqT8P0NMLiJ{R#7{2GRdeHoiT*W#0E zU!0Wk!t!F0G8trv{_;8MGAqh{K3;f|@8O(U7NSkt_;Ty*{u*{HXVc(EJM89m`|Tb` zjg*klNT6gcYQY?5#hnLLpZ0#&JU6atGwWU}(=A}rt{gCbwcT3JNx25-fG99!9Z zJk$JL9*3uCR(2Xn3y6fq^xP2Je7W6#EgL4~8$z}oX2wN(^t$Dx&+IDxBx}hvZ8FbB zwMcGWQ$56Zolrp`K&7?`&LwT6D8VFP(zxWT>{`)2A%ALo4ZiVZy|k9?a2c%9Cf_Bh zn`tewtBuZCl}QXYA1enPE5LN^SNj82(e#T+pt-wP+SX^hx7vWih1@ZXzdr56F*#6j zILEMl$etu^EB1ZYZNkBH+C*1USVIh8#+Nq(YN0L23}gkjbPJK1`S0PiwB8W-&8cZ~ z>rm@@u8l0~JW$A8?d4#~>w*H3O7sJO%bs(UCt(R?tT&9c*mSK1NN+qU`ssnZ)MGxq za)?UFKV7?aldxmuKidxpv8~4w}q(j$K7! zo)w1HF>`AiO50F{$%g>s6&ujxa8v>ZTJ`UNTBYujX)D8R9pGqk(ZZu}2o<*=$={4* zf<|$i<26fgKAY4$R2M>ExX1_s#u$JY6P~B1{0r8+Kf+p0pQrpz)U}OHSoH+7 z3luGI6u3}S60+lh*c*=@C^NFrAue9%b@{Tn_?b3 zf`FV59Aq33fs9!@h&C8^g^XV4aRtbs!0C$H#6#VBW z8%DxL0SX4d_RrZ{<8(S-!#hiTV?@5uH7o6nCZVNUOLe|Cd?PyCMh^TlfPkEhxehrV z>1BE@bZBO=xWAqih?ZIHq_ki{Vo1p$ zbR}kujm*~n0J_>_SmTXR3$E0_LO~hGTvylL5b=(u;X7*;@kXsZ-K(TY1kzeag(ks1 zSdaqbfJrU1{oIfRC@X=9@q6}Y(f&R7A}v1l(eCZ;Vvbm?40)=--p-8MsZB{q6>!z>p$WL6QYDN;$t2sXGNouqSFQ)<$9g8JHfSRUGUobI-h zCS>_A7k5&e91uYnJu^jWP@H)tu8XHqP}UkV;9XPpXz;JZ%{*IpAI3U;<LOa zmo%k@V$SpAyUGRthDLqi)W;bCYs!36`vv?@)h*uRNsTYGrQf#f>6ej4+YTFPjV9mE zb>Q|Uzem0`e#<@|_?xUldEy@pz`B&tm~NzCLtJvw1G+M<-!{e!Lu}i?8O?c}pX_Ji zPX*pfFM@t0`1ivTO}NV;adQ-5PCU6{oNQ&_V?2xwISf2k!}td-Z)TUKhpe;wceD0x zcSq;{0EK^K9b((Y^IGfIvR=<)FP65NUWcgO{i1J_c}2`otY}Amg4IND&Eji_?d>fv!api2D0j2A{Hb&Er$IB_hk`<1|yCX2csg{VqfJ>JRa&#HE^3bK<0ANN@JqlR4At+gbzM`&ekOt&egN6;CnBu;f;7=I-(4H}mPtz=}hY|T{BF5XISwsi?1~TFR$$FC5GD0IDlb`xnpd&E<=_qOP`l-EY+%oh~@5ao)rC* zyglH{XtX<-@AX@!dG6)Cms5&Md)Y}NID|Bjj>CS!?SZ&0c?ZQ$huW8gVVT+sV`XzU znHt3MDTfTCf(xAX`^O-I!0+~49s$$*G$OLK@YSu&h(cp;?6GY$844G1+8S2_jFY$r z1LoqmKZ^bUO;+;y+S%lu-Uzl9K4j8LM{}GXGn@i(oFKr*JoRCwf<8F$H^aMY(2!gz z{iT3tnp5Spp}t^8T#OONR5^0GWUp=MY-&V&HD|Q^`9C4po z?e%McXJI0%EFw-$#m0a7_441s{{R@;cx%OW@kOUkV~ER-Iq*lQ!*}U`I@h_&dNpk? z%#LQ6SY%}974qCyTk7;&t#YFql&*ENrd&L+u~r=qJm7*qP%DAc1XHdG9Pcj*@#duTby@g{>7}Kv=qd4BXTYz^d>N(FJ{clSAOB0^Zn`%-z=|fUY zUi3|9+j7KLmFx5FyO99naj!=@SXhLNs_~E3xNieoMifTF1r7~*MZTdL$AOMVt$wqT=Tz#_R%eTdl$_nq zJowe6t0dhd%fpSRYLBq+ z4tjgnjl>xEPIpYs3d>W;VDQ|VvcNWLM@`UH2{#oVg?a*b0n#n!Zv#Clh3AKjra)20 zwS2_!)>gRHo~J|b-uL@AVcm=m&b_n3{vnFvOH$o_wcz?V3vG_|-FRbEk}Gsw{{W47 zOBFc3b($$$^r>}_Wx{X=UwUocy5=poIW^4MUdUx1HgapH@cxjxq=2(y8100 zEyomc9xKzN)a_gb1o~s8c;AV98+-k)C&(-8-7n!!+M#i?8^@+<8_(JB+)iK0+n(EV zUmck71$fl8INaVbRw&7=M7E|fN3L^TVeuow$*D8!85sK4SK?@Aw9e7gR}rae(#0{2 zvcP^7_gVFNv@a8e3z*~=;kwN?%N}rf{Oi%Y6Y!tx{zCbA#yQ1U@O7fy&KdnhdRCi# zYo@{!J~$lu*XEe1Vk=a<-iK`%SgUW~h;-{R&a0nF!q?!65W{Eybj51;ug5T1TR9J$ zcFrrqe0lN41-o)&JfH7Zzn0+~<7SShE<>R44~(PIU&~e?_c^Y8{w_&wOnz%0eB!*X z#Qy*g&bM|-$Wt7ib6oOkcQQ)N<`JIN?Yy&-Q%|hHcB~~z z(^n(foG!7fvKfQ67t9Pm{w!ARr{hZvE5XpuJ?*v&Vk>yZ%!m8TYmwDtl1s~`xNqH) z^0)J@4^r^-n&k7OB#??-aB;g8`es*zaq#P1xpimYv9x4Duxiy#F!*ak2$ zUTd%T{8-zx7~OF4ZC-v&zux!yR)yDx=kVBI@-FEj4}s1B&OcA*RTD(h6#19xm+ddg zw!jgL_bd)T{5#jv!w#0ka~w_Y#64QhCyL%Tk?t~8Q{|1n$NvDYUUjVA-r7zjl&as{ zWxxssey6Y=r`EkQ;va(T^hh5584(frSYf#H25<-mxXyZDo-38ne2aPHoBKoefQEDA zD@79LAY|p7YQ>etpUcG;R6PzA;|uiZJ__gLVbIp}d& z9yjqT_>;u2TiT?WaY)MBTklysgaDQ@KResII}DOfU@LFL*V;YirW$=iRJOFAJV41@idm2#LkT+x-tlaG7%z#3%6)L#5(mI>H7Zw zj^6g>;nglCl1wCy(X2$N82hSDI3BgjU0zyT&Zc=@HD%lY>bdn5v}IuNOQh7Hk2pcvz^$>b4=SiZ;Px~<~HBmAGejhw!EY;?#{2pw1Ptm`pV6`T#J(Qa7ViPstz*HI;~~yLA6yLk9@a&zsgHAIH;1%{ z=Si)ky1h{)x`{F)+Sv|x>ySYF{VN+-(MF?jHT;vs3Atk-U%WiReqLdL(xfP8vxTJVK;!T#?IqjAsMa9+<3MQRk7QnnhreHb=|0x_b`} z&tV~XV%)x_RXF`>uCL+Sok@Um6|!e?DUcP%>5ADc2ARc8c_q7Xk)K-VFCo(H^k^+& z)MmE3oqWl>$Qgu((;%Dy)}Yk9D|>Hx6fLZwJFLp!{zkN-_$RCQn${`p4UB|FQbt># za7iQs?~dN|$uR8R<3_prU8bRLCDeSzV7f6%ht*C&2E8lb&&7RzSMlYDyq_VgRIYe9q^b{M>~Dj&t6<3*k4x-7`tQ zy1Uaf`E^*~MO0t329a5b0ILIy-A{4B=~>j3m4NU3Pw^(}#oi`pw3%komrjw@Vpww5 zSRg1!h$_6Uep~`c;8xy)7OfqqGQih1>`Tf0qLInui~jy>xW!aAj%l{7I; zrPy5gQJgE=vRqA$r5Z9A40FL5z^bv>U(2q`eAZ_~(5|;3R=swMAY*c~WbOI6&w9o> zvZX5>{)MUO`o)f;EOOfE7g4#6=HA*BWwj@!Bjb$sUO5#Xhjjf*LD6I|q)?mrz!wQ} z(%DNQ?Kv^GF|fvZ;~5VNeD-aslTCyWfDG4~yW`npT;n>Q^bGUP#Sp zad{)j9}s*Ws#`~LTKX9lducq(i+Q3exz8I}&I<1w zIyX$`9A1ZGAI5Kj5bH46Gqk&4N#l`Zh}=e5_o|by9^VImEPQ+WC+n8>5IxQ6Y3XriW2Q?U*{6wAD#ztu{_y7* z`GXQSV48pI5%B`g;`hK07W_TGfg#baY%U=d4`{C(4ABIVG-9g4A2D|*%WNR1I5o=r z4EVPXf`4g$7GMFM-eSGvkkl{6nK&U8aku zUVmz7`e}dkTL+MCxkZnXM1%kW;0>oaz^vQ2+qv_X?2GZ@)4;bnj)i+Xw^2`ZHJDqq z;@())4eicKt~&aIQbVF!czff0#>U`}DB9mc;9q@j$RS4AID6YdSs&O!4cEr;7FOhaV5_f8j5VJtpQW``KR; z6^r*V$p}oc7V5Dm2X;U#8$AU`j(_4+x_nIdvjxk+In^h#xRwZ3*t^E!;9A@hi~dfG2U}si!8rcddBR?%nOJ;?jab zVG^)SoR5|@7!nLC0;i_$_DDU?#Ax8xbhVI}B6Q4Kq5=kxxKaSx4qJi%10)Y>=e#3p zap4O~k#(z1+Dt03v~t^_mg?Y^!bVQw9kQb;xxj8f2AG+d;-3>;>Ze^(p$~0{@0L^z9iC*oLu?o8byJRi`mMVU8^3kbi7M7kYzO=fy z{{VzfMrcuWOS?lK_OQ1YBu#xF+ZN^8zDx>sh>lzD3ecW?Y<$P#zku2&!tWnx(?OzX zx-Ow%AZgapc_oc`8zWa{`Pd?{d;$Kq8REUN(^Hb`;f{^=h?h~kiZofJK#{}dNTv{7 z80EHuyao)TxxlY5(>zP3d|C14m^5hhjYq+D_au4u^A>i95;r`I0;y1=AH}({*cI4n z8pZE~{ug+1^e&-v6HZyDp50bKI7y1QEZtZqpw4hdJu@u9{id1^4(jtr(Oky7Ht zfXG*fD-fp#BL{Nh3~JZx*{J+I@tj8cz@HH0T}k31$oy3u~xZ@S2 z@doNogm;f^p-xT$;0$uWjlC-;U-)OD zJ(z{>#-8gE`LSH;ELBjj8^MqU1QFK*ADE`-zBO;`^CD_mt(dm?65jG8x`+k^RBlbr z<>#jC43URi1vR{N<4D%UB%4hKY^bxWma!2QOFjf>m;|W@10#dfoT%6wCye|f;e9gl zGP=aAEztwX7r$%rloF&2D2gnOk!bQ;03nXqe$cWPm<3|0s$;>x4r3#)1Pq@m)HO|C zT+~`UOH}a^P9|^(xP@gAhj|kl4-Hr7L8upOk|c-RRmxc++U0_z&PAbOj9pz zqglePocD${`4W4W?kA1hlE-7JNtSGUgr0InMO?YmZ8Tjx*xy>D`e2vr*H-tCEHl{U z4%aBj9GeD0?-DRS-WjfGFgUM<-W`j>y4?2G@!HEPzZVdJCCYaRCeXkP{TqxG<2CA9 zVV6hJzS1vaj`Apx*5m&o<;~Ok;Q3*>OO>L+}DM~SSZ%QO-r## zRy}W1@XFuM<~NkA-ErQrwa*C)*ogya>}yBD{w27U)=?$9BS)NqH)kG(oo}d3s7w?@ z&?)F!t9~87`t|%*#kg;(S8`I(o{cEpH#m`_eUehFH)GeKtS=L2YZHlf{G|R>>3YY9 z{{ZEwQ;O#Gogm9@$&Jc-8u&$wsqLcTYVM)TpG@-8jZ}M13t@9Xk{BmUMr{4b#o9>bBcpN@T$(?nm)ZftF+O69UV_< zY%z@e?&IlIhr`Nh<+oxD8&>;NfGO$M+P#0_M~AMpd!*i*LB>1OUIh42X{U(#NJm1$ zrF%bt^k{VEkIPj9BaBtn%jGLwgeq&X*!Um8%`KWP@G=iL&2}H~t&#c2=lz&X1;kz~;7Zl-*d;-3dcaTBWi*dCb0c`uJV8n(>su!4On+%I({S#tfw za9%lRuHc`eY#I7j(r1dh_Lu{U5hkkh9h>z^{T_*DDzpPsugrP z?;3cT<|gviHkDI>x3zf(k9C{Gj7X0yyCFaDD(JP3icvL-o2HUSUSd!?`sTc6#h(x+ z)LP(A8_gSZ0OK8N>F`-(Bkw~~OWa$%YVX81h7mmP+=TwLz8KY7O-ZcY>MK|lHgh{= zgm@Xr$0Yg=_0Cy%&OH}IyNMy2&A34n3I{??Px2LsrfNe;f#kng17VKEVe_4*0Oz%R zeh!VJar2!W^O&qI=WD3va7M^a60(fso^kkdUS+Cy%H~_gh9^iRWg(ck%4B1VVzPCA z7~N{R$~~0ETJho>wLA zyu*#700d6N;N+9(j8`XN;wT>f0LYc@tS!gyrIbh-Sye$KCO8h*+739)XwBhExitvd z{@}#C!l>gV@rE6T$#GfHBT_5}9r{+mCLuGS4+B~a;eC=%d4E60* zE;R{fym%~p{m1XdtY1HY{A$v$mWt&`^?SIkV>=^YIN)P=Jwf%V8s+Ho+irZw)GH@( z{{YslTg8)3uywk(Ho3?IU7rHK~e{i{L zdEIRn)6)n_mHC@t@TuPKkyNpo}A{nEkfMH5H_b|6z6F3Nh|Aud8svKzSAH?xQUWk z8wCRf+4Uog`&5_T4&+TKbuDIf(yZhJBy%0}&OH$aI6QK3RGBT5BH3ZbNfhsw+a&%qcTe&3ULw1< zwVumX)hBk^zC?p_G3kH+Bk7vZwAAeN+p?Ehg|eypkE&WBUA=*lF}Qv;r{S*!_-9ww zV6=)$d2i-tSm4v7xK_A3cnZW23Fep)S@cdd+z@6ACDt)_U}!>*dGg}tWe znN}e??wNN5D7YUifamLj(-i>RKqJ4&_!Hn}tD$MjYThZ*JU`^l(?JE)jWgJS2@8Y; zv$S<=a@5^F#Lu8=k8P^UZLHe7&pS1;yc->Y^SMqiak%5V8gY%lcaC(s-49E$8b_a^ z+D6-8bda;F0g>09-Th5#_-4mJ@!Z8OMy+wFTt;WOxRNB+x?DD4eF^Pv}wBFyCRg!CjS7J zX~8-A3eiR0&1A-!_N-de{Ed9h{#Pg##Mx=nA{{x z3EJQcoDO<%=xf|B4Y!B90UfRF+*i6%NS7A6#ktM7<{{V?}a7gPGpyp~bVJy;_ zBb+P`X(OC0(6B!$F*|U)0@?41`Dem@1()H#zSd!}foIlY`%~Uou@buEGpp>OXvp2_j-R45R=U51SnN*XBo#eiIJ}{{X>58cW9p-^jKyNE>PkS3X;|IV=6OH>acE4}PgP8v_7+Gs*lbWWeHY=DXJQ4L(aSl3LqdM|jc_#okhRMI(|ikC^nv1$15_ z(&E%KmY(j;3(JHIQD!;slfeG~SHV1r-qUnwY@mwX7LMHBHd`X=R5#9dQ_U% zg_0QVqVqhG`^0C=2R|uM$mkDR(I7ly#Qy*sZTvCe%bOOy1I$DHp>=K+B*U{WmV1f<;h2w=03+^?4IIa^-<@*fh+}w@ufMr=eR}TD+Ij-Q52GXIg!p+sd}sgeyrJ?E$ww z;$IDrho1G_%VTK=f$nsxi+dY=BF63SAfDirv$wXC1urqi>>~nLuQ@o!dh#EDe-!@! z;UxI2r0LpJOFxTsMqL|Bxt#BZOt(8@nnXZ6v{>K_g8-b9*Ev3oy5HTo=`>Qj8G}UuHDPEkOP2DNb3j3 z3k#+Ao2Td&cK-lkT@Byu@=Y+4&yq%96sTeVju_XkLZcl4u2SdriSbswVsw88+x@2G zK{`izl6j2bBwWIrm032fci`Y-pG;*d65Wq!vC$Sg>$xHL3#Mu?O>N|?uXkv=ijqMx z2Ve)?{{WU{v-5TJ(eQVGw7pNoTD{Jt@UO)l6triSMX=N3^X*KminBAIG$;wbIsE56%PjeO1De{o<~?&q9m-;PBqlzXl3?EB%r5-~P<=9Q;jM8e%JZzFFE zYf&K#QU2F&^2!4!+P4wNodXfgbQ+h(KZ^eVvz@$pXO8~>u=Sq~s5Z~7>lRs!q2a?X zkRCZnAV0{;a_1a$^JqVA&ldbG(xtKZr|^GRg=0QMch`1d!#4bqSSaUqe(~@1uVnZ+ z`+WQw_?c%8k!Px=ogAuGZ9iCui*{px$O~Ww&rRJikViSuNDAH_(|kYT+ZnZAg+3+H zH2E%`JFl@v8%b?E+lgTT>l0uu?Yxy?*R)qZ6m0xysmBJXVQp)v*~k&?uMjERI!FTm zr<@SO2Z5X(wZ0*I0Gr`Ii>L7)!<#!B8SdN7)GX~4U|mHE44C=yyudkMm#I1HSiUIn z6^HFFq|CZ))wP$4>`N?CPauj`@>~5(?DdE5V07;^(6oOKm~G@uQtt9gV5oeil1QAtA~}{#yFqdplw_;nfGTTW#vU`a z)2=SuWy+=o8vaq-gTO85Sg#>UN3C4XVFBN-4n@BrrYl2s&DYo%XGvT|s4U=3d;RK`oW& zQ7U}H_lVE?$4$z(Q7KSyz+S$2@ngc5?P)A_SC-=0a~ZU{nd6mLYVKW*?j=BX6Y~`V zp#uW50v#R;OFdFKbgdfl#9Z7oXvr!~<~DhZ$Or)LIKUyg9CM28FFbQ5kElqJ!*>>> zuAr=Q`ELL@#?sj=r0`da5Pf;`?+>oGZ>mn#P}=H@ke$#QF)DXpu@8>i zQLJlMy8AVxf=8GE8(ZAVbJc%@GXv$7I5^HWl6M^DhBRn@Xj@CEUB^DA_HR6fNNsLm zbPu?a0u=xO?TmDsI!cLN)%&%7_H8;FE9vMs|# zpqXNmZZW!@iJ@E&^a%$2RhAqHko_c1#cHs>6i^HmQ9<1k;E@QzwP2xE%r(%uJ z8t13+o#Mn!?BmkCYs5bb9{$YvkbuMPp5naQ#U2{|z?`GTbI8HszfHnbp;7WDspr;( zosM$r$G0X-7H&Q3q3{N?F0FX6hHcJAQ=eMnd_ktVznF=&PfvR6e`m;6@5I4e9H8BU z_}5*0G%TWwV0|y}`@wQu+rmnKPfqyhUb&=vF1Z%zF5dkySbqxbJc~Hnc5#8yz1PAw z3pI#WA55C>p_fK5x@5^*=DaE3X2R}_Hy*gfdOnHp`cEZVD8Sp+wEQ`yK(ebHzdd?) zhb^OcLZAz{jQ6XK4NFUnK=Yez7HdQb%hIhzOlyqvuII#l83~pE5Z;xVz8T|hmQ37i#S{l;_LWVzG|9Ws^F2@xnC1_F*S%-U@7`mnXX9c zM$z2kg%*f8)IW1ER^^hqW{ff{l8%@)p{V$MFErnjb?aDqVe_L0xc9E#LL%ndY2zZj zd=(a_c0^@u4?os^8m+~ed_vX| zDohYu1DpYXpW|GIh_p-+X>jktqFLNt^ z5P(Plk2&kYjGw}?X7MD@hiF`hBwQ9JIIN%Tc^4trHv|B~AZOCLBO4UXtH!=Lhg|U# z&u?)outXLZM2vSTB86OnP77eM_hVT1`knmRrNz(9C)?%8R2#g)dW`iW*YK>^ZqnUN zybFmkcS`G=A3!?SNubzW_?cQODOcygvbwLxX9OQq@8cAsTNlI(?P)N+UiDx;#3c_*$o80RhuWYdk@utY6``AE`8S95m? z0R-cW`eby?QhiNhY{zf3j#qfj+MsU6maXA^S5ML+yq4U{Wi|knSA0aCgK#(p+N9Aw zDr-6}oVKxFTuE$-R6%eB#A%;E;DA1usJtuUOZ^ev_TKJ$D_NQ0kOBl#;~52UhQ>!x z-`=Ivb)6?vyCQojB9YYXp3#EtJ(!`u{D`P+F^qqRziKRQH2Y%2b%3-Wx2XV;Op{*l z?zeu&Shm&U6Dd(GrNnYF<&2Ji4l3=B!!L&( z8n&4)d~+IZOi1>VK<)q_;CW(ZQhu4^+P6zfn73n}(SK$6uWpO#9wYGmlPVXM7_J4( z(V*(zA;vOCL!N8UJUj49{t>HqUY-d zWj?#0Xx@58*9&LGOM5p?`B<23oSL|9%Z$Svjl7?UL?TY zakvu4jPiYXi_Lpf*5en}w|Z@)c1$5iuH`;pQ~X2~`Nth`TsOR~Htur%C(@rp*9G>S zc|F(*k3MMGQds+v0XX3Lp0&_;HGuGLp>lNy%32kh?D4i1bY|K>1bp3z1F$6YskHAG zYeGee^HEESTa^Wp%@$>l@)s@GXN>#ws`vgk@m7g{XqPtg==TfLuv=BU*OM7cpxLFFeSTnHN*A_3QTtx;R zSDG;dB{78|20-9+u0sC+z_NTsfn~hDHq%8ZGD&G74f1-jsOmnw>S(+jap7WRwDD$# zt0lxHDXpg_DT1lOl1~{Pl-A&9TH9*(9}s4nMY-D*+8FMkiAuvHeuN*CbIA7<>mRen z#e3g_nk=)~81+PgV5U3wFBQOiWj6E&8NnR$Tn~fvZBt3muAb*s)BMfgtHSFe$TP-C z=r-W~6)n}Bwx6Tj+_mnREybew4j8K_{{WVMG064Je7W%B z#j{)ZI`mlB-bZC)98GZSixU|M$avp_lEm;io9jDm{uFCvTbSk_ zF`oc@$G1<`x}h*V$KmJ1TcdGn2Z-`X~6Z@fD5@6xU&!@NNoEfl4A za=FHOupFK%(ZArI{{Xb*w}CW>yvDJL3%3xx}? zSzX)@+wX@O-}dd0j@~zL@p;yeq^4rg;E~T^$0xC`WcX9@9lR(mZR3gHyYp1sEB)Lk z#(&zc%s<%k_N=(tZLc+GVzq`lbS}~z&nvKvH#Tq=ZgLmbo-6N9g}yG*J}3Ax-LH{l z)UB>0kL?hE=^<7)AdX1E6%eWKSP!A}uZc42Pj_x4f#Oyy=*Tf5eGW!wyV51pb!c9| zi+Gc8`B0!LMmDhSeLbtjA@Jt2;r;ToqW;P9suU5o4W4=6;{zVm>pu#-cy1Cjz8Icv zxOMX4$D#Mlb*2WslWTu#buQV~L&3C+w}Ke#zbzy@KCTw!HD) zt7?#4YI56{#vcLAvCdXC816T1@9Cr7toiFw~66!&GMdRzM zX>|w$*ETk46>XJN>{2@taz7JX@^>iuYvIHA+frmzA`LkeE@6i2P>td%oE8E+f&-$h~S4~xGNzhezrJrBkDeXoi9CT-cR^$S)!&E=ni?xodlG>uH6 zs_GUD50x}QO72mNBLR<_jMvTgJ~-7sWG{&J+Mj~#uC*TsYO~wJ=92EJPs+_IO9-HnMzy(?&&~4f*Bj%I z6y+iT6k&lNmKo$$Pq-dkrhG;brN)n`SIP z+qvR7*L;}r*p6`LEOHoTyUicNSGRu&Z==&S zJ6ox==+a1%Spy<%B#<)fjG6u{D<(kshXcf}b$fpc_|j=EwDX`#s4R~1MR1c@JfMJf zqZL#q4f4tIRGLohlPB_WOG>d*LpSgg(R|${{TYPF5!uuG+6w| zqg>}WMl6^aB}|7H$l5rpD;-63t+s_U+iPjsG+7&0nFEd7{_%cg!NJ;7pg%o10bO+2 zygtUt-$>M;YiSEZh_ayk!xAf~V8E02g9E|N2(Gi>&%?erH!uTf03!p6{wVQNFO_EcyWM}0 z-G{ECxmn8HsfJcxO3b~wFCNC{Jw23IVjbQj>RPQ zF*PkFJwjGl04t7$lTGj}Odk{>^xY#~*{uExWXFVTN;oUuRxuzi75&!~PvB&AuUcL2q>VQR4^D*Vte<-b%D5 zDH&Ix%&dNTc)vkw8%Yt6hm7(KbKeiVRD2=gMV#&t90Eu4ucAIHd?b6Pmt0i2JTtQ141+J%^I$W9`Y5W=ZnGUO@M5{jSxbN#;(c$liaIL&b z921=N#eQUXU&oq{gsv7#JDFoDe(vLn`X}Kh#(T|9&Leo@LPr@rYwO-&$sSuAvp+z* z3F7!}l{YqXjC0bxYr?)Gf)rLGJv-OK-w(bp`AUVc_#9@vKScP;bS--U*aB-THfb0> zW+f{hP;0(9vuG52!yH#Paq%7qBrCm+E9cEq<3-iRc9uWzFaEV~+KgnQ1EqW2mcJD8gvJkSp0&^XMUp$H2+lyrHRRO6Q=GY) z)Rfur+*g0uI)OuCBwx5JqE?DGP4wD;wj@yjnH?0NUa^LpC2BKDEwx8tHY5 zoEbK;#?hSm)KkJnGQEu=n|dCv9ks@>E4%TvayaYlT<)o?Tu*f^ky8vxHxdRnW3^wk z*X}f+(a6}y+6`mtdcc`a+0JJJ1^of9q`~17_d6p6YqE~9<2yThXrS_1DHLIb+;r$` zhVjOx);6syJ0VM*rc%w&cc{K4c+NXDXSm;Ua=9UZ!GEoJuZzAU$*t@Xq>~`d*xkNS z{x$AV!p17d(kjg6zAH~*t6x9%a@@rN<1#tsW*tp%`o^{9ymsn#I}Nx!@&5qpuA|5L zzuRUhJh@wfy{I$kdS9K~psL-!_dkwKjExEWt z9&4k_YCGY52d;UpBg7iN_MP;yt0S`$!`1C~Q<7`9jw8K=Vd@sobsI57gbw1ij0T;E zrk-23XMLNSt`{A#Rh}sAE+dv;#E4nqQZNjeDyQ5G6ZJJp>hf(m)K8Nl?jV8N6-D`!mCAH%j9m5WT4kX=NH zGx4}89pOi=TW{iue}=Gp5o-i~G}C_5hk`l0=~RqmP!~VSrSawZTxu5(2WM~kYxRFQOYEXOdUW5Mk+q&Q;b(f;2m>8@Xga- zYr1X5u!wODnp}%`99z}kDwf%O6iN}XU%3a#@cs@^?@n+?H{LDQ zAl;iSGId@DAcqu4~cZk?rQMG0%mn+;IvQp}6het4r1 zFF(N9Ja87vYNWjfuQb~@? z$?+ZLu(yda=-O75r_8Le-CxR)Z9#?#pzRnVuo*cVoK|#y5$rq`@!e{0-`;u5%e5@_ z+m$%R^2|$edt-t+)b?I0(jmR`m6k^t-z3*qx-IV`vHt+aaafn%8W+P%>0u9qG{8Rd ziw4xDn;_?Ok~TWv{Gz2tsExt*Z$L_44Za+3g)7u=; zZ=mbibe9^&jcajub(>~4HkynJBdd}N1DqV>E_xmXJ`dUuYp^Td>b@q5&OiBWVPg%Q zyo}vW)m#EdKD9@}oq#t0ySJ?afgbKx%)HNLN=%#+*~x0oX5W9>ZWxIfam-xB;= z)ieUqYg(?g9rw)5X%dLxS0g!NjPZ^~y;C0<8{#FyJi4q_GR3w@L~yBccn4`b=cQ@z zpeX5@W#*TnTim_B*esPyQrz5p$IC<493F>1kF|QG&%=KS{gzhIH6_(jI6T-9?Y0TS z4hSb8d*_}jiM`ZUQMKD=tlesn+!je9zlBq5c+VL6cdI&2jCJn{+E}fn=82|983qJw zGZ1?oopDg5eTq!&J`(&#d#lUs2gRzU#ea5^#>OX)BZVHIX1xpHe~E5>3S7og;`39A z=Iy4L?j&g=Yi>3mU~!OZm()BrsC;13q`5cRUYDg?M<3cuOPO+@f>h%q@yYsB-VyOO zpAUp>{4t|m$Ei%jj2w8fW|VXNEN-r1=v zrN;2vc9kPMj+|FTb)eelu;zI0O#FllmV@=u)sAk)wFVJ6$s3 z(o)kycEMu>cLJEk2>cCxQ-8rJIN6dVS~Cj;KP{YS)b>)sW()918M;xQ475LQ6k5ZrXD<+=m;mGBRb z?>s55D3&uKt6}4JE9wC_?g`C)rvCuJHU2%`cw0zkg4};@bLHplRZo6;@(%kC!7O{soU(=bbj4(Mg}F-U#@a;h!FOE<5?%E+yRRrFQw4 z3`YEMkl5qtTvos0Zw|$O=2*;^$EW_=QYV1?FXwB?cX@FrX%xb-{hz2g zaC3k;Ju*P4bz9QIN6=02O3}VF*^7xHOW0*tUfrY2kjO|Z#GZ@`V?XTI)E*D`Ves4G zzLk689b-|0TGl3EA&|D^Oy~Dw+;P*^zYhNZYERoeb*~-HbTS~3LGvYv-a_*3`H5!a zo|WjI0Do%V7TW1rWrnk>YTCV)n=Gs%Rt%vf`L-(&>P|^NOjeMn4Ii-Ze$aDxzfg}) z@Wz{ArfIOaxBGp>;U0NF2Wl@T3Zw#f!L57eE`bAUbg{AI`O7tBg!Z!ArgWfDlJ=?C>qmXe55D!mse@bbA$LRk64|P8ZLLS>)YwN~LgC-Cw zA8gAh?4}h5*}@ zf%&_(Ip?KmC&Wv8ttwl4J!02Th_Bi1CbXEvBq#|xSPX&J?{(s>AS?Vu_+Q~)k2)pH zTIQE8gSEsv?Qf8P*i<1-TZ5C=@UNS7KN5UO{fvA@`d+(zbE8EShRQ_^u;$3+9IhEw>wrk;Vu$ zPvN!ZmGR5KmwHgVbklVi+8?qB#l6Lq!D4~qCqE!$+y*e)j{=!<@IC$qc+X7nXM@9C zcs}4V&0`xQHJpwxe6UB{!T0ATyiW7QS04p`YQ0Cp+GJBEnc^l*R?b_Zc1gLn$sZWy zNZr>UG3koRV0*9Z<*h!erTF*Z_Pc9#E<8SFnj2{)^QC#B23X~Zh#WeQst*LK0nc7T z_Sx}es_I@I)pU91x4(}`j$tavUR2V&B`Ftq13G+TN5m<4%luZ07d4IN&XG{ z#@^(5Kf>P^!K&!Gb8A)?I=#i7prTK=+^knRESM-Imv_to-0`<09FTZsdhwqM*)E3- zoR-dRbnDoeM1-}%-ABOz^ZvO;K?kTHgVQzZ+E0lX>-PTuY!>=e-l1x?*7}P1pJa4Q zlu5!2lqc@D&DV~Mq-YOs*KQ}#d<8n`T1B0c$0wN-Hqy%STd`KcgB*lqmm~~4Z^i-p z)#u(7_?dNk;oG=xA=78w<=kz&v-@NoN#R6*WwvBvETo}iIBa0<-s-2o@md&Ocw#pG zU=wY)v}AIu(FXZh7t9$_x!6v2f=E0u_^+gRKK|N!J!4(9(`2|n$u881By5aFG7#tw z-7&EkJvV{@tB%FEJ|CT1P&eKjyo*7{qCAJ@1Z`{qjuA=cfL8{&A0BuP zEo)nSPCKYH$>#;6F(;K1%t0*5K@6dY$Qb!pk`4|l)%9N==-O4yo}!Lqy@{4FES75w zjKB5OR7UB~E%K5Bb;fE9OGnW4>np87#RJ?$A&u?fVG)2gA(bC;vKJf?xMcMQ2CeKP z%X|yseMiIgQ(NjfO{TA=@ADzNcM>hbf6o?W`?4=B@}A=(99O%12JuDD{3Mq5{z_Rw z#CewU6%r;oa&fo6Ko75K^4(qtygR0*rQyjgVkNx0!XYlQDQxYL05P#v?H>N3nm36op|oA#LO%Ny z9P`|R$mnX#j=c@^=gV-w^fl*D>K8YD7qzx+(?nrci5a>f@1OJdRj&f0>AH@3Vaamq8@Iu@`*&RKrqVT=YX!DMsRqb+Z)($bq z{{Ra2vknZwSHRj)O}@R1ql%Je&$rM?qTQIu&eD3;zlSxE9LwA{=}-8NWRTAzsK>2y zIvxJm@xUPDd)M<;ZI(I}CqjFsdemyEE2G$~rWX*aDd38!toVH|VP*gl-9{@j!#d9B z5qj5EVG~{{+s-TECr+h0p6=&!l9A&+F8E@XZzssE4^URTL*kdfsP7<nzVVwMpz2ieC@TJrw@5BSD=8wFRB z{{XL5wa*#brM!|Wl^kSx*YEXeHwR;qFz9wR{y7hHi4%}p&}O$H)?o1*GNVd@L>W7e z-v0nU*1mpkijV!S$t-39nA%Sntt;;yE~92-676C+$9m$SPNbcfdo|Fb@$W#2Kz6)M zh8^+Vyw%}@O`J3e1Fw2ti(e6Dk4u(6HKak&vyI<{ao!p6oxg}6YjGwY9A>_TmJU|2 zai`SpZ~Sv|V%>r^_TAR9Z~iJvrz-hYyZr3N++zUOGpKluJre1ok)^qd_>ZM?-X!td zT6N4X6R2fzjEv&Fj6DSfWOPO$@fzMmz7tC#mu&v+PC|69BgTFtj@HR--fWczBoC!t z)V29_8%eKjW?kxe3~{@ydDn||D0R@mb0Z%9mD^84M;jG&Zxlxa5yN39mEDeajD*iq z`d2-x>w?DSOaRzBITcCdG3rXF*x20CnC>pMGYYJ1W^s+@10>_Ec0$aC>||c*?H$38 z09knzPVdE%>Y8z}OH_+8d5tFA7d?i0fH~vduU$Q?Qn^`TbR8Qw+<(Hc_3L;h(-{4u z%O|clKY^`d3xi}@r0E`tVv(;rryiV2%>Ql@Qo!C8nKU#nGTy2a?BSw1V zsJOAW((T$wquRN~{O5CeRW)=C*oQ{dj-#%^8Co@BV|HPHipo@z?aga=j^-EP= zf(wbGS(UTr#vJTFm+6Ybw$-iTxlQUq_apd6tw@(9Ym@d^{P|dc8e=(Q(BiQfI(Z1K z<}ePwb#d02K9MPr*e(L#el*CSmfdHP;Yl$`UP& zAG8neK3J@4>@s@dvOI0#zYBP_7}aOiRvR#W%V^fC6}_?yov0&k%(htd&2rZs9lVY$ zm$F+~`uWm;!~Oy?$MUFj(WQ86QS+`Mmt2lMRty+nnBe0lC5nTBMh1F!p(eFPXQ22$ z!#6%H(ap+14X=lw1}IwM>S+br58;go0gtc;pIYv`HSoS&GVDR+TUnWbVWvTI9jxyU zc_(!qVn#cF0q5@u@vrz=dak*w3xfso)ar@~nL zU!iz|Rln4A6Jwx38@;?&2^)?3)flVs<*6BLWSpoSYsGv`dr?YK+o5KU0sL7 zD`vTEJHk42c9v0(D)&?={*`!3aEYE!9AE|HjGpznc6Ty6jVDw1iK}XHY7EwzO|+RQ z3d0z3J$4-9@yFJ^eou`Su>3HON^cZdcyj#<3;kHZ9M{(h%vn`RDBF&i!8{7`4-)vm z!>n7`P9u`ySofVeIT(_AG|%^xb@_VZ>sb27!wGeZRG(6oC@tC|?6$|{$D$74`qOn* z%+uLPtD~>+UyXb{;*9{=S?k)*h%Tmc=FKF2T5WGe>Uqym-mmye;ntaXp)$j!T&AKI z2-7(uK7$ypH{qVIplA|J7MEi!y}gJ(X}XXWF=Nv_XRp^4>0b?X-x7Rnyn9axTI#x1 zj*g2yr#b@i^MIg`GVPLfj)Rg#Qg~v*M4#&jNUFMjlkx4XJ6?>;98;-b^=5kT^k}M{IhU^WTlS{+;j=bKxHgYBOB; zig?57S9bpZy}X)3(F}*~@t<++nz``5NYs2YrP%74&AGhQ?o_OC>I>$w=N~YS{{Yuo zM2D%;d?WbLuIO`JT3Bewd0_U~H_VQTkIn$kUj4r+b;gkgh}J(H&8gjKdflX=CtG#e zvGmwM1J@(*ti5_47Q+psnw9y`^sBp%ntQoYKl|n}&Ik3bm&5)g@a~JK9X9UUQt`C6 ziWV(4#r&9o$Ub|>P6jx}c>2*9*nFh=-^VMhN5N9b3u-o3>^CbsB}Ln04m0(ySn&76 zEn~$W2kfmazv5lun*><|alT}_b{PnGA^3&jzYO?t!ddm(Aq+Z#n60d%l*+n! z7{it$2O0FN>2*8L0%)Jw+D+SEUdM~uX(M(rLc{L$>_1AcYte2BHa@KVnLleSOX7Eg zH6IIjpG&#Wd_!R|MY_6=X^odWjPxVZKi0ig!&;wQit_$#T*$W;&uwcQe<5}l+*{C* zUm||XzADvxdEgymSMf@HkL`0T$*D-2ca{X;cPvKUodNf*`u_mqmG{A)37rMvhr};; zI^T&9VPXn!h{vA8C)}S(-i=qwX>N%=i|~)e{cphDBN5zMM=p|)2t3IoC7H3D9FBNl zUdixZ;=hG_XRAeHs*6RppZ#QVsA$U{;W_JH5qPiT{NEe(JuYn;NGvSHqTO9WPS)Lk zI3GnE)|bLxiqGNS6kbPn0MufYgKlCM$!vD~t4?q|)AsED0D@3>Bje|XuQh)Qn6!;v z;uZ+D?f#E((CuXD*1rzFXm9u@@5G;gekQqtRq*ZN+xd(lo+K|6pdO%(-7EIH!#^JN zU2pvw6MGymNw}*&8+}E3qFUH|O4FdW)a`9kDlZEqs)Ksg5;s?`*bAC?{&{{Vs$_>)c5Ba=|O z(a{luGPTLeoPbVo)BFv6Gw|d73G93;1KG418e71oQ!=-ltS|uEoDPGIwe{uJmu$`D zME92h7AjhAnY(oM&-1H#kA?iGEw7@!iC|gdl6Z;(xlkMi_5_nupt=F`1Rt<(hqW7@ zv|WbQE~CQ0w-^yyC?J4}IO82_wzmD0;n1|CTRkqy92Sa5zS5hR<2#gbk;Qv=ilWqH zm2WI>wCQezwW5>sbCG_{(FWX-x-+bem<=Fc2vlISq~&dXMMctB_3cSbQ%p zgY`v{TRlP@xf_Ig(T*?yBcSG&#UB^6Plpy(&kU=nT(pY}>PGi65uQh78Sm>|m50PD zUl`vQR?f;(<_*!b;J25IlgIM*s(vl-{lv0LW74$wq>&7y2*;R%1d*Pms>ul>#=ax? zi%9tUePUAP`@<@VR^lk-BHr*Eef2)zcdkEG)f?etz+7q3>pFWMDWtTQ%9b_v8RtLJ zy>G^!4z*2dO^Wi`8yh*ydw$C(#EB*e2tT|?9@wu1Z;H{);w@7|@ivoxqF*cdPr3ro zNKSCUhZrO4Sh>wfo9yVKKz%}!&VSkW>s}E*hqdnkYtw34JnOCK(-6kllp)42agU{M_#@-|a7k#I zGkI`bOjbq?6|E&1<0rQjiL`DG7*>X71iGA!>ZgEPI6Zq-2kjm3 z*nC&;4ykviui37&4Yw9A07)hyFwG(yk%ig;^uRuq!+3YcR-Y2??Uz=LI}K;U(|P9U z8|2iYAH(?9r^(?N?^9WuL(~PWuZc7%rm&X-5l*N&XO38d&urFiV0d@!Me$D8;a}|8 zrd~~RcWbR)>7z`I3-KU%SjhQO4=LZKa=6b=&fC9>E30Zk-(J7DQ4QL{*DUM3WCsY_ zxhThO;Kzf4t-;9kPxvT*?FZsN4E$TS@aPueEeg>z#x52?G>OYigP*)keGUdO&3Jx? z@qY7Lb-bTW(RDc+7Wej&+XeGgx)m$W@Q!+dqt>&%!1gZ&TG?p2l(uqR7;Nu(P;Jka zp+XNe2T&XU&9t4YHvptIeuVfju4*0&wwlC4f2-(Hk|s?>B9-j75J-+?847vZBLc6C zF+YC1C*dE&%{J~o?5V8&$3B-T7)*C9wH*A&03hK?o(^~?1b44=_;2DJGDniyYlPDh zMG%>;BQF$~95XVUt}?m6$ppC_O(CPE_^0s-$4%5<&PNeDH~KaiMBzZlLOxY-kaiQ$ z{H%WMdDXrAmOdraZ!{=wEo^3xO3O4-OCB6;1=Vx87){{X;7@ZsQKho}vbGv75K@g!apJHrnU={+M`G`EWG0>`6B7)K(84Tl5_ zb&sg0G)d^1t@>Sfv&x#;QvAt>o+8%?IxBFm8xUMD=YR+&f=3j53HWC|q46e365SFK z9x_a&wlL@z9#j#GXP!N)htl;4_02;^y1unpH2Z=iwUAsA6$lFPw?1mHASp)Zzz|q% zW9>f(bhvyk@djD#blp=>)1pQ(U9#pW;*mfCK&*F38y%#M$`}#f=kuDpxOQ|xH$IB7 z_;+(*;rKkYF0q4^eqB#+YeP%$!`d>EGq>8k8{y}LBGfdP**hnw6*7Ci)uY zd}VX~j?M;l)|fvKYexerHFsUiC54K5Gn(X7HhrsxhgV2s46H$`ekRo=oHTjH z?*5g@eXC@wyK|2Dug~Q@ja_oCIfp5*!r~w~8e@gcM00Ve<%oOeV@n1N6 zE!NepV+?u`j%(G8bCHgmlV0uPFBN~nK&~Mt=0BBrSBUf(nnwJ=#eBst z5op)0sNtRBx|LTAD94$cJ)ei6j`lO^eJioh{5fkpp^-u#rfZhe^qY%2p)9{LFB}e) z-gpLRW`$M0VmepR;_K4PBBJ7RQ*Aq!KFQ>$PzN5B7ulr#>HO=VmqwF*;4z+pp*B(j z_f33_e7cn>xSbb1Le_pE*Lj@8Tfn_IY+bdgv90zv)0vdS_!#}&Y} z$*|P6PQp0P8@)dYzaNNn{VvySt=no61>ET-K~MK{+P|f#<@b9N%4=%3jjKxWFj!?+ z_w8Y~KGm71_^tHqJS%ga`Nd{ypAl{EL~3K$VFiat1PVt4nV#=WXyFp!Hv+ zPX0WfQDOG8xe-FD?!XmXg_=`%gxzM^<^E}i0RI4Z)@Gw{bji0v6Fh16m~tw-TJ5a& zlCylSh2`Ek)L1x097Uak#jL@yl)JB3ZLm$HLnkEwlYQL zEJ6`1j-!Mdurv794w&m>tQW+B+biebZo-p{{u!;~I}p{2GfQP0@JA^OBU0<`*yHm& zQ9gCw3Bd+byjMPBl6r0!C-fDct7$$)qcm{gqfNyd`@=jR#9;n4q2YM1Zd}6As@p08 z;Cc$#AsMftq!38boX8gu#s@5Ye-GcVN`IH`*{Cn1Nh|;;x=za=T@V<|z%XJl%f~=P}GR@XAysCgd zib)t9qp$#+4A&XryOfVra^EbZlG(0{S=Qy$?xE5yCY5yiNg8!t1aXi7CxPDpcPD|` zxcz$Zbvx-o50L%VKY6~i#9XqTD~ngaji-_DyN}AN+X9yF5XI!Gz_Jg$<&T=NMYHMd z6teCfhd8S?jHbb^-L^2e5t0C6M$&i(v8)=tg(GXjx`v30=IWMr`jnqG+i0!@tV%z; z^5Kae?1lB@aBAO z*;Hdb=hW7om*MI4i%BPjIc2uf6ia;Qn}xJ&;RClkE^*IJMo6UKG|Z`|lpL!Op1$>7 z?&fQVqFEoz@|R-qj0}_gYR;t;kGwk{%AKU%zMpg?5Zm0Z@hf4!EP?_2 zC{eJau2|6gG!jtvH)&xhf z6Tq5eMYlV_CA1?RrCu@^{VS=x)NHj#_FGG(-%1~T>2&z6wiDEXm@Ct#PCo5S-$BIZ zyiIKmjp1ocx}%4c5=i2anWO8G<`PIJ*PQWQb+^d3X8Xi)ee2aeDchSL39Cx!9_c_u zN7=jO#x@Rv*oyOeizySrjXE!wu6D4_3X%NlI(j>l_0Z>THM^U;N3x1p5*Kams&E{R znf0yj4SY|J;b(>BO)Av4h-U{E(sFFU>d3t}ka4*51CF)A8|SyXEfWZ~kYAA0tIoSUIXIfl*$sTwz zGW5XhpRcuLhsVl3>82|I3Xe_MLsju^o$&WVxV+M|WrFKbk>_n+RVD8(iyWCq;~6CS zeJhO8{{U*Q5NU}kX;zWB;7_Yv6C;G@?&M&1?O9(CJP&u`ZCXuRQJ&)BSCh>9DSQU? z0eUDLa4RoN@b0UxL%&?MheOaT68Ht+oBse0+v%}*o5#1Xubt&aKFz0= z{u0?aBL^b_zHe`cwze9uweWMvZ4yRfz0~B7Yby5%&&oeq+0lG+b0&%-@TQ8f*(0B| zYL@_+ubg!`IXKTW+g%QI6Qi)ybuWoNCAo*hwh~`Hn;E#8-9A|3j01!`U>x?ZWcZWu zBgMZAbiWVY>N-}DYo^~sEwsHBBnx*GXBaFwCnTsDubZ#@QLFfNIcApW=D#_)7Ivp_ z80jR3smbU&kzJhLAl7~xY4)1WizS~`@wTY}HujgTy{wRrm7RMxJTGhjC>iMg01%+@ zZSJuiFO{q`i)ClBv_I(a%^=_9x4Mjeb=vrA#X5rg7SrrthAaO76U`*C8;KV&1!&7k zfd2rnI0vU6TJk>@UH<^WE$~~y9vFi1^>vRE%_a5a#GE)XxXOE#1m~XJyJ>toxO;zy znzoFx-dNapY8kF%P541>v5ddgMn93OaVx7IZ4fU`~1T^1k^dSvllAD~aH{7~?wwc;-d$pwX$gZtZv zm3B!Q?jQ$mRsfO-uJ_=Eu-XljI-iRowbM{bzR=2~3zzo%$3FG6d$Tj|--0^NhW;dI zOD(&Yu14=YH)G2xGC}m~`qxpaY8sz}WwrjniUAYidzt2P(MgU-Tpp(wuaot?OJ49) z4}0PJrT))I+Tv8%nJ$Yaao4AB%hI~fgx|C`iM&&DX8M(;pJ$634YOUEJBl;!8V=yE0FbBl4z~Bn%Vz)sGYSmdeXTM!Jm3ktmZ1 zAt09Y$6R8*XYq}i>3%YsSg^Xgw3%I|b&){a2>Yx#R;}8g+m;WdwPCV=<1$3$7if3jKr0KaCPB0Hc>s*F|@%K^i(@&^)i^6dS zSxAO>12>$X)Agm#t$_7Uk5;+|h`bvzz(28dStV3SZJ)?^Pd_@5_nY*uAO8S^%U}3V z+I!pU`mVh&w+ki1Ok|oMFiB(F55m1OU-8d`JXztzxO=e*%&j3pfu1Jg_Y#Q zpWt7I_UZQ7B%a1s3YQVUS=YYf=rRu!N@)R^pnlbUCGqyQx6>PlqLNHS_Fx3gqxhR6 z0G$3cm*Wo_KEG`|nx36_-)n70! zlNUC(3G&JZTyt96$qk^N*17AV-i{g#ng+FBv7)uq6#o_SUsIyB|+!byRjHo@r;~6}E zF9-4VoZ93U9wXCbFLi4XxDu$w*@xZ-{g6ftYJSxp8l>=d?3bl3m8x55o+$Bs--{C6Fh6B{8aJvo#v=7t*=tiH2KxW=aJA88#(U5fc%AE zcuvjY)n<^W0&*TFJg6j$vv>6v8OH{?FA(UuFN(EgxA7*QapCPsa@dSQ4_-lIv_L^L` zlT9VVM;QoPaT|)@@ZpHv!sk1-9Arz=a5)`HFADg_UoqTV-szWcNKuqp`O9mK-eh|j zw-izVEBDcxKQOMA%fs(4hpy6Bx7GYGXGV=~RvE2>gkLSvLJYR@WG>fVm**gkK^Qvc zhIIzI{?j(MS1AZsuA!3JM>ge9nAr-SI42)0i@R!`Kq-T(;SE1c*R-3fgua5qQhze) z;um&EE)$G45?`&THm41VYCx~a=?R;%IA~bc#~4G(k+_W_R0+q zMHb@?x|uC5V;vG!@&q2W8$608H{WRMQvN3q9h^REf(z8KfE9X)jW9U@Y(w6Z;q$tBcSVq$oR z&zKGf!2pKGJ9A#y@GDQyE&d`+4xe^nd3=kA;B&eg)Yz|pI4Zy%ho%l|<}=x)hiKnq zef98~B{m*P7TN-lxk{8c0hEKzau4ZUR-I~jIOjYb9X#1;s%)3GVTM6oYxU`58GoaoxiPm@5FsFIU#e7)#Y|0 z{WuJFugo)k3Z+XB(pocmSclZ(b+3l)s!mLMKs|WKsxR;`TqrE7$G1xNO(Vn6T*O;A z&O2tU-1v4lA>0UW!xg3nfM{}iNjK;zSA7o>(tHfQ9d!YR4gUjFPw=xWh9X?OZgS(Q^hbmE+u=8vXvD@c zeFbq5XcKrkbtQ76jDgm^(AInh9o?}kfQIe2HRJyP6#gH<4YL)5jFa@Q=v-z{=+SeA z=b09dFwuNqZopQ-1HEp=D0BpcB-aBtO&?j>4i%0$uENt*j6kD<_}AyJ+SaKhE{4~! z`B8g#@WRSuX%KzZW0Gr^)U^pDybg~fQpP@391o|xWca5|)-A0f@+RFFenLnaZ{<_F$r7Aw&Och!kK%>DiZsi3BxQzJ%P9wq!#Nd! zt!f7L(UMaXm>h*1t#OvCD81VYF5uX~IL&C`Z3KE&u`RsXbBL}ZHwfE8XQ8e)Tkzt% zYjGo}ow#)af%w&kl2nRDI|Q+hnRf!;m164Gmlx48#Okbi?l`35V)=}nN>OS^${czc z%hWY+`f0QBlo0#8#ryvNO0$1r=G>cvD0{YeHGzF`9h8`lEOFG-@{*m0L#holw32Oh z1LeqjajJDnb};|H`W3_xu|{6i+Qpual4s}y_KnKQ?325T!*wwf4L;zd4GiVC;%tG^Gm zKd@@EPvxU*h0`tkJCv?PFmA=2Vd9?N6*9Sv&fThUxc1|}U)Hs?9Xi0-*;s%r0G_qXHLPu;E#OIGR(27Qz&PuH_=?iJwK}X2UCxqQDOD(>^5A!^nG%tW zuWGj14q&!nD_b*0Cx4j80l+_{WZi2MiJ%Jq0432@Bm2w$0PD}?PQJIdy}y-|GUFKp zV~<+SNg|3tBl(+?l-zny29~;~Ou6!zi@Cw$p-g8!`OoQ6cz0KaM!mPXk&M1WBPtn!~ zXx>XW!Mt(zgUG7eN$wY?=UmujiYT0%G?>S=ThMRr*8G-XDNKn&jQyb-JHvl5=~>tD zt%k88qlr=}at3qXy&yL4R6*hR?l)lHO(ZcfA9^_OBqa68b^{)QvL<9j%MP6?dtpAM z307iaw*`RD3zMFn^-AgoxL{W}Uc!d#t^|RGD{I2OB)QRNdFTft3vIy?jCv((TH3yqF}1vi+iqVnGTlgZ1Y^}%SRC}}SwDG!rL9@n z>Nab6XwrR=AIsX$mxnnjhu0i{dw?pgmu;rYIw))MHh%MuE)cALQiz76_?nN!6i~`l6^TmW~SFIyBq!)@G}1ZWeb3o^V_o+ z)o!hsPTsrJg2eR3F}trA=C`f1Jp)gMG_$yCD~WTR>?4S{JmHvUZaExhB#d;b+8%>x zr|Qo7g~gqn!y>8}?$}%aPB6&7F$4T-puD%Zx|Oa5FJpE9v}FNbd@=?H9;3ZuG&6?y z!fr;39jv$FMYWJh5pL39zyK8sk~50u{{XOHm%vvuNqHJdk>&`&{{ULsfFnJ)$?wx0 z^IaFk=@I-togXvKGGlt()vzpFG65!lgOBmh3Lea3`_Uls> zW>+yoJOdk9PZ5Up z!L~@G@~wb>O8RHR{spu6b>OWRN3@MT&7#^z3W4(ZjH)-1Il(8I`JeWcm}we~#p_9f za>TI5fAVYMDq>|?vsde(^jUn=A@Ve|PaIlF;)m5WMrrnDQ5$o-_ELQ-p^wJ4emc}! zRMb3)t`{Ce3HKM!W6*kc&3u*OAB#6yd^m*e4b+j=v-CfU`reUw=Pby;{{RVIE7qr$ zRB-0GwM@d5`Ffvd!Qi#g{5yBzj}a)5XJ$=0(>OOW1C9Q^SU=9VzZv*4Ux+?j7bT?g z6a`brO!ddN0G-LG3%+@$!^qTDjYqY6Kjd(EeZlHTbi`9nv_S9S+H=~>+Ku^D=P^)s#gm+Z6?@dD}XmQNGgcrGT?udba48m6Gb5X1@p0G4Rq zH#`Hd zcSqR`qc6lCj(#82b&s1!@NR}RFCn-FHC@?r>d%wF&KDg)ss0e^T93qQe;xRSdj)&D z*q&>LL9^{fR%09kJP45nJv}SVY_)@DqW!PLT7#_my`*tZ1WmP`CH@vYhI7Z%_pd|v zZ{j^>=fLuKSHiaT&@YM{-RioO&ICF|ien0-v+iMlu z4bH=^Jhac|-NtL@e-Zer`*p41(9JO^k^=NKp3pYm(Lyb0qg zUk!YF(R^Xz7K&Gr+3xHu<4m*yFP1qv@y6r+_A4^u;uCnH+sCupe*Vix=i@jFe`t(pt4M$1`5hX|l&cL&!TdslDq5`BB&4~OU0^efiB zxYR9_gCyh~%s2oJI-GG{6BdDKKf`+~y$ex_3!^kV?cee+0lJ<5Zr=6YLE`OaRc#?P zZDTf;sx6q1v81kya7n=<1B%w1-%xJnrP+LV)^&dl>2{ta(Cn7+&SsX_v2sY@AFtQ7 zad!Uz7OuQi@iseXtSzpf0KoFG`J++%BZ}Ey+781(bej6cGi?pd$wVi4+3G!Omhm5o zblpSa75UO_u#7A6@7{u&O1}op5oomVbA7!j~Qs(#OmT2TquM)|Yh#qnWt#lq2@gIon8|*f^ zO`#VoOWY$7aAOVkbDz)fsph#Y*z{ASwwGwKw3exK(uKHL9GM6mhJL&X$MLs>CGpL? zx`ni@6}_~`Opv#iAYvN}^745ZKA5QP^{=&yxo3sOPWRk!A~^$Yd!BoGR#(DX{YOmI zZ{>kn_Rdlo-ZvRm$2lX{o|L5~QRs5`g|0k4G_<>p_C<7&-f_#gQHCA;a7nEH01s<6 zUlDZuM?k!e-p+U}v&U^Q-hu9Cfj6DIODP>YXY;J)^tuDn{4?OYThE79ht9XSwY|6U-Q#?V zasdATS?TNauZ=%$pC9UTYxa6l!DV&fjWXCnXBmqdlRq>0nK>kabDs6=zZtxH{{RRN z?CC$l*e}h#tKunz_K$Xdap%CSe|5N@yh$8qo_#CjZ9m7?zErv%uM;@Do64I_ycyWX z_~LGgN1)@8(xwsW2NfTUr%PoTb?};LLWr%W8>4VWcI2FAX>X_|y$`@%II{SREcD6Z zPY!Bj7+c*$TV(E2{al`mq!FC@dRLt4p9XF;T`_Jgnpw2?zEdlM=0~0YVf8(0rn0w5 zJU4GN5E*V^m1UH|`-?DE49YS0{%rjiR+5!~_J0M9H}0J<4W)BT9oamEH?8rz{{IS zA&%i)FwXu_!7jKvwsU|RZVpc;uifdorQCP7(#vOWdm70Gp=Br=9kYQVbU6S3%6h5D z2CUf2Cx;@Prj>5(pm@Zk+i5@!Be{wB*ytFXkU-sr8*4$zHZAM_01##H&z)*5H5-U- zV;2_CM{%~%N$MjzaUS#B7Dope2ROUoPYZv-L1%S!r^_@}$${jCQ7kX1N_k>r$S~$9W~hw&o^^)xZ$68OQ@@1sPke+;Pr38shYSh+40Kbt^=;gM2p| zqMY;lvJNr;!1o7?kIJfYu_wFu58?UHb;~_g_ivQz9F205hhw|t%K?C`kaDC3&THup zg($mIOoaf>PK%4` zY`zwK#qfv2f?G)1INYd3AEN$8tp5Op8VZdnH1hnSpbfonJuAP5 zOO6;ea((OP@EJsD)3Qe-<#WV7C3r}^vk*HT)#2VE(InEf26Z1OIXD&e7m0i>mr$~m z$RfOB;#YyS z{85lMHsR_GcxAVV8_mxD0M5D{AI0wxA2}b5aN=@QmCIw5?9Adtr z_&ef3aSADaPsY9j@NbN~o2cb2k)CjIUs8Mx_@5H_EY4IAGtGSmhjsp?ntLhi$;3~Z zC)aBNUif1a$_oJYlV^S0<( z6mE~4{8!*>tLRJ$Q?zhD-B02lT6Uu$fbO@CYIisozfVyE~t1s}kLd6Kl>P z@}+Xoj-2(bcf?n6i=|{41ZU|?)n}6SB>}c=IrYtDYI@w(uNsXy9q z&5gpMxzr?$3h;B@s$1#}beow}5!ed36^l=2CCmQi7>07)T=oEWqhRJM-s>_;r~RHx zvCdTb3YWw83k}Og^DI7k;1=#d&1PI`(ZdVjRiro{F#DeW09vQ2G|{Y5$S|yaLJvYQ zk@e=G#>h?F>#nD`Ge;bc=0yyJ*+BmQSeWQ4sPROX_N(WxDG?$d$L3M$e+>S$Q&;g3 zmT4N#e33wfB#RWA&_9?iMn;{!(cmI&TX2TUgiPmQ6Osd&P)^ ze=7ur=L70+e=2mAli1ztz0%(?HY6nqj9`KZ`k&IWEp=Zh5kr)TU(7iE_pNB$NGF}5 zl2=(Biz&_l7|wqaObXB$ZQ!^~?6I?f9y@`aOJtg@jI`@Z z*`$zk4msST8L5h}8_~3qNXi>yj+_2ae;$QZK_MejFTd_wcdtd-Dmilv8x-{}#=yA(+q{(4(Fxr)MffbTM06C(E?F5g4z-nmZ&>IY5KVIZn( zJwU-fS{CHV6#oEd?+>P%;(NA?If~)D;7z=cf=^We`5{h5;gE5~az6kqV!HV0;U$>u zh-z^=9B1yG!}S&H-?Sc^2ECx!>LP2)i^vX^QDa#yl$-p0oue7{ge4QLTgKEj0r`H{S zn6HGdMy3{bd!yQ=Q8c+v%%9pT;L-R?<7qVb%(B=x-)S@Q&b`6+9V?d8G)ZH&;B~L1 z{{U@xZFQ(^reB&{8OT=0XBZ>#!2GMk^z9+uKal5dUOLy)V6kqk2)1eUZGGeD5BMkd zg4PW`UDV}_Z`_vQyYLBb=07U>mq++&`o@EyFK6>h9Ba(5N1P{I{M-YA4h?jdpBX$$ z@uEreFArKSiD#|9_GO*4Lkn+aGnaPk0OXJ6PdHv2@H~m5eTjmGl#T8>h5qgIbHOrF*pVh{8@X)bcSH4sV3n@Y3mj2Rvb@%i(Dt)nxFN zp7P!5^Db5w0H_E$5?dq>RrCWqM~D71>OTyA7XJWdTU|7|*N(L#s_Kj+`O-Z8>=TZ> zgU>t)?EFjc;XD=a>JJI&tiEOIt@f=Z-doWa=cWe$_U&9p!tVyhbMdE3xbYsJCFC9(P?^Bs2@C5 zh)j}6j=-p2!yn$oa$Xkrq`FszHJt)WHHJM`QE_hZwpv6&Q=g_d{A!nrKWHnhZ{vi% zF~4<=Z5G1d>GqMIlGwguiUjo-C$P`ujyuFQR~A1LblU|4HHMtGQuD^|yu%&2HQmpB zPFAdYzK<(kd>7K@(x!$_Aeg*`;ooWbhxM;O_=b@$!k})V&7H-@M7w~`8QX*2yt7|x ze+>LJhCMEIwUFN0xR)O=SrEAc*pg~1KZu|3ko+d{uA1iVD+{S_E>-^k<$%sIJ+OLL zL~7p1&d0HMe@gKD`ZRLfO?3^W&7YVg3 zC9pbiRQ>AUNP~^aJ-Gbox!*w@)%BxmejYleiC~(An%fy|BS};>!!O=eJ#sxOh|&CM zZ+oX&>JO*dX{_$aIF*Y$asz{v&O25QhIMTd!h58(y_M`HmkJQL7U~;}jPO|ghPyi? zwEqBvV_UR`PLAcV_PB$mCAq=PTGvo8yiKajcj7dY!tZX-y04nkjGSjINcIAvz46;L zUun{&Q8lc)=8PuRxF6k9*Kg9ZyeqG2#_Hnk+eWt$Oo;3-teEq3*Q*qm(^|U;<&g_mck*n7_f4FUtD)J%j$CY z$HIE`#h#pz-b04Cw}KJP!eexV9=Qq6y>9qV;&+9-Sz`pBWWCZ%*z(~B%&nf;>5<3h zUXh{rMsEw<+uV7QNLgCuDPv&86OJ-RLNYP+sgvjgJ|Fmpr})ppHnS~+a9+lU^PuI+ z@}-BSGm~APgZv77J*r>a&n(jEk}^+jU5A#Xk(5BEJP8QN^!cmI{5z~iqWn_V{5^T3 zq0x+zTxqw+$0jz$Q$DOf9E|*<7|E<3+k@h@@5A2-_*D3RRn3LHs!4ec-zsJ@50Q^N z81QliG2f`Dac$XwyU{M>2>M%$s)!hJss;+OV3F6F z^ZiELc%MUC{Vngc+f6PYf(zU&{hK&pwT^yx@DDwE^O0TJ{7=#TG+SYukNpo& zxrma{$^QT>wqxCk&r9QHEVrG;jfH@cdI-zSuQPaA!1f&Sdt0s!GRg~;Dod z>{K3lS2WrMhs2Bc=hDrsuDJ!%%{);P<=&MUWgv54E9StiS<+a(Mt}f_oEQdnJ+Ze9+w5$j@VW8DSD(+TA$Z zxCG^Mo}SsOJ|NVhwp&9C@k1Tj5{2JmFDhG(at|2;+<{n1=%pK)RtHJE@VD7!mf~F{ zvRd7vF4huY4Z|P7oNfwz2p=tI{5RI%@IIrcLv&))bSs#Axn+bra!0vH1%T*52;smW z@-fL;=lm_H-HlFrdGz;+duaDd99dyBWQGo_%N4@0ILH|t1!yLc@ImI=!4ey!l30qa zZQU5{#xO`KcAvf3y|F4q9;e}rddtF|6}7(77HKreLWz_?9FRi9fIPxfoC3!WjseDT zip%j`rk=;`5Jh2i_ME%I@rfGVM93r~V;KQKz$?KdV2o7wW$`7v8>@e3Yr0L08ZG=~ zCc3nhKX%)2K}T#dzX1AR9@VMv?mLeVc#l!g^$Q4RYe|5$cxPWUw>ttdv~eK=`F;r)vwc z<4l<$W1snANFd|T=Q;Ya5X1<>N zle{?(kGx+jlC`b*(-cQ(e1qqB$lc|?uF^0Lb6+`|Ni|~Iva$MK;RKA?qs7QzFl(!i zk&5BI93j)R*r3iMO_Z^x2_(tc(cuuhO5|6XAqc`dsQF>JM&3d^7PQ!edI+ri-rDz~dG9ClO^2 zD#R!+f?IhJDD$3W_OC4CZhsoTX{~v0%8)*^?Mp`?OQ$EP7_M^ic7euoUSui8se~aY zPVF7$mEx>p50A#ZGvQywsdRglg{34@WONV5mJqtI z?hY%A)O4@1rS6$c|`chjNTRKzYHZ_}AhE)t!^uNpU36yKQpk z?t200T5{b1kpR&}pP15JSj+rdhE>19s_T9lv(&8C=GR=)TYi2=lW7%I=1o%3icjV- zAI5S`bGny@zGBxXp}Q4-z4>&YNm!|_+0Uj(su~G*caA{fxvgPZOOKp+d{mm(n_+m$ zgsKilHAc@+7rL4)!xoLzqa6LxM+fu81f_D7&Fk1)f~8qKL8r^&lYb;@d3f=~7}|kj z+z@>-D?0DyV!as&QY$)r%OM2k6bRS1(xuk*k1pNJk+(i!TmU^yZB1t!I!soRum>cz z-h8HSe*6mLH2b+Dxl_D_$gLf3#NK_w#F3(5gBD)2z}eL!eJa}K*2Z^uyp>mN+e;Eg zYnE7KwwV063)ZYP)U3HyJ5TW*^-f!<5>ln|r?0&rHmo&vdlj}Xgep+4T!M2^PbgG4 zU>puJS$49BZWN4{?MZPfO>#XOxIHO>a$QZ@-JK+8zEKHbB;1QPz5_V+TIeG`B_L|>;C}P2CcbSY!D%7CxweK1AvDZ{HlzO z(Ks7P+Ce09ky?Hge?H>nI*B7_g8u-t*Z}(g0W^T2u(XE*IIA{un{$8%y*uoZS;!m` ztT_kJ`qfl3&PkCN@>bvrI104eQKCB4Pmv8asYjxpT*DRk>shRtPc zg^-m}INgrFsmI|{Pd%lU#C8poQtca$n{dfcMtT#B`sSD$-W*#?DB^+MS>-3@SpHbo z=-lSFtuBrv%qhJ7?Te3`uc5)g6~Mu&qLna@A|>HbN&S86rPDQ+fgzM{X%hiJ0IB}~ zdy!b&r5m01hP8QoJtDQc8yMtZ#75E-)EQkWjY;jLERUGT)Z7Nd24Z>Ha4d=^NAu^;O9GiT*iIp|Ndc(=r#g4dd5+|iX( zc)8_ScLUJ!0ml{F_*=v?=xVxz5-gGN8^wE4HnVE++(felSt6deBePTv#nuX`IQz#)Zj0LDbe<{u8|L#mm|17Pl4`ap1gD z#8N?#k(E4-2(P>U0B6sI+AoG9uyJE;aXqRN9M2futbX%nu21P*&+RMlTf?6mJ{iwt zEv2yWJ+wY;hNKe`EAjiH!;*|R!RgkX3*4v5dS6>!(#;Vn zr!-wnWVcKXssK6k{{Z#Z!1^A$eelP|0#L6V#iWfC(Vhta;g|HUfB0|vPifkkM-;b7 z6o>d_IQ*-^%y6^zj-pq&=_-9u^wc+sG~znfA>z9^ucSnX92W)8psF4n_@Qm6qBK(n zQ`{Vi-#X+nLLE-;YVfO4y0N7{bm079q`_~bIcCNI;~i_sbl-+Q+j``>gGK8Xw1qJ7pRyGmz z$gS-g;Y9a0V7<9Jc~i3(KDFz96ZlyT#;Ve^VSvXN#yZ!d>3#u<-VS3Nfl)>io#i7M z^`jSLeD|t+F?lqSa6H+5^KJXs{{SYRYw(iW!@di?ypkBCztm785%;a3{^FljI49^U z=*ReYEqws(^c zv)kHAE^Ze;E2@0h$EP2ya2B2zzVX+`tJ!?%3_8`!sVjs303O!Z%VX$*zN+}$We3B* z2wg}VdGms@oM5Qv7xVsg^KPBvHt^Nl(^w*ltB9HfYKW@MC%B%~VPJ^<;{{4Vitk9AE-CX-Rr-EHh61O0nE`IGe9Rz44EE24Z~ z_<^O|Byr0Ob1t68a zY$Kdo+>xn~Gh<|T*9 zqn>vTeX&eT^t*3__Eu3vtjlc-m%4LpvuO^*H#<+14_>T43|GOw8+D8CAGe6KWw%W? z#9B0-W9j!Lgp<0Q$WS*M2nTTBagoLaeUb4~;-&Ax&xV$|R<&ww?poZu>i`Xb<^1Wv z`I!`wJDeK$@Ain*j;XHc9xA(AZEnj~h*`Ao&9X~-sPGtPu#p`OGI+@6oZNefBgK9j z!xw~nX)U~lZ|p5#C9UnNAaiNGu#y6JE6)Ua*Gb_o5DUKwTzD^CcUg3qj5eBtkG-N@ z*%9Nq1{vV-yVzH2AAmG{OT+g*BGWaCQu=1A4Yb-M{{ZV^GO1>jPI!kZtBfC+vN4L| z{CnV;yf@-M?MQ9rTV-E0{H5Hx|4Ik@c?r;{HE~o*uRq_Q`Lo+snG{G#ieg<^UPHeEHjz;9z@`Rqyp1Sg)hAca1d5 z+eqiIowm2x<>VhwHnwq}TC3r066Z|UY~+pypHPUGW{){+W9G;>IO&7(^MlujPT+ce zy`|}w9t+d#uP%f?*=r?%xJh>+OA?_C8{-97w8JBVwAUkj4d#b;b)#x0_cyE>7aMl) zduP7imm{GDy%SW_tlz4|sK-U$dTR+S&Gnj!0!gGz+_fg&>eK zjz$h~-nvW68(TPaJuZ8TMV0d;R|*_LlY@{GcNkz7D90*E9D~Q4$*bM9)H7Q&l1Xa# zl0X66Ty0WDJwW|ycIs_UT1$ZpZFlA(T-^wiNws;zn+{lQI46P!)1q4ex8eT)5L{1X zELyd_q_#80BDjI#ScF^imRS^Mu_LZI9r^BjGpI#vqv<-Hhb(ETlz_`D6A2`1h@{*P z9;Ne)@N?^4SsmH5T9*{b=@0OK|Jjr$RJ`Bv87a~0{1dh=gzY2Orx z(iivV?yq_{BCjqNmZQ|=lh(!0jk=^?VxOLX*XPg2TjiU^(`-En&*xuE{?We~1)ha+ z%`xb3GuFN(y7AlV-YA+`8+a|$_vilr)n6atMB`SZAq`s9o8chl?8}!LY_dYwUA$Kr zuUHA^AxKl!y^}(+O;=A`bCLN|?mi58*Fxk)wOhBXepiLZx=pr@>X-?2Jm2Ae!^!na zq?$xKbDR;@zLxk`@T{LN_kA#WdR8xjydIirMzO{+z42bh;9ICHAXRdsaK}pd+}jmj z7gpONOgrwe}FzdIB)$L=&ws1nr<|)T=aaea=Be%VXs*q2v zYoja4>erH{p66?tDS_rnsyo$zqs&K7(zZM|qNL8QMt4_XZ>PlsL3ZaA`d$x$si71!$$ zXi<1--q~b$E@XC+>DQ3l;RoOW=iJvdHj8C_^4eZSI{};q$^5JPJw)>v66w~_*dr{g z8F>I4WcELWbKWDF*4<()n3&{qSFSDBP7mhCE935rZLBH0ZF3SmtH%`3gOyhq+gk}S z+CA2xY5Ti0Mvc4YHD^(stf1R?jpsPd1y$3vTOB6R=Q2+B#9}zMY=M1AC#m+uRFdZI z_V~>>MNAU=xzEypY-DRK=A%DM*Ex3ywkONT$LU+1A=hB@nk7;dM_l!+PqxJQ=CTrY zGVZ4_44=IG=CiJ*GQR|pJu9N}qn%jHVT#-zndCS>hjUq1@motU8h+8Q1o3kxpz!xOis2e;@dqciF=J1Q)3#=DQAk8@I7DSL&N1fV@AhS6T=5X=;v z+j~+@;1*BiY7lN9;lI!6nx9vWI}5ptIcWEiv^}2Yl2D}iLvcUdsIM+f{g@%X>g4fM zChQlt#wa6I7~V2XSKlo5-eJzba6R!(c|?LSAl-!+r^@>wFb})S4{X%QjIZai^lVi6 zex|lVfI~BGC)a{8`sS-iY|V|l=kTO!cUA)(XpC$o>}=_}`mM-V<7uRSl~MrRKq9|F zaNeVjT6M+6#;K?aVI+nY;UOddqtgQdu;Y(vhO1F(uMNY>%C97dQ*j)Cc;cP<4DWs; zv0V?tmQf(KFh1z%g8W%w*&Z&G7r&BbJJ?^f;@#(*t;^{F(bZDsid92wP&QwEA5Guk4zu)irUgG zCx_%S#u#TPa!=uoD+5c^1Q!Dgq%gxX_eYNj;V2_TfL2iUI^uPU0}V`ixgKN6=~~ zo%}?GePV3R3?5VY5lU1TreH|p7|wkgv-_ znNj4Op^88n8Q}8^<%U^tfDax0YTW)D)vZfhXu2PXtaO<^RbtMe%XA=j93GjeNN#3L z-l+zF;#*alEX3`dd11yEJ;z-B6|FY4Z+>C8NYOxDrAJ)%u47u8QjU1jQ;Jj}N`;X& zF~Pyl0OvJ!9}!wvXkK*CtF_DQNYYHp0A8+F9XS{|p{p^ceGf^~{wQ2s+T7ae5>0t= zBrCE>q-i9F0|Oa2I2-}f+PbSxgO|Q9(@vwIOtVJE=Y=KQ(UJ2EgL`#+RoomO|KfR(E02SQB_V*lX2d|eAM0h zp1tsUJjUxfGYck}oC!8KK zI@ixSFN-`e;<)2`=6gxA`{}?fC;*?87Z^WNTN=N?Ov~Rhs5s)Tz=3Wv@|+ShXi8oXw`0|#v_am`Dn-K$G$7*FA#sh z9Q1z?cz)URZy8CDsw{gsACOH}gFjn90p0N!rK5e;I$^pWhq5 zWpDVH=_R~Nq{g9|<3@I$$@X}U%sX;3*0_290Be61_&PYPHG3H4y)lQDIhCT4JoF5w zkbb_^`t{)tAHz6gmmGjFPaW%o{i%OskA|PLe}*+Fd`F?dZ>XdXELy#mA-lCIG8mD= zuc%;ZWro96_mVD-HgRcHS3fbn3I5Vo9w7LeJi6q`2ipRrvX;W31f9Q+^sln~6XF|N z`w&sb%m;ke=C#*>F0Oo6KA)o{)}Pu^> z+dcKQx!TdWB3QpV@p1WHy!5`~+vROpAE!PBf+V&K!z||{*J)uGx^SvCgXlU}%%1{3 zYAqAuR-FS`T&R`!YiD33jQ0dr&|VMmTv9}i95FqQ7_R}+lqJg1%ZR5X8?(2z)0HC| zg>v5!u41#&rE=H`rH{~6ooC`zmX~7hEXfpTK6iEs6j#B2w}0)uKaXO(@VA8SWEL{! zE4%gJ%)J^k?g8$5*QG+5sO)em6O@-Or<4BHKebdIH~6n6nWs!!M6q%{(VUB!bMr9u zP5uK45P*N410XDTiAwZ+%`v$cvq@=NZ(XKMXTd5zbPFSX5HS>=(H?i+F1 z+z&tppd5Y`;zAXix%NoPF_leDn%h8<=Uj&AQ_6WS8c@fpWzQsZ6_u~}c0GIchq#si z&n?EI6ro~V88;rieX91C;+r^~Zm#bB&*glPpghYbwJw+Oe?qbNiFqZllI=8YGHbb5 zI>iy4SB1_pKqObCfyGo)(Oky81r>Q5_rvc8OW-E>h2m$GWI(9|kz;-trQiXcdYYTy z?~87{CGhW4@Z|3U-pj7}Zmihal0BA`L>t0*oe~waX9tiOkfQqv{!bzx2a(Q`M zDgYl${QqNPFjh7H02=#h6=tJpA3au@=Dbg8vhkbgc3LK-blYtt zd32l8j$@i80YUb|DF@QJj}~fgajEFq^bUd+f*V_vjdE^5Bzb`LV8KE9*P3cx8j?LT z#7#52(CM0tJEJ^@Gb^B!f3!BQ=~}-Eyk8;k0ESsJ{hg{6oqMgkfCeA#hbqVMt<^@- zk%XZn?9X-Zeg6QFq+Z;@CYB2eq@L#oKVpcHk`uZSgPwWhSFU(d#9E()ykG_Ov|6Wy zCf{U_MPf!L(=Dcsf+O5um;x^VaYj=AVkZG52yAmYG5lG-?kORdi z9SAMgBEE_HRfmRjIdr)LHI>BvNw-tA#H1B+8>!9(Y_%lw{{Y&n!`5Cg5v7(bH7jgh zVJ6B6P!@0rIWfqfuHljkDaQuBRPn!t=D+wu;twy*ON$tO-L;~|{{SfYMt*~H`4XUR z`HU0CPhb6`v!v+O(nQyAUB@##44gcjHtlY}ayH{6XQgqr{yLMyR@Tzom@W&1ySAO; z1@J@LU0X?v0>8dqNXm?M6{sIHd@Av+_l9rfe>z*Z-Xjy*nOQ~BH*j;100$h7c=xYe z(fnDcXnq%MUN(JC!)o#+a>tGf6v@jv?`!rm$H#rN7*Oz{PxfgT6Ra~p0+FhS{)?bA7~c}~S7c0MH4lTh$9 zdnL5iB1@08>9;Ydwvp0C`J$8ilBw#ajtL!VUkiAi!^N6pxTFo0Kx#N(4f!F^~vL$ zTYJ4q@&sw^Ev^rmZedGA{{TuyAUvWXLMYc*w{j(B#$@uc2IN z(P`gowz!tw>JutmLun$&^JfKQLIC*+s9yXW^{uTVMY+`U*rdAE4aL2k%gGhEw~>rd zvZ*SvbyeOD7X%I26xsr{{++1Hsl{`9YFY(9XORp40IOoy*i@gCeAo=VJq>hMkXh?~ za=>GXT{+?Mpkt4-qkfNBdVkA8c_i0M{{RT3v@N4tJ%!DlnRCg#lJCt-=lxqK zz~O?p#$S`iTDz&}nn3u0E%ujjZ1#7^UPv#2MhF}fZ!jL)K=i;F9Yu4=SZi}z_D}dh zb>a_=e`qgr@kgCzZe&DTfKO`h4;K6nGT$?-VZ#z}jzxZy`0L?}TB5V6D}$a0uPxQS0tUHh zOgqDM82h#6du&!7N>5vbSSd%8`LE!T-1sEMfRPw(KOg@9Rec+z{79W_;|nI-3?6#a zc3%b}f?(ff2OY6ngW*I%(e{P~x`1+P^bCs^Rur6*^E|0xnwz=v-|a8ry<1qenhSM# z8L~L%73SXz{1b6;A|=P)4amn@`ftTw1=(4h*_z;9|Xt!~P1lxUgO5ryV%1AHix}Bi)V#dVY<49lekZ z#eCik#x;2=Ee}p|(&knF0EF$d{Ov);8YOcHa@ac9yV8 zsV^h02L`@H{iD2dJ;vtC41>mgmGreT$~9oQ5$4pKle#)+zi0`yT|l9e81J5y>fa0X z`8EFlEz1I+1N6;&0i<}Q{X9UsOD|mYudhF6uM|j{ZMT^C1PoWXljT&fb=Ay0w4UeN z9}cXe`y#eF{*~K(mf!pT0O#|s8SrP0LDQC4rs>pHx%iI2{o!A2UoxQtl(aK*lV|hM zuIg65+7}Z?Y-fnM^4ES8FZHZ#V)M+2o+~A{bI`;|1b#Ihse6|V5_xNkV{s$!sy1(V zJ=?#UKF))c$2sf;e@CTb8{HaqmwNW64pl#KZlt`@I7s9589n`}+Qz-3_R!fSu^RzYQz_^T}l<$l6#X%;1VC5 zQ}{ovNbw7ID;X-d;1ix{lAuG9*v~&owR?8r-XwPUfLs&(++=?+`cwo=OfmT7WEn9+X97D z#ABfAP`$HU>9SmpyAy?P$2k5~pAFXK85|1I({1L}2iYP8gPgF>>-bVi+5pCk%cG;I z%&|5wKZ!}>^8U4FL-57cps79*D|XNCrf)4e9_OuFdz%jr5p4iV+0dQRle#26-`+o` z*0Ov-tWB!j0`j|r8&omi4@^}FtCr@EPqk)OypnfbsSIB){q*1AOK)|g+!9{a^C|Og zHwwG4+W=MsW1po+vgfq}J9qImk}O0YWV~420Vy5AoSt*RUsnAGrDVR9VI{~|ui6xp zZj9VQagTDDKHqn(V#hF0K^<#PNWDqbpq!M14Z=azJa_s~B<@>p3*E&Ro8%jav7a*8 z#J~?zw~_dOc&#rAM``g`~(9W`e0!<4Q+&>Rm?7T6l z3#-+T>Cjx$rMSZo@j?1WWcgQJ}n07gftM1|RH|IQ$#)uR-x1 zw`Z)`MG?BSjM+oBLm_`LT%3WH!NJFJd)Fi3jahA`mNt(|vsQhjqqh*sPi(53H`g3~ zHL>I8v=@PzNLo_}0$rucsTk)Rj-Bg@dYTy8mZ=TSkjbZ9>Eh_HbxrcLk)Hf;oP+vS zWu=C%Yjl^_6F~*b$+cVUo=PA0dFSXcRCPI8+(i;u{jt%vs73itp)}79%Q zBvzKm6~m0>RZo2PKT6UuxSO$;CDrY|h}P0OrkyKqF(jZlKi(iz`fd8&w6Mz__^gn; zLwC%BzcmJ{;_W)h-WxXX)S6S;t{2Fk=4|ycwhm5vbf~X9F=-cv{{XZkyJ;sXRbjV3 zBt(pj$LUT`QP??~+|kpu`4vR)q=_4S#wQ!=*S}}K+H+FyF07XFN~YdY2b9Ax$a(4y z9qZ@qCh|tJHnK?zqKu*4f|YYZ*56BbU7Cfp)os^P4Mf@qD_)GSK(ys4g z`#y`T#;k<^0dC*;eqI3Y&2pYA_!r`jh1b7rODTn_k+$X|z8Bzd9CSR_$=(b7q_4a` zaLUge%r4uOEEOyCU~)eS`qTD&{k$7Zv5QRA-Su4tXUfAX7xJI`+~+lIDXU2p?HnD^ z=pF(1_IxXAXCAdA#Ty&uQMkvSQR!ZvsCd`UW60bIv(ronfAZgDhM zT6MuJ!1Opno=0l)aalvF{26ky(81;rr!^{(<~u*wm-ZLeKV-iUUTgj>(Qm9ZtA_hD zNpk~4_DFDcvYtaQ!TK8YuZ zjImtC+Z$-eP~(7D-~d3Wh8|R3viO&nVsUe;H&Hu!9}s+R{{Vtid_edY<0a2qB2rNp5Of-4l=kP8vSI3 zEn~t73twAF3~r&8RFPgLAMdFn`q$2%@KNN`JZ-1=MiXgn*OF>XWSk%pG-|mkk<=1L z{qie=8G@x4%Cw%xqlC?8ImV3QpPBHF#P1aAek+~D*0F!~OyoiwjpMd`y{pZ&pAAW2 zYv#*sGDbL7WFcGVafL*;_D;7t0vj{8sS|lrmZmlO1b~(>@n?qv9T^ z<#>BW)^DXGpnFJLE1vs@1D|^OL&4tx<<K3266NpSKpts=Y?!^F9S(yaTIgQ zs7#3nkd|=D+lzvE$QkF~-n{R?-w(vltRQ)1faDC<9y=Q;%E;rVjCA8u3qvxauI%hR#*8{qMkPsk6wnpa`CUk2hl9`4IfO9ELYJicXF@sfOl*jqaYL9 zn)mPcDKG6IX?x>%d=cT9{QG|o7XH$^Mg?B*@W`jXmcJ=E`^CMhd{28qO0KzsdWWvAN+hscSXj zqBhSic$!OjkuW|<6vnQ&;D8k8HMKg864BFqb=4#Iui#6qLrk##)N3R5i7XYFE}{kv z?3l>}t{AIeZuz@`Up9O#_=$f9hde>2MJ$h}+C?Sxs<_0~%m>WKeZvAr`#Ab~MgIWB zukCLWuD@Y%CY=Vi6|J733m2F9z-A=zpO|MDt@*ULk~r;K#&P(L`s+>BV+B0JRBd zD_vIiMbxdNiqPKLT*eDK0KaHn>PbDl`5CR92Sm2jWVX4t33Xv`=HKlt)+ap``;&^> zyzrEH<5fzYc0_V4r@{_O3W6;YlkZ zlldI;S$Am+OD>uqF}d^*Tcy3Y zRVysg38M>-?+$jHFF9|zJvsC*gYw1Ty+cLwt|YvU=1F0h;JGq+A*7K*1S25{$8b*U z?%aV?iT|kNIQmc-`2By6QtW*>G8=j z$1A2`eaq2X<{kPsIUd#5d^ptKShkwh>8x$^`}?MfCzVNxJEcw8|YJ9c!J7*j0dghgAmtaWab~-IuU=lo)8|i7J*;y+HSt?rM##jR z^yj5{M~l20FN+%5n@9U)#t8*m)O4*29|K!WC|oG_+g=p-ilqn1eT@B` z+4JXzz5>G@ppWH)jPZ)~T^qsApxVW?6&J7r8LwpU$HLnai5XCkI&JG#{6p~eCAA(< z3)ep_eWpQ<#8#&3&rwd2?0J@z;v1bMfJZnKw;vL(Bb2UlkM^r}_rigq!Aw?co(bOB zE&l+Hn)!_Ph$icDbJ-&=V4kPq<<^a-#ib#64d_gbg9G_=ti4}b)3i+vTU$E>^BuPt z9m&VxU535!Ge>^|h^_QpMj6`Spjp_)7C-%!1nMHFedo!PiVQIdSo8Fx1tX`=G0LHTU86LStE7QJ#_H6xc=Y%lSG#!R zL6ny}Pp%Cn>;hOi&vX8hBRD~j3GL}rZ|7^s0AdM4f%KwjKp>19 zQ+JmG;n8s8?+;o_YgptgC7EDl8-QW= zs?!0>XPH&={U&xP zV&?UfT3l@l(If{0@fFMIx3Fq9>2G-A7mJ9lm=-EKBLT@@rz7dbZt1pqot#J@oISZA zfrEr>d*c-&O!UtRU&Chrj`H&IStR?)gfwgP&JTLj@n6}aWSlWnWgo-19Dq&;&l$~e zS}mT|7ZP1tO6T}C{Nw5~U0%O=b1s;TX&t^DHZfHF<9d#`{VR^5)f97@r;Vh7_3drv z0^1uwXcd1P^%X*26f^;MI$Ulz!zhtdde&9#rNq}PL{4$)DroFPNWW-N89jC>+`o-l zdI>8Vx4t~p=FyhfB!}#=Hsle&sn5SsYHtPK+d(D7nuL0d*0(qA;f)5xjZfZobI@ZQ z25POZiodcn$%GMXh+}cvz46Z(;;UU=d66G29szgz%t$u7`C(3RKcV?jyl<>nX|Tf>ic8r^B?-tN5!JDtJ?igf2@rUPM16b3 z7N2R<>?NO5Y~8dFluJNK`EWWMb^^0JYo;cT;@g`|Ij2}-J5ndlm`AT3xvg;}uZJz} zE%cf1H9NzWGeri}2!DvN>9WwJk@bvoq#l^wZZe;SUQ~=U2ImyZ7lZqu~v=!Pp z^C`Aq8Av0jJXYt1d{c3_Tg5ZR&G!^H-aeH>$9@mE@Sd*?o%QrrI*r6a;^-r9Ff#yl zG3ZzhzLeQ?6XC5A-tnGPy}J3J34^gcPd)ztr9@+?XQuck;yv$&RQ~|P%6og2Y=s}i zpM2NVUj;vI9}##W(qAuDGnYkJRLPP!#xQuVfGjnKvAG5QV1G*Av+;A=$cM~r>B_c9 z!S$_GNkQ2G`&poTZ}>a=QrlSgXT{{Uw<2<|_Hut89Nz0N%i zdk%;38%gmtnBQQ(wz^bI;UpOY=z0As;x8Kf3h@Sy9P?O8nzfQZw+%hEO2&sAx6?nR zdH(=}JVmPbGg(%(zPOE);f6@a?kmccO&W0h(bDle(!7tYbWemjy~ph>soiVZb?ut! zZ3XR<`6gAnv~>YxjHw})N~iJ^F&gw-C{||Vb9(cDE2pT(`N(K!cnHd-%2>a80G8Z}OF<&nD z=fZkFh$oWj2`-_yU;q+rD0msk9^{Y0xeHH+vdcQBm)E^|lrkD|s;bXZhFFCqWMElY zwbC<<&g^$PG09?kn%2?uDeo@UB~HCa?OpGOz8RLZCE*1IGTxu!eXHC)3jWJYscF_) z&Y^v!>H1vrh)SD)ZonXZ@I*)54U{K8D*_FB(n>2+7beda@c#hp?WuUm=GhDvSF%o- zQkioaDzi5k;Z+rwo&XAKx7GguWU=6{fVMt4hgH)rH2b|mTL~?qnN7{NmZ(+aOyyO} z;ZH+@gWA5l_>22i_-o*ofUGnR4(XPf3Tckg$@X=P?rxsu*|0y{}YBG+3FcV}Ohx(R;K6ZOZP3E(YfP4j!eTKK>57XJXnUK{ZDifpa~52IZ@mZ@gs zf1zBGcLF=c%v;kL_5!^d_Ljeo;+KN_8LQ})O7;xb7I)Ek+mD#`cG5hXfjIr}nQ}V} zbsTw3hmAC?UFB))qn7Q|M|~5Fo2zUuG9IsihFMR}n|UlVRy+l%&F}|Ti&26j;xC7K zoTl3T)Jj2k4h)g*{{WV9DG|C!f-;J7L15e3$Oe2XOAj2jlcee`JP+c%7gN#FcNkG3 z7yBt=*LqD9OdJe?qlt(;A=89D;$mmZD2?T-(W|Q#*YQ7R% zYiTCZFLWJXYFb-B_-kn7X5NT#l2L?AGt?^%_@^m!0-ulcq#iZCSWe4w`;=olI2*pW z$g1$C_C@4oB11&mkNZ{C{8iLnS@8@WCAC;huD*rtV2!a1SBOZQ0o7HBSrvK#CANm8o2bo_%AurtYosH3%2$vM)nG?%o$JJphGtNPeTf@z7_cOL zK*<%;coW2Wmw~0Z)8>NK^GLkhZ!KgSqm&Vi<0Iv9*@Yb7NvT{w_n|HG{q5z5LxNp~~?N3^RQodVg zE`HHr5`f=oz?oEm@&dyr2ZDbZ-|(w_yI9*U`zWM;H4IV%8swZNNjtdX-yJHOxlxJK zY2FdHxVRdomb=1B3(FM943P(O9>ybw8-nxKjN_W^VAG-3b=wQuIc(9z%f)bvxQUk_ z7U|y|dUfWxsch2MO-p-wu^y#7D>QL=Wq!~%INYRU6OsA!6|?Z`O`F616Nc+jd6nk5 z@(>gcBL^g;hdg6~dFzUlS?Inx&^0d$={jVQ!eFzC6f;2Qca%b?AoRdE>)O4W;NO5` z*Yshi$0JE;ZWm&=EHV!rc|2pMYV#iy_@3v)zY(Xn)8$mLjtK3dhBLV)v;cve@Jj;a z#y;~BIt+Wq?8T&iWB7kdv7da7ViSVL_#B)Zeih^9^&DW?7ijh0hTauAFNf!O%QeK4 zv;4=D&uX7mkC)8!5!-!%42OXEEogpUrp*?45HLD{{Z!1=cppQzv2hM$FPa!feP*9j91s! z$Z=SDeDYUEKh}KCV@B~S+HkFv^fj?(;$^!yZvOSePXv}Sm0(qtqqo%B7xeedcyf%I zYAmN;x#;$qf-HcrkbT~99&#ShG&4%@nu&r{a8pAgGDw*a(CBaiNd9Y4-1{Wnp$ z;G}56)<_T973A)x7z%w!s@M9xzMpYCs`1X#sZxXHDi89ktIM04tvITKu()C=>7+0A z3b_%(DluX+%Y)PTQ*Od+&4%jYTZtqxs4;+W)mGx<-Q1aFK4YHerEKaJirP>`nm;t1 zz?q9kw)K@)qaJRDm0IEpII2&o#{{S?= zlZ^V+!KhooXjT*tC{l7qK~M%*z54?)FZ$2#j-%;S+T+Y@mu5N6PgDQhf$0 zD{FQnkTXCQKe5HqZ$W?u%~mb6yK5L7<-UPgmuZbg=EX-9!$$(DlDX^0T7|4ySdwKK zSsbAKDpIhLu`SK5wX`WByD~m@oMWX%*WyhMOHyz*`H^Jsrx-t;KT3-JV>PrE@&yLW zxH#i>PHKwAOvQ=F9061^yoytjeW-%=SOaX4mpyUDYHOkwNpv~hT}b_Ef}8_W7}yJm zs?Lq_$X6pB1v2{l-OeOsRXY_p?}3cfNgpTDlnk1oCvxLRR!HQG;gn!hqgb9aU>GPp za4IWw8+tPyzzUVD*`VAr?lH)vah3ktA!Eq=srHxeZ!X|R<%(&G$Z?TVO7Tw^`9yF1 z;5a{80BoAAz+yw-w_YlJJ5iZu$zL#kbRZy_uMMkQN~nW#J7Tm=+*UF97TZgz;z>I1 zKHh?u7pAy)ip;C0?Qla0A`i6V(0yxLLe~iee6Z1sDO5tHO!OGfvFTjJx}E9&=<@e#ALj74uGPJ|17XU{phLC!U^sbjgxwqA=Ww*Jp5dEZLH<7nGw{O=K%_XhNO2#GC z?#NzH24v-V2OpJUJpTY>Z#1xD{ni-=@U0uoG3_QPBRVpjq$$RJzSUOl_S{HRS*sE1 z`KU-68Df`ut>2j`mHf_%oCEdj1#M}z_If>%OFG-%T#T~9DEE zk|>#Wl+F!QWxx!_Y-`K894Pgzg%d(jlg;LCiAmw#p&v?~RI$1!mgjsZFwH3c09e^1 z;8q;7EyU4V$GF7pfgu>(_*VI{w6*hHTs6yq910YY*cc}R=~CT>oz1CrGQ!Ruv=|1@ z`1g%PyhTS$lgaPvT*b}VpHq2W?Wb4{*q6GuZK9p4hqt&{niiJfhS*)?Eakg!dJ2hc zZ){{t#DZmg?ttL0pfpBWiMtivz#kGkcaBlkBP+YM<|mWQB-38cp``P}i*x~Fml(+H zlTYydgLtD^m|c9%(Uy_VNLRT&zx`^nd2Ok9s_MqpNn7mu0>!t5Nb*2nKMLP{K}zOK zI(5zI^TIPkgsCbrGCk>+7mV|TBq5Aoiq(+aLvFSftX>7WGp>5Fk6MD~Ol@0K)TX(_ zZET`f3OKX;^lVG?J1-qbh^L9t~<+>t0m8aGEnLA37;+2_M$8bPX*pt#1Bx zj6lLdPf|JJv*$*N_@Nz5M5AGzgW-ROSK1Bj#M*t$-ko(YA(wVzY=fMPV!PcN_L9^u zX110`vyKQN`x%xybyqFwc~ z09w;ha9#@|D&lTBt zKjI~)hqVQ{nm4$#EwM~*<;@EYjJ^8fuccb4CAmr`yXw9K(Jrhlzp^z$ZFMIS$205> z0Lcg4y?v{h@ptUUuj!j-7WUT>N`d0?rVR3A6M&#0BQv~_`H3WEYk4FX zau0GVxA5P_8LjVa?hd0W-9aEoW{KGE$OL?ck&~XD)jaXtqHK9~x$rAnj^SJT7V{FW z;UYu5TppMsuNeI5?T75=;s|u{Jo-eCTcap>C3KI=3^Evk*#M4*7&tw~E7vvejMD42 z@S$e2duw)$WF^LVW+$)SAmCtnR~O>%h?;Ck1nipL5!@ zc%-&Ow{y}w2{w=WB6yd>`sJkBRp*L4IMUrU+-%d@NU|c41hE_>fpXtAGl>eGfY*|I zO#PvC4P;%jO*Oh|msb}uqvlC7D{o){>>H7u0LQI!{wnbm+gm)B^38KR5SXQvrxHM8 zAcSC5a0qU>>zdQ}ZSbk}jYh*)y}PrLX1DX1#N=S(#6_5?QND!Qi z7S9~_i2PflX%UGM9a7T_(?M%Bqufmi0}lpR2Vyh14^n|hA+wS+wF8~_pQg+3&i7C8 zR4=GaVzWghwdw&h4tAa7H%x{lPCACqUc5)gF9+G&X_0uN!O)xk01Wt!IW9yN^0AW7 zYXy=ARvU>z2L$d=S0IeA>sR7Mjn0*J`fj8BqX-IbH0cguzKTQ340R~O{a_o%n4y6g zE94iRCzr!|McuxiBQ>nf9iS{By3_7dfUL@-WbNLJ$}q#QVT$UCvI7H8*8agHQY1n- zp|@*wbZ$mL0f(aiPuD)x(0F6U^LQ6inRLLCGsvxT9I6@%&_~RLFb2-uJ;pO!hO2ht zecB`|=giBqXG{Wm)~1UZYZ`p=-fmmSq7M@T)T<2shaZhMurl9{bv-Kk#Ij4JX_p!~ zxnkEgtuU6=PZ;v^^8f~N8>bkm(n7jrwX;MQEf9Un$IX%mHMEmy+SZl!o4~^2*%?_N z+Ok{#czwr%x1j)J_4UL$rLCc|)0)kpkxieKFm~7r?{ZikbHM}t1KzTGgkopQuG|ej z%1KJYES_V>fwy)sk?F>3L&J{gpSn}hPRPpo5rRlH$LV@a%3LR$>ckEkt}9q+?WL@5j4bW2Fz();ts$goX;E3;-Z8e4 zUoZ{F&2V$T;9w3ouTSv4g1V)kwUy)DZvl8`^Sr;ZfHK5-@th12OAJ>F;n-Rk0hD{+ruR6H$XsOVlUj(GJL?$+&0 zFLCAvG3TjfZiE5+`d3suH>Y@)#1{HoabL-7xQfGKtY&PI#hmmTn6c<_kULa+L8$dQ zp9uI`=H5G5?5^bV=8?R+b#Tocu=zwzM%Lx=KjhntHs+l5HKAE&;sM| zub;jWX{W-TF4P)W5=gFNNY3$+Q-0CULNmwVUrG2F>DnHW)3)I*$8x0PGYkbEsr_r` zGwi|?p!xe;%1r8fPvQlWcH$2_b6mV$B=Tf+-MbjBOT^mDx|X8<0K6erx2;EMF<8Rm zIq#bOSmIprHLJ=AY1~nUr*RjFM6Hd)S3{xQMIZ#Q{A?0i?^hx5QdrBhw@*V} z7EzW^t5=?)xwNF?9S>sCZlgBf2F-OkLdPUz3}cMfgxLI0j?qD4a((e#=YW194N^cB zK;QwMYw!J@86`>D?80>?-1QrMA~(Zeb5|hKBTxYxie`s&mo}^s$cZ(D6S@<_q2 zpu=$0Dsfek)Wz0j-Ik2AgRw^js`!t=iF;#;t1hmt~ZdKGpSi#v914>||4bagcMISIgcc zL;axf4t{P8ekq$^;f2OH)A(Mdi&Ec(Av%T1<#H1l0=;vw)$jBIul$+#62(Vwi2q0vgC^O%bQDNi*P%D z9+<9kQqUs3R~g&C+c>YX&9ZuUygQ`&n!>%05%60v)LJL%Ff(1(lkNJ{m)-!2Nx5lZ zKpPyKb*LWwV>lq^>t8vN@M5RUI<|K@y|R0shZEadM5+LmCT!z-3>wF{w3_mF-sHr; zbVtcPwbAK11ebOLRM(yuA=p009H5uDZh7`lKQqO1&vdqztm`WXhXjr@U)hJ0-OARs zp>;Hnugh+yZ^l+`F7}kc`5s-Dz>elt;O5oX%ZDA1sw?W%|B4p?r*GC zBRdOd#t|1PD>_@}f&iu1j@j!;>QZG*abmo<-LOLxV+sdd(vBE@h8LSf2h$!q1{o>=v#*PVT}G+rMQ#KazMwmRCvox zmjrtnxUQsxGqmyoXQRK(l(LH6OuFaHDByb)~zluH&m~tyz25PIKZgx?qn8TMhb>tPrg6> zb(;d5r{(FIfId5Pt6o?Z$GH_%-(l(2oFid1akNv_w^GxvqCGBNBmqbs^*ycJfc@u!2hdgR8&+7PF6k}PcO$i9++DG_Y~hD$n@!YO)RMS-sC0tC}{m{+P{iABZ7;J9%sBReXy zWP!zVv@@phEKr!ExPXBfU!NV1t!6k|J7}5{^E%|b+&|GrQSbOxr-${pC(|0$1Xz$` zcuej0UV7Fai1en@E?;>&hf#{fAhAwMsV?q95x6fn?M2ncnSfg(x{efn6$lFyTf?^I z-~ryGFfa?Y-*v%YpcdmNrxj^jLg|ypw+!(FMPHCgMnaDG`quu7uQrEcERZZwPQaqd zu?Lj>38*|<96Bh2O^*fb(0QNT^FN7=XGM5gIUYH}GGrrzf-2InrLGU+m$oKI_x+)t zZA z(wB)w3qK}rQU|3?;-3)7smX0?VQkRD23?La%;%{;)A6f;1Qx4tXW~?OPTB6|Xh1zw zlZ>CwKZR*rYWCI_nx&|9k?&?(XP0l?je6tj#b--lKAElB$#7JkC{BPJoDATfO!H4c zy#zO6FtDkXSy!koMlxwV3rfSI(dE#!SrTHdmoSh3Jyp6jZ1}UnY?^iS#o%k5Hdt+x zN71r`o__&Z`k5)NTTQ3P?k&u?wGgAP!Jm*of4hU zxl6tXvD5A*(<9ZbE-p0bL(J%phm3_Qk;hEn{sywGZ|^43EmeG}EGOI`=jM&UGcH#%FMPOKI7R{~0q}nWbxbsURpOxfXl*gt= z6@VmRXjg9zvax%4jzYIwyiKMc4e1_F6_ghV{Oof?p7mj&6X0f!d z4%+yJDCChPi&Bd6BPU}VWw5}G_{cuuwr})ZS4Hu2X;952t^6QMrp$*1Nn>M?j>G}e zIpTm)u^xjB_lNb{n`vRWl{~gtP)bqaQQc2^($joxERu*WrriWTal{Jp#=Xe*98)xx zZxi0#YZJfNujEFY2!(rLhd@YIHgoRmn+#F;0cAVB_ zIom^`);voXh5pd;+xe^j629>9ob5Zo&#$F(*OO`EQ1Z>UlW@#9c>}2a^Aa?Tt?Es_nLO4 z6oTT$RTh`pe31fwaVa|*L7o0wi-}htV2sj?ZY%XU&x3yrbggFmUFug8_74jYL~l98&0wr1LXR6A9|kO|=8c*Ej(xUmgm zA{&Ud&E;IFR@5MDpP0sQk^|LwQW=?Xxt6tz65I}6#MxW?MG0c4+=h#Qy*n0F0cHKDBej z8ro{QTvFOYC6xAe4{;K#!cQtUB$&4VHw1CHNdS%H5z?}L+&oKrHQmIUE~m@N_|KSq ze!S8Gh0KyYp428ZCoK~F(0xbNtY~(qZs6R`paT(!S2-kMNav1mO}Ms_AW=+A&C}-L zxXoM9tp&xq$|UmT*i;>)0M|e$zkm-@$DtK60MxXxZ6SCrng=A{G;9^Ij{g9s{{XJ3 zUA(%(tn)_3Mo_0`1hxlS-qUa4Xw))qW&{^y;GbWuThu%*sciwYyNT{6Q6ni*01R-T zo}cOm1P^`}@?lU`%BCOpwhZ9ytU9)B*tn)v;?b zMFSZC2yzC{!M{4McdC7&?Os#++nB@T{{Rrl0<<)%8Ey<^)U-z>PJKAVNmn`z4k#zq zBP>i2u>s2Ef`ioHR_xN+Sx$Dam^VTO+?vPmEzq{TS(Kt&e8_hm2RH-@*70qR*!)7x z0wIQ18x>n2P6kGOj(D0pZO(bct*=TX>6@+kB9@&y>TyJuBBf9_X>_wvpcy^G?vQsp{O4Nj=U@ zVBaXD^gV%m8DXV(e%0)xA8XWEXPsJ2+hSG14_;imbjaEcIThAf#x1nlRCdIq1y#r1 z9<{*uPI%+i;f{B3^C)KwbI@P`>;SJwOZC+C1!T{bF|_+v=NxNBRVlW*Xl14D%-OUy zy1l{YJ?no@@bH2lr2)@++>X^RNR?v?j@@xfIr}svy({oMRzRZVQR-hh=wn@I$gms? z8s>Gq4a`i+Hs-r2H3;Mtb5GHiR3mER)}t@Oa?aGu=T_Su0k7EKTi;<=A3D)lj0r4ja8vkNW8QZv*K@vbYxdU8t-m=_W+IUH9Luj^7-?^Z0n zmEh-?y0}=&UCrsn=c4$F;zW1$r+dHWn&PcA#@BUhA{JfQHONV=%X1k}%mMG3^?!t# zDo14-obKv<^Is8x%d2DYYBPKOM^?z|yfNV~vcjz3epR=4uVV>jA5MDLQ{kTug+*pw zI&x_7ewq_cbzCkw``7HT;nzbW!QT3(~V6`ucrN{Fp8vf01R~PN#ds~4t!%ABCiz$~Ap!NxQq!&DX&&P0SR{<0j$(OOhC#ymk^O5sP%~>+_t((N5Lst! ztDaSR4A=C8rDKq-soL1sT>3MBqmIf3vw`e(GV$LSv<0Ca#2q{xU79@;ND6SD2~8F zAU>6?s_S-fTY((UG_AosN7Aq+xo2!_ZYj55E&*YpiR7M9G>P&?i@|g2^dGH59l~7M z`PU3WjxeVL)^*LBO^yyfl~}gZ;|u%Q2u0_fK>RB)r(q_(NaR0ch;AKZcHN)DrDn~& z+`G0|)%$C;8;zh=+SuGhQ=IX0nBKKL6>M=Zeq)^8II4m|WB>Q9@ee2J4 z3)Pvx1BK5%yw_i?UZj5uEG@Sy%J;KeNC!m9sxCj^AfLv#!4C%?%9X%+MAF-t6lH3> zvH65mn_I?#HXc*1Dey$H-DEEq=|;d*@^(f}I#k+W{{ToOd2o%i`Wzl;@R)pwFj#P?!)1hVW`r@}TynLmu}4pS{!E`ct+zLZlIzY?zEMy^d)> z;Gar-X@kyjNp@pERx-yuNuX)mmMeK011{V}{qlGf7$JxX%e$~A8K+y^G&XFkcO!od zS9?vfi9rQeaqUezBo=AWe^+g$IsA0iFG5AmN*YRVHdEEx_MbTu#9 z4ci=+-|l!nN}B=EX}8z1NX*a_e#dX=^r`Oejl^XoVgWpmI{Rj^G|v&;TK?!<%8{Nn z=Cv&RL<~xkS=&w(vdw}Whow&-8k#PsmW;8&z54X7yld9i7Qz`KH&;XeiU(8fYm09Y z=yPHxx4f}t`4NUcckrdK3uzWhxt{246EXh)RqB4Ys;(OK?qryJmdLFV ze-B($jY|4GPTtU4Z9^LFd=9OOwJQ)ysoS;QupK06R6vTP_893}S|GJszbaKSTHQK% z!#KbIuk$ruLXK#sSwr%v>GycY^{BKRa$OqQMv^GyIeY`gGeo7Oz_k{Xx^{+=lZdWw z3=y#%N|DFvD_2DDqv}_ecXq|gKbdUA90nj{gZOh%+TL2+=-O?(=+A5BGZ4RgXTP>_ z`c#&8ZKe2p&mWsIo2A@3k%T$?vr!pp28V_9R@b~jky^#FM}=jL5NC9VILZ1G`BT0g zYF78&FrMz>O}CcQw5UMgTyi+=o@yOB?r#TOX}1=#s!eF=Gcp~(ZpZ`jC-bN@?+rbU zsWc2^Q>ic701cLS+dnY&r)_QrLGe1=!EfRJ0213MOOrH~w>vmSls@2}U97)_ZtFL& z>fhM@A<`On=58(Vk3Q6(IaAOUN6tM(WCufkXmvjsYj&D}ippE{mT07j zytt#ej4Z0kIU5{q;~&MJV+V#jF{$Yv5hSt+CH1VS_PczzQzSqFc4y^w3diIPxyEtG z#YL%jaKYlOWvn$1?FQ1~j%Z<>Nc(I^NKjDn5N*&yvNW4#_TX-W- z`(=&WCcg|U_fbHh(1rtZc~WP39Bw;K0BrC&aQc_Ub!|>pHaeD{cN~J|8_NsXA~2Fp z;t-ADare;qh(YQ{U1=`mG-i0x7&JJf7Wa1dx(=-AD3a!IsT8?Ij$b`8QFDaecOWbQ zY#i4iapMbZPHAAjOX#PC3@UD29%$F2ubK|xH(&*F$VLbwy)PO|Z7LCK0k@9!R%^R! zOp95!oS7EgvE{!RF~(UO0kdgcr=N{&?`@5Q*MY9%-Xut&K5Rf7B&bgk0;81}I8mRN z5_Wna$3sqk5zD4r+pUev`)xi%S=kk%Y?H7R$Q%%QVEgk>_=8#(mKM{rCe}5Pl`gH- zQxX*o<+2V3ImR$C#yGA6S(aN(OeE<8U0WyyXonIe1`nYa9At5vaaS~*bM4U2Z7^74 zx+@#;kr_LRNDpQWlkPG*)Hd50CbsrdUWqpo_MJ5JTWw|HU61A zSN7@4s{E3u^M;3+HEC`49ENl_ABd_^?gi;DlT(h` z4X&6DvTnvQamXX-_*LyP@?AROTUL=&B!yi^JGPPt`W`M zQ9&H6?Qt4y;5S?o$K~tkQEuQ#_BLdZke$G*0y{4})~ALL8@Zf3D>DUDP48yqem86_x(*4i{km0BDi`{7om=YjbYb zUv`>U5<~* z-M;;-XM`vH{1yYyk6KJaG>r<{OPe%kUMX8I89f_4v(me3Ee<%+G?p885jN!@a(=bO zd^OY#vEoZjD)V~VH0y~-IYm5RV?6O)_J^&a>KAt5)^ZD24`{kX@molY-`Y0{ zB#~F{C>&$`?lX@xR65_+@Z6#rFk!e^$B9uEhLwGZy^IDV>vbS4}8vSM&`vg^&krCCbCJP!vNm3`kpfG71oS?&;vW|Dq_DJ=Hj+W>iunm;F~YmkR9SV zR}AF;0Klu3@gAY}`AJ-j*{*lQI*gXLV{2n1)S7mrm%Bobz3Azi@>^&(lr$%;~1&QF`mN}8OLECX!tRqxYLul z#(gQCDYcGOakl`9^REGXLK0X-`}|;kO0nZlAGB7j`^@IQaiN`3t3@LzPq7}ga|WMq z+cA;U^vz=GJ~~@K**!XQ*11OU440QOIdg(VLp`J2%2|8$tllo1Q*8DynQN%Ind=`1 zJZPHMqN%j}xbL2|^q!O9KeSjz`vA!FubI99cv<4ql2zk7c*nhcap9|PvS`qz?b}CO zV!d3Vg(~#hXBH+-jJXl~nnj>n!{MQSsND;DNu=_vus(UppPj(&xgm4wpGtM5&5Fom zcQL?#q+Vg*dfUB|9!lu+2H_=K-FjR z)k7n;GTFslx4cMntMz=Bc-IHqI{yGo)Ry2ZHc&-wWPhE@?Tj9n10^FF#?O!ncJ6+Z#lCjr>{H(hjQxG- znKa21w@LxK7Y*N=2vVaXxbIcmf@p=dVs+Bp;aZU3Z)YP?MH)8yeiKZ^9K|OI- zZUx?{I{9$F-NN~3PI2_u_sIeL{A4qu=&;RD7|Iy-~NsyQv`Z%0VVSjgW8r@l{h?ih0qcf;iZ4N(^SAaj?yZ zwaIN=LmvH3lh z12tn#hC8dW+mvnNlZvS=q_1?xZUOI6d1rA4gH_DB3v*ub;ZK_kZp;tN$31b-Q}ier z@_&?PV)Ulz@kX~QWFLM#ze>3Se`3VX2;6oR40@c=6=F%{npd+xhwgTHYv6D*ju0bNj_y6Vn8apRGHTklh1L zQ=#1n=Z*`Ar$Ff4S~gyz*BCh!b5GP_P@Z%$G!xJEc?cXh?0&e#Gr)-+r(rdv!l}BB z+2e{v9Z&F&=ThE5Amip3e#Fup_BJk(>BE;|Zv$j>2tYia@J$_(o zQ{k=lfiJ}058n8q(^9+8A(P6#wbQPa-udnD!GuyUDvTI{Hv&3?*17wkE!D;4#ld%x zwn~qlRH}ss=zd&}YP$}J_MawOVT?u|F^u51%**ry{nUKC$8J+i6wSC1G)G zAaEsA0hF;IVB}yNcT-H!gKHM@Sv0H)K@1{9P0Rp7m>i5U;Yju7nc=%xCcKSq)#ORl zqmK*n=NVNe^T)r^x-SWMPB|SVNm>ZvV9v+yL_-mV{{UwM6P`O@@m%eJ*y+9m{?ogT z?m0Y-RQVB3;Ww)p6Nw4P#ATFakKJV%>MOCk6ZkJfmfc`YJ6W`mo>qwfbeXVOl$?nb zh7JhN%y~Jf&a0;QX4>>=^4?lr&uW*9_R6xAg|YkPUU$Ndpz#7` z*!`j#`|Upm$+wF#$cPEvFu7n9aLdNr61*I3PMebhcUSm?o+Z;#%Tu(mw$<#uWHVe9 zw3zMOL{t3jY|Jr*e8=-=isQUrdnM)Mx?IbuTWgTE$Wr5CN=^XEl|EZB1P#dBU2u3g zR^zo#6@S84b#AuT7Jq1nlW8QXu}L&d(iV`MZXd!);I;u3jj4FUQJ+pr8DN#4*jBW&A)>nvL zG_uogt} z202#7TL5?Fw!9oXI|cmuCEtjkw~JNT4C$y?OB`16gP7fl?;j&|>GGE5sS|r@XtP`V zK=7^K+3f}O-jflzyS7#-aTFtlnjzN<)y6?-`{M{&^O1QCJ>1a%dg7F3J zhWkmfeJ$>gz1^CS*6~Z8Og+^buEIW3k&-$!5|pk@r`=Dh+(-7bUp3g6fP9^}7zfY* zwUY(QO?n-^Wj#IVbnBVzb2KvfPq2fye8lmb525{QM#30wV`++cFx$tL8P7kbQ;JME zi`M!y8p5=8kt|aM40jNq4C5r8)SA?*eR(ytv@acgL9v;)#lO# zx=9ol9%~hgdi>E~jQ)SeXG3j#nrBzsoBUSp8QPV!c2GXBkqj%QrsV*9+k=X-ro1ZngdwcTs7sY4=Pzu za7%JZo}={X(yDkz#G_ERwO=jX_Qq8JjzCZ|oO+7FQ7SUI>HZa%cyn52`#eJGM`w2{ znKm?KN3wyqo(6I#2FzhsqNo1=U;0<0_;*yYhQbR#1NmwYrg4T9=hu?f zESWxqxOD#j?R&`WXND-{OzvbD&TF~wtVspRysphEbZ=VC@P>{qH3+1XVqY(KZ|PRA zZ`($^e>7wh$81;R{8O7MQ*x8h4l%jhTzJFDwAyfS-!| z?YNCyI*O0Nw%%5+8v)9&qbJ2URgC4b9ePQ^$ohZ4-Xj*eY;C_d9Wz}#ot^3Aa0+Zfk#2(IvmMJnh@U9?r>JOF@tg8pY&EcD+2x((;IOgcaiU2?+?QXEv-p-vA3taaDNiK7S`5me6pjQ z``58gCH53Kn%IWdMsi*l_?FsZgx-xhRQ=!j!}-_f{4br!VXnHLEsCq{DmQca=F{ZAf`7N(Z?{r^c3feY`T^Yf z{uM)3ySu%Wt{yq=qi_r?%3E+f0^{5ok3rNgbt~y7Yv}FJK*(-dGQeejhj$t3D^^b! z!xJnPkxcfsT=}j~%%A{Shhkeloqti8lyrN&TSwI5jpk_Qxr~O5KKcXO9sdAK0Zg^B zu$t;f^#`0OL%LXY;m>jjtn0mUSz^Drl3ZNOrJhsE74ARJtzuiq`j``3PU^Pnu>$pP zK|mJuKkV5x!4;&}QNc4|B)CvX9;XJW>Xyyt$!lW~TWDA_bCTV1#Pp}xT8%?hfZbfB zrkV-gEQ9VS#z-H;*yu7d>MO6CRJ6R&JgZ1oOz`co-edCty1#wDepek?i2I}EQPZAk zTd*E^b7H?UNiOE>tH?E5Qn`}#mkOvAcHu`#k5tv9y}Nlfs(UEF6)uUQTN?A2WdY!^M){^K%Cx8W6a$=n z)bG5zf+0lWBth~5qjXKbGJ6E@>c&dhhGqHd|?6Lt|>Z3XjB zc*_rzW}{fh_o)W#N{l(?uG!f&()o%|Cvr_t7H^EP+A88*CAvF znDqYuXWplaQAi;J?=b6%z-z+JO7T)bKbG07`9SndCseijBwJjhk|tOhdg<&df><0F)Wi)a?V%W>OA6nL%MVjuxofV3q3xG4% zKjB>dk*didk0*s{=zrlgbcn_nef|FckF6$}0sjEmZn}zmq+pH8IocI^{{T90fln_O`M_2_kRaG5##%h5mJGP|;>zB*sjJQN&?!vP|Qz&{spG_{Pe6E2v%w-^sU0 z%O1QPj8EbG>OE7%%Po?G5pRt|s{4Lkz#1H+_aLd^tqSp?kbRm)Sz{si{ub )CLN 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 stops[i] && t <= stops[i+1]) { + float localT = (t - stops[i]) / (stops[i+1] - stops[i]); + return mix(colors[i], colors[i+1], localT); + } + } + + return colors[NUM_STOPS - 1]; // fallback +} + +void main() +{ + vec2 uv = v_texCoord; + + vec4 scene = texture(t_scene, uv); + vec4 lines = texture(t_lines, uv); + + vec3 sceneFilter = getGradientColor(scene.r); + + vec3 color = mix(vec3(0.576, 0.376, 0.729), sceneFilter, 1.0 - lines.a); + + FragColor = vec4(color, 1.0); +} diff --git a/examples/sample-scenes/assets/lines/lines.frag b/examples/sample-scenes/assets/lines/lines.frag new file mode 100644 index 0000000..c32e20f --- /dev/null +++ b/examples/sample-scenes/assets/lines/lines.frag @@ -0,0 +1,38 @@ +#version 330 core + +// Outputs colors in RGBA +out vec4 FragColor; + +in vec2 v_texCoord; + +uniform sampler2D t_sceneDepth; +uniform vec2 u_pixelSize; + +float linearDepth(float z, float near, float far) +{ + return (2.0 * near) / (far + near - z * (far - near)); +} + +void main() +{ + vec2 uv = v_texCoord; + float depth = linearDepth(texture(t_sceneDepth, uv).x, 0.1, 100.0); + + float maxDifference = 0.0; + vec2 offsets[4] = vec2[]( + vec2(u_pixelSize.x, 0.0), + vec2(-u_pixelSize.x, 0.0), + vec2(0.0, u_pixelSize.y), + vec2(0.0, -u_pixelSize.y)); + + for (int i = 0; i < 4; i++) + { + float neighbor = texture(t_sceneDepth, uv + offsets[i]).x; + neighbor = linearDepth(neighbor, 0.1, 100.0); + float diff = neighbor - depth; + maxDifference = max(maxDifference, diff); + } + + float alpha = maxDifference > 0.025 ? 1.0 : 0.0; + FragColor = vec4(vec3(1.0), alpha); +} diff --git a/examples/sample-scenes/assets/monkey/demo.bin b/examples/sample-scenes/assets/monkey/demo.bin new file mode 100644 index 0000000000000000000000000000000000000000..dc6e3e9b3b9598951793a7d39a9cac864189cf5b GIT binary patch literal 91104 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 Vnml)^9(U$RR1sQu&g8k&{{ddjN{;{l literal 0 HcmV?d00001 diff --git a/examples/sample-scenes/assets/monkey/demo.gltf b/examples/sample-scenes/assets/monkey/demo.gltf new file mode 100644 index 0000000..2b91503 --- /dev/null +++ b/examples/sample-scenes/assets/monkey/demo.gltf @@ -0,0 +1,104 @@ +{ + "asset":{ + "generator":"Khronos glTF Blender I/O v4.3.47", + "version":"2.0" + }, + "scene":0, + "scenes":[ + { + "name":"Scene", + "nodes":[ + 0 + ] + } + ], + "nodes":[ + { + "mesh":0, + "name":"Suzanne" + } + ], + "meshes":[ + { + "name":"Suzanne", + "primitives":[ + { + "attributes":{ + "POSITION":0, + "NORMAL":1, + "TEXCOORD_0":2 + }, + "indices":3 + } + ] + } + ], + "accessors":[ + { + "bufferView":0, + "componentType":5126, + "count":2109, + "max":[ + 1.325934886932373, + 0.9392362236976624, + 0.8223199844360352 + ], + "min":[ + -1.325934886932373, + -0.9704862236976624, + -0.7782661318778992 + ], + "type":"VEC3" + }, + { + "bufferView":1, + "componentType":5126, + "count":2109, + "type":"VEC3" + }, + { + "bufferView":2, + "componentType":5126, + "count":2109, + "type":"VEC2" + }, + { + "bufferView":3, + "componentType":5123, + "count":11808, + "type":"SCALAR" + } + ], + "bufferViews":[ + { + "buffer":0, + "byteLength":25308, + "byteOffset":0, + "target":34962 + }, + { + "buffer":0, + "byteLength":25308, + "byteOffset":25308, + "target":34962 + }, + { + "buffer":0, + "byteLength":16872, + "byteOffset":50616, + "target":34962 + }, + { + "buffer":0, + "byteLength":23616, + "byteOffset":67488, + "target":34963 + } + ], + "buffers":[ + { + "byteLength":91104, + "uri":"demo.bin" + } + ] +} diff --git a/examples/sample-scenes/assets/water/shaders/water.frag b/examples/sample-scenes/assets/water/shaders/water.frag new file mode 100644 index 0000000..6cd0ae1 --- /dev/null +++ b/examples/sample-scenes/assets/water/shaders/water.frag @@ -0,0 +1,45 @@ +#version 330 core + +out vec4 FragColor; + +// Inputs from vertex shader +in vec3 v_worldPos; +in vec3 v_normal; +in vec3 v_color; +in vec2 v_texCoord; + +uniform sampler2D t_texture; +uniform sampler2D t_noise; + +uniform float u_time = 0.1f; +uniform vec2 u_resolution; + +// Fog calculations used to modulate distortion with distance +float linearDepth(float z, float near, float far) +{ + return (2.0 * near) / (far + near - z * (far - near)); +} + +void main() +{ + // Mesh uv to sample mask texture + vec2 uv = v_texCoord; + // Screen uv to sample scene texture + vec2 screenUV = gl_FragCoord.xy / u_resolution; + + + // Blur is less strong when its farther from the camera, same math as fog + float depthMask = max(1.0 - (linearDepth(gl_FragCoord.z, 0.1, 100.0) * 1.5), 0.0); + + // Reduce strength when the flame is looking up + float dotWorldY = 1.0f - abs(dot(normalize(v_normal), vec3(0, 1, 0))); + + + // Calculate scene texture uv offset + vec2 offset = vec2(0.005 * sin(0.2 * gl_FragCoord.y + u_time), 0); + float maskk = 1.0 - clamp(0.05 * length(v_worldPos), 0.0, 1.0); + vec4 backgroundColor = texture(t_texture, screenUV + (maskk * offset)) + (maskk * 0.1); + + FragColor = backgroundColor; + // FragColor = vec4(vec3(maskk), 1.0); // Debug mask +} \ No newline at end of file diff --git a/examples/sample-scenes/include/ApparentCircularMotionScene.h b/examples/sample-scenes/include/ApparentCircularMotionScene.h new file mode 100644 index 0000000..ce082b0 --- /dev/null +++ b/examples/sample-scenes/include/ApparentCircularMotionScene.h @@ -0,0 +1,50 @@ +#pragma once + +#include + +using namespace WeirdEngine; +class ApparentCircularMotionScene : public Scene +{ +private: + // Inherited via Scene + uint16_t m_count = 8; + std::vector m_circles; + + void onStart() override + { + m_simulation2D.setDamping(0.0f); + m_simulation2D.setGravity(0.0f); + + for (size_t i = 0; i < m_count; i++) + { + Entity entity = m_ecs.createEntity(); + + Transform& t = m_ecs.addComponent(entity); + t.position = vec3(0, 0, 0); + + SDFRenderer& sdfRenderer = m_ecs.addComponent(entity); + sdfRenderer.materialId = i % 16; + RigidBody2D& rb = m_ecs.addComponent(entity); + + m_circles.push_back(entity); + } + } + + void onUpdate(float delta) override + { + auto time = getTime(); + + if (time > 1) + { + // return; + } + + for (size_t i = 0; i < m_count; i++) + { + float angle = 1 * 3.1416 * i / m_count; + auto& t = m_ecs.getComponent(m_circles[i]); + t.position = (10.0f * ((float)sin(time + angle)) * vec3(cos(angle), sin(angle), 0)) + vec3(15, 15, 0); + t.isDirty = true; + } + } +}; diff --git a/examples/sample-scenes/include/Classic.h b/examples/sample-scenes/include/Classic.h new file mode 100644 index 0000000..500d0e8 --- /dev/null +++ b/examples/sample-scenes/include/Classic.h @@ -0,0 +1,64 @@ +#pragma once + +#include + +using namespace WeirdEngine; +class ClassicScene : public Scene +{ +private: + + Entity m_monkey; + Entity m_ball; + + // Inherited via Scene + void onStart() override + { + m_renderMode = RenderMode::RayMarching3D; + + { + Entity entity = m_ecs.createEntity(); + Transform& t = m_ecs.addComponent(entity); + t.position = vec3(0,0,0); + + MeshRenderer& mr = m_ecs.addComponent(entity); + + auto id = m_resourceManager.getMeshId(ASSETS_PATH "monkey/demo.gltf", entity, true); + mr.mesh = id; + + m_monkey = entity; + } + + { + Entity entity = m_ecs.createEntity(); + Transform& t = m_ecs.addComponent(entity); + t.position = vec3(0, 1, 0); + + auto& sdf = m_ecs.addComponent(entity); + sdf.materialId = 0; + + m_ball = entity; + } + } + + void onUpdate(float delta) override + { + Transform& cameraTransform = m_ecs.getComponent(m_mainCamera); + + { + Transform& t = m_ecs.getComponent(m_monkey); + // t.position.z = 10 * sinf(getTime()); + t.rotation.y = getTime(); + t.position.y = 1.0f; + } + + { + Transform& t = m_ecs.getComponent(m_ball); + // t.position.z = 10 * sinf(getTime()); + t.position.x = 2.0f * sinf(-getTime()); + t.position.z = 2.0f * cosf(-getTime()); + + } + + // t.position = cameraTransform.position + vec3(-10, -6, -20); + } +}; \ No newline at end of file diff --git a/examples/sample-scenes/include/CollisionHandling.h b/examples/sample-scenes/include/CollisionHandling.h new file mode 100644 index 0000000..a016712 --- /dev/null +++ b/examples/sample-scenes/include/CollisionHandling.h @@ -0,0 +1,83 @@ +#pragma once + +#include +#include + +using namespace WeirdEngine; +class CollisionHandlingScene : public Scene +{ +public: + CollisionHandlingScene() + : Scene() { + }; + +private: + // Inherited via Scene + void onStart() override + { + // Create a random number generator engine + + for (size_t i = 0; i < 10; i++) + { + float y = 10 + i; + float x = 2 * i; + + int material = 4 + (i % 12); + + float z = 0; + + Entity entity = m_ecs.createEntity(); + Transform &t = m_ecs.addComponent(entity); + t.position = vec3(x + 0.5f, y + 0.5f, z); + + SDFRenderer &sdfRenderer = m_ecs.addComponent(entity); + sdfRenderer.materialId = material; + + RigidBody2D &rb = m_ecs.addComponent(entity); + } + + // Floor + { + float variables[8]{0.5f, 1.5f, -1.0f}; + addShape(0, variables, 3); + } + + // Wall right + { + float variables[8]{30 + 5, 0, 5.0f, 30.0f, 0.0f}; + addShape(m_sdfs.size() - 1, variables, 3); + } + + // Wall left + { + float variables[8]{-5, 0, 5.0f, 30.0f, 0.0f}; + addShape(m_sdfs.size() - 1, variables, 3); + } + + m_ecs.getComponent(m_mainCamera).targetPosition = vec3(15.0f, 15.0f, 10.0f); + m_ecs.getComponent(m_mainCamera).position = vec3(15.0f, 15.0f, 10.0f); + } + + void onUpdate(float delta) override + { + } + + float m_lastTime = 0.0f; + void onCollision(WeirdEngine::CollisionEvent &event) override + { + float t = getTime(); + if (t - m_lastTime < 0.1f) + return; // Avoid multiple collisions in a short time + + m_lastTime = t; + // m_simulation2D.setPosition(event.bodyA, vec2(15.0f, 15.0f)); + // m_simulation2D.addForce(event.bodyA, vec2(2.0f * sinf(t), -20.0f)); + m_simulation2D.addForce(event.bodyB, vec2(0.0f, 10.0f)); + + // Entity a = m_ecs.getComponentArray()->getDataAtIdx(event.bodyA).Owner; + // Transform &at = m_ecs.getComponent(a); + // at.position.x = 15.0f + sinf(event.bodyB * 123.4565f + t); + // at.position.y = 5.0f + (event.bodyA % 10) * 2.5f; + // at.isDirty = true; + } +}; diff --git a/examples/sample-scenes/include/DestroyScene.h b/examples/sample-scenes/include/DestroyScene.h new file mode 100644 index 0000000..a311d45 --- /dev/null +++ b/examples/sample-scenes/include/DestroyScene.h @@ -0,0 +1,79 @@ +#pragma once + +#include + +using namespace WeirdEngine; +class DestroyScene : public Scene +{ +public: + DestroyScene() + : Scene() { + }; + +private: + std::array m_testEntity; + bool m_testEntityCreated = false; + + float m_lastSpawnTime = 0.0f; + + void onUpdate(float delta) override + { + if (m_testEntityCreated && (getTime() - m_lastSpawnTime) > 0.5f && Input::GetKeyDown(Input::U)) + { + for (size_t i = 0; i < m_testEntity.size(); i++) + { + m_ecs.destroyEntity(m_testEntity[i]); + } + + m_testEntityCreated = false; + } + else if (!m_testEntityCreated) + { + for (size_t i = 0; i < m_testEntity.size(); i++) + { + Entity entity = m_ecs.createEntity(); + Transform& t = m_ecs.addComponent(entity); + t.position = vec3(15.0f + (i % 10), 30.0f + (i / 10), 0.0f); + t.isDirty = true; + + SDFRenderer& sdfRenderer = m_ecs.addComponent(entity); + sdfRenderer.materialId = 4 + m_ecs.getComponentArray()->getSize() % 12; + + RigidBody2D& rb = m_ecs.addComponent(entity); + m_simulation2D.addForce(rb.simulationId, vec2(0, -50)); + + m_testEntity[i] = entity; + } + + m_testEntityCreated = true; + m_lastSpawnTime = getTime(); + } + + if (m_testEntityCreated && Input::GetKeyDown(Input::I)) + { + for (size_t i = 0; i < m_testEntity.size(); i++) + { + RigidBody2D& rb = m_ecs.getComponent(m_testEntity[i]); + + if (i > 0) + { + m_simulation2D.addSpring(rb.simulationId, rb.simulationId - 1, 1000000.0f); + } + else + { + m_simulation2D.setPosition(rb.simulationId, vec2(0, 15)); + m_simulation2D.fix(rb.simulationId); + } + } + } + } + + // Inherited via Scene + void onStart() override + { + { + float variables[8]{ 0.0f, 0.0f }; + addShape(0, variables, 3); + } + } +}; diff --git a/examples/sample-scenes/include/Fire.h b/examples/sample-scenes/include/Fire.h new file mode 100644 index 0000000..ea7cfed --- /dev/null +++ b/examples/sample-scenes/include/Fire.h @@ -0,0 +1,474 @@ +#pragma once + +#include + +using namespace WeirdEngine; +class FireScene : public Scene +{ +private: + Shader m_flameShader; + Shader m_particlesShader; + Shader m_smokeShader; + Shader m_litShader; + Shader m_heatDistortionShader; + Shader m_blurShader; + Shader m_bloomShader; + Shader m_brightFilterShader; + Shader m_backgroundShader; + + Mesh *m_quad = nullptr; + Mesh *m_cube = nullptr; + + Texture *m_noiseTexture = nullptr; + Texture *m_flameShape = nullptr; + Texture *m_sceneTextureBeforeFire = nullptr; // Copy of scene texture for heat effect + Texture *m_postProcessTextureFront = nullptr; + Texture *m_postProcessTextureBack = nullptr; + Texture *m_brightPassTexture = nullptr; + + RenderTarget *m_postProcessRenderFront; + RenderTarget *m_postProcessRenderBack; + RenderTarget *m_bloomRenderTarget; + + RenderTarget *m_postProcessDoubleBuffer[2]; + + RenderPlane m_renderPlane; + + std::vector m_lights; + + void onCreate() override + { + m_renderMode = RenderMode::Simple3D; + + // Base shaders + m_backgroundShader = Shader(SHADERS_PATH "renderPlane.vert", SHADERS_PATH "backgroundGrid.frag"); + m_litShader = Shader(SHADERS_PATH "default.vert", SHADERS_PATH "lit.frag"); + m_bloomShader = Shader(SHADERS_PATH "renderPlane.vert", SHADERS_PATH "bloom.frag"); + m_blurShader = Shader(SHADERS_PATH "renderPlane.vert", SHADERS_PATH "blur.frag"); + m_brightFilterShader = Shader(SHADERS_PATH "renderPlane.vert", SHADERS_PATH "brightFilter.frag"); + + // Custom shaders + m_flameShader = Shader(SHADERS_PATH "default.vert", ASSETS_PATH "fire/shaders/flame.frag"); + m_particlesShader = Shader(ASSETS_PATH "fire/shaders/fireParticles.vert", ASSETS_PATH "fire/shaders/fireParticles.frag"); + m_smokeShader = Shader(ASSETS_PATH "fire/shaders/smokeParticles.vert", ASSETS_PATH "fire/shaders/smokeParticles.frag"); + m_heatDistortionShader = Shader(SHADERS_PATH "default.vert", ASSETS_PATH "fire/shaders/heatDistortion.frag"); + + m_lights.push_back( + Light{ + 0, + glm::vec3(0.0f, 1.0f, 0.0f), + 0, + glm::vec3(0.0f), + glm::vec4(1.0f, 0.95f, 0.9f, 2.0f)}); + + // Load meshes + // Quad geom + { + float size = 0.5f; + std::vector vertices = { + // positions // normals // colors // UVs + {{-size, -size, 0.f}, {0.f, 0.f, 1.f}, {1.f, 1.f, 1.f}, {0.f, 0.f}}, // bottom left + {{size, -size, 0.f}, {0.f, 0.f, 1.f}, {1.f, 1.f, 1.f}, {1.f, 0.f}}, // bottom right + {{size, size, 0.f}, {0.f, 0.f, 1.f}, {1.f, 1.f, 1.f}, {1.f, 1.f}}, // top right + {{-size, size, 0.f}, {0.f, 0.f, 1.f}, {1.f, 1.f, 1.f}, {0.f, 1.f}} // top left + }; + + std::vector indices = { + 0, 2, 1, // first triangle + 3, 2, 0 // second triangle + }; + + std::vector textures = {}; + m_quad = new Mesh(1, vertices, indices, textures); + m_quad->m_isBillboard = true; + } + + // Cube geom + { + float size = 0.5f; + std::vector vertices = { + // positions // normals // colors // UVs + // Front face + {{-size, -size, size}, {0.f, 0.f, 1.f}, {1.f, 1.f, 1.f}, {0.f, 0.f}}, + {{size, -size, size}, {0.f, 0.f, 1.f}, {1.f, 1.f, 1.f}, {1.f, 0.f}}, + {{size, size, size}, {0.f, 0.f, 1.f}, {1.f, 1.f, 1.f}, {1.f, 1.f}}, + {{-size, size, size}, {0.f, 0.f, 1.f}, {1.f, 1.f, 1.f}, {0.f, 1.f}}, + + // Back face + {{-size, -size, -size}, {0.f, 0.f, -1.f}, {1.f, 1.f, 1.f}, {1.f, 0.f}}, + {{size, -size, -size}, {0.f, 0.f, -1.f}, {1.f, 1.f, 1.f}, {0.f, 0.f}}, + {{size, size, -size}, {0.f, 0.f, -1.f}, {1.f, 1.f, 1.f}, {0.f, 1.f}}, + {{-size, size, -size}, {0.f, 0.f, -1.f}, {1.f, 1.f, 1.f}, {1.f, 1.f}}, + + // Left face + {{-size, -size, -size}, {-1.f, 0.f, 0.f}, {1.f, 1.f, 1.f}, {0.f, 0.f}}, + {{-size, -size, size}, {-1.f, 0.f, 0.f}, {1.f, 1.f, 1.f}, {1.f, 0.f}}, + {{-size, size, size}, {-1.f, 0.f, 0.f}, {1.f, 1.f, 1.f}, {1.f, 1.f}}, + {{-size, size, -size}, {-1.f, 0.f, 0.f}, {1.f, 1.f, 1.f}, {0.f, 1.f}}, + + // Right face + {{size, -size, size}, {1.f, 0.f, 0.f}, {1.f, 1.f, 1.f}, {0.f, 0.f}}, + {{size, -size, -size}, {1.f, 0.f, 0.f}, {1.f, 1.f, 1.f}, {1.f, 0.f}}, + {{size, size, -size}, {1.f, 0.f, 0.f}, {1.f, 1.f, 1.f}, {1.f, 1.f}}, + {{size, size, size}, {1.f, 0.f, 0.f}, {1.f, 1.f, 1.f}, {0.f, 1.f}}, + + // Top face + {{-size, size, size}, {0.f, 1.f, 0.f}, {1.f, 1.f, 1.f}, {0.f, 0.f}}, + {{size, size, size}, {0.f, 1.f, 0.f}, {1.f, 1.f, 1.f}, {1.f, 0.f}}, + {{size, size, -size}, {0.f, 1.f, 0.f}, {1.f, 1.f, 1.f}, {1.f, 1.f}}, + {{-size, size, -size}, {0.f, 1.f, 0.f}, {1.f, 1.f, 1.f}, {0.f, 1.f}}, + + // Bottom face + {{-size, -size, -size}, {0.f, -1.f, 0.f}, {1.f, 1.f, 1.f}, {0.f, 0.f}}, + {{size, -size, -size}, {0.f, -1.f, 0.f}, {1.f, 1.f, 1.f}, {1.f, 0.f}}, + {{size, -size, size}, {0.f, -1.f, 0.f}, {1.f, 1.f, 1.f}, {1.f, 1.f}}, + {{-size, -size, size}, {0.f, -1.f, 0.f}, {1.f, 1.f, 1.f}, {0.f, 1.f}}, + }; + + std::vector indices = { + // Front face + 0, 2, 1, 2, 0, 3, + // Back face + 7, 5, 6, 5, 7, 4, + // Left face + 11, 10, 9, 9, 8, 11, + // Right face + 12, 14, 13, 15, 14, 12, + // Top face + 19, 18, 17, 17, 16, 19, + // Bottom face + 22, 21, 20, 20, 23, 22}; + + std::vector textures = {}; + m_cube = new Mesh(2, vertices, indices, textures); + } + + // Fire textures + m_noiseTexture = new Texture(ASSETS_PATH "fire/fire.jpg"); + m_flameShape = new Texture(ASSETS_PATH "fire/flame.png"); + + m_sceneTextureBeforeFire = new Texture(Screen::rWidth, Screen::rHeight, Texture::TextureType::Data); + m_postProcessTextureFront = new Texture(Screen::rWidth, Screen::rHeight, Texture::TextureType::Data); + m_postProcessTextureBack = new Texture(Screen::rWidth, Screen::rHeight, Texture::TextureType::Data); + + m_postProcessRenderFront = new RenderTarget(false); + m_postProcessRenderFront->bindColorTextureToFrameBuffer(*m_postProcessTextureFront); + + m_postProcessRenderBack = new RenderTarget(false); + m_postProcessRenderBack->bindColorTextureToFrameBuffer(*m_postProcessTextureBack); + + m_postProcessDoubleBuffer[0] = m_postProcessRenderFront; + m_postProcessDoubleBuffer[1] = m_postProcessRenderBack; + + // Bloom Texture and Render Target + m_brightPassTexture = new Texture(Screen::rWidth, Screen::rHeight, Texture::TextureType::Data); + m_bloomRenderTarget = new RenderTarget(false); + m_bloomRenderTarget->bindColorTextureToFrameBuffer(*m_brightPassTexture); + } + + void onDestroy() override + { + m_flameShader.free(); + m_particlesShader.free(); + m_smokeShader.free(); + m_litShader.free(); + m_heatDistortionShader.free(); + m_blurShader.free(); + m_bloomShader.free(); + m_brightFilterShader.free(); + m_backgroundShader.free(); + + m_quad->free(); + delete m_quad; + m_cube->free(); + delete m_quad; + + m_noiseTexture->dispose(); + delete m_noiseTexture; + m_flameShape->dispose(); + delete m_flameShape; + m_sceneTextureBeforeFire->dispose(); + delete m_sceneTextureBeforeFire; + m_postProcessTextureFront->dispose(); + delete m_postProcessTextureFront; + m_postProcessTextureBack->dispose(); + delete m_postProcessTextureBack; + m_brightPassTexture->dispose(); + delete m_brightPassTexture; + + m_postProcessRenderFront->free(); + delete m_postProcessRenderFront; + m_postProcessRenderBack->free(); + delete m_postProcessRenderBack; + m_bloomRenderTarget->free(); + delete m_bloomRenderTarget; + + m_renderPlane.free(); + } + + // Inherited via Scene + void onStart() override + { + m_debugFly = false; + } + + float m_time = 3.1416f; + void onUpdate(float delta) override + { + if (m_debugFly) + { + return; + } + + if (!Input::GetKey(Input::Space)) + { + static float speed = 0.15f; + if (Input::GetKey(Input::R)) + { + m_time -= delta * speed; + } + else + { + m_time += delta * speed; + } + } + + Transform &cameraTransform = m_ecs.getComponent(m_mainCamera); + + static float amplitude = 12.5f; + + cameraTransform.position.y = 2.0f + tan(0.5f * m_time); + cameraTransform.position.x = amplitude * sin(m_time); + cameraTransform.position.z = amplitude * cos(m_time); + + cameraTransform.rotation = -cameraTransform.position; + } + + void renderFire(WeirdRenderer::Camera &camera, float time) + { + // Particles + m_particlesShader.use(); + m_particlesShader.setUniform("u_camMatrix", camera.cameraMatrix); + m_particlesShader.setUniform("u_camPos", camera.position); + m_particlesShader.setUniform("u_time", time); + m_quad->drawInstances(m_particlesShader, camera, + 10, + vec3(0, 1.0f, 0), + vec3(0, 0, time), + vec3(0.01f)); + + glEnable(GL_BLEND); + + // Smoke + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthMask(GL_FALSE); // Don't write to the depth buffer + + m_smokeShader.use(); + m_smokeShader.setUniform("u_camMatrix", camera.cameraMatrix); + m_smokeShader.setUniform("u_camPos", camera.position); + m_smokeShader.setUniform("u_time", time); + m_quad->drawInstances(m_smokeShader, camera, + 50, + vec3(0, 1.0f, -0.1f), + vec3(0, 0, time), + vec3(1.0f)); + + glDepthMask(GL_TRUE); + + // Fire + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + m_flameShader.use(); + m_flameShader.setUniform("u_camMatrix", camera.cameraMatrix); + m_flameShader.setUniform("u_camPos", camera.position); + m_flameShader.setUniform("u_time", time); + + m_flameShader.setUniform("t_noise", 0); + m_noiseTexture->bind(0); + + m_flameShader.setUniform("t_flameShape", 1); + m_flameShape->bind(1); + + // draw flame + m_quad->draw(m_flameShader, camera, vec3(0, 1.15f, 0), vec3(0, 0, 0), vec3(6)); + + m_noiseTexture->unbind(); + + glDisable(GL_BLEND); + } + + void onRender(WeirdRenderer::RenderTarget &renderTarget) override + { + + WeirdRenderer::Camera &sceneCamera = getCamera(); + float time = getTime(); + + glDepthMask(GL_FALSE); + m_backgroundShader.use(); + m_backgroundShader.setUniform("u_camMatrix", sceneCamera.cameraMatrix); + float shaderFov = 1.0f / tan(sceneCamera.fov * 0.01745f * 0.5f); + m_backgroundShader.setUniform("u_fov", shaderFov); + + // m_renderPlane.draw(m_backgroundShader); + // glFrontFace(GL_CW); // Clockwise = front face + // m_cube->draw(m_backgroundShader, sceneCamera, sceneCamera.position, vec3(0.0f), vec3(100.0f)); + // glFrontFace(GL_CCW); // Counter-clockwise = front face (default) + + glDepthMask(GL_TRUE); + + GL_CHECK_ERROR(); + + // Render stuff + m_litShader.use(); + m_litShader.setUniform("u_time", (float)time); + m_litShader.setUniform("u_ambient", 0.025f); + + // Take care of the camera Matrix + m_litShader.setUniform("u_camPos", sceneCamera.position); + m_litShader.setUniform("u_camMatrix", sceneCamera.cameraMatrix); + + // Pass light rotation + glm::vec3 position = m_lights[0].position; + m_litShader.setUniform("u_lightPos", position); + glm::vec3 direction = m_lights[0].rotation; + m_litShader.setUniform("u_directionalLightDir", direction); + glm::vec4 color = m_lights[0].color; + m_litShader.setUniform("u_lightColor", color); + + // bind current FBO + // m_sceneRender->bind(); // TODO: keep rendering to the same fbo. Should pass the render target and the color and depth textures + + // Floor + m_cube->draw(m_litShader, sceneCamera, vec3(0, -500.0f, 0), vec3(0), vec3(4, 1000, 4)); + + m_cube->draw(m_litShader, sceneCamera, vec3(0.2f, 1.75f, -2.5f), vec3(-3.14f / 4.0f), vec3(1)); + + // m_monkey->draw(m_litShader, sceneCamera, vec3(0, 1, -2.5f), vec3(0, (-3.14f / 2.0f), 0), vec3(1)); + m_cube->draw(m_litShader, sceneCamera, vec3(0.8f, 1.0f, 2.5f), vec3(0.5f, 0.5f, 0), vec3(1)); + // m_cube->draw(m_litShader, sceneCamera, vec3(0.3f, 0.5f, 3.2f), vec3(0, 1.2f, 0), vec3(1)); + // m_cube->draw(m_litShader, sceneCamera, vec3(0.9f, 1.5f, 2.9f), vec3(0, 0.75f, 0), vec3(1)); + + m_cube->draw(m_litShader, sceneCamera, vec3(-2.0f, 2.75f, 0.9f), vec3(-0.75f, 0.0f, 0), vec3(1)); + + + GL_CHECK_ERROR(); + + // m_cube->draw(m_litShader, sceneCamera, vec3(0, 0.0f, 0), vec3(0), vec3(0.1f, 2.0f, 0.1f)); + + // Heat effect + + // Don't write to depth buffer + glDepthMask(GL_FALSE); + + // Copy scene texture // TODO: replace with glCopyTexSubImage which copies from the current read framebuffer attachment to the given image + glCopyImageSubData( + renderTarget.getColorAttachment()->ID, GL_TEXTURE_2D, 0, 0, 0, 0, // 2 = scene texture + m_sceneTextureBeforeFire->ID, GL_TEXTURE_2D, 0, 0, 0, 0, + Screen::rWidth, Screen::rHeight, 1); + + // Heat effect shader + m_heatDistortionShader.use(); + m_heatDistortionShader.setUniform("u_camPos", sceneCamera.position); + m_heatDistortionShader.setUniform("u_camMatrix", sceneCamera.cameraMatrix); + + // Takes copy texture + m_heatDistortionShader.setUniform("t_texture", 0); + m_sceneTextureBeforeFire->bind(0); + // Noise texture + m_heatDistortionShader.setUniform("t_noise", 1); + m_noiseTexture->bind(1); + // Flame texture + m_heatDistortionShader.setUniform("t_flameShape", 2); + m_flameShape->bind(2); + + GL_CHECK_ERROR(); + + // Time to animate effect + m_heatDistortionShader.setUniform("u_time", (float)time); + // Screen resolution to calculate screen uvs + m_heatDistortionShader.setUniform("u_resolution", glm::vec2(Screen::rWidth, Screen::rHeight)); + + // Render quad + m_quad->draw(m_heatDistortionShader, sceneCamera, vec3(0, 1.25f, 0), vec3(0, 0, 0), vec3(6)); + + // Write to depth buffer + glDepthMask(GL_TRUE); + + // Fire + renderFire(sceneCamera, time); + + if (Input::GetKey(Input::P)) + { + return; + } + + // Post processing + m_bloomRenderTarget->bind(); + m_brightFilterShader.use(); + m_brightFilterShader.setUniform("t_colorTexture", 0); + renderTarget.getColorAttachment()->bind(0); + + m_renderPlane.draw(m_brightFilterShader); + + m_blurShader.use(); + m_blurShader.setUniform("t_colorTexture", 0); + + bool horizontal = true; + static int amount = 10; + + for (unsigned int i = 0; i < amount; i++) + { + m_postProcessDoubleBuffer[horizontal]->bind(); + + m_blurShader.setUniform("u_horizontal", horizontal); + if (i == 0) + { + m_brightPassTexture->bind(0); + } + else + { + m_postProcessDoubleBuffer[!horizontal]->getColorAttachment()->bind(0); + } + + m_renderPlane.draw(m_blurShader); + + horizontal = !horizontal; + } + + // bind a different FBO + renderTarget.bind(); // bind to fbo 0 + + glDisable(GL_DEPTH_TEST); + + // Use scene texture and a shader to apply pp + m_bloomShader.use(); + m_bloomShader.setUniform("t_sceneTexture", 0); + renderTarget.getColorAttachment()->bind(0); + + m_bloomShader.setUniform("t_blurTexture", 1); + RenderTarget *finalTarget = m_postProcessDoubleBuffer[!horizontal]; + finalTarget->getColorAttachment()->bind(1); + + if (Input::GetKey(Input::B)) + { + finalTarget->getColorAttachment()->bind(0); + } + + m_bloomShader.setUniform("u_pixelOffset", vec2(0.5f / Screen::rWidth, 0.5f / Screen::rHeight)); + + m_renderPlane.draw(m_bloomShader); + } +}; + +class FireSceneRayMarching : public FireScene +{ +private: + void onStart() override + { + m_renderMode = RenderMode::RayMarching3D; + + Transform& cameraTransform = m_ecs.getComponent(m_mainCamera); + + cameraTransform.position = vec3(0.0f, 1.0f, 10.0f); + cameraTransform.rotation = vec3(0.0f, 0.0f, -1.0f); + } +}; \ No newline at end of file diff --git a/examples/sample-scenes/include/FireworksScene.h b/examples/sample-scenes/include/FireworksScene.h new file mode 100644 index 0000000..446e616 --- /dev/null +++ b/examples/sample-scenes/include/FireworksScene.h @@ -0,0 +1,56 @@ +#pragma once + +#include +#include + +using namespace WeirdEngine; +class FireworksScene : public Scene +{ +public: + FireworksScene() + : Scene() { + }; + +private: + // Inherited via Scene + void onStart() override + { + // Create a random number generator engine + std::random_device rd; + std::mt19937 gen(rd()); + float range = 0.5f; + std::uniform_real_distribution<> distrib(-range, range); + + for (size_t i = 0; i < 60; i++) + { + float y = 50 + distrib(gen); + float x = 15 + distrib(gen); + + int material = 4 + (i % 12); + + float z = 0; + + Entity entity = m_ecs.createEntity(); + Transform& t = m_ecs.addComponent(entity); + t.position = vec3(x + 0.5f, y + 0.5f, z); + + if (i < 100) + { + } + SDFRenderer& sdfRenderer = m_ecs.addComponent(entity); + sdfRenderer.materialId = material; + + RigidBody2D& rb = m_ecs.addComponent(entity); + } + + // Floor + { + float variables[8]{ 0.5f, 1.5f, -1.0f }; + addShape(0, variables, 3); + } + } + + void onUpdate(float delta) override + { + } +}; diff --git a/examples/sample-scenes/include/ImageScene.h b/examples/sample-scenes/include/ImageScene.h new file mode 100644 index 0000000..2212763 --- /dev/null +++ b/examples/sample-scenes/include/ImageScene.h @@ -0,0 +1,168 @@ +#pragma once + +#include + +using namespace WeirdEngine; +class ImageScene : public Scene +{ +public: + ImageScene() + : Scene() { + }; + +private: + std::string binaryString; + std::string filePath = "cache/image.txt"; + std::string imagePath = "SampleProject/Resources/Textures/image.jpg"; + + // Inherited via Scene + void onStart() override + { + // Check if the folder exists + if (!std::filesystem::exists("cache/")) + { + // If it doesn't exist, create the folder + std::filesystem::create_directory("cache/"); + } + + if (checkIfFileExists(filePath.c_str())) + { + binaryString = get_file_contents(filePath.c_str()); + } + else + { + binaryString = "0"; + } + + uint32_t currentChar = 0; + + // Spawn 2d balls + for (size_t i = 0; i < 1200; i++) + { + float x; + float y; + int material = 0; + + x = 15 + sin(i); + y = 10 + (1.0f * i); + + std::string materialId; + while (currentChar < binaryString.size() && binaryString[currentChar] != '-') + { + materialId += binaryString[currentChar++]; + } + currentChar++; + + material = (materialId.size() > 0 && materialId.size() <= 2) ? std::stoi(materialId) : 0; + + Entity entity = m_ecs.createEntity(); + Transform& t = m_ecs.addComponent(entity); + t.position = vec3(x + 0.5f, y + 0.5f, 0); + + SDFRenderer& sdfRenderer = m_ecs.addComponent(entity); + sdfRenderer.materialId = material; + + RigidBody2D& rb = m_ecs.addComponent(entity); + } + + // Floor + { + float variables[8]{ 15, -5, 25.0f, 5.0f, 0.0f }; + addShape(m_sdfs.size() - 1, variables, 3); + } + + // Wall right + { + float variables[8]{ 30 + 5, 20, 5.0f, 30.0f, 0.0f }; + addShape(m_sdfs.size() - 1, variables, 3); + } + + // Wall left + { + float variables[8]{ 0 - 5, 20, 5.0f, 30.0f, 0.0f }; + addShape(m_sdfs.size() - 1, variables, 3); + } + } + + vec3 getColor(const char* path, int x, int y) + { + // Load the image + int width, height, channels; + unsigned char* img = wstbi_load(path, &width, &height, &channels, 0); + + if (img == nullptr) + { + std::cerr << "Error: could not load image." << std::endl; + return vec3(); + } + + if (x < 0) + { + x = 0; + } + else if (x >= width) + { + x = width - 1; + } + + if (y < 0) + { + y = 0; + } + else if (y >= height) + { + y = height - 1; + } + + // Calculate the index of the pixel in the image data + int index = (y * width + x) * channels; + + if (index < 0 || index >= width * height * channels) + { + return vec3(); + } + + // Get the color values + unsigned char r = img[index]; + unsigned char g = img[index + 1]; + unsigned char b = img[index + 2]; + unsigned char a = (channels == 4) ? img[index + 3] : 255; // Alpha channel (if present) + + // Free the image memory + wstbi_image_free(img); + + return vec3( + static_cast(r) / 255.0f, + static_cast(g) / 255.0f, + static_cast(b) / 255.0f); + } + + void onUpdate(float delta) override + { + // Get colors + if (Input::GetKeyDown(Input::P)) + { + auto components = m_ecs.getComponentArray(); + + // Result string + std::string result; + result.reserve(components->getSize()); + for (size_t i = 0; i < components->getSize(); i++) + { + RigidBody2D& rb = components->getDataAtIdx(i); + Transform& t = m_ecs.getComponent(rb.Owner); + + int x = floor(t.position.x); + int y = floor(30 - t.position.y); + + vec3 color = getColor(imagePath.c_str(), x * 10, y * 10); + int id = m_sdfRenderSystem2D.findClosestColorInPalette(color); + + result += std::to_string(id) + "-"; + } + + saveToFile(filePath.c_str(), result); + std::cout << "Image saved" << std::endl; + } + } +}; diff --git a/examples/sample-scenes/include/Lines.h b/examples/sample-scenes/include/Lines.h new file mode 100644 index 0000000..936cb77 --- /dev/null +++ b/examples/sample-scenes/include/Lines.h @@ -0,0 +1,111 @@ +#pragma once + +#include + +using namespace WeirdEngine; +class LinesScene : public Scene +{ +private: + Texture *m_colorTextureCopy; + + RenderPlane *m_renderPlane; + Shader *m_lineShader; + + RenderTarget *m_lineRender; + Texture *m_lineTexture; + + Shader *m_combinationShader; + + Entity m_monkey; + + void onCreate() override + { + m_renderPlane = new RenderPlane(); + m_colorTextureCopy = new Texture(Screen::rWidth, Screen::rHeight, Texture::TextureType::Data); + m_lineShader = new Shader(SHADERS_PATH "renderPlane.vert", ASSETS_PATH "lines/lines.frag"); + + m_lineRender = new RenderTarget(false); + m_lineTexture = new Texture(Screen::rWidth, Screen::rHeight, Texture::TextureType::Data); + m_lineRender->bindColorTextureToFrameBuffer(*m_lineTexture); + + m_combinationShader = new Shader(SHADERS_PATH "renderPlane.vert", ASSETS_PATH "lines/combination.frag"); + } + + // Inherited via Scene + void onStart() override + { + m_renderMode = RenderMode::RayMarching3D; + m_debugFly = false; + + { + Entity entity = m_ecs.createEntity(); + Transform &t = m_ecs.addComponent(entity); + t.position = vec3(0, 0, 0); + + // MeshRenderer &mr = m_ecs.addComponent(entity); + + // auto id = m_resourceManager.getMeshId(ASSETS_PATH "monkey/demo.gltf", entity, true); + // mr.mesh = id; + + auto& sdf = m_ecs.addComponent(entity); + sdf.materialId = 0; + + m_monkey = entity; + } + } + + void onUpdate(float delta) override + { + Transform &cameraTransform = m_ecs.getComponent(m_mainCamera); + cameraTransform.position.y = 5.0f; + cameraTransform.position.z -= 10.0f * delta; + + Transform& monkeyTransform = m_ecs.getComponent(m_monkey); + monkeyTransform.position = cameraTransform.position; + monkeyTransform.position.z -= 5.0f; + } + + void onRender(WeirdRenderer::RenderTarget &renderTarget) override + { + m_lineRender->bind(); + glClearColor(0, 0, 0, 0); // Set clear color + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear both buffers + + // Copy + glCopyImageSubData( + renderTarget.getColorAttachment()->ID, GL_TEXTURE_2D, 0, 0, 0, 0, // 2 = scene texture + m_colorTextureCopy->ID, GL_TEXTURE_2D, 0, 0, 0, 0, + Screen::rWidth, Screen::rHeight, 1); + + // --- Line detection --- + m_lineShader->use(); + + m_lineShader->setUniform("t_sceneDepth", 0); + renderTarget.getDepthAttachment()->bind(0); + + m_lineShader->setUniform("u_pixelSize", vec2(1.0f / Screen::rWidth, 1.0f / Screen::rHeight)); + + glDisable(GL_DEPTH_TEST); + glEnable(GL_BLEND); + m_renderPlane->draw(*m_lineShader); + glDisable(GL_BLEND); + glEnable(GL_DEPTH_TEST); + + // --- Final draw --- + renderTarget.bind(); + + m_combinationShader->use(); + + m_combinationShader->setUniform("t_scene", 0); + m_colorTextureCopy->bind(0); + + m_combinationShader->setUniform("t_lines", 1); + m_lineTexture->bind(1); + + glDisable(GL_DEPTH_TEST); + glEnable(GL_BLEND); + m_renderPlane->draw(*m_combinationShader); + glDisable(GL_BLEND); + glEnable(GL_DEPTH_TEST); + } +}; \ No newline at end of file diff --git a/examples/sample-scenes/include/MouseCollisionScene.h b/examples/sample-scenes/include/MouseCollisionScene.h new file mode 100644 index 0000000..a02d449 --- /dev/null +++ b/examples/sample-scenes/include/MouseCollisionScene.h @@ -0,0 +1,74 @@ +#pragma once + +#include + +using namespace WeirdEngine; +class MouseCollisionScene : public Scene +{ +public: + MouseCollisionScene() + : Scene() { + }; + +private: + Entity m_cursorShape; + + // Inherited via Scene + void onStart() override + { + for (size_t i = 0; i < 600; i++) + { + + float y = (int)(i / 20); + float x = 5 + (i % 20) + sin(y); + + int material = 4 + (i % 12); + + float z = 0; + + Entity entity = m_ecs.createEntity(); + Transform& t = m_ecs.addComponent(entity); + t.position = vec3(x + 0.5f, y + 0.5f, z); + + if (i < 100) + { + } + + SDFRenderer& sdfRenderer = m_ecs.addComponent(entity); + sdfRenderer.materialId = material; + + RigidBody2D& rb = m_ecs.addComponent(entity); + } + + // Floor + { + float variables[8]{ 0.5f, 1.5f, -1.0f }; + addShape(0, variables, 3); + } + + { + float variables[8]{ -15.0f, 50.0f, 5.0f, 5.0f, 2.0f, 10.0f }; + Entity star = addShape(1, variables, 3); + + m_cursorShape = star; + } + } + + void onUpdate(float delta) override + { + // Move wall to mouse + { + CustomShape& cs = m_ecs.getComponent(m_cursorShape); + auto& cameraTransform = m_ecs.getComponent(m_mainCamera); + float x = Input::GetMouseX(); + float y = Input::GetMouseY(); + + // Transform mouse coordinates to world space + vec2 mousePositionInWorld = ECS::Camera::screenPositionToWorldPosition2D(cameraTransform, vec2(x, y)); + + cs.m_parameters[0] = mousePositionInWorld.x; + cs.m_parameters[1] = mousePositionInWorld.y; + cs.m_isDirty = true; + } + } +}; diff --git a/examples/sample-scenes/include/RopeScene.h b/examples/sample-scenes/include/RopeScene.h new file mode 100644 index 0000000..91c7c54 --- /dev/null +++ b/examples/sample-scenes/include/RopeScene.h @@ -0,0 +1,162 @@ +#pragma once + +#include + +using namespace WeirdEngine; +// Example scene demonstrating how to create a rope of connected circles using springs. +class RopeScene : public Scene +{ +public: + RopeScene() + : Scene() + { + } + +private: + Entity m_star; + double m_lastSpawnTime = 0.0; + + void onStart() override + { + m_debugInput = true; + + // Load fixed-width font for on-screen text + loadFont(ENGINE_PATH "/src/weird-renderer/fonts/small.bmp", 4, 5, + "ABCDEFGHIJKLMNOPQRSTUVWXYZ[]{}abcdefghijklmnopqrstuvwxyz\\/<>0123456789!\" "); + + print("Nice rope dude!"); + + constexpr int numBalls = 60; + constexpr int rowWidth = 30; + constexpr float startY = 20.0f + (numBalls / rowWidth); + constexpr float stiffness = 20000000.0f; + + // Create 2D rigid bodies in a rope/grid layout + for (int i = 0; i < numBalls; ++i) + { + float x = static_cast(i % rowWidth); + float y = startY - static_cast(i / rowWidth); + int material = 4 + (i % 12); + + Entity entity = m_ecs.createEntity(); + + auto& t = m_ecs.addComponent(entity); + t.position = vec3(x + 0.5f, y + 0.5f, 0.0f); + + auto& sdf = m_ecs.addComponent(entity); + sdf.materialId = material; + + m_ecs.addComponent(entity); + } + + // Connect balls with springs (down and right) + for (int i = 0; i < numBalls; ++i) + { + if (i + rowWidth < numBalls) // Down + { + m_simulation2D.addSpring(i, i + rowWidth, stiffness); + } + + if ((i + 1) % rowWidth != 0) // Right + { + m_simulation2D.addSpring(i, i + 1, stiffness); + } + } + + // Fix top corners + if (numBalls >= rowWidth) + { + m_simulation2D.fix(0); + m_simulation2D.fix(rowWidth - 1); + } + + // Add base shapes (walls, ground, custom) + float vars0[8] = { 1.0f, 0.5f }; // Floor shape + addShape(0, vars0, 3); + + float vars1[8] = { 25.0f, 10.0f, 5.0f, 0.5f, 13.0f, 5.0f }; // Custom shape + m_star = addShape(1, vars1, 3); + + float vars2[8] = { 30.5f, 3.5f, 30.0f, 3.0f }; + // addScreenSpaceShape(3, vars2); // UI overlay shape + + float vars3[8] = { 15.0f, 0.0f, 15.0f, 2.0f }; + addShape(3, vars3, 3); + } + + void throwBalls(ECSManager& ecs, Simulation2D& sim) + { + if (sim.getSimulationTime() <= m_lastSpawnTime + 0.1) + { + return; + } + + constexpr int amount = 10; + for (int i = 0; i < amount; ++i) + { + float y = 60.0f + (1.2f * i); + + Entity entity = ecs.createEntity(); + + auto& t = ecs.addComponent(entity); + t.position = vec3(0.5f, y + 0.5f, 0.0f); + + auto& sdf = ecs.addComponent(entity); + sdf.materialId = 4 + ecs.getComponentArray()->getSize() % 12; + + auto& rb = ecs.addComponent(entity); + sim.addForce(rb.simulationId, vec2(20.0f, 0.0f)); + } + + m_lastSpawnTime = sim.getSimulationTime(); + } + + void onUpdate(float delta) override + { + // Animate custom shape over time + { + auto& cs = m_ecs.getComponent(m_star); + cs.m_parameters[4] = static_cast(std::floor(m_simulation2D.getSimulationTime())) % 5 + 2; + cs.m_parameters[3] = std::sin(3.1416f * m_simulation2D.getSimulationTime()); + cs.m_isDirty = true; + } + + if (Input::GetKey(Input::E)) + { + throwBalls(m_ecs, m_simulation2D); + } + + if (Input::GetKeyDown(Input::M)) + { + auto& cam = m_ecs.getComponent(m_mainCamera); + vec2 screen = { Input::GetMouseX(), Input::GetMouseY() }; + vec2 world = ECS::Camera::screenPositionToWorldPosition2D(cam, screen); + + float vars[8] = { world.x, world.y, 5.0f, 0.5f, 13.0f, 5.0f }; + addShape(1, vars, 3); + } + + if (Input::GetKeyDown(Input::N)) + { + // Duplicate last SDF, add new shape with it + m_sdfs.push_back(m_sdfs.back()); + m_simulation2D.setSDFs(m_sdfs); + + auto& cam = m_ecs.getComponent(m_mainCamera); + vec2 screen = { Input::GetMouseX(), Input::GetMouseY() }; + vec2 world = ECS::Camera::screenPositionToWorldPosition2D(cam, screen); + + float vars[8] = { world.x, world.y, 5.0f, 7.5f, 1.0f }; + addShape(m_sdfs.size() - 1, vars, 3); + } + + if (Input::GetKeyDown(Input::K)) + { + auto components = m_ecs.getComponentArray(); + int id = components->getSize() - 1; + + m_simulation2D.removeShape(components->getDataAtIdx(id)); + m_ecs.destroyEntity(components->getDataAtIdx(id).Owner); + } + } +}; diff --git a/examples/sample-scenes/include/ShapesCombinations.h b/examples/sample-scenes/include/ShapesCombinations.h new file mode 100644 index 0000000..ccaee8f --- /dev/null +++ b/examples/sample-scenes/include/ShapesCombinations.h @@ -0,0 +1,140 @@ +#pragma once + +#include + +#include + +using namespace WeirdEngine; +// Example scene demonstrating how to create a rope of connected circles using springs. +class ShapeCombinatiosScene : public Scene +{ +public: + ShapeCombinatiosScene() + : Scene() + { + } + + + +private: + + Entity m_circle; + Entity m_circle2 = 0; + float m_circleRadioud = 0.0f; + vec2 m_initialMousePositionInWorld; + + void onStart() override + { + m_debugInput = true; + + // Floor shape + { + float vars0[8] = {0.5f, 2.5f}; + addShape(CustomShape::SINE, vars0, 2, CombinationType::Addition, true, 0); + } + + std::random_device rd; + std::mt19937 gen(rd()); + float range = 20.0f; + std::uniform_real_distribution<> distrib(-range, range); + + // Boxes + { + std::uniform_real_distribution<> distribY(0, 5); + + for (int i = 0; i < 0; ++i) + { + float x = distrib(gen); + float y = -2.0f + distribY(gen); + + float vars2[8] = { x, y, 3.0f, 5.0f, 1.0f, 0.0f }; // Custom shape + addShape(CustomShape::BOX, vars2, 4 + i, CombinationType::Addition, true, 1); + } + } + + + + // Circle + { + float vars[8] = { 0.0f, 7.5f, 5.0f}; + addShape(CustomShape::CIRCLE, vars, 3, CombinationType::Addition, true, 2); + } + + // Subtract star + { + float vars[8] = { -2.5f, 12.5f, 5.0f, 0.5f, 13.0f, 5.0f }; + addShape(CustomShape::STAR, vars, 0, CombinationType::Subtraction, true, 2); + } + + + // Cursor circle + { + float vars2[8] = { 25.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f }; // Custom shape + m_circle = addShape(CustomShape::CIRCLE, vars2, 0, CombinationType::Subtraction, true, CustomShape::GLOBAL_GROUP); + } + + { + float vars2[8] = { 0.0f, 0.0f, 30.0f, 0.0f, 0.0f, 0.0f }; // Custom shape + addShape(CustomShape::CIRCLE, vars2, 0, CombinationType::Intersection, true, CustomShape::GLOBAL_GROUP); + } + + vec3 camPos = vec3(0.0f, 7.5f, 15.0f); + // m_ecs.getComponent(m_mainCamera).position = camPos; + m_ecs.getComponent(m_mainCamera).targetPosition = camPos; + } + + void onUpdate(float delta) override + { + auto& cameraTransform = m_ecs.getComponent(m_mainCamera); + float x = Input::GetMouseX(); + float y = Input::GetMouseY(); + + // Transform mouse coordinates to world space + vec2 mousePositionInWorld = ECS::Camera::screenPositionToWorldPosition2D(cameraTransform, vec2(x, y)); + + + + if(Input::GetMouseButtonDown(Input::RightClick)) + { + m_initialMousePositionInWorld = mousePositionInWorld; + } + + if(Input::GetMouseButton(Input::RightClick)) + { + vec2 v = mousePositionInWorld - m_initialMousePositionInWorld; + m_circleRadioud = std::min(10.0f, length(v)); + } + else + { + m_circleRadioud -= delta * 10.0f * (m_circleRadioud + 1.0f); + m_circleRadioud = std::max(0.0f, m_circleRadioud); + } + + + { + + CustomShape& cs = m_ecs.getComponent(m_circle); + cs.m_parameters[0] = m_initialMousePositionInWorld.x; + cs.m_parameters[1] = m_initialMousePositionInWorld.y; + cs.m_parameters[2] = m_circleRadioud; + + cs.m_isDirty = true; + } + + + if (m_circle2) + { + CustomShape& cs = m_ecs.getComponent(m_circle2); + cs.m_parameters[0] = m_initialMousePositionInWorld.x; + cs.m_parameters[1] = m_initialMousePositionInWorld.y; + cs.m_parameters[2] = std::max(0.0f, m_circleRadioud - 0.1f); + + cs.m_isDirty = true; + } + } + + // void onCollision(WeirdEngine::CollisionEvent &event) override + // { + // m_simulation2D.fix(event.bodyB); + // } +}; diff --git a/examples/sample-scenes/include/SpaceScene.h b/examples/sample-scenes/include/SpaceScene.h new file mode 100644 index 0000000..c6fefbf --- /dev/null +++ b/examples/sample-scenes/include/SpaceScene.h @@ -0,0 +1,85 @@ +#pragma once + +#include +#include + +using namespace WeirdEngine; +class SpaceScene : public Scene +{ +private: + std::vector m_celestialBodies; + uint16_t m_current = 0; + bool m_lookAtBody = true; + + // Inherited via Scene + void onStart() override + { + // m_debugFly = false; + + m_simulation2D.setGravity(0); + m_simulation2D.setDamping(0); + + loadRandomSystem(); + } + + void loadRandomSystem() + { + + std::random_device rd; + std::mt19937 gen(rd()); + + std::uniform_real_distribution floatDistrib(-1, 1); + std::uniform_real_distribution massDistrib(0.1f, 100.0f); + std::uniform_int_distribution colorDistrib(2, 15); + + size_t bodyCount = 1000; + float r = 0; + for (size_t i = 0; i < bodyCount; i++) + { + + Entity body = m_ecs.createEntity(); + + vec2 pos(floatDistrib(gen), floatDistrib(gen)); + Transform& t = m_ecs.addComponent(body); + t.position = 500.0f * vec3(pos.x, pos.y, 0); + + SDFRenderer& sdfRenderer = m_ecs.addComponent(body); + sdfRenderer.materialId = 4 + (i % 3); + + RigidBody2D& rb = m_ecs.addComponent(body); + m_simulation2D.setMass(rb.simulationId, 1.0f); + + for (auto b : m_celestialBodies) + { + m_simulation2D.addGravitationalConstraint( + m_ecs.getComponent(body).simulationId, + m_ecs.getComponent(b).simulationId, + 100.0f); + } + + m_simulation2D.addForce(m_ecs.getComponent(body).simulationId, (10.f * vec2(-pos.y, pos.x)) - (10.0f * vec2(pos.x, pos.y))); + + m_celestialBodies.push_back(body); + } + + lookAt(m_celestialBodies[0]); + } + + void onUpdate(float delta) override + { + if (Input::GetKeyDown(Input::E)) + { + m_current = (m_current + 1) % m_celestialBodies.size(); + } + + if (Input::GetKeyDown(Input::F)) + { + m_lookAtBody = !m_lookAtBody; + } + + if (m_lookAtBody && m_celestialBodies.size() > 0) + { + lookAt(m_celestialBodies[m_current]); + } + } +}; diff --git a/examples/sample-scenes/include/Text.h b/examples/sample-scenes/include/Text.h new file mode 100644 index 0000000..fd7b6ee --- /dev/null +++ b/examples/sample-scenes/include/Text.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +using namespace WeirdEngine; +class TextScene : public WeirdEngine:: Scene +{ + +private: + + + + // Inherited via Scene + void onStart() override + { + std::string example("Hello World!"); + + print(example); + } + + void onUpdate(float delta) override + { + } +}; \ No newline at end of file diff --git a/examples/sample-scenes/include/Water.h b/examples/sample-scenes/include/Water.h new file mode 100644 index 0000000..c733034 --- /dev/null +++ b/examples/sample-scenes/include/Water.h @@ -0,0 +1,403 @@ +#pragma once + +#include + +using namespace WeirdEngine; +class WaterScene : public Scene +{ +private: + Shader m_flameShader; + Shader m_particlesShader; + Shader m_smokeShader; + Shader m_litShader; + Shader m_heatDistortionShader; + Shader m_blurShader; + Shader m_bloomShader; + Shader m_brightFilterShader; + Shader m_backgroundShader; + + Mesh* m_quad = nullptr; + Mesh* m_cube = nullptr; + + Texture* m_noiseTexture = nullptr; + Texture* m_flameShape = nullptr; + Texture* m_sceneTextureBeforeFire = nullptr; // Copy of scene texture for heat effect + Texture* m_postProcessTextureFront = nullptr; + Texture* m_postProcessTextureBack = nullptr; + Texture* m_brightPassTexture = nullptr; + + RenderTarget* m_postProcessRenderFront; + RenderTarget* m_postProcessRenderBack; + RenderTarget* m_bloomRenderTarget; + + RenderTarget* m_postProcessDoubleBuffer[2]; + + RenderPlane m_renderPlane; + + std::vector m_lights; + + void onCreate() override + { + m_renderMode = RenderMode::Simple3D; + + // Base shaders + m_backgroundShader = Shader(SHADERS_PATH "renderPlane.vert", SHADERS_PATH "backgroundGrid.frag"); + m_litShader = Shader(SHADERS_PATH "default.vert", SHADERS_PATH "lit.frag"); + m_bloomShader = Shader(SHADERS_PATH "renderPlane.vert", SHADERS_PATH "bloom.frag"); + m_blurShader = Shader(SHADERS_PATH "renderPlane.vert", SHADERS_PATH "blur.frag"); + m_brightFilterShader = Shader(SHADERS_PATH "renderPlane.vert", SHADERS_PATH "brightFilter.frag"); + + // Custom shaders + m_flameShader = Shader(SHADERS_PATH "default.vert", ASSETS_PATH "fire/shaders/flame.frag"); + m_particlesShader = Shader(ASSETS_PATH "fire/shaders/fireParticles.vert", ASSETS_PATH "fire/shaders/fireParticles.frag"); + m_smokeShader = Shader(ASSETS_PATH "fire/shaders/smokeParticles.vert", ASSETS_PATH "fire/shaders/smokeParticles.frag"); + m_heatDistortionShader = Shader(SHADERS_PATH "default.vert", ASSETS_PATH "water/shaders/water.frag"); + + m_lights.push_back( + Light { + 0, + glm::vec3(0.0f, 1.0f, 0.0f), + 0, + glm::vec3(0.0f), + glm::vec4(1.0f, 0.95f, 0.9f, 2.0f) }); + + // Load meshes + // Quad geom + { + float size = 0.5f; + std::vector vertices = { + // positions // normals // colors // UVs + { { -size, -size, 0.f }, { 0.f, 0.f, 1.f }, { 1.f, 1.f, 1.f }, { 0.f, 0.f } }, // bottom left + { { size, -size, 0.f }, { 0.f, 0.f, 1.f }, { 1.f, 1.f, 1.f }, { 1.f, 0.f } }, // bottom right + { { size, size, 0.f }, { 0.f, 0.f, 1.f }, { 1.f, 1.f, 1.f }, { 1.f, 1.f } }, // top right + { { -size, size, 0.f }, { 0.f, 0.f, 1.f }, { 1.f, 1.f, 1.f }, { 0.f, 1.f } } // top left + }; + + std::vector indices = { + 0, 2, 1, // first triangle + 3, 2, 0 // second triangle + }; + + std::vector textures = {}; + m_quad = new Mesh(1, vertices, indices, textures); + m_quad->m_isBillboard = false; + } + + // Cube geom + { + float size = 0.5f; + std::vector vertices = { + // positions // normals // colors // UVs + // Front face + { { -size, -size, size }, { 0.f, 0.f, 1.f }, { 1.f, 1.f, 1.f }, { 0.f, 0.f } }, + { { size, -size, size }, { 0.f, 0.f, 1.f }, { 1.f, 1.f, 1.f }, { 1.f, 0.f } }, + { { size, size, size }, { 0.f, 0.f, 1.f }, { 1.f, 1.f, 1.f }, { 1.f, 1.f } }, + { { -size, size, size }, { 0.f, 0.f, 1.f }, { 1.f, 1.f, 1.f }, { 0.f, 1.f } }, + + // Back face + { { -size, -size, -size }, { 0.f, 0.f, -1.f }, { 1.f, 1.f, 1.f }, { 1.f, 0.f } }, + { { size, -size, -size }, { 0.f, 0.f, -1.f }, { 1.f, 1.f, 1.f }, { 0.f, 0.f } }, + { { size, size, -size }, { 0.f, 0.f, -1.f }, { 1.f, 1.f, 1.f }, { 0.f, 1.f } }, + { { -size, size, -size }, { 0.f, 0.f, -1.f }, { 1.f, 1.f, 1.f }, { 1.f, 1.f } }, + + // Left face + { { -size, -size, -size }, { -1.f, 0.f, 0.f }, { 1.f, 1.f, 1.f }, { 0.f, 0.f } }, + { { -size, -size, size }, { -1.f, 0.f, 0.f }, { 1.f, 1.f, 1.f }, { 1.f, 0.f } }, + { { -size, size, size }, { -1.f, 0.f, 0.f }, { 1.f, 1.f, 1.f }, { 1.f, 1.f } }, + { { -size, size, -size }, { -1.f, 0.f, 0.f }, { 1.f, 1.f, 1.f }, { 0.f, 1.f } }, + + // Right face + { { size, -size, size }, { 1.f, 0.f, 0.f }, { 1.f, 1.f, 1.f }, { 0.f, 0.f } }, + { { size, -size, -size }, { 1.f, 0.f, 0.f }, { 1.f, 1.f, 1.f }, { 1.f, 0.f } }, + { { size, size, -size }, { 1.f, 0.f, 0.f }, { 1.f, 1.f, 1.f }, { 1.f, 1.f } }, + { { size, size, size }, { 1.f, 0.f, 0.f }, { 1.f, 1.f, 1.f }, { 0.f, 1.f } }, + + // Top face + { { -size, size, size }, { 0.f, 1.f, 0.f }, { 1.f, 1.f, 1.f }, { 0.f, 0.f } }, + { { size, size, size }, { 0.f, 1.f, 0.f }, { 1.f, 1.f, 1.f }, { 1.f, 0.f } }, + { { size, size, -size }, { 0.f, 1.f, 0.f }, { 1.f, 1.f, 1.f }, { 1.f, 1.f } }, + { { -size, size, -size }, { 0.f, 1.f, 0.f }, { 1.f, 1.f, 1.f }, { 0.f, 1.f } }, + + // Bottom face + { { -size, -size, -size }, { 0.f, -1.f, 0.f }, { 1.f, 1.f, 1.f }, { 0.f, 0.f } }, + { { size, -size, -size }, { 0.f, -1.f, 0.f }, { 1.f, 1.f, 1.f }, { 1.f, 0.f } }, + { { size, -size, size }, { 0.f, -1.f, 0.f }, { 1.f, 1.f, 1.f }, { 1.f, 1.f } }, + { { -size, -size, size }, { 0.f, -1.f, 0.f }, { 1.f, 1.f, 1.f }, { 0.f, 1.f } }, + }; + + std::vector indices = { + // Front face + 0, 2, 1, 2, 0, 3, + // Back face + 7, 5, 6, 5, 7, 4, + // Left face + 11, 10, 9, 9, 8, 11, + // Right face + 12, 14, 13, 15, 14, 12, + // Top face + 19, 18, 17, 17, 16, 19, + // Bottom face + 22, 21, 20, 20, 23, 22 + }; + + std::vector textures = {}; + m_cube = new Mesh(2, vertices, indices, textures); + } + + // Fire textures + m_noiseTexture = new Texture(ASSETS_PATH "fire/fire.jpg"); + m_flameShape = new Texture(ASSETS_PATH "fire/flame.png"); + + m_sceneTextureBeforeFire = new Texture(Screen::rWidth, Screen::rHeight, Texture::TextureType::Data); + m_postProcessTextureFront = new Texture(Screen::rWidth, Screen::rHeight, Texture::TextureType::Data); + m_postProcessTextureBack = new Texture(Screen::rWidth, Screen::rHeight, Texture::TextureType::Data); + + m_postProcessRenderFront = new RenderTarget(false); + m_postProcessRenderFront->bindColorTextureToFrameBuffer(*m_postProcessTextureFront); + + m_postProcessRenderBack = new RenderTarget(false); + m_postProcessRenderBack->bindColorTextureToFrameBuffer(*m_postProcessTextureBack); + + m_postProcessDoubleBuffer[0] = m_postProcessRenderFront; + m_postProcessDoubleBuffer[1] = m_postProcessRenderBack; + + // Bloom Texture and Render Target + m_brightPassTexture = new Texture(Screen::rWidth, Screen::rHeight, Texture::TextureType::Data); + m_bloomRenderTarget = new RenderTarget(false); + m_bloomRenderTarget->bindColorTextureToFrameBuffer(*m_brightPassTexture); + } + + void onDestroy() override + { + m_flameShader.free(); + m_particlesShader.free(); + m_smokeShader.free(); + m_litShader.free(); + m_heatDistortionShader.free(); + m_blurShader.free(); + m_bloomShader.free(); + m_brightFilterShader.free(); + m_backgroundShader.free(); + + m_quad->free(); + delete m_quad; + m_cube->free(); + delete m_quad; + + m_noiseTexture->dispose(); + delete m_noiseTexture; + m_flameShape->dispose(); + delete m_flameShape; + m_sceneTextureBeforeFire->dispose(); + delete m_sceneTextureBeforeFire; + m_postProcessTextureFront->dispose(); + delete m_postProcessTextureFront; + m_postProcessTextureBack->dispose(); + delete m_postProcessTextureBack; + m_brightPassTexture->dispose(); + delete m_brightPassTexture; + + m_postProcessRenderFront->free(); + delete m_postProcessRenderFront; + m_postProcessRenderBack->free(); + delete m_postProcessRenderBack; + m_bloomRenderTarget->free(); + delete m_bloomRenderTarget; + + m_renderPlane.free(); + } + + // Inherited via Scene + void onStart() override + { + m_debugFly = true; + } + + float m_time = 3.1416f; + void onUpdate(float delta) override + { + if (m_debugFly) + { + return; + } + + if (!Input::GetKey(Input::Space)) + { + static float speed = 0.15f; + if (Input::GetKey(Input::R)) + { + m_time -= delta * speed; + } + else + { + m_time += delta * speed; + } + } + + Transform& cameraTransform = m_ecs.getComponent(m_mainCamera); + + static float amplitude = 12.5f; + + cameraTransform.position.y = 2.0f + tan(0.5f * m_time); + cameraTransform.position.x = amplitude * sin(m_time); + cameraTransform.position.z = amplitude * cos(m_time); + + cameraTransform.rotation = -cameraTransform.position; + } + + void renderWater(WeirdRenderer::Camera& camera, float time) + { + } + + void onRender(WeirdRenderer::RenderTarget& renderTarget) override + { + + WeirdRenderer::Camera& sceneCamera = getCamera(); + float time = getTime(); + + glDepthMask(GL_FALSE); + m_backgroundShader.use(); + m_backgroundShader.setUniform("u_camMatrix", sceneCamera.cameraMatrix); + float shaderFov = 1.0f / tan(sceneCamera.fov * 0.01745f * 0.5f); + m_backgroundShader.setUniform("u_fov", shaderFov); + + m_renderPlane.draw(m_backgroundShader); + // glFrontFace(GL_CW); // Clockwise = front face + // m_cube->draw(m_backgroundShader, sceneCamera, sceneCamera.position, vec3(0.0f), vec3(100.0f)); + // glFrontFace(GL_CCW); // Counter-clockwise = front face (default) + + glDepthMask(GL_TRUE); + + GL_CHECK_ERROR(); + + // Render stuff + m_litShader.use(); + m_litShader.setUniform("u_time", (float)time); + m_litShader.setUniform("u_ambient", 0.025f); + + // Take care of the camera Matrix + m_litShader.setUniform("u_camPos", sceneCamera.position); + m_litShader.setUniform("u_camMatrix", sceneCamera.cameraMatrix); + + // Pass light rotation + glm::vec3 position = m_lights[0].position; + m_litShader.setUniform("u_lightPos", position); + glm::vec3 direction = m_lights[0].rotation; + m_litShader.setUniform("u_directionalLightDir", direction); + glm::vec4 color = m_lights[0].color; + m_litShader.setUniform("u_lightColor", color); + + // bind current FBO + // m_sceneRender->bind(); // TODO: keep rendering to the same fbo. Should pass the render target and the color and depth textures + + // Floor + m_cube->draw(m_litShader, sceneCamera, vec3(0, -500.0f + 1.0f, 0), vec3(0), vec3(4, 1000, 4)); + + GL_CHECK_ERROR(); + + // m_cube->draw(m_litShader, sceneCamera, vec3(0, 0.0f, 0), vec3(0), vec3(0.1f, 2.0f, 0.1f)); + + // Heat effect + + // Don't write to depth buffer + glDepthMask(GL_FALSE); + + // Copy scene texture // TODO: replace with glCopyTexSubImage which copies from the current read framebuffer attachment to the given image + glCopyImageSubData( + renderTarget.getColorAttachment()->ID, GL_TEXTURE_2D, 0, 0, 0, 0, // 2 = scene texture + m_sceneTextureBeforeFire->ID, GL_TEXTURE_2D, 0, 0, 0, 0, + Screen::rWidth, Screen::rHeight, 1); + + // Heat effect shader + m_heatDistortionShader.use(); + m_heatDistortionShader.setUniform("u_camPos", sceneCamera.position); + m_heatDistortionShader.setUniform("u_camMatrix", sceneCamera.cameraMatrix); + + // Takes copy texture + m_heatDistortionShader.setUniform("t_texture", 0); + m_sceneTextureBeforeFire->bind(0); + // Noise texture + m_heatDistortionShader.setUniform("t_noise", 1); + m_noiseTexture->bind(1); + // Flame texture + m_heatDistortionShader.setUniform("t_flameShape", 2); + m_flameShape->bind(2); + + GL_CHECK_ERROR(); + + // Time to animate effect + m_heatDistortionShader.setUniform("u_time", (float)time); + // Screen resolution to calculate screen uvs + m_heatDistortionShader.setUniform("u_resolution", glm::vec2(Screen::rWidth, Screen::rHeight)); + + // Render quad + m_quad->draw(m_heatDistortionShader, sceneCamera, vec3(0), vec3(-3.14f / 2.0f, 0, 0), vec3(1000)); + + // Write to depth buffer + glDepthMask(GL_TRUE); + + // Fire + renderWater(sceneCamera, time); + + if (Input::GetKey(Input::P)) + { + return; + } + + // Post processing + m_bloomRenderTarget->bind(); + m_brightFilterShader.use(); + m_brightFilterShader.setUniform("t_colorTexture", 0); + renderTarget.getColorAttachment()->bind(0); + + m_renderPlane.draw(m_brightFilterShader); + + m_blurShader.use(); + m_blurShader.setUniform("t_colorTexture", 0); + + bool horizontal = true; + static int amount = 10; + + for (unsigned int i = 0; i < amount; i++) + { + m_postProcessDoubleBuffer[horizontal]->bind(); + + m_blurShader.setUniform("u_horizontal", horizontal); + if (i == 0) + { + m_brightPassTexture->bind(0); + } + else + { + m_postProcessDoubleBuffer[!horizontal]->getColorAttachment()->bind(0); + } + + m_renderPlane.draw(m_blurShader); + + horizontal = !horizontal; + } + + // bind a different FBO + renderTarget.bind(); // bind to fbo 0 + + glDisable(GL_DEPTH_TEST); + + // Use scene texture and a shader to apply pp + m_bloomShader.use(); + m_bloomShader.setUniform("t_sceneTexture", 0); + renderTarget.getColorAttachment()->bind(0); + + m_bloomShader.setUniform("t_blurTexture", 1); + RenderTarget* finalTarget = m_postProcessDoubleBuffer[!horizontal]; + finalTarget->getColorAttachment()->bind(1); + + if (Input::GetKey(Input::B)) + { + finalTarget->getColorAttachment()->bind(0); + } + + m_bloomShader.setUniform("u_pixelOffset", vec2(0.5f / Screen::rWidth, 0.5f / Screen::rHeight)); + + m_renderPlane.draw(m_bloomShader); + } +}; diff --git a/examples/sample-scenes/src/main.cpp b/examples/sample-scenes/src/main.cpp new file mode 100644 index 0000000..04f607a --- /dev/null +++ b/examples/sample-scenes/src/main.cpp @@ -0,0 +1,41 @@ + +#include + +#include + +#include "CollisionHandling.h" +#include "ApparentCircularMotionScene.h" +#include "Classic.h" +#include "DestroyScene.h" +#include "Fire.h" +#include "FireworksScene.h" +#include "ImageScene.h" +#include "Lines.h" +#include "MouseCollisionScene.h" +#include "RopeScene.h" +#include "Text.h" +#include "Water.h" +#include "ShapesCombinations.h" + +int main() +{ + SceneManager &sceneManager = SceneManager::getInstance(); + sceneManager.registerScene("shapes"); + sceneManager.registerScene("rope"); + sceneManager.registerScene("cursor-collision"); + sceneManager.registerScene("fire"); + sceneManager.registerScene("lines"); + + // sceneManager.registerScene("collision-handling"); + // sceneManager.registerScene("water"); + // sceneManager.registerScene("fireRayMarching"); + // sceneManager.registerScene("classic"); + // sceneManager.registerScene("text"); + // sceneManager.registerScene("empty"); + // sceneManager.registerScene("image"); + // sceneManager.registerScene("fireworks"); + // sceneManager.registerScene("circle"); + // sceneManager.registerScene("space"); + + start(sceneManager); +} \ No newline at end of file From ff8fa8a5e3980e82b5c265da66c0dd3e5e70b652 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= <49535803+damacaa@users.noreply.github.com> Date: Mon, 18 Aug 2025 16:12:03 +0200 Subject: [PATCH 17/17] Improved materials --- src/weird-renderer/Renderer.cpp | 2 +- .../shaders/2DMaterialColorShader.frag | 30 ++++++------------- 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/src/weird-renderer/Renderer.cpp b/src/weird-renderer/Renderer.cpp index fa4518d..ce24514 100644 --- a/src/weird-renderer/Renderer.cpp +++ b/src/weird-renderer/Renderer.cpp @@ -293,7 +293,7 @@ namespace WeirdEngine m_2DMaterialColorShader.setUniform("u_resolution", glm::vec2(m_renderWidth, m_renderHeight)); m_2DMaterialColorShader.setUniform("u_staticColors", m_colorPalette, 16); - m_2DMaterialColorShader.setUniform("t_colorTexture", 0); + m_2DMaterialColorShader.setUniform("t_materialDataTexture", 0); m_distanceTexture.bind(0); m_2DMaterialColorShader.setUniform("t_currentColorTexture", 1); diff --git a/src/weird-renderer/shaders/2DMaterialColorShader.frag b/src/weird-renderer/shaders/2DMaterialColorShader.frag index 4a71d48..d88895b 100644 --- a/src/weird-renderer/shaders/2DMaterialColorShader.frag +++ b/src/weird-renderer/shaders/2DMaterialColorShader.frag @@ -17,16 +17,10 @@ uniform mat4 u_camMatrix; uniform vec2 u_resolution; uniform float u_time; -uniform sampler2D t_colorTexture; +uniform sampler2D t_materialDataTexture; uniform sampler2D t_currentColorTexture; uniform vec3 u_staticColors[16]; -float map(vec2 p) -{ - return texture(t_colorTexture, p).w; -} - - vec3 randomColor(int index) { float seed = float(index) * 43758.5453; float r = fract(sin(seed) * 43758.5453); @@ -35,30 +29,24 @@ vec3 randomColor(int index) { return vec3(r, g, b); } - void main() { - vec2 screenUV = gl_FragCoord.xy / u_resolution.xy; - vec4 color = texture(t_colorTexture, screenUV); + vec2 screenUV = v_texCoord; + vec4 color = texture(t_materialDataTexture, screenUV); float distance = color.x; + int materialId = int(color.y); float mask = color.z; - vec3 c = u_staticColors[int(color.y)]; + vec3 c = u_staticColors[materialId]; - vec2 uv = (2.0 * v_texCoord) - 1.0; float zoom = -u_camMatrix[3].z; - vec2 pos = (zoom * uv) - u_camMatrix[3].xy; - float aspectRatio = u_resolution.x / u_resolution.y; - vec2 zoomVec = vec2((zoom * aspectRatio) - 1.0, zoom); - - - // Decide whether to use blended or background - // c = distance <= 0.0 ? c : background; vec3 currentColor = texture(t_currentColorTexture, screenUV).xyz; - c = mix(c, currentColor, 0.99 * mask); + // TODO: uniform? + float zoomFactor = (zoom - 10.0) * 0.02; + zoomFactor = smoothstep(0.0, 1.0, zoomFactor); + c = mix(c, currentColor, 0.9 * (1.0 - zoomFactor) * mask); - // FragColor = vec4(c, distance); FragColor = vec4(c, mask); }