<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: The Struggling Dev</title>
    <description>The latest articles on DEV Community by The Struggling Dev (@downtherabbithole).</description>
    <link>https://dev.to/downtherabbithole</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F741800%2F77992c2e-9c4b-4140-8786-3fe83cf5880d.png</url>
      <title>DEV Community: The Struggling Dev</title>
      <link>https://dev.to/downtherabbithole</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/downtherabbithole"/>
    <language>en</language>
    <item>
      <title>Liquid Glass Lacks Visual Hierarchy</title>
      <dc:creator>The Struggling Dev</dc:creator>
      <pubDate>Tue, 03 Feb 2026 14:00:00 +0000</pubDate>
      <link>https://dev.to/downtherabbithole/liquid-glass-lacks-visual-hierarchy-4b05</link>
      <guid>https://dev.to/downtherabbithole/liquid-glass-lacks-visual-hierarchy-4b05</guid>
      <description>&lt;p&gt;I've always been obsessed with deep technical details, but lately, I’ve realized I actually give a damn about aesthetics. Now, let’s be clear: I am &lt;strong&gt;not&lt;/strong&gt; a designer. But that’s the thing about visual design — everyone has a "feel" for it. You can tell when something is pleasing (or broken) without necessarily knowing the terminology.&lt;/p&gt;

&lt;p&gt;I recently updated to the latest macOS (Tahoe), and honestly? I don't like it.&lt;/p&gt;

&lt;p&gt;I already keep transparency effects turned off because I’m a dev and I like to actually see my windows, but even then, it's just not a good-looking OS anymore. I’m not a fan of the "round everything" trend, but that’s just &lt;strong&gt;personal preference&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;However, there is one thing I think is objectively "wrong" with the new &lt;strong&gt;Liquid Glass&lt;/strong&gt; aesthetic: the death of visual hierarchy.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Chrome Way: Logical Union
&lt;/h1&gt;

&lt;p&gt;Look at the tabs in Google Chrome:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxgovz54n3w8nr4fbn89k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxgovz54n3w8nr4fbn89k.png" alt="Tabs with obvious visual hierarchy in Google Chrome" width="800" height="326"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The active tab header clearly connects to the rest of the UI. It’s one cohesive piece. A union. Your brain immediately understands: "This tab owns this content."&lt;/p&gt;

&lt;h1&gt;
  
  
  The Safari Way: Floating Pill Syndrome
&lt;/h1&gt;

&lt;p&gt;Now, look at Safari:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzj63rt37qqv6qgt7xtor.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzj63rt37qqv6qgt7xtor.png" alt="Finder" width="800" height="482"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It’s just a pill-shaped button with zero connection to the rest of the UI. It just floats there. Logically, it could belong to anything — or nothing. We’ve traded "clear relationship" for "ooh, shiny floating shape."&lt;/p&gt;

&lt;h1&gt;
  
  
  Finder and the Sidebar Identity Crisis
&lt;/h1&gt;

&lt;p&gt;Another example of this hierarchy breakdown is the window controls in Finder.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7127w1wfvjunszdqbjri.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7127w1wfvjunszdqbjri.png" alt="Tabs with " width="255" height="246"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We have a sidebar on the left for shortcuts. Sitting inside that sidebar are the traffic light buttons (Close, Minimize, Maximize).&lt;/p&gt;

&lt;p&gt;Hierarchically, those buttons are "glued" to the sidebar panel. If we follow basic logic, clicking those buttons should close or minimize the panel, not the entire window.&lt;/p&gt;

&lt;p&gt;I made a quick mockup of what it would look like if the buttons were actually placed on the window canvas itself:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsq2ys8k9m9w4wnunyq5p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsq2ys8k9m9w4wnunyq5p.png" alt=" " width="545" height="262"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’m not saying my mockup is "prettier", but at least it's &lt;strong&gt;logical&lt;/strong&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Why should we care?
&lt;/h1&gt;

&lt;p&gt;When we break visual hierarchy in favor of "vibes" or "Liquid Glass" effects, we make the interface harder to parse subconsciously. But for us devs, the problem goes deeper than just aesthetics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The "Glass" Tax&lt;/strong&gt;: All those real-time blurs, transparency layers, and frosted glass effects aren't free. Your GPU is constantly working to recalculate those shadows and refractions every time a window moves.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Battery Drain&lt;/strong&gt;: If you're working on a MacBook, every unnecessary GPU cycle is a hit to your battery life. I’d much rather have an extra 30 minutes of compile time than a pretty blur effect on my terminal background.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cognitive Load&lt;/strong&gt;: As a dev, I struggle enough with complex codebases; I don't want to expend extra brain power figuring out which "floating pill" belongs to which parent container.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Is Apple losing the plot on logic, or am I just a grumpy dev who misses sharp corners? To me, it feels like we’re sacrificing the "Functional" part of "Functional Design" to chase a trend that looks great in a marketing screenshot but fails in daily use.&lt;/p&gt;

</description>
      <category>ux</category>
      <category>uidesign</category>
      <category>macos</category>
      <category>productivity</category>
    </item>
    <item>
      <title>AI Made Me More Productive — and Less Sure Why I Code</title>
      <dc:creator>The Struggling Dev</dc:creator>
      <pubDate>Tue, 23 Dec 2025 20:10:46 +0000</pubDate>
      <link>https://dev.to/downtherabbithole/ai-made-me-more-productive-and-less-sure-why-i-code-2l8e</link>
      <guid>https://dev.to/downtherabbithole/ai-made-me-more-productive-and-less-sure-why-i-code-2l8e</guid>
      <description>&lt;p&gt;It's been a while since my last post. One thing that has been occupying my mind lately is AI. I think this post won't have much structure — just some ramblings about AI.&lt;/p&gt;

&lt;p&gt;In 2017 — almost ten years ago, how time flies — I did some further education in AI. At that time, deep learning was the big thing. Hardware advances allowed for deep neural networks with multiple hidden layers, the way it had been imagined since the 50s and 60s, starting with the perceptron. I was fascinated — by the  possibilities, but also by the relative mathematical simplicity. Specialized, trained networks could, for example, find cancer in medical imaging. A neural network doesn't get tired, isn't distracted by private issues, and doctors do too many hours anyway.&lt;/p&gt;

&lt;p&gt;Now, LLMs have entered the world. &lt;a href="https://dev.to/downtherabbithole/intelligence-is-dead-25h"&gt;I still think they're not smart&lt;/a&gt; and I'm questioning the future of software development — and maybe even the future of the world and humanity. I use LLMs mainly for learning. My favorite use case is using it as a sparring partner: discussing ideas and approaches. One thing I recently started doing, is setting up "think tanks". For example, I tell ChatGPT to assemble a group of software architects and experts who have different views and then let them dissect an idea of mine, or come up with a solution of their own. I learn a lot that way.&lt;/p&gt;

&lt;p&gt;Not having to write boiler-plate code sounds good on the surface. But  I think I sometimes need the "downtime" of doing something relatively "brainless" for a few minutes — to recharge, or to let my mind wander and come up with new ideas. And if it's more than a few minutes of repetitive work, I automate it — the old-school way — as many of us have done before AI was a big thing.&lt;/p&gt;

&lt;p&gt;I'm ambivalent about AI when it comes to juniors. On the one hand, it allows them to punch way above their weight in many ways — speed, sometimes even quality. On the other hand, junior positions seem to be shrinking. I've heard tech leaders say we'll only need senior developers to check and fix what the AI produces. That raises questions: Is that work senior developers actually want to do? How do developers reach a senior level if we stop hiring and training juniors? Or, is the idea that AI won't need checking and fixing by the time the current generation of senior developers retires?&lt;/p&gt;

&lt;p&gt;It feels empty. I'm pondering whether my problem is that I always loved the process, the craft, and never the product. Should I just find a way to use AI to make a lot of money, jump off the train, and follow the craft without any pressure? And if I did, would I still want to?&lt;/p&gt;

&lt;p&gt;I've been using ChatGPT for a while now, and I've started dabbling with Cursor and agentic AI in other IDEs as well. I see the benefits — the speed, the productivity — but I'm not sure whether the joy of developing can persist in the long run.&lt;/p&gt;

&lt;p&gt;Maybe that's the real question AI forces us to ask.&lt;/p&gt;

&lt;p&gt;What are your thoughts?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>craft</category>
    </item>
    <item>
      <title>Documenting Decisions</title>
      <dc:creator>The Struggling Dev</dc:creator>
      <pubDate>Thu, 30 Jan 2025 19:32:42 +0000</pubDate>
      <link>https://dev.to/downtherabbithole/making-decisions-3d9a</link>
      <guid>https://dev.to/downtherabbithole/making-decisions-3d9a</guid>
      <description>&lt;p&gt;Meetings are for decisions, and these decisions need to be documented.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Decisions &lt;strong&gt;must&lt;/strong&gt; be documented and, in addition to the decision made, include the following additional information or refer to it:

&lt;ul&gt;
&lt;li&gt;Reasons why the decision was made&lt;/li&gt;
&lt;li&gt;Any alternatives considered&lt;/li&gt;
&lt;li&gt;Any accepted consequences resulting from the decision&lt;/li&gt;
&lt;li&gt;When the decision was made&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Following a decision, it &lt;strong&gt;must&lt;/strong&gt; be defined who needs to be informed about the decision by when and in what form.&lt;/li&gt;

&lt;/ul&gt;

&lt;h1&gt;
  
  
  Architecture Decisions
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Decisions concerning software architecture &lt;strong&gt;must&lt;/strong&gt; be documented in the following Architecture Decision Record (ADR) format.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;h1&gt;
  
  
  ADR-001: Concise title
&lt;/h1&gt;

&lt;p&gt;Status: [Draft, Proposed, Accepted, Deprecated, Superseded]&lt;/p&gt;
&lt;h2&gt;
  
  
  Context
&lt;/h2&gt;

&lt;p&gt;A value-neutral description of the forces at play (technological, political, social, and project-specific)&lt;/p&gt;
&lt;h2&gt;
  
  
  Decision
&lt;/h2&gt;

&lt;p&gt;The decision made, including the reasoning behind it.&lt;/p&gt;
&lt;h2&gt;
  
  
  Alternatives
&lt;/h2&gt;

&lt;p&gt;Any alternatives that were considered, including &amp;gt; reasons why they were rejected.&lt;/p&gt;
&lt;h2&gt;
  
  
  Miscellaneous
&lt;/h2&gt;

&lt;p&gt;Additional information like "Create on", "Accepted on", ...&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;ADRs &lt;strong&gt;should&lt;/strong&gt; be put under source control and be stored in the corresponding project in the &lt;code&gt;doc&lt;/code&gt; folder.&lt;/li&gt;
&lt;li&gt;Pull requests &lt;strong&gt;can&lt;/strong&gt; be use for discussion and approval of the ADR.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Make sure to check out Michael Nygard's post on &lt;a href="https://www.cognitect.com/blog/2011/11/15/documenting-architecture-decisions" rel="noopener noreferrer"&gt;Documenting Architecture Decisions&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Decisions in recurring meetings
&lt;/h2&gt;

&lt;p&gt;For recurring meetings I like to keep them in one large document containing the meeting minutes, to dos, ... The format for decisions is similar to the one for to dos, see &lt;br&gt;
&lt;a href="https://dev.to/downtherabbithole/managing-to-dos-in-recurring-meetings-when-theres-no-common-tool-l8h"&gt;Managing To Dos in recurring meetings, when there's no agreed upon tool&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DECISION-001&lt;/strong&gt; Architecture decisions must be put under source control

&lt;ul&gt;
&lt;li&gt;Reason: Decisions can be discussed in pull requests, changes are tracked, decisions are readily available for the developers and close to the code&lt;/li&gt;
&lt;li&gt;Consequences: access is limited to readers of the repository&lt;/li&gt;
&lt;li&gt;Alternatives: Confluence, Wiki, Word documents&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TODO-001&lt;/strong&gt; John 2025-02-14: Inform all architects and developers about this decision&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;If there's no organization-wide decision tracking tool, the above format &lt;strong&gt;can&lt;/strong&gt; be used.&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Managing To Dos in recurring meetings, when there's no agreed upon tool</title>
      <dc:creator>The Struggling Dev</dc:creator>
      <pubDate>Wed, 04 Dec 2024 16:42:00 +0000</pubDate>
      <link>https://dev.to/downtherabbithole/managing-to-dos-in-recurring-meetings-when-theres-no-common-tool-l8h</link>
      <guid>https://dev.to/downtherabbithole/managing-to-dos-in-recurring-meetings-when-theres-no-common-tool-l8h</guid>
      <description>&lt;p&gt;Meetings often lead to to dos and tasks that need tracking. Especially when you're moderating a recurring meeting, like a status meeting, you want to make sure you're on top of these to dos. One challenge I sometimes face is, that there's no common tool for managing to dos that all participants of the meeting use. The reasons for this can be manifold. Different departments use different tools, different tools are used for different kinds of to dos, ...&lt;/p&gt;

&lt;p&gt;One approach that has worked well for me for recurring meetings where multiple departments participate, is to create one long meeting notes document and track the to dos within this document.&lt;/p&gt;

&lt;p&gt;Because most people know Word and feel comfortable with it, it usually ends up being a Word document. While writing the meeting notes I just add to dos in the following format.&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;%status-emoj% &lt;strong&gt;TODO[%optional-id%]&lt;/strong&gt; %assigned-to% [%optional-due-date%] %to do%

&lt;ul&gt;
&lt;li&gt;%date% %status-info%&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;An example within some made-up meeting notes looks like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It's time to replace servers in segment B2 before we launch "Fancy Product". Marc suggests we should switch from Intel to AMD because of performance and pricing. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;TODO-001&lt;/strong&gt; John 2025-01-21: Make suggestion for new servers (Intel vs AMD) &lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can go as fancy or as bare bones as you want with the format.&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;TODO&lt;/strong&gt; John: John needs to do something&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TODO&lt;/strong&gt; John 2024-12-31: John needs to do this by the end of the year&lt;/li&gt;
&lt;li&gt;🔁 &lt;strong&gt;TODO-001&lt;/strong&gt; John 2025-01-31: John ...

&lt;ul&gt;
&lt;li&gt;2024-12-04 First milestone reached&lt;/li&gt;
&lt;li&gt;2024-12-15 John needs more time because project A got prioritized, due date adjusted from 2024-12-31 to 2025-01-31&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;TODO-001&lt;/strong&gt; John 2025-01-31: John ...

&lt;ul&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;li&gt;2025-01-31 Done &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you're using Word you have the added benefit of having a decent to do list by searching the document for "TODO-".&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu9kao06v4w1r6cv63v25.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu9kao06v4w1r6cv63v25.png" alt="Word to do list" width="614" height="770"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It sure is not perfect, but it works - at least for me. In keeping with my initial post about using &lt;a href="https://datatracker.ietf.org/doc/html/rfc2119" rel="noopener noreferrer"&gt;RFC 2119&lt;/a&gt; to convey intent, here are my rules for these sort of to dos.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To dos &lt;strong&gt;must&lt;/strong&gt; be written down for all participants of a meeting to access&lt;/li&gt;
&lt;li&gt;To dos &lt;strong&gt;must&lt;/strong&gt; be assigned to at least one person&lt;/li&gt;
&lt;li&gt;To dos &lt;strong&gt;should&lt;/strong&gt; have a due date&lt;/li&gt;
&lt;li&gt;To dos &lt;strong&gt;should&lt;/strong&gt; be tracked in the tool appropriate for the assignees of the to dos. If the participants can't agree on one tool, the above format &lt;strong&gt;can&lt;/strong&gt; be used. &lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>notes</category>
      <category>productivity</category>
      <category>collaboration</category>
    </item>
    <item>
      <title>Quickie post test.</title>
      <dc:creator>The Struggling Dev</dc:creator>
      <pubDate>Wed, 27 Nov 2024 18:19:24 +0000</pubDate>
      <link>https://dev.to/downtherabbithole/quickie-post-test-5b83</link>
      <guid>https://dev.to/downtherabbithole/quickie-post-test-5b83</guid>
      <description></description>
    </item>
    <item>
      <title>Hopefully Helpful Notes, Best Practices, ...</title>
      <dc:creator>The Struggling Dev</dc:creator>
      <pubDate>Tue, 26 Nov 2024 19:47:35 +0000</pubDate>
      <link>https://dev.to/downtherabbithole/trying-out-quickie-posts-1ace</link>
      <guid>https://dev.to/downtherabbithole/trying-out-quickie-posts-1ace</guid>
      <description>&lt;p&gt;Thought I'll give quickie posts a try. But as it turns out, this is too large for a quickie post ;). So, back to the normal full post.&lt;/p&gt;

&lt;p&gt;Over the years I've been collecting notes, checklists, best practices, rules, ... that are more or less related to working as a software developer. I've recently started to write them down in a more formal way, and thought why not share some of them in this "new" quickie posts format. &lt;/p&gt;

&lt;p&gt;To start this off, we'll go a bit meta and define some rules for writing these best practices, ... down. I'll be leaning on the &lt;a href="https://datatracker.ietf.org/doc/html/rfc2119" rel="noopener noreferrer"&gt;RFC 2119&lt;/a&gt; to convey how much leeway for example rules allow.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In general, the main key words according to RFC 2119 &lt;strong&gt;should&lt;/strong&gt; be used, e.g. &lt;strong&gt;must&lt;/strong&gt;, &lt;strong&gt;must not&lt;/strong&gt; instead of &lt;strong&gt;shall&lt;/strong&gt;, &lt;strong&gt;shall not&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Key words &lt;strong&gt;must&lt;/strong&gt; follow English spelling conventions regarding capitalization.&lt;/li&gt;
&lt;li&gt;Key words &lt;strong&gt;must&lt;/strong&gt; be emphasized using bold formatting.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>rfc</category>
      <category>bestpractices</category>
      <category>notes</category>
    </item>
    <item>
      <title>MVP as an Excuse for Bad Code?</title>
      <dc:creator>The Struggling Dev</dc:creator>
      <pubDate>Wed, 23 Oct 2024 15:42:00 +0000</pubDate>
      <link>https://dev.to/downtherabbithole/mvp-as-an-excuse-for-bad-code-e94</link>
      <guid>https://dev.to/downtherabbithole/mvp-as-an-excuse-for-bad-code-e94</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;I've recently had a discussion about Minimum Valuable Products (MVPs) and what exactly they are. Actually it was more buzzword name-dropping. The interesting part was, the context in which it came up: hardware development. &lt;/p&gt;

&lt;p&gt;In this case, the MVP was explained to me as an "ugly gray box". A thing you put together to see if an idea works. It's just made up of some cobbled up components that are laying around, some duct tape and  an idea.&lt;/p&gt;

&lt;p&gt;Ever the critical developer, this struck a nerve. To me this sounded more like a prototype than an MVP. Don't get me wrong I don't want to say it's wrong to call what they do an MVP. Hardware development is a very different beast than software development - I assume, and I'm not even sure whether this is a widely adopted nomenclature. It just made me think of the "dangers" when you interchange these two things in software development. And for software developers who've never heard of an MVP, this sounds like a free ticket to hack something together.&lt;/p&gt;

&lt;p&gt;Which, at least in my opinion, it's not. So let me try to define different approaches to software development. This is my interpretation, if you disagree let me know - I'd appreciate it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Proof of Concept (PoC) or Exploratory Programming
&lt;/h2&gt;

&lt;p&gt;What you do when you hack something together after you've identified the critical parts of the thing you want to implement. E.g., you have to implement a feature in an existing app, you need to use some framework tool you've never used, you have no idea whether you're approach will work, ... . You know, you'll need to properly integrate your feature, get the values from your data store, pass them properly to your feature, validate, ... But you kick that all to the curb, misappropriate the first and easiest to call button in your app, pass some constant values in, ignore all code style rules and just figure out if there's a chance this will work. &lt;/p&gt;

&lt;p&gt;This is no product, just loose parts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prototype
&lt;/h2&gt;

&lt;p&gt;You have an idea for a product, but you're not sure there's a market for it. You implement all you need to figure this out as quickly as possible. Whether there's a market or not, you usually scrap the prototype. If you have a market, you start working on an MVP. If not, you start over with your next idea - or in lack of one, go to the next pub.&lt;/p&gt;

&lt;p&gt;This is also no product, but it has the potential to become one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Minimum Viable Product
&lt;/h2&gt;

&lt;p&gt;Now we finally have a product, it's right there in the name. We know we have a market. We now have a software product, that we will need to maintain. So this is the point where we stop programming and start developing. We concentrate on the most important functional requirements and leave the nice to haves out. We might also postpone some non-functional requirements as long as they aren't relevant to our architecture decision. While PoCs and prototyping are acceptable reasons to program quick and dirty and don't care about architecture, this stops when we start implementing an MVP. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Sometimes people ask "What's the difference between a programmer and a software developer?" I recently heard a definition, I kinda like. The difference is time. When you're developing software, you take into account that your software needs to grow (needs to develop) and change because it will be around for many years. A small program you just use once, or that never changes, doesn't need to adhere to the same standards of maintainability. In the end it's a mind set.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Closing the circle
&lt;/h2&gt;

&lt;p&gt;One reason I can see why it's less "dangerous" to call the ugly gray box an MVP, is that creating hardware and software are so vastly different things. When you create a hardware product you inherently know that you have to start from scratch once you're finished, you can't copy hardware as you can software. Your ugly gray box consists of off the shelve parts and your battery is duct-taped to the box because it's too expensive to commission a special Möbius strip-shaped battery so it fits into your fancy designer box. Your ugly gray box also won't get the approval stamp from any authority.&lt;/p&gt;

&lt;p&gt;I'm curious to hear other points of view, so drop me a comment if you like.&lt;/p&gt;

&lt;p&gt;Thanks for reading and keep on struggling.&lt;/p&gt;

</description>
      <category>softwaredevelopment</category>
      <category>mvp</category>
      <category>prototyping</category>
    </item>
    <item>
      <title>A Take on Agile, Scrum, Lean and DevOps: Conclusion</title>
      <dc:creator>The Struggling Dev</dc:creator>
      <pubDate>Wed, 16 Oct 2024 15:42:00 +0000</pubDate>
      <link>https://dev.to/downtherabbithole/a-take-on-agile-scrum-lean-and-devops-conclusion-59k7</link>
      <guid>https://dev.to/downtherabbithole/a-take-on-agile-scrum-lean-and-devops-conclusion-59k7</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;This post has been sitting around as a draft for a long time. I was wondering whether it would even make sense to publish it. But then I thought "finish what you've started" and conclude the series that started almost two years ago. So, let's get into it:&lt;/p&gt;




&lt;p&gt;Now that we have a basic understanding of Agile, Scrum, Lean and DevOps - let's compare them. Which one is the best? Which one should you choose? Although, I have a feeling you already know where this is going. ;)&lt;/p&gt;

&lt;p&gt;Over this series we've established that Agile and Scrum, as well as Lean and DevOps build pairs. Let's see what happens when we juxtapose these two pairs. I'm using the principles behind the Agile Manifesto as an anchor for the comparison.&lt;/p&gt;

&lt;h2&gt;
  
  
  Continuous Delivery of Value in Small Batch Sizes
&lt;/h2&gt;

&lt;p&gt;The first principle behind the agile manifesto states:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Principle behind the Agile Manifesto #1&lt;/strong&gt;&lt;br&gt;
"Our highest priority is to satisfy the customer through early and continuous delivery of valuable software."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And the third one:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Principle behind the Agile Manifesto #3&lt;/strong&gt;&lt;br&gt;
"Deliver working software frequently, from a couple of weeks to a couple of months, with a preference to the shorter timescale."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Scrum&lt;/strong&gt; wants us to work in short Sprints with Sprint Reviews at the end of every Sprint. That way, we get feedback from the stake holders and users to see whether we're still on track and our product increment is valuable to them. Features should be broken down to be completable in one Sprint, tasks should be doable in a day or so.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lean&lt;/strong&gt; is all about improving value streams and removing blockages and waste, so we can delivery value of high quality to the customer. We do this by working in small batch sizes, removing waste, ... . &lt;strong&gt;DevOps&lt;/strong&gt;, unsurprisingly, agrees with this and describes this in detail in its First Way: The Principles of Flow. Work in small badge sizes, automate deployment (CD) so that we can deploy multiple times a day, ...&lt;/p&gt;

&lt;h2&gt;
  
  
  Collaboration
&lt;/h2&gt;

&lt;p&gt;Software development is a team effort. The agile manifest has two principles dedicated to this fact.&lt;/p&gt;

&lt;p&gt;Principle 4&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Principle behind the Agile Manifesto #4&lt;/strong&gt;&lt;br&gt;
"Business people and developers must work together daily throughout the project."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and principle 6&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Principle behind the Agile Manifesto #6&lt;/strong&gt;&lt;br&gt;
"The most efficient and effective method of conveying information to and within a development team is face-to-face conversation."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With &lt;strong&gt;Scrum&lt;/strong&gt; we implement this by building multidisciplinary teams that include the product owner and in the best of cases the users themselves.&lt;/p&gt;

&lt;p&gt;One can argue that &lt;strong&gt;Lean&lt;/strong&gt; and &lt;strong&gt;DevOps&lt;/strong&gt; are less focused on collaboration with the customer. But the big thing is that development and operations need to work closely together, it's in the name. This gets expanded with DevSecOps, MLOps, ...&lt;/p&gt;

&lt;p&gt;Or as Taiichi Ohno said:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Teamwork is everything"&lt;br&gt;
-- &lt;em&gt;Taiichi Ohno, Toyota Production System - Beyond large-scale production&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Learning and Motivation
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Principle behind the Agile Manifesto #5&lt;/strong&gt;&lt;br&gt;
"Build projects around motivated individuals. Give them the environment and support they need, and trust them to get the job done."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Scrum&lt;/strong&gt; recommends self-organizing teams that take ownership of the product. &lt;strong&gt;Lean&lt;/strong&gt; wants us to foster a blameless culture that allows for failures and encourages continuous learning and experimentation, which &lt;strong&gt;DevOps&lt;/strong&gt; manifests in the Third Way: Principles of Continual Learning and Experimentation. &lt;/p&gt;

&lt;h2&gt;
  
  
  Technical Excellence
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Principle behind the Agile Manifesto #9&lt;/strong&gt;&lt;br&gt;
"Continuous attention to technical excellence and good design enhances agility."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This principle doesn't seem to be addressed explicitly by &lt;strong&gt;Scrum&lt;/strong&gt;. One could argue that the Daily Scrum meeting at least allows for the voicing of such concerns ("What impedes my progress?"), which the Scrum Master then should address. This lack might also be one of the reasons for a point of contention that arises when it comes to the incorporation of software architecture in agile methods. &lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Toyota Production System&lt;/strong&gt; on the other hand is more explicit. It wants us to swarm problems and remove waste and ever improve the way we work. &lt;strong&gt;DevOps&lt;/strong&gt; even gives us a concrete number and recommends its practitioners to spend 20% of cycle time on paying down technical debt.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reducing Waste
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Principle behind the Agile Manifesto #10&lt;/strong&gt;&lt;br&gt;
"Simplicity--the art of maximizing the amount of work not done--is essential."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In &lt;strong&gt;Scrum&lt;/strong&gt; we talk to our customers regularly to make sure what we do/planned is still what they need. Approaches like Minimum Viable Products (MVP) help us to focus on what really required. And &lt;strong&gt;Lean&lt;/strong&gt; cherishes almost nothing more than reducing waste. &lt;/p&gt;

&lt;h2&gt;
  
  
  More Similarities than Differences
&lt;/h2&gt;

&lt;p&gt;There are many more comparisons left to be made and parallels to be drawn. But I'll leave you with the ninth principle, and let Taiichi Ohno have the last word in this section.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Principle behind the Agile Manifesto #9&lt;/strong&gt;&lt;br&gt;
"Agile processes promote sustainable development. The sponsors, developers, and users should be able to maintain a constant pace indefinitely."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Scrum&lt;/strong&gt; wants us to continuously deliver value to the customer, plan our Sprints accordingly and "find" our velocity to forecast future work.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Speed is meaningless without continuity".&lt;br&gt;
-- &lt;em&gt;Taiichi Ohno, Toyota Production System - Beyond large-scale production&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;In the end, there are more similarities than differences. Agile, Scrum, Lean and DevOps are all mindsets - ways to think at whose hearts lies continuous improvement. Although they might use different terminology, they're very similar if not the same as far as intent goes. There are even tools like Kanban to visualize work and work in progress that are used by practitioners of all of these mindsets.&lt;/p&gt;

&lt;p&gt;They're also all more than the sum of their parts. Just planning some Daily Scrum meetings and throwing planning poker cards on a table isn't enough. And that's why they're hard, they require the most difficult kind of change: organizational change. &lt;/p&gt;

&lt;p&gt;You see things differently? Awesome, leave me a comment!&lt;/p&gt;

&lt;p&gt;Thanks for reading and keep on struggling.&lt;/p&gt;

</description>
      <category>agile</category>
      <category>lean</category>
      <category>devops</category>
      <category>scrum</category>
    </item>
    <item>
      <title>(Game)Dev with Emacs - Because it's not Already Hard Enough Without it</title>
      <dc:creator>The Struggling Dev</dc:creator>
      <pubDate>Mon, 07 Oct 2024 16:42:00 +0000</pubDate>
      <link>https://dev.to/downtherabbithole/gamedev-with-emacs-because-its-not-hard-enough-without-it-3h07</link>
      <guid>https://dev.to/downtherabbithole/gamedev-with-emacs-because-its-not-hard-enough-without-it-3h07</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;This is another tale from down the rabbit hole. See &lt;a href="https://dev.to/downtherabbithole/creating-an-emacs-major-mode-because-why-not-11de"&gt;Writing an Emacs Major Mode - Because Why Not? (DEV)&lt;/a&gt;, for my first post on Emacs. As I wrote in my post on &lt;a href="https://dev.to/downtherabbithole/struggling-dev-does-character-design-1aem"&gt;Character Design (DEV)&lt;/a&gt;, I'm playing around with writing a game. And to make it even more fun I've decided to use Emacs for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Emacs Setup and Syntax Highlighting &amp;amp; Code Complete
&lt;/h2&gt;

&lt;p&gt;I'm using Doom Emacs. I'm refraining from reproducing the installation guide here and just point to it for anyone who's interested: &lt;a href="https://github.com/doomemacs/doomemacs?tab=readme-ov-file#install" rel="noopener noreferrer"&gt;Doom Emacs #Install (GitHub)&lt;/a&gt;. After the installation we activate support for C++ and LSP (auto-complete and stuff) by removing the comments from the init.el file (just press &lt;code&gt;SPC+f+P&lt;/code&gt; on the Doom dashboard and choose init.el). Search for "lsp" and "(cc" and remove the leading &lt;code&gt;;;&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="c1"&gt;;;lsp&lt;/span&gt;
&lt;span class="c1"&gt;;;(cc +lsp)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Additionally, we add the following line to the end of the init.el.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;setenv&lt;/span&gt; &lt;span class="s"&gt;"LSP_USE_PLISTS"&lt;/span&gt; &lt;span class="s"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This line improves LSP performance significantly, see &lt;a href="https://emacs-lsp.github.io/lsp-mode/page/performance/" rel="noopener noreferrer"&gt;LSP Mode - Performance&lt;/a&gt; for more performance tips.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you've already used LSP mode in Emacs without plists active, you have to delete the LSP package first - otherwise you'll get errors when activating LSP mode the next time.&lt;/p&gt;

&lt;p&gt;According to the documentation you should be able to just delete the package by using &lt;code&gt;package-delete&lt;/code&gt;, but in my case I couldn't find any lsp packages listed. If you run in the same problem, and are on MacOS, here's how to delete the packages manually.&lt;/p&gt;


&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# cd into your .emacs folder and search for lsp stuff&lt;/span&gt;
find ./ &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"lsp*"&lt;/span&gt;
&lt;span class="c"&gt;# and delete it - after checking the results. In my case, removing the following did the trick.&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; .//.local/straight/build-29.1/lsp&lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; .//.local/straight/repos/lsp&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;p&gt;After these changes we close Emacs and run &lt;code&gt;doom sync&lt;/code&gt;. Back in Emacs we can run &lt;code&gt;M-x&lt;/code&gt; &lt;code&gt;lsp-doctor&lt;/code&gt; to check whether we're all good.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftfar4thmyhzwizgkpful.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftfar4thmyhzwizgkpful.png" alt="Doctor Who? Doctor LSP!"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The native compilation part is intriguing, but I haven't had time to check this out more closely and after switching to plists performance was fine.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If we now open a projectile project (a directory with a .git folder should automatically be identified as a project), we have syntax highlighting and code completion.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhx15p6q7aw6so7qit2wz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhx15p6q7aw6so7qit2wz.png" alt="Code completion in Emacs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we have some of the creature comforts for developing in place, let's compile an application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compiling an Application
&lt;/h2&gt;

&lt;p&gt;While we're in a project we can start compilation with &lt;code&gt;SPC+p+c&lt;/code&gt;. The issue is, we have to provide the compile command ourselves. For my project I'll call CMake. Typing this every time we want to compile is annoying, luckily Emacs remembers our last command. But what if we have multiple components we want to compile, which we should've because tests 😉.&lt;/p&gt;

&lt;p&gt;For this case I've started using bash files to compile and run my projects.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="nv"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$cmd&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"build"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;build &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; cmake &lt;span class="nt"&gt;--build&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$cmd&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"run"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;build &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; cmake &lt;span class="nt"&gt;--build&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ./MyGame
&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$cmd&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"configure"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;build &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; cmake ../src
&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$cmd&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"test"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;build &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; cmake &lt;span class="nt"&gt;--build&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ctest
&lt;span class="k"&gt;else
  &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;build &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; cmake &lt;span class="nt"&gt;--build&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ./MyGame
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this file in our project root, we can just &lt;code&gt;SPC+p+c&lt;/code&gt; and &lt;code&gt;./run.sh&lt;/code&gt; for most cases when we want to compile and run the application. Easy as pie, or is it?&lt;/p&gt;

&lt;h2&gt;
  
  
  Should you Ditch your IDE?
&lt;/h2&gt;

&lt;p&gt;I'm not deep enough down the rabbit hole to really provide a profound answer to this question yet, sorry. But, I'll do it anyway - as someone still on the journey might describe the destination he hasn't yet seen: NOOOO. Thanks for reading.&lt;/p&gt;

&lt;p&gt;But let me elaborate. Like with everything in life there are trade offs.&lt;/p&gt;

&lt;h3&gt;
  
  
  What I Like
&lt;/h3&gt;

&lt;p&gt;It's a stupid argument, (Doom) Emacs just looks and feels "cool" 🤓. It has that "hacker"-aesthetic.&lt;/p&gt;

&lt;p&gt;But on to some functional arguments.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Highly optimized for keyboard input. Emacs does have mouse support, but I rarely use the touchpad for anything with Emacs.&lt;/li&gt;
&lt;li&gt;It's relatively light-weight and easy on resources.&lt;/li&gt;
&lt;li&gt;It's highly customizable&lt;/li&gt;
&lt;li&gt;Org mode. Org mode is just great for taking notes and organizing tasks. I might write a post on it one day. If you're interested, check out &lt;a href="https://orgmode.org/" rel="noopener noreferrer"&gt;Org Mode&lt;/a&gt; in the mean time.&lt;/li&gt;
&lt;li&gt;I enjoy just having one editor for everything - kinda like an integrated development environment 🤔. Sure you can code and take notes in CLion or VS Code as well, but can you also browse?🤪 Emacs has you covered with the &lt;a href="https://www.gnu.org/software/emacs/manual/html_mono/eww.html" rel="noopener noreferrer"&gt;Emacs Web Wowser (eww)&lt;/a&gt;. It's basic, it's ugly and I've used it only a few times for quick searches, but it's there.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Stuff that's Missing or That I Don't Like
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;I've previously used CLion for C++ development, and there's a few things I miss. For one, I'm not a C++ developer by trade, so I mostly suck at it. CLion is great that way as it supports you with tips and best practice hints. Maybe, likely, I can add linters to Emacs as well.&lt;/li&gt;
&lt;li&gt;There are some vim commands that are not mapped in EVIL mode.&lt;/li&gt;
&lt;li&gt;Debugging seems to be possible, didn't get around trying it out.&lt;/li&gt;
&lt;li&gt;The basics are much harder than with any IDE. Not only do you have to do something extra to get code completion, you (might) have to do even more to make it fast.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For all its power, Emacs makes you earn it. It's not comfy - it might be after you've configured everything you need. If you ever get there:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/urcL86UpqZc"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  The Struggles
&lt;/h2&gt;

&lt;p&gt;Emacs - Emacs is suffering. It makes you swear, it makes you despair, ... and it makes you celebrate if/when it finally works. Emacs is interesting, powerful and highly customizable. And with that comes the big downside. Although you most likely can do everything with Emacs, it takes time, research, configuration, trial &amp;amp; error and nerves.&lt;/p&gt;

&lt;p&gt;The biggest challenge when writing this post, was what I should leave out. For example reproducing the Doom Emacs installation guide seemed to be a waste of time and space. If something's missing, don't hesitate to ask/point it out in the comments.&lt;/p&gt;

&lt;p&gt;Thanks for reading and keep on struggling.&lt;/p&gt;

</description>
      <category>gamedev</category>
      <category>emacs</category>
    </item>
    <item>
      <title>Shadow Shader Experiments</title>
      <dc:creator>The Struggling Dev</dc:creator>
      <pubDate>Mon, 30 Sep 2024 16:42:00 +0000</pubDate>
      <link>https://dev.to/downtherabbithole/shadow-shader-experiments-5bdh</link>
      <guid>https://dev.to/downtherabbithole/shadow-shader-experiments-5bdh</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;After playing around with light and normal mapping, the next thing that comes to mind is shadows. Instead implementing something from the shelf like shadow mapping or ray tracing, this post deals with a custom implementation. The implementation is based on an idea, it has (severe) limitations, it might not work, other people might have already implemented or refrained from implementing it because they had more foresight than me, ... I don't know, but we'll find out.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;DISCLAIMER&lt;/strong&gt; Again, this is just experimental stuff and likely a waste of time. If you value your time, stop reading now. If you proceed, don't say I haven't warned you. ;)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, let's go and have some stupid fun! ;)&lt;/p&gt;

&lt;h2&gt;
  
  
  The Basic Idea
&lt;/h2&gt;

&lt;p&gt;Given that I have a pixel art game with a low resolution (currently tending to 320x240 internal) and I already have some per-pixel lighting in place, the idea is as follows. For every pixel I'll check whether the neighboring pixel is elevated and in between the current pixel and the light source, and if so darken it a bit. One limitation that is a given is that my shadows can only be one pixel large.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fodci81u2dp1my026wfvp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fodci81u2dp1my026wfvp.png" alt="Light source and shadow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We need eight values for every pixel that indicate whether each of the neighboring eight pixels is elevated or not. It's kind of a degenerated gradient descent map.&lt;/p&gt;

&lt;p&gt;We might even be able to adjust the shadow based on the difference in angle between the light source and the pixel that casts shadow on the current pixel.&lt;/p&gt;

&lt;p&gt;We already have the direction from the current fragment to the light source from the normal mapping post.&lt;/p&gt;

&lt;h2&gt;
  
  
  An Attempt at Implementation
&lt;/h2&gt;

&lt;p&gt;We need eight boolean values for each pixel. Similar to using normal maps to store non-color information, another texture seems to be a good candidate for this. We can just use the bits of an 8bit color channel to encode this information. Let's define the pixel directly above as 0 and then go around clockwise.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+---+---+---+
| 7 | 0 | 1 |
+---+---+---+
| 6 | x | 2 |
+---+---+---+
| 5 | 4 | 3 |
+---+---+---+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0: 1000'0000
1: 0100'0000
2: 0010'0000
3: 0001'0000
4: 0000'1000
5: 0000'0100
6: 0000'0010
7: 0000'0001
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;if the pixels 0, 1 and 2 were elevated in comparison to x, we would encode the value &lt;code&gt;1110'0000&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To make it easier to draw the gradient map, we convert these binary values to hex values. &lt;code&gt;b1110'0000&lt;/code&gt; becomes &lt;code&gt;0xE0&lt;/code&gt; for example. We'll use the red channel, therefore the entire color's hex values is &lt;code&gt;0xE00000&lt;/code&gt;. We could create a small tool to make this process easier, but since it's not clear whether this approach has any future, we just do everything by hand.&lt;/p&gt;

&lt;p&gt;We have a 3x3 grid which leads to quite a few permutations, but we don't need all of them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi5egqx52po6ugidq5l6j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi5egqx52po6ugidq5l6j.png" alt="Palette and gradient map"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At the top of the image is the final gradient map for the floating rock. Most of the image is black, which means there's no difference in elevation. Below are some of the possible variations from which we can pick the corresponding color.&lt;/p&gt;

&lt;p&gt;Now that we have the texture we'll need to do something with it in the fragment shader. First we need to a add another texture sampler, from which we'll then get the red channel. We can check whether a certain direction is above the current fragment by using the bitwise  AND operator &lt;code&gt;&amp;amp;&lt;/code&gt; (details in the shader comments).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight glsl"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="k"&gt;uniform&lt;/span&gt; &lt;span class="kt"&gt;sampler2D&lt;/span&gt; &lt;span class="n"&gt;gradientMap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="c1"&gt;// Get the red channel from the gradient map, this is where our elevation information is stored.&lt;/span&gt;
    &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;redChannel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;texture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gradientMap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TexCoord&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// Convert it to an integer as bit-wise operators only work with integers&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;intValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;redChannel&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Check for each direction if it's elevated (1) compared to our current position.&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;north&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;intValue&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mh"&gt;0x80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;northEast&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;intValue&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mh"&gt;0x40&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;east&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;intValue&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mh"&gt;0x20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;southEast&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;intValue&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mh"&gt;0x10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;south&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;intValue&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mh"&gt;0x08&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;southWest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;intValue&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mh"&gt;0x04&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;west&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;intValue&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mh"&gt;0x02&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;northWest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;intValue&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mh"&gt;0x01&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Next, calculate the shadow by adding a value for every direction whose pixel is elevated. The values for the variables north, northEast, ... are all greater 0 if they are elevated and should therefore add to the shadow value.  The step function returns 0 if the value of the second parameter is smaller than the first one, and 1.0 otherwise. This way only the directions &amp;gt; 0 contribute to the shadow value and all to the same degree.&lt;/span&gt;
    &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;shadow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;north&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;vec3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;lightDir&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;shadow&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;northEast&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;vec3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;lightDir&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;shadow&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;east&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;vec3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;lightDir&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;shadow&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;southEast&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;vec3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;lightDir&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;shadow&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;south&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;vec3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;lightDir&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;shadow&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;southWest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;vec3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;lightDir&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;shadow&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;west&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;vec3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;lightDir&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;shadow&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;northWest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;vec3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;lightDir&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// limit the value to a range of 0 to 0.5, choose whatever range looks good to you&lt;/span&gt;
    &lt;span class="n"&gt;shadow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shadow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Since we subtract the value later on, we need to set the alpha channel to 0.0f. If we'd set it to a greater value we'd influence the transparency of the final fragment color.&lt;/span&gt;
    &lt;span class="kt"&gt;vec4&lt;/span&gt; &lt;span class="n"&gt;shadowVec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;vec4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shadow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shadow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shadow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;FragColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;texture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TexCoord&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ambient&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;diffuseColor&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;shadowVec&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's have a look at how it looks like.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg8mfnz74h830izev8xcq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg8mfnz74h830izev8xcq.png" alt="Nothing can hide in these small shadows"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The little dot about the green stuff is the light source.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's nothing to phone home about, but it's not that bad either.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Struggles
&lt;/h2&gt;

&lt;p&gt;I wasn't sure whether I should implement it and even more so whether I should blog about it. Also, there were multiple times when I wanted to optimize for performance and had to remind myself that this is just a proof of concept and now was not the time to do that. I might have wasted some time for a solution that I'll likely not use in the future. On the other hand I've learned some things and have gotten more comfortable with shaders. Trying to figure out something from the ground up, without just following a tutorial or checking what other options are out there. Aaaand it was fun.&lt;/p&gt;

&lt;p&gt;Implementation-wise I took a detour by using &lt;code&gt;floatBitsToInt&lt;/code&gt; which just interprets the float bits as an int. Not what I was looking for and the solution was much easier - a simple &lt;code&gt;int(x)&lt;/code&gt; cast.&lt;/p&gt;

&lt;p&gt;Thanks for reading and keep on struggling.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Major Mode Comfort Functions, Because I'm Lazy</title>
      <dc:creator>The Struggling Dev</dc:creator>
      <pubDate>Mon, 23 Sep 2024 16:42:00 +0000</pubDate>
      <link>https://dev.to/downtherabbithole/major-mode-comfort-functions-because-im-lazy-5ego</link>
      <guid>https://dev.to/downtherabbithole/major-mode-comfort-functions-because-im-lazy-5ego</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;After creating a &lt;a href="https://dev.to/downtherabbithole/creating-an-emacs-major-mode-because-why-not-11de"&gt;bare bones major mode for Emacs&lt;/a&gt;, there are some minor things to do to improve the overall usability. For one, I currently have to build the &lt;code&gt;.el&lt;/code&gt; file every time I want to use the mode. And for another I have to manually choose the mode for a given buffer. Let's fix these two annoyances.&lt;/p&gt;

&lt;h2&gt;
  
  
  Registering the Major Mode when Emacs Starts
&lt;/h2&gt;

&lt;p&gt;We could just always load the file on startup. A better way though is to autoload the major mode. This way the actual function is only evaluated if and when the mode is used. Kinda like a method definition in a header file. The time tracking mode is not really complex, but why load it every time we start Emacs when we can do it only when it's actually being used. To achieve this we add the following line to &lt;code&gt;init.el&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;autoload&lt;/span&gt; &lt;span class="ss"&gt;'time-tracking-mode&lt;/span&gt; &lt;span class="s"&gt;"/path/to/major/mode/directory/time-tracking-mode.el"&lt;/span&gt; &lt;span class="s"&gt;"Major mode for time tracking."&lt;/span&gt; &lt;span class="no"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;the first parameter is the name of the function that defines the major mode&lt;/li&gt;
&lt;li&gt;the second one is the path to the file containing the function&lt;/li&gt;
&lt;li&gt;the third one is the doc string of the function&lt;/li&gt;
&lt;li&gt;and the fourth one says that function can be called interactively&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.gnu.org/software/emacs/manual/html_node/elisp/Autoload.html" rel="noopener noreferrer"&gt;Autoload (GNU)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If we now restart Emacs the major mode should be available without us first needing to open and evaluate the file manually.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We can test whether it's really only loading the major mode when needed. If we change the file name to something that doesn't exist, we get an error when we activate the major mode for a buffer, saying that the file couldn't be found.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Great, let's move on to the next quality of life feature.&lt;/p&gt;

&lt;h2&gt;
  
  
  Activating the Major Mode When We Open a File with a Certain Extension
&lt;/h2&gt;

&lt;p&gt;We can tell Emacs to switch to a major mode whenever a file with a certain extension is opened. The only thing we need to do is to add the corresponding mapping to the &lt;code&gt;auto-mode-alist&lt;/code&gt;. We can do this by adding the following to &lt;code&gt;config.el&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;add-to-list&lt;/span&gt; &lt;span class="ss"&gt;'auto-mode-alist&lt;/span&gt; &lt;span class="o"&gt;'&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\\.ttr"&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;time-tracking-mode&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;"\\.ttr"&lt;/code&gt; is the file extension&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;time-tracking-mode&lt;/code&gt; is our major mode&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If we restart Emacs and open a file with the extension &lt;code&gt;ttr&lt;/code&gt; the time tracking mode gets activated automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Struggles
&lt;/h2&gt;

&lt;p&gt;Still struggling a bit with the Emacs documentation, but it's getting better. I'm taking more time to really read the articles - I'm still quite impatient though - small changes and have to read 2 pages of text ;). What I'm not really getting the hang of is where to put the functions. &lt;code&gt;add-to-list&lt;/code&gt; for example didn't work in &lt;code&gt;init.el&lt;/code&gt;, only in &lt;code&gt;config.el&lt;/code&gt;. Need to read up on that.&lt;/p&gt;

&lt;p&gt;Thanks for reading and keep on struggling.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Jergen gets Normal Mapped</title>
      <dc:creator>The Struggling Dev</dc:creator>
      <pubDate>Mon, 16 Sep 2024 07:42:00 +0000</pubDate>
      <link>https://dev.to/downtherabbithole/jergen-gets-normal-mapped-aaj</link>
      <guid>https://dev.to/downtherabbithole/jergen-gets-normal-mapped-aaj</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In my &lt;a href="https://dev.to/downtherabbithole/struggling-dev-does-character-design-1aem"&gt;last post&lt;/a&gt; I checked the art side - still struggling with the word art - of my player character. This time we're moving on to something more technical, although it's still a bit "artsy". Currently Jergen looks a bit bland. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4lr6lmrnwubxzskk24zh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4lr6lmrnwubxzskk24zh.png" alt="Bland li'l Jergen" width="267" height="344"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I've therefore decided that its time to turn the lights on. &lt;/p&gt;

&lt;h2&gt;
  
  
  Let There Be Light
&lt;/h2&gt;

&lt;p&gt;Okay, Jergen already looks sufficiently lit 😉. This is because the current shaders use the texture's color as the final color value to draw. Let's change this so we can later see the effects of lights being shone on Jergen.&lt;/p&gt;

&lt;p&gt;First, we'll add some ambient light. I'm struggling to explain ambient light in my own words, but to me it's just the base light level. All the light that is scattered around from multiple light sources, so you can't really make out where the light comes from anymore. Therefore, neither distance to, or direction of the light source matter. I feel the fragment shader does a better job at explaining it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight glsl"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Fragment shader&lt;/span&gt;
&lt;span class="cp"&gt;#version 330 core
&lt;/span&gt;
&lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="kt"&gt;vec2&lt;/span&gt; &lt;span class="n"&gt;TexCoordinate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="kt"&gt;vec4&lt;/span&gt; &lt;span class="n"&gt;FragColor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;uniform&lt;/span&gt; &lt;span class="kt"&gt;sampler2D&lt;/span&gt; &lt;span class="n"&gt;tex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ambient light, kind of a base light amount that is just there.&lt;/span&gt;
    &lt;span class="c1"&gt;// All zeros would mean it's completely dark.&lt;/span&gt;
    &lt;span class="kt"&gt;vec4&lt;/span&gt; &lt;span class="n"&gt;ambient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;vec4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;FragColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;texture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TexCoordinate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;ambient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The ambient light is white, but dimmed to 1/5 of.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;While proof reading this post, I realized that I made a mistake. I'm also making the final &lt;code&gt;FragColor&lt;/code&gt; transparent by multiplying &lt;code&gt;ambient&lt;/code&gt; by &lt;code&gt;0.2f&lt;/code&gt;. This makes the entire image darker. All the images in this post contain this mistake. The GIF at the end corrects this and uses &lt;code&gt;vec4 ambient = vec4(0.2f, 0.2f, 0.2f, 1.0f);&lt;/code&gt; instead.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For completeness' sake, here's the corresponding vertex shader:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight glsl"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Vertex shader&lt;/span&gt;
&lt;span class="cp"&gt;#version 330 core
&lt;/span&gt;
&lt;span class="k"&gt;layout&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="kt"&gt;vec3&lt;/span&gt; &lt;span class="n"&gt;inPosition&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;layout&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="kt"&gt;vec2&lt;/span&gt; &lt;span class="n"&gt;inTexCoordinate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="kt"&gt;vec2&lt;/span&gt; &lt;span class="n"&gt;TexCoordinates&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;gl_Position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;vec4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inPosition&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;TexCoordinate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aTexCoordinate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Can you still see Jergen? It's quite dark where he is.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Famixb3l4a3me0uc846rj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Famixb3l4a3me0uc846rj.png" alt="Find Jergen" width="447" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, we add diffuse lighting. Diffuse lighting is the light that comes from a certain light source. Therefore, in contrast to the ambient light, the direction of the light plays a role in how it affects the object it illuminates. If our object faces the light source, it should be brighter, if it's facing away it should be in shadow. We'll need to give the light source a position in our world. With that, we can then calculate the direction from our object to the light.&lt;/p&gt;

&lt;p&gt;Since the fragment shader doesn't know the world position of the fragment it shades, we need to pass it in from the vertex shader. &lt;/p&gt;

&lt;p&gt;Let's add a new output to the vertex shader and assign a value to it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight glsl"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Vertex shader&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="kt"&gt;vec2&lt;/span&gt; &lt;span class="n"&gt;TexCoordinate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="kt"&gt;vec3&lt;/span&gt; &lt;span class="n"&gt;FragPosition&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// &amp;lt;- added&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;gl_Position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;vec4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inPosition&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;TexCoordinate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;inTexCoordinate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;FragPosition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;inPosition&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// &amp;lt;- added&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we add the corresponding input to the fragment shader. We also define variables for our light source and a surface normal for the object we want to illuminate. The variables are described in the following code snippet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight glsl"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Fragment shader&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="kt"&gt;vec2&lt;/span&gt; &lt;span class="n"&gt;TexCoordinate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="kt"&gt;vec3&lt;/span&gt; &lt;span class="n"&gt;FragPosition&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;- added&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ambient light, kind of a base light amount that is just there&lt;/span&gt;
    &lt;span class="kt"&gt;vec4&lt;/span&gt; &lt;span class="n"&gt;ambient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;vec4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// The color our light emits, Halloween season is around the corner,&lt;/span&gt;
    &lt;span class="c1"&gt;// so let's make it a creepy red.&lt;/span&gt;
    &lt;span class="kt"&gt;vec4&lt;/span&gt; &lt;span class="n"&gt;lightColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;vec4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;vec3&lt;/span&gt; &lt;span class="n"&gt;lightPosition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;vec3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// We normalize the direction vector because we're only interested in the direction.&lt;/span&gt;
    &lt;span class="kt"&gt;vec3&lt;/span&gt; &lt;span class="n"&gt;lightDirection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;normalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lightPosition&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;FragPosition&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Next we define a normal for the fragment. The normal defines which way a surface is facing.&lt;/span&gt;
    &lt;span class="c1"&gt;// With this information we can figure out if it's facing towards the light source &lt;/span&gt;
    &lt;span class="c1"&gt;// and should therefore be illuminated. &lt;/span&gt;
    &lt;span class="c1"&gt;// Our normal for every fragment points to the right side towards the light source.&lt;/span&gt;
    &lt;span class="kt"&gt;vec3&lt;/span&gt; &lt;span class="n"&gt;normal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;normalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// We use the dot product to figure out how much the surface normal and the light direction align.&lt;/span&gt;
    &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;diffuseStrength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;normal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lightDirection&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;vec4&lt;/span&gt; &lt;span class="n"&gt;diffuse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;diffuseStrength&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;lightColor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// We now add the diffuse light component to the already existing ambient component before multiplying.&lt;/span&gt;
    &lt;span class="n"&gt;FragColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;texture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TexCoordinate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ambient&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;diffuse&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we calculate this for the top left corner of the rectangle &lt;code&gt;(-0.25, 0.5)&lt;/code&gt; that displays Jergen we get.&lt;br&gt;
&lt;code&gt;lightDirection = (10, 1, 0) - (-0.25, 0.5) = (10 - -0.25, 1 - 0.5, 0 - 0) = (10.25, 0.5, 0)&lt;/code&gt;&lt;br&gt;
which is &lt;code&gt;(0.998812, 0.0487226, 0)&lt;/code&gt; when normalized.&lt;/p&gt;

&lt;p&gt;The strength of the diffuse light is then:&lt;br&gt;
&lt;code&gt;diffuseStrength = (1, 0, 0) * (0.998812, 0.0487226, 0) = 1 * 0.998812 + 0 * 0.0487226 + 0 * 0 = 0.998812&lt;/code&gt;&lt;br&gt;
The diffuse light is therefore strong, which makes sense as the surface normal is almost parallel to the light direction. Let's see how this looks.&lt;/p&gt;

&lt;p&gt;Jergen bathed in creepy red light.&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fipwpmc1ubltw5m2x1qeb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fipwpmc1ubltw5m2x1qeb.png" alt="Jergen bathed in creepy red light" width="230" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's see what happens if we change the normal to point upwards. This way the normal and the light direction are almost perpendicular, which should result in a much darker image.&lt;br&gt;
But let's calculate the diffuse contribution before we actually check.&lt;/p&gt;

&lt;p&gt;The lightDirection stays the same. The new diffuse strength is&lt;br&gt;
&lt;code&gt;(0, 1, 0) * (0.99812, 0.0487226, 0) = 0 * 0.998812 + 1 * 0.0487226 + 0 * 0 = 0.0487226&lt;/code&gt;, which matches our expectation.&lt;/p&gt;

&lt;p&gt;And now, curtains up. Jergen's almost gone again.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5xthm3ukzkwgu2ez6h7g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5xthm3ukzkwgu2ez6h7g.png" alt="Jergen's hiding in the dark" width="217" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we have some basic lighting in place, let's go a step further and introduce normal mapping.&lt;/p&gt;
&lt;h2&gt;
  
  
  A Map of Normals
&lt;/h2&gt;

&lt;p&gt;So far we've used the same normal for all fragments and have set it directly in the shader. We could define the normals in our vertex data, along with the positions and the texture coordinates. That way we would be able to specify a normal per vertex. For Jergen that would mean we could specify four vertices as Jergen is just a rectangle. But, we want more. We want to be able give different normals to Jergen's face, his backpack, .... The way to do this, is to use a normal map.&lt;/p&gt;

&lt;p&gt;A normal map is just a texture in which the colors represent normal vectors. You can check out an example in the &lt;a href="https://en.wikipedia.org/wiki/Normal_mapping#/media/File:Normal_map_example_with_scene_and_result.png" rel="noopener noreferrer"&gt;Wikipedia article on normal mapping&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we wanted to reproduce the same lighting we currently have, we'd just create a texture where each pixel contains the value &lt;code&gt;(1, 0, 0)&lt;/code&gt;, or &lt;code&gt;(0, 1, 0)&lt;/code&gt; respectively. But as mentioned before, we want different normals for (almost) every pixel. But how do we create a normal map? An easy way, at least for pixel art with few pixels, is to just pick colors from an existing normal map - like the one from Wikipedia. And that's exactly what I did. First, I copied the existing texture of Jergen and started to pick colors from the normal map.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhlgk40ji35ks95ntyp3g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhlgk40ji35ks95ntyp3g.png" alt="Why do normal maps look everything but normal?" width="265" height="311"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now comes the fun part, we're going to use that map to give Jergen some more variation. Since we'll use a separate texture for the normal map with the same dimensions of the main texture, we can reuse the texture coordinates and keep the vertex shader as is.&lt;/p&gt;

&lt;p&gt;The fragment shader needs a few changes though.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight glsl"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Fragment shader&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="k"&gt;uniform&lt;/span&gt; &lt;span class="kt"&gt;sampler2D&lt;/span&gt; &lt;span class="n"&gt;tex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;uniform&lt;/span&gt; &lt;span class="kt"&gt;sampler2D&lt;/span&gt; &lt;span class="n"&gt;normalMap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;- We need to add another sampler for the normal map&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;

    &lt;span class="c1"&gt;// We now use the normal map to get the normals for the current fragment&lt;/span&gt;
    &lt;span class="kt"&gt;vec3&lt;/span&gt; &lt;span class="n"&gt;normal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;normalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;texture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;normalMap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TexCoordinate&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's all she wrote.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvngrbp8n87shuuere8un.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvngrbp8n87shuuere8un.png" alt="Normal mapped Jergen" width="185" height="287"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And here's the same thing with a light source that circles Jergen.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ya309mM7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://raw.githubusercontent.com/mightymdev/blog/main/small-non-transparent-ambient.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ya309mM7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://raw.githubusercontent.com/mightymdev/blog/main/small-non-transparent-ambient.gif" alt="Light show Jergen" width="348" height="358"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And another bonus (no idea why the video quality is so bad after uploading):&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/fh3O6irnpEQ"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  The Struggles
&lt;/h2&gt;

&lt;p&gt;There was one small hiccup at the end when I switched to using the normal map in the shader. The texture had no effect. So I first changed the first texture to the normal map file to see whether I could draw the normal map - everything OK. Reverse everything, try to bind the normal map to texture unit one, draw the color texture on unit two, ... In the end I just forgot to activate the second texture unit - d'Oh.&lt;/p&gt;

&lt;p&gt;The math was also easier than I had anticipated, mistook the dot product for the cross product, ...&lt;/p&gt;

&lt;p&gt;All in all, this one went relatively smoothly. Dividing the task into sometimes ridiculously small steps, like just adding one more variable to the shader and testing it or defining variables within the shader instead of passing them from the host code, helped a lot.&lt;/p&gt;

&lt;p&gt;Thanks for reading and keep on struggling.&lt;/p&gt;

</description>
      <category>gamedev</category>
      <category>normalmapping</category>
      <category>opengl</category>
      <category>glsl</category>
    </item>
  </channel>
</rss>
