diff options
| author | Matt Kosarek <matt.kosarek@canonical.com> | 2026-02-19 16:58:58 -0500 |
|---|---|---|
| committer | Matt Kosarek <matt.kosarek@canonical.com> | 2026-02-19 16:58:58 -0500 |
| commit | da0eedbf1733e40613215ecd117e1a4e049089ad (patch) | |
| tree | d83d5dc63b50efbd45084d692ae037cbe0f02b25 | |
| parent | 4d1beea73810af4641d074f974ad9c196a7e8d6e (diff) | |
Removed photo gallery + added cute little grass rendering for the rabbit and a nice gradient backgroundHEADmaster
34 files changed, 570 insertions, 311 deletions
diff --git a/_posts/sitemap.org b/_posts/sitemap.org index 3ea35fd..f85e6a3 100644 --- a/_posts/sitemap.org +++ b/_posts/sitemap.org @@ -1,6 +1,6 @@ #+TITLE: -#+DATE: 2026-02-18 at 17:14 +#+DATE: 2026-02-19 at 15:52 #+HTML_LINK_HOME: / diff --git a/public/images/dog_and_me-min.jpg b/public/images/dog_and_me-min.jpg Binary files differdeleted file mode 100644 index 6e4e040..0000000 --- a/public/images/dog_and_me-min.jpg +++ /dev/null diff --git a/public/images/dog_and_me.jpg b/public/images/dog_and_me.jpg Binary files differdeleted file mode 100644 index 1dd3e13..0000000 --- a/public/images/dog_and_me.jpg +++ /dev/null diff --git a/public/images/ebi.jpg b/public/images/ebi.jpg Binary files differdeleted file mode 100644 index 8f4b77e..0000000 --- a/public/images/ebi.jpg +++ /dev/null diff --git a/public/images/friends-min.jpg b/public/images/friends-min.jpg Binary files differdeleted file mode 100644 index 0fbb4c1..0000000 --- a/public/images/friends-min.jpg +++ /dev/null diff --git a/public/images/friends.jpg b/public/images/friends.jpg Binary files differdeleted file mode 100644 index b2c0e5e..0000000 --- a/public/images/friends.jpg +++ /dev/null diff --git a/public/images/gardens-min.jpg b/public/images/gardens-min.jpg Binary files differdeleted file mode 100644 index 3503bfc..0000000 --- a/public/images/gardens-min.jpg +++ /dev/null diff --git a/public/images/gardens.jpg b/public/images/gardens.jpg Binary files differdeleted file mode 100644 index 450dbd2..0000000 --- a/public/images/gardens.jpg +++ /dev/null diff --git a/public/images/portrait-min.jpg b/public/images/portrait-min.jpg Binary files differdeleted file mode 100644 index 49e8cde..0000000 --- a/public/images/portrait-min.jpg +++ /dev/null diff --git a/public/images/portrait.jpg b/public/images/portrait.jpg Binary files differdeleted file mode 100644 index 26e2d81..0000000 --- a/public/images/portrait.jpg +++ /dev/null diff --git a/public/images/resume.jpg b/public/images/resume.jpg Binary files differdeleted file mode 100644 index 450dbd2..0000000 --- a/public/images/resume.jpg +++ /dev/null diff --git a/public/index.html b/public/index.html index d632f3b..55e4ba2 100644 --- a/public/index.html +++ b/public/index.html @@ -27,40 +27,6 @@ </ul> </nav> </header> - <section id='image_container'> - <figure class='image_item'> - <img src='images/ebi.jpg' /> - <figcaption> - Hanging with my son, circa July 2023. - </figcaption> - </figure> - <figure class='image_item'> - <img src='images/gardens-min.jpg' /> - <figcaption> - Me at Longwood Gardens, circa December 2021. - </figcaption> - </figure> - <figure class='image_item'> - <img src='images/portrait-min.jpg' /> - <figcaption> - Me in front of my desktop, circa August 2021. - </figcaption> - </figure> - <figure class='image_item'> - <img src='images/dog_and_me-min.jpg' /> - <figcaption> - Hanging with my dog named Rizzy, circa May 2020. - </figcaption> - </figure> - <figure class='image_item'> - <img src='images/friends-min.jpg' /> - <figcaption> - Hanging with my friends, circa July 2019. - </figcaption> - </figure> - - - </section> <section> <h2>About Me</h2> <p> diff --git a/public/posts/dec_29_2025.html b/public/posts/dec_29_2025.html index 48981c7..cbdd3c2 100644 --- a/public/posts/dec_29_2025.html +++ b/public/posts/dec_29_2025.html @@ -29,17 +29,17 @@ </div> </div> <div id="content" class="content"> -<div id="outline-container-org762ecbe" class="outline-2"> -<h2 id="org762ecbe">What have I been up to?</h2> -<div class="outline-text-2" id="text-org762ecbe"> +<div id="outline-container-org5ff3714" class="outline-2"> +<h2 id="org5ff3714">What have I been up to?</h2> +<div class="outline-text-2" id="text-org5ff3714"> <p> 2025 has been one busy year for me! I feel as though I've been working on a dozen things at once and have spent much of my working (and personal) days productively. Miracle finally feels like it's getting somewhere fast, the Flutter multi-window work is landing at a solid pace, and Mir is feeling like a truly solid option for Wayland compositor development. I will refrain from speaking too much on the Flutter and Mir work in this post, as those are best left in the hands of Canonical. </p> </div> </div> -<div id="outline-container-org0499ab5" class="outline-2"> -<h2 id="org0499ab5">Miracle Update</h2> -<div class="outline-text-2" id="text-org0499ab5"> +<div id="outline-container-org6ce815f" class="outline-2"> +<h2 id="org6ce815f">Miracle Update</h2> +<div class="outline-text-2" id="text-org6ce815f"> <p> Miracle has come a long way this past year. I now feel entirely confident using it as my daily driver, minus a few hiccups that I encounter in-between releases. A lot of great things are cooking for 2026 too, </p> @@ -108,9 +108,9 @@ v0.9.0 of Miracle will probably be a long time in the making. My estimate is tha </p> </div> </div> -<div id="outline-container-org719e1b4" class="outline-2"> -<h2 id="org719e1b4">Conclusion</h2> -<div class="outline-text-2" id="text-org719e1b4"> +<div id="outline-container-orgba126b9" class="outline-2"> +<h2 id="orgba126b9">Conclusion</h2> +<div class="outline-text-2" id="text-orgba126b9"> <p> 2025 has been a whirlwind of a year, and I'm sure that 2026 won't slow down at all for me. A lot of the long term projects that I've been working on are finally coming together, and I feel as though I am on the cusp of making software that I'm truly proud of. On top of that, I am engaged! What a time to be alive :) I hope you all have a lovely New Year with your friends, family, cats, dogs, and everything else. </p> diff --git a/public/posts/feed.xml b/public/posts/feed.xml index d1388a0..3493910 100644 --- a/public/posts/feed.xml +++ b/public/posts/feed.xml @@ -5,7 +5,7 @@ <link>https://matthewkosarek.xyz/</link> <description>The RSS feed for Matthew Kosarek's Blog</description> <language>en-us</language> - <lastBuildDate>Wed, 18 February 2026 17:14:00 -0400</lastBuildDate> + <lastBuildDate>Thu, 19 February 2026 15:52:00 -0400</lastBuildDate> <item> <title>Update December 29, 2025</title> diff --git a/public/posts/hello.html b/public/posts/hello.html index 629ed69..4966f18 100644 --- a/public/posts/hello.html +++ b/public/posts/hello.html @@ -29,9 +29,9 @@ </div> </div> <div id="content" class="content"> -<div id="outline-container-org6bad422" class="outline-2"> -<h2 id="org6bad422">TLDR</h2> -<div class="outline-text-2" id="text-org6bad422"> +<div id="outline-container-org9e9109d" class="outline-2"> +<h2 id="org9e9109d">TLDR</h2> +<div class="outline-text-2" id="text-org9e9109d"> <ul class="org-ul"> <li>Create a new folder</li> <li>Put <a href="https://raw.githubusercontent.com/mattkae/matthewkosarek-xyz/master/index.css">index.css</a>, <a href="https://raw.githubusercontent.com/mattkae/matthewkosarek-xyz/master/publish.el">publish.el</a>, and <a href="https://github.com/mattkae/matthewkosarek-xyz/blob/master/publish.sh">publish.sh</a> in the folder</li> @@ -45,9 +45,9 @@ </ul> </div> </div> -<div id="outline-container-orgc7f45b6" class="outline-2"> -<h2 id="orgc7f45b6">Introduction</h2> -<div class="outline-text-2" id="text-orgc7f45b6"> +<div id="outline-container-org33cf264" class="outline-2"> +<h2 id="org33cf264">Introduction</h2> +<div class="outline-text-2" id="text-org33cf264"> <p> I've recently fallen in love with <code>org-mode</code>, specifically when I use it with <a href="https://www.orgroam.com/">org-roam</a>. I find the whole workflow of creating, tagging, and - later on - searching for information on my computer to be very elegant. On top of that, now that I have the time, I want to begin writing blog posts to better work out my thoughts. With both of these things in mind, I am again turning to the universal tool for human prospering: <code>org-mode</code>. This time, I want to see how it can help me turn a simple org file into a blog post on my website. My requirements are: </p> @@ -70,9 +70,9 @@ And that's pretty much it for now. Without further ado, let's jump into getting </p> </div> </div> -<div id="outline-container-orga3a87b9" class="outline-2"> -<h2 id="orga3a87b9">Basic HTML File</h2> -<div class="outline-text-2" id="text-orga3a87b9"> +<div id="outline-container-org041489b" class="outline-2"> +<h2 id="org041489b">Basic HTML File</h2> +<div class="outline-text-2" id="text-org041489b"> <p> As a pilot, we are going to use this org file that I am currently writing (<code>hello.org</code>) as our guinea pig. The goal is to have this org file be our very first blog post. </p> @@ -112,9 +112,9 @@ We then do a <code>chmod +x publish.sh</code> to make it an executable and run i </p> </div> </div> -<div id="outline-container-orgf20d464" class="outline-2"> -<h2 id="orgf20d464">Disabling features that we don't want</h2> -<div class="outline-text-2" id="text-orgf20d464"> +<div id="outline-container-org0353981" class="outline-2"> +<h2 id="org0353981">Disabling features that we don't want</h2> +<div class="outline-text-2" id="text-org0353981"> <p> The next thing will be to remove some of the generated items that I didn't ask for, namely the table of contents, author, section numbers, creation time stamp, and the validation link. </p> @@ -143,9 +143,9 @@ The next thing will be to remove some of the generated items that I didn't ask f </div> </div> </div> -<div id="outline-container-orgb6e0609" class="outline-2"> -<h2 id="orgb6e0609">Styling & Code Highlighting</h2> -<div class="outline-text-2" id="text-orgb6e0609"> +<div id="outline-container-org8a1e73c" class="outline-2"> +<h2 id="org8a1e73c">Styling & Code Highlighting</h2> +<div class="outline-text-2" id="text-org8a1e73c"> <p> Next thing on our list is custom styling. This can be achieved by first installing the <code>htmlize</code> package from <code>melpa</code> / <code>elpa</code>. The EmacsWiki describes this as "a package for exporting the contents of an Emacs buffer to HTML while respecting display properties such as colors, fonts, underlining, invisibility, etc" (<a href="https://www.emacswiki.org/emacs/Htmlize">reference</a>). If used "out-of-the-box", the buffer will be exported to HTML with all of the styles inlined (e.g. if you underline something in your org file, you will generate a <code><span style="text-decoration: underline">...</span></code>). However, we are more interested in styling everything by ourselves: we don't want <code>htmlize</code> making assumptions about what underlining means to us! Luckily, <code>htmlize</code> gives us the option to export with class names instead of inline styles so that we can specify each style for ourselves. </p> @@ -315,9 +315,9 @@ If we run the publish again, we can see that we have full styling on our code sn </p> </div> </div> -<div id="outline-container-org456f7f8" class="outline-2"> -<h2 id="org456f7f8">Images</h2> -<div class="outline-text-2" id="text-org456f7f8"> +<div id="outline-container-org624adfd" class="outline-2"> +<h2 id="org624adfd">Images</h2> +<div class="outline-text-2" id="text-org624adfd"> <p> Our first two criteria have been met! Next on the list is solving images. As an example, let's use this <a href="file:///_posts/assets/squirrel.jpg">squirrel image</a> that I found online with an open source license. The ideal situation would be: </p> @@ -362,16 +362,16 @@ So what's the fix here? Well, we have two options, but I am going to go with the That's all there is to it! There are simpler ways as well, but that should do it: </p> -<div id="org04b7deb" class="figure"> +<div id="org9dc849e" class="figure"> <p><img src="/_posts/assets/squirrel.jpg" alt="squirrel.jpg" width="300" /> </p> <p><span class="figure-number">Figure 1: </span>A Cute Squirrel</p> </div> </div> </div> -<div id="outline-container-orgd7df786" class="outline-2"> -<h2 id="orgd7df786">Creation Date</h2> -<div class="outline-text-2" id="text-orgd7df786"> +<div id="outline-container-org328d8f8" class="outline-2"> +<h2 id="org328d8f8">Creation Date</h2> +<div class="outline-text-2" id="text-org328d8f8"> <p> Let's add the creation date below the title next. To start, we will modify the publish command to remove the title (<code>:with-title nil</code>) and, in its place, show a preamble bit of HTML that contains a formatted <code>div</code> with the title and the "last modified" span.z </p> @@ -430,9 +430,9 @@ Note that the downside of this is that the created date will change whenever you </p> </div> </div> -<div id="outline-container-org6b24595" class="outline-2"> -<h2 id="org6b24595">Generating the Directory</h2> -<div class="outline-text-2" id="text-org6b24595"> +<div id="outline-container-orge04dce8" class="outline-2"> +<h2 id="orge04dce8">Generating the Directory</h2> +<div class="outline-text-2" id="text-orge04dce8"> <p> For every org file in my <code>_posts</code> folder, I would like to create a link to the generated HTML file at the <code>/posts.html</code> page of my website. You can think of this as the "directory" of all posts. My criteria is: </p> @@ -492,9 +492,9 @@ If you open the <code>sitemap.html</code> file in your browser, you will see a b From here, you may customize it however you like. The following are my customizations. </p> </div> -<div id="outline-container-orgde34f50" class="outline-3"> -<h3 id="orgde34f50">Sitemap Title</h3> -<div class="outline-text-3" id="text-orgde34f50"> +<div id="outline-container-orgf0a663f" class="outline-3"> +<h3 id="orgf0a663f">Sitemap Title</h3> +<div class="outline-text-3" id="text-orgf0a663f"> <p> I changed the title to "Matthew's Blog Posts". </p> @@ -516,9 +516,9 @@ I changed the title to "Matthew's Blog Posts". </div> </div> </div> -<div id="outline-container-org8eecd95" class="outline-3"> -<h3 id="org8eecd95">Format blog entries in the list</h3> -<div class="outline-text-3" id="text-org8eecd95"> +<div id="outline-container-orgbf15acf" class="outline-3"> +<h3 id="orgbf15acf">Format blog entries in the list</h3> +<div class="outline-text-3" id="text-orgbf15acf"> <p> I like to include the creation date on the blog posts. To do this, we can use <code>org-publish-find-property</code> to find the date property of the org file. Afterward, we can format a string that includes our formatted timestamp and the <code>org-publish-sitemap-default-entry</code>, which is just a link with the title of the post. </p> @@ -539,9 +539,9 @@ I like to include the creation date on the blog posts. To do this, we can use <c </div> </div> </div> -<div id="outline-container-orgcb3d39d" class="outline-2"> -<h2 id="orgcb3d39d">Tags & Filtering</h2> -<div class="outline-text-2" id="text-orgcb3d39d"> +<div id="outline-container-orgbbf6e44" class="outline-2"> +<h2 id="orgbbf6e44">Tags & Filtering</h2> +<div class="outline-text-2" id="text-orgbbf6e44"> <p> I use <a href="https://www.orgroam.com/">Org-roam</a> for all of my note-taking and, in the next blog post, I plan to demonstrate how I will hook up my Org-roam note-taking workflow to my blogging. In the meantime, just know that we can add tags to the top of our org files like this: </p> @@ -761,9 +761,9 @@ Finally, let's append the following to <code>posts/posts.css</code> so that our </div> </div> </div> -<div id="outline-container-org937024c" class="outline-2"> -<h2 id="org937024c">Conclusion</h2> -<div class="outline-text-2" id="text-org937024c"> +<div id="outline-container-org6068a86" class="outline-2"> +<h2 id="org6068a86">Conclusion</h2> +<div class="outline-text-2" id="text-org6068a86"> <p> There are many more customizations that I plan to do on this system in the future, but I plan to leave this for now so that I can actually get to some blogging. I will proofread and fix my mistakes as time goes on, but this should be a good jumping off point for anyone interested in using org for their own blogging system. </p> diff --git a/public/posts/jul_28_2025.html b/public/posts/jul_28_2025.html index d93cf72..ea4e38f 100644 --- a/public/posts/jul_28_2025.html +++ b/public/posts/jul_28_2025.html @@ -29,9 +29,9 @@ </div> </div> <div id="content" class="content"> -<div id="outline-container-org7b55006" class="outline-2"> -<h2 id="org7b55006">What have I been up to?</h2> -<div class="outline-text-2" id="text-org7b55006"> +<div id="outline-container-org030cdd7" class="outline-2"> +<h2 id="org030cdd7">What have I been up to?</h2> +<div class="outline-text-2" id="text-org030cdd7"> <p> Whoops! I missed this month's update by a <i>long</i> shot, but I still want to get it out there before the end of the month. </p> diff --git a/public/posts/june_08_2025.html b/public/posts/june_08_2025.html index f467a84..9dc6840 100644 --- a/public/posts/june_08_2025.html +++ b/public/posts/june_08_2025.html @@ -29,9 +29,9 @@ </div> </div> <div id="content" class="content"> -<div id="outline-container-orgb3a2b27" class="outline-2"> -<h2 id="orgb3a2b27">What have I been up to?</h2> -<div class="outline-text-2" id="text-orgb3a2b27"> +<div id="outline-container-org3a3efed" class="outline-2"> +<h2 id="org3a3efed">What have I been up to?</h2> +<div class="outline-text-2" id="text-org3a3efed"> <p> Another month has gone by, so I guess it's time to see what I've been up to. </p> diff --git a/public/posts/may_06_2025.html b/public/posts/may_06_2025.html index 563bcab..0dbf222 100644 --- a/public/posts/may_06_2025.html +++ b/public/posts/may_06_2025.html @@ -29,9 +29,9 @@ </div> </div> <div id="content" class="content"> -<div id="outline-container-org8c64ed6" class="outline-2"> -<h2 id="org8c64ed6">What have I been up to?</h2> -<div class="outline-text-2" id="text-org8c64ed6"> +<div id="outline-container-org447f3cf" class="outline-2"> +<h2 id="org447f3cf">What have I been up to?</h2> +<div class="outline-text-2" id="text-org447f3cf"> <p> I've been meaning to do these little blog-post type updates for a while, and I figured now is as good a time as any. So let's start :) </p> diff --git a/public/posts/sitemap.html b/public/posts/sitemap.html index 9bcfbaf..fac5832 100644 --- a/public/posts/sitemap.html +++ b/public/posts/sitemap.html @@ -34,13 +34,13 @@ <li><p> <a href="dec_29_2025.html">Update December 29, 2025</a> </p> -<div class="sitemap_date" id="org63552bd"> +<div class="sitemap_date" id="org5a5f43e"> <p> December 29, 2025 </p> </div> -<div class="sitemap_tag" id="orgcae0db0"> +<div class="sitemap_tag" id="org1ea5026"> <p> update </p> @@ -49,13 +49,13 @@ update <li><p> <a href="jul_28_2025.html">Update July 28, 2025</a> </p> -<div class="sitemap_date" id="org04d89e2"> +<div class="sitemap_date" id="orgf97cfcd"> <p> July 28, 2025 </p> </div> -<div class="sitemap_tag" id="orgd7ad92d"> +<div class="sitemap_tag" id="org8ef1374"> <p> update </p> @@ -64,13 +64,13 @@ update <li><p> <a href="june_08_2025.html">Update June 08, 2025</a> </p> -<div class="sitemap_date" id="org1099798"> +<div class="sitemap_date" id="org22eabd6"> <p> June 08, 2025 </p> </div> -<div class="sitemap_tag" id="orgf02bca4"> +<div class="sitemap_tag" id="orgd49a1ce"> <p> update </p> @@ -79,13 +79,13 @@ update <li><p> <a href="may_06_2025.html">Update May 06, 2025</a> </p> -<div class="sitemap_date" id="org901b9bf"> +<div class="sitemap_date" id="orgea2a74c"> <p> May 06, 2025 </p> </div> -<div class="sitemap_tag" id="org0b4777f"> +<div class="sitemap_tag" id="org0b31acc"> <p> update </p> @@ -94,13 +94,13 @@ update <li><p> <a href="hello.html">Hello, Org</a> </p> -<div class="sitemap_date" id="orgf000d64"> +<div class="sitemap_date" id="orgd9dcebd"> <p> June 20, 2023 </p> </div> -<div class="sitemap_tag" id="orga3ba3e0"> +<div class="sitemap_tag" id="org52466f8"> <p> technology,home </p> diff --git a/themes/dist/output.wasm b/themes/dist/output.wasm Binary files differindex 550e5ff..d0b2635 100755 --- a/themes/dist/output.wasm +++ b/themes/dist/output.wasm diff --git a/themes/meson.build b/themes/meson.build index 563ddc6..f99bf5c 100644 --- a/themes/meson.build +++ b/themes/meson.build @@ -40,6 +40,8 @@ sources = files( 'src/shaders/sun_vert.cpp', 'src/shaders/snowflake_frag.cpp', 'src/shaders/snowflake_vert.cpp', + 'src/shaders/grass_frag.cpp', + 'src/shaders/grass_vert.cpp', # Autumn theme 'src/autumn/autumn_theme.cpp', @@ -84,7 +86,9 @@ shader_inputs = files( 'src/_shaders/sun.frag', 'src/_shaders/sun.vert', 'src/_shaders/snowflake.frag', - 'src/_shaders/snowflake.vert' + 'src/_shaders/snowflake.vert', + 'src/_shaders/grass.frag', + 'src/_shaders/grass.vert' ) # Custom target that runs whenever shader files change diff --git a/themes/src/_shaders/grass.frag b/themes/src/_shaders/grass.frag new file mode 100644 index 0000000..a72f078 --- /dev/null +++ b/themes/src/_shaders/grass.frag @@ -0,0 +1,11 @@ +varying lowp vec2 vUV; + +void main() { + lowp float halfWidth = 0.5 * (1.0 - vUV.y); + lowp float distFromCenter = abs(vUV.x - 0.5); + if (distFromCenter > halfWidth) discard; + + lowp vec3 baseColor = vec3(0.15, 0.45, 0.10); + lowp vec3 tipColor = vec3(0.40, 0.75, 0.20); + gl_FragColor = vec4(mix(baseColor, tipColor, vUV.y), 1.0); +} diff --git a/themes/src/_shaders/grass.vert b/themes/src/_shaders/grass.vert new file mode 100644 index 0000000..0cf0285 --- /dev/null +++ b/themes/src/_shaders/grass.vert @@ -0,0 +1,25 @@ +attribute vec2 position; // Local quad vertex: x in [-0.5, 0.5], y in [0, 1] +attribute vec3 instancePos; // Per-instance: world-space base of blade +attribute float instancePhase; // Per-instance: random phase offset for sway +attribute float instanceHeight; // Per-instance: height scale multiplier + +uniform mat4 projection; +uniform mat4 view; +uniform float time; +uniform float bladeWidth; +uniform float bladeHeight; +uniform float swayAmount; + +varying lowp vec2 vUV; + +void main() { + vec3 cameraRight = vec3(view[0][0], view[1][0], view[2][0]); + float h = bladeHeight * instanceHeight; + float sway = sin(time * 1.5 + instancePhase) * swayAmount * position.y; + vec3 worldPos = instancePos + + cameraRight * (position.x + sway) * bladeWidth + + vec3(0.0, 1.0, 0.0) * position.y * h; + + gl_Position = projection * view * vec4(worldPos, 1.0); + vUV = vec2(position.x + 0.5, position.y); +} diff --git a/themes/src/main.cpp b/themes/src/main.cpp index 60e6aed..ec7630b 100644 --- a/themes/src/main.cpp +++ b/themes/src/main.cpp @@ -1,117 +1,132 @@ -#include "webgl_context.h" +#include "autumn/autumn_theme.hpp" #include "main_loop.h" -#include "renderer_2d.h" #include "mathlib.h" +#include "renderer_2d.h" +#include "spring/spring_theme.hpp" +#include "summer/summer_theme.h" #include "theme.h" #include "types.h" -#include "summer/summer_theme.h" -#include "autumn/autumn_theme.hpp" -#include "spring/spring_theme.hpp" +#include "webgl_context.h" #include "winter/winter_theme.hpp" #include <cstdio> #include <emscripten/fetch.h> void load(ThemeType theme); void unload(); -void update(f32 dtSeconds, void* userData); -EM_BOOL selectNone(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData); -EM_BOOL selectAutumn(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData); -EM_BOOL selectWinter(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData); -EM_BOOL selectSpring(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData); -EM_BOOL selectSummer(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData); +void update(f32 dtSeconds, void *userData); +EM_BOOL selectNone(int eventType, const EmscriptenMouseEvent *mouseEvent, + void *userData); +EM_BOOL selectAutumn(int eventType, const EmscriptenMouseEvent *mouseEvent, + void *userData); +EM_BOOL selectWinter(int eventType, const EmscriptenMouseEvent *mouseEvent, + void *userData); +EM_BOOL selectSpring(int eventType, const EmscriptenMouseEvent *mouseEvent, + void *userData); +EM_BOOL selectSummer(int eventType, const EmscriptenMouseEvent *mouseEvent, + void *userData); WebglContext context; MainLoop mainLoop; ThemeType type; -Theme* active_theme; +Theme *active_theme; int main() { - context.init("#theme_canvas"); - emscripten_set_click_callback("#theme_button_default", NULL, false, selectNone); - emscripten_set_click_callback("#theme_button_autumn", NULL, false, selectAutumn); - emscripten_set_click_callback("#theme_button_winter", NULL, false, selectWinter); - emscripten_set_click_callback("#theme_button_spring", NULL, false, selectSpring); - emscripten_set_click_callback("#theme_button_summer", NULL, false, selectSummer); - - return 0; + context.init("#theme_canvas"); + emscripten_set_click_callback("#theme_button_default", NULL, false, + selectNone); + emscripten_set_click_callback("#theme_button_autumn", NULL, false, + selectAutumn); + emscripten_set_click_callback("#theme_button_winter", NULL, false, + selectWinter); + emscripten_set_click_callback("#theme_button_spring", NULL, false, + selectSpring); + emscripten_set_click_callback("#theme_button_summer", NULL, false, + selectSummer); + + return 0; } // -- Scene loading, updating, and unloading logic void load(ThemeType theme) { - if (type == theme) { - printf("This theme is already active.\n"); - return; - } - - unload(); // Try and unload before we load, so that we start fresh - - type = theme; - mainLoop.run(update); - - switch (type) { - case ThemeType::Autumn: - active_theme = new AutumnTheme(&context); - break; - case ThemeType::Winter: - active_theme = new WinterTheme(&context); - break; - case ThemeType::Spring: - active_theme = new SpringTheme(&context); - break; - case ThemeType::Summer: - active_theme = new SummerTheme(&context); - break; - default: - break; - } + if (type == theme) { + printf("This theme is already active.\n"); + return; + } + + unload(); // Try and unload before we load, so that we start fresh + + type = theme; + mainLoop.run(update); + + switch (type) { + case ThemeType::Autumn: + active_theme = new AutumnTheme(&context); + break; + case ThemeType::Winter: + active_theme = new WinterTheme(&context); + break; + case ThemeType::Spring: + active_theme = new SpringTheme(&context); + break; + case ThemeType::Summer: + active_theme = new SummerTheme(&context); + break; + default: + break; + } } -void update(f32 dtSeconds, void* userData) { - if (!active_theme) - return; - active_theme->update(dtSeconds); - active_theme->render(); +void update(f32 dtSeconds, void *userData) { + if (!active_theme) + return; + active_theme->update(dtSeconds); + active_theme->render(); } void unload() { - delete active_theme; - active_theme = nullptr; - - type = ThemeType::Default; - glClearColor(0, 0, 0, 0); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - if (mainLoop.isRunning) { - mainLoop.stop(); - } + delete active_theme; + active_theme = nullptr; + + type = ThemeType::Default; + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + if (mainLoop.isRunning) { + mainLoop.stop(); + } } // -- HTML5 callbacks -EM_BOOL selectNone(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData) { - printf("Default theme selected\n"); - unload(); - return true; +EM_BOOL selectNone(int eventType, const EmscriptenMouseEvent *mouseEvent, + void *userData) { + printf("Default theme selected\n"); + unload(); + return true; } -EM_BOOL selectAutumn(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData) { - printf("Autumn theme selected\n"); - load(ThemeType::Autumn); - return true; +EM_BOOL selectAutumn(int eventType, const EmscriptenMouseEvent *mouseEvent, + void *userData) { + printf("Autumn theme selected\n"); + load(ThemeType::Autumn); + return true; } -EM_BOOL selectWinter(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData) { - printf("Winter theme selected\n"); - load(ThemeType::Winter); - return true; +EM_BOOL selectWinter(int eventType, const EmscriptenMouseEvent *mouseEvent, + void *userData) { + printf("Winter theme selected\n"); + load(ThemeType::Winter); + return true; } -EM_BOOL selectSpring(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData) { - printf("Spring theme selected\n"); - load(ThemeType::Spring); - return true; +EM_BOOL selectSpring(int eventType, const EmscriptenMouseEvent *mouseEvent, + void *userData) { + printf("Spring theme selected\n"); + load(ThemeType::Spring); + return true; } -EM_BOOL selectSummer(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData) { - printf("Summer theme selected\n"); - load(ThemeType::Summer); - return true; +EM_BOOL selectSummer(int eventType, const EmscriptenMouseEvent *mouseEvent, + void *userData) { + printf("Summer theme selected\n"); + load(ThemeType::Summer); + return true; } diff --git a/themes/src/main_loop.cpp b/themes/src/main_loop.cpp index e5397ca..743892e 100644 --- a/themes/src/main_loop.cpp +++ b/themes/src/main_loop.cpp @@ -2,30 +2,36 @@ #include <cstdio> #include <cstdlib> -EM_BOOL loop(double time, void* loop) { - MainLoop* mainLoop = (MainLoop*) loop; - if (!mainLoop->isRunning) { - return false; - } - - if (mainLoop->lastTime == 0) { - mainLoop->lastTime = time; - return true; - } +EM_BOOL loop(double time, void *loop) { + MainLoop *mainLoop = (MainLoop *)loop; + if (!mainLoop->isRunning) { + return false; + } - long deltaTime = time - mainLoop->lastTime; + if (mainLoop->lastTime == 0) { mainLoop->lastTime = time; - mainLoop->elapsedTime += deltaTime; - mainLoop->numFrames++; - float deltaTimeSeconds = static_cast<float>(deltaTime) / 1000.f; + return true; + } + + long deltaTime = time - mainLoop->lastTime; + mainLoop->lastTime = time; + mainLoop->elapsedTime += deltaTime; + mainLoop->numFrames++; + float deltaTimeSeconds = static_cast<float>(deltaTime) / 1000.f; - if (mainLoop->elapsedTime >= 1000.0) { - printf("FPS: %d\n", mainLoop->numFrames); + if (mainLoop->elapsedTime >= 1000.0) { + printf("FPS: %d\n", mainLoop->numFrames); - mainLoop->elapsedTime = 0.0; - mainLoop->numFrames = 0; - } + mainLoop->elapsedTime = 0.0; + mainLoop->numFrames = 0; + } - mainLoop->updateFunc(deltaTimeSeconds, NULL); + // Ignore any update with a greater than 0.1 change. We were + // probably tabbed away, so this is uninteresting to us. + if (deltaTimeSeconds > 0.1) { return true; -}
\ No newline at end of file + } + + mainLoop->updateFunc(deltaTimeSeconds, NULL); + return true; +} diff --git a/themes/src/renderer_2d.h b/themes/src/renderer_2d.h index d572533..16c5cbe 100644 --- a/themes/src/renderer_2d.h +++ b/themes/src/renderer_2d.h @@ -1,61 +1,59 @@ #pragma once -#include "webgl_context.h" -#include "types.h" -#include "shader.h" #include "mathlib.h" +#include "shader.h" +#include "types.h" +#include "webgl_context.h" struct WebglContext; /// Responsible for rendering Mesh2Ds struct Renderer2d { - WebglContext* context = NULL; - Mat4x4 projection; - u32 shader; - Vector4 clearColor; - - struct { - i32 position; - i32 color; - - // TODO: vMatrix is not standard and does not belong here - i32 vMatrix; - } attributes; - - struct { - i32 projection; - i32 model; - } uniforms; - - /// Load with the provided context and shader programs. If the shaders are NULL, the default - /// shader is used - void load(WebglContext* context, const char* vertexShader = NULL, const char* fragmentShader = NULL); - void render(); - void unload(); - f32 get_width(); - f32 get_height(); + WebglContext *context = NULL; + Mat4x4 projection; + u32 shader; + Vector4 clearColor; + + struct { + i32 position; + i32 color; + + // TODO: vMatrix is not standard and does not belong here + i32 vMatrix; + } attributes; + + struct { + i32 projection; + i32 model; + } uniforms; + + /// Load with the provided context and shader programs. If the shaders are + /// NULL, the default shader is used + void load(WebglContext *context, const char *vertexShader = NULL, + const char *fragmentShader = NULL); + void render(); + void unload(); + f32 get_width(); + f32 get_height(); }; struct Vertex2D { - Vector2 position; - Vector4 color; - Mat4x4 vMatrix; + Vector2 position; + Vector4 color; + Mat4x4 vMatrix; }; struct Mesh2D { - u32 vao; - u32 vbo; - u32 ebo = 0; - u32 numVertices = 0; - u32 numIndices = 0; - Mat4x4 model; - - void load(Vertex2D* vertices, u32 numVertices, Renderer2d* renderer); - void load(Vertex2D* vertices, - u32 numVertices, - u32* indices, - u32 numIndices, - Renderer2d* renderer); - void render(Renderer2d* renderer, GLenum drawType = GL_TRIANGLES); - void unload(); + u32 vao; + u32 vbo; + u32 ebo = 0; + u32 numVertices = 0; + u32 numIndices = 0; + Mat4x4 model; + + void load(Vertex2D *vertices, u32 numVertices, Renderer2d *renderer); + void load(Vertex2D *vertices, u32 numVertices, u32 *indices, u32 numIndices, + Renderer2d *renderer); + void render(Renderer2d *renderer, GLenum drawType = GL_TRIANGLES); + void unload(); }; diff --git a/themes/src/shaders/grass_frag.cpp b/themes/src/shaders/grass_frag.cpp new file mode 100644 index 0000000..5a62cf2 --- /dev/null +++ b/themes/src/shaders/grass_frag.cpp @@ -0,0 +1,14 @@ +#include "grass_frag.h" + +const char* shader_grass_frag = "varying lowp vec2 vUV; \n" +" \n" +"void main() { \n" +" lowp float halfWidth = 0.5 * (1.0 - vUV.y); \n" +" lowp float distFromCenter = abs(vUV.x - 0.5); \n" +" if (distFromCenter > halfWidth) discard; \n" +" \n" +" lowp vec3 baseColor = vec3(0.15, 0.45, 0.10); \n" +" lowp vec3 tipColor = vec3(0.40, 0.75, 0.20); \n" +" gl_FragColor = vec4(mix(baseColor, tipColor, vUV.y), 1.0); \n" +"} \n" +" \n"; diff --git a/themes/src/shaders/grass_frag.h b/themes/src/shaders/grass_frag.h new file mode 100644 index 0000000..16cc29a --- /dev/null +++ b/themes/src/shaders/grass_frag.h @@ -0,0 +1,4 @@ +#ifndef SHADER_GRASS_FRAG +#define SHADER_GRASS_FRAG +extern const char* shader_grass_frag; +#endif diff --git a/themes/src/shaders/grass_vert.cpp b/themes/src/shaders/grass_vert.cpp new file mode 100644 index 0000000..c9d2955 --- /dev/null +++ b/themes/src/shaders/grass_vert.cpp @@ -0,0 +1,28 @@ +#include "grass_vert.h" + +const char* shader_grass_vert = "attribute vec2 position; // Local quad vertex: x in [-0.5, 0.5], y in [0, 1] \n" +"attribute vec3 instancePos; // Per-instance: world-space base of blade \n" +"attribute float instancePhase; // Per-instance: random phase offset for sway \n" +"attribute float instanceHeight; // Per-instance: height scale multiplier \n" +" \n" +"uniform mat4 projection; \n" +"uniform mat4 view; \n" +"uniform float time; \n" +"uniform float bladeWidth; \n" +"uniform float bladeHeight; \n" +"uniform float swayAmount; \n" +" \n" +"varying lowp vec2 vUV; \n" +" \n" +"void main() { \n" +" vec3 cameraRight = vec3(view[0][0], view[1][0], view[2][0]); \n" +" float h = bladeHeight * instanceHeight; \n" +" float sway = sin(time * 1.5 + instancePhase) * swayAmount * position.y; \n" +" vec3 worldPos = instancePos \n" +" + cameraRight * (position.x + sway) * bladeWidth \n" +" + vec3(0.0, 1.0, 0.0) * position.y * h; \n" +" \n" +" gl_Position = projection * view * vec4(worldPos, 1.0); \n" +" vUV = vec2(position.x + 0.5, position.y); \n" +"} \n" +" \n"; diff --git a/themes/src/shaders/grass_vert.h b/themes/src/shaders/grass_vert.h new file mode 100644 index 0000000..7ab52b6 --- /dev/null +++ b/themes/src/shaders/grass_vert.h @@ -0,0 +1,4 @@ +#ifndef SHADER_GRASS_VERT +#define SHADER_GRASS_VERT +extern const char* shader_grass_vert; +#endif diff --git a/themes/src/spring/grass_renderer.cpp b/themes/src/spring/grass_renderer.cpp index 685f733..e4a210c 100644 --- a/themes/src/spring/grass_renderer.cpp +++ b/themes/src/spring/grass_renderer.cpp @@ -1,29 +1,138 @@ #include "grass_renderer.hpp" #include "../renderer_3d.h" +#include "../shader.h" +#include "../shaders/grass_frag.h" +#include "../shaders/grass_vert.h" +#include "mathlib.h" +#include <cmath> +#include <cstddef> -void GrassRenderer::load(GrassRendererLoadData params, Renderer3d* renderer) { - const f32 COLUMN_INCREMENT = GRASS_BLADES_PER_COL / params.area.x; - const f32 ROW_INCREMENT = GRASS_BLADES_PER_ROW / params.area.y; - for (i32 r = 0; r < GRASS_BLADES_PER_ROW; r++) { - i32 indexOffset = r * GRASS_BLADES_PER_ROW; - f32 y = ROW_INCREMENT * r; - for (i32 c = 0; c < GRASS_BLADES_PER_COL; c++) { - f32 x = COLUMN_INCREMENT * c; - i32 index = indexOffset + c; - grassBlades[index].position = Vector3(x, y, 0); - grassBlades[index].top_offset = Vector2(0, 0); - } - } -} +void GrassRenderer::load(GrassRendererLoadData params, Renderer3d *renderer) { + bladeHeight = params.grassHeight; + + // Place blades randomly within a circle. Using r = R*sqrt(u) with a + // uniform u in [0,1] gives uniform areal density (no center clustering). + const f32 radius = fminf(params.area.x, params.area.y) * 0.5f; + for (i32 i = 0; i < NUM_GRASS_BLADES; i++) { + f32 r = radius * sqrtf(randomFloatBetween(0.f, 1.f)); + f32 theta = randomFloatBetween(0.f, 2.f * PI); + f32 x = params.origin.x + r * cosf(theta); + f32 z = params.origin.y + r * sinf(theta); + grassBlades[i].position = Vector3(x, 0, z); + grassBlades[i].top_offset = + Vector2(randomFloatBetween(0.f, 2.f * PI), // sway phase + randomFloatBetween(0.5f, 1.5f) // height scale + ); + } + + // Compile grass shader + shader = loadShader(shader_grass_vert, shader_grass_frag); + useShader(shader); + + // Attribute locations + attributes.position = getShaderAttribute(shader, "position"); + attributes.instancePos = getShaderAttribute(shader, "instancePos"); + attributes.instancePhase = getShaderAttribute(shader, "instancePhase"); + attributes.instanceHeight = getShaderAttribute(shader, "instanceHeight"); + + // Uniform locations + uniforms.projection = getShaderUniform(shader, "projection"); + uniforms.view = getShaderUniform(shader, "view"); + uniforms.time = getShaderUniform(shader, "time"); + uniforms.bladeWidth = getShaderUniform(shader, "bladeWidth"); + uniforms.bladeHeight = getShaderUniform(shader, "bladeHeight"); + uniforms.swayAmount = getShaderUniform(shader, "swayAmount"); + + // Base quad: two triangles forming a unit quad + // x in [-0.5, 0.5], y in [0, 1] + Vector2 quadVertices[] = {Vector2(-0.5f, 0.0f), Vector2(0.5f, 0.0f), + Vector2(0.5f, 1.0f), Vector2(-0.5f, 0.0f), + Vector2(0.5f, 1.0f), Vector2(-0.5f, 1.0f)}; + + glGenVertexArrays(1, &vao); + glBindVertexArray(vao); + + // Static quad VBO + glGenBuffers(1, &quadVbo); + glBindBuffer(GL_ARRAY_BUFFER, quadVbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), quadVertices, + GL_STATIC_DRAW); + glEnableVertexAttribArray(attributes.position); + glVertexAttribPointer(attributes.position, 2, GL_FLOAT, GL_FALSE, + sizeof(Vector2), (GLvoid *)0); -void GrassRenderer::update(f32 seconds) { - + // Dynamic instance VBO + glGenBuffers(1, &instanceVbo); + glBindBuffer(GL_ARRAY_BUFFER, instanceVbo); + glBufferData(GL_ARRAY_BUFFER, NUM_GRASS_BLADES * sizeof(GrassInstanceData), + NULL, GL_DYNAMIC_DRAW); + + // instancePos: vec3 (x, y, z) at offset 0 + glEnableVertexAttribArray(attributes.instancePos); + glVertexAttribPointer(attributes.instancePos, 3, GL_FLOAT, GL_FALSE, + sizeof(GrassInstanceData), + (GLvoid *)offsetof(GrassInstanceData, x)); + glVertexAttribDivisor(attributes.instancePos, 1); + + // instancePhase: float at offset 12 (after 3 floats) + glEnableVertexAttribArray(attributes.instancePhase); + glVertexAttribPointer(attributes.instancePhase, 1, GL_FLOAT, GL_FALSE, + sizeof(GrassInstanceData), + (GLvoid *)offsetof(GrassInstanceData, phaseOffset)); + glVertexAttribDivisor(attributes.instancePhase, 1); + + // instanceHeight: float at offset 16 (after phaseOffset) + glEnableVertexAttribArray(attributes.instanceHeight); + glVertexAttribPointer(attributes.instanceHeight, 1, GL_FLOAT, GL_FALSE, + sizeof(GrassInstanceData), + (GLvoid *)offsetof(GrassInstanceData, heightScale)); + glVertexAttribDivisor(attributes.instanceHeight, 1); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); } -void GrassRenderer::render(Renderer3d* renderer) { +void GrassRenderer::update(f32 dtSeconds) { time += dtSeconds; } +void GrassRenderer::render(Renderer3d *renderer) { + useShader(shader); + setShaderMat4(uniforms.projection, renderer->projection); + setShaderMat4(uniforms.view, renderer->view); + setShaderFloat(uniforms.time, time); + setShaderFloat(uniforms.bladeWidth, bladeWidth); + setShaderFloat(uniforms.bladeHeight, bladeHeight); + setShaderFloat(uniforms.swayAmount, swayAmount); + + // Build and upload instance data + GrassInstanceData instanceData[NUM_GRASS_BLADES]; + for (i32 i = 0; i < NUM_GRASS_BLADES; i++) { + instanceData[i].x = grassBlades[i].position.x; + instanceData[i].y = grassBlades[i].position.y; + instanceData[i].z = grassBlades[i].position.z; + instanceData[i].phaseOffset = grassBlades[i].top_offset.x; + instanceData[i].heightScale = grassBlades[i].top_offset.y; + } + + glBindBuffer(GL_ARRAY_BUFFER, instanceVbo); + glBufferSubData(GL_ARRAY_BUFFER, 0, + NUM_GRASS_BLADES * sizeof(GrassInstanceData), instanceData); + + glBindVertexArray(vao); + glDrawArraysInstanced(GL_TRIANGLES, 0, 6, NUM_GRASS_BLADES); + glBindVertexArray(0); } void GrassRenderer::unload() { - + if (vao) + glDeleteVertexArrays(1, &vao); + if (quadVbo) + glDeleteBuffers(1, &quadVbo); + if (instanceVbo) + glDeleteBuffers(1, &instanceVbo); + if (shader) + glDeleteProgram(shader); + vao = 0; + quadVbo = 0; + instanceVbo = 0; + shader = 0; } diff --git a/themes/src/spring/grass_renderer.hpp b/themes/src/spring/grass_renderer.hpp index 88879f3..14ef067 100644 --- a/themes/src/spring/grass_renderer.hpp +++ b/themes/src/spring/grass_renderer.hpp @@ -5,29 +5,59 @@ #include "mathlib.h" #include "types.h" -const i32 GRASS_BLADES_PER_ROW = 24; -const i32 GRASS_BLADES_PER_COL = 24; +const i32 GRASS_BLADES_PER_ROW = 48; +const i32 GRASS_BLADES_PER_COL = 48; const i32 NUM_GRASS_BLADES = GRASS_BLADES_PER_ROW * GRASS_BLADES_PER_COL; struct GrassRendererLoadData { - Vector2 origin = Vector2(0, 0); - Vector2 area = Vector2(480, 480); - f32 grassHeight = 12.f; + Vector2 origin = Vector2(0, 0); + Vector2 area = Vector2(480, 480); + f32 grassHeight = 4.f; }; struct GrassUpdateData { - Vector3 position; - Vector2 top_offset; + Vector3 position; + Vector2 top_offset; // top_offset.x stores per-blade sway phase offset +}; + +struct GrassInstanceData { + float x, y, z; + float phaseOffset; + float heightScale; }; struct GrassRenderer { - - GrassUpdateData grassBlades[NUM_GRASS_BLADES]; - - void load(GrassRendererLoadData params, Renderer3d* renderer); - void update(f32 dtSeconds); - void render(Renderer3d* renderer); - void unload(); + GrassUpdateData grassBlades[NUM_GRASS_BLADES]; + + u32 vao = 0; + u32 quadVbo = 0; + u32 instanceVbo = 0; + u32 shader = 0; + f32 time = 0.f; + f32 bladeWidth = 1.5f; + f32 bladeHeight = 6.f; + f32 swayAmount = 0.3f; + + struct { + i32 position; + i32 instancePos; + i32 instancePhase; + i32 instanceHeight; + } attributes; + + struct { + i32 projection; + i32 view; + i32 time; + i32 bladeWidth; + i32 bladeHeight; + i32 swayAmount; + } uniforms; + + void load(GrassRendererLoadData params, Renderer3d *renderer); + void update(f32 dtSeconds); + void render(Renderer3d *renderer); + void unload(); }; #endif diff --git a/themes/src/spring/spring_theme.cpp b/themes/src/spring/spring_theme.cpp index 8b09366..4b62795 100644 --- a/themes/src/spring/spring_theme.cpp +++ b/themes/src/spring/spring_theme.cpp @@ -1,6 +1,8 @@ #include "spring_theme.hpp" #include "../renderer_3d.h" +#include "../shader.h" #include "../shader_fetcher.hpp" +#include "../shapes_2d.h" #include <cstdio> #include <emscripten/fetch.h> @@ -49,7 +51,15 @@ SpringTheme::~SpringTheme() { unload(); } void SpringTheme::load(WebglContext *context) { state = SpringThemeState::Loading; renderer.context = context; - renderer.clearColor = Vector4(160, 231, 160, 255.f).toNormalizedColor(); + renderer.clearColor = Vector4(174, 216, 230, 255.f).toNormalizedColor(); + + renderer2d.load(context); + background = new RectangularGradient( + renderer2d, Vector4(174, 216, 230, 255).toNormalizedColor(), + Vector4(144, 238, 144, 255).toNormalizedColor(), renderer2d.get_width(), + renderer2d.get_height(), {0, 0}); + + grassRenderer.load({Vector2(0, -20), Vector2(96, 96), 3.f}, &renderer); fetch_shader({"themes/src/_shaders/renderer3d.vert", "themes/src/_shaders/renderer3d.frag"}, @@ -74,6 +84,9 @@ inline f32 rotationLerp(f32 start, f32 target, f32 t) { } void SpringTheme::update(f32 dtSeconds) { + if (state != SpringThemeState::Loading) { + grassRenderer.update(dtSeconds); + } switch (state) { case SpringThemeState::Loading: return; @@ -98,6 +111,15 @@ void SpringTheme::update(f32 dtSeconds) { yDir = -1; bunnyTarget = bunnyPosition + Vector3(randomFloatBetween(0, xDir * 25), 0, randomFloatBetween(0, yDir * 25)); + // Clamp bunnyTarget to within the grass circle (origin=(0,-20), radius=48) + const Vector3 grassCenter(0, 0, -20); + const f32 grassRadius = 48.f; + Vector3 toTarget = bunnyTarget - grassCenter; + toTarget.y = 0; + if (toTarget.length() > grassRadius) { + toTarget = toTarget.normalize() * grassRadius; + bunnyTarget = Vector3(grassCenter.x + toTarget.x, 0, grassCenter.z + toTarget.z); + } auto direction = (bunnyTarget - bunnyPosition); auto distance = direction.length(); direction = direction.normalize(); @@ -197,12 +219,29 @@ void SpringTheme::update(f32 dtSeconds) { void SpringTheme::render() { renderer.render(); + + // Draw the 2D gradient background without writing to the depth buffer so + // the 3D content rendered afterwards is unobstructed. + glDepthMask(GL_FALSE); + useShader(renderer2d.shader); + setShaderMat4(renderer2d.uniforms.projection, renderer2d.projection); + background->render(); + glDepthMask(GL_TRUE); + if (state != SpringThemeState::Loading) { + grassRenderer.render(&renderer); + // Restore the 3D renderer's shader after the grass shader took over + useShader(renderer.shader); + setShaderMat4(renderer.uniforms.projection, renderer.projection); + setShaderMat4(renderer.uniforms.view, renderer.view); bunnyMesh.render(&renderer); } } void SpringTheme::unload() { renderer.unload(); + renderer2d.unload(); + delete background; bunnyMesh.unload(); + grassRenderer.unload(); } diff --git a/themes/src/spring/spring_theme.hpp b/themes/src/spring/spring_theme.hpp index 6079958..4ee5684 100644 --- a/themes/src/spring/spring_theme.hpp +++ b/themes/src/spring/spring_theme.hpp @@ -2,44 +2,50 @@ #define SPRING_THEME_HPP #include "../mathlib.h" -#include "../types.h" +#include "../renderer_2d.h" #include "../renderer_3d.h" #include "../theme.h" +#include "../types.h" +#include "grass_renderer.hpp" +class RectangularGradient; enum class SpringThemeState { - Loading = 0, - LoadedShader, - LoadedBunny, - PreHop, - Hopping, - Idle + Loading = 0, + LoadedShader, + LoadedBunny, + PreHop, + Hopping, + Idle }; class SpringTheme : public Theme { public: - SpringTheme(WebglContext*); - ~SpringTheme(); - Renderer3d renderer; - SpringThemeState state; - f32 bunnySpeed = 5.f; - Vector3 bunnyPosition = Vector3(0, 0, 0); - Vector3 bunnyTarget = Vector3(0, 0, 0); - Vector3 hopIncrement = Vector3(0, 0, 0); - - f32 numHops = 0; - f32 hopCount = 0; - f32 bunnyHopAnimationTimer = 0.f; - f32 stateTimer = 0.f; - f32 bunnyRotation = 0.f; - f32 targetRotation = 0.f; - - Mesh3d bunnyMesh; - - void load(WebglContext*); - void update(f32 dtSeconds); - void render(); - void unload(); + SpringTheme(WebglContext *); + ~SpringTheme(); + Renderer3d renderer; + SpringThemeState state; + f32 bunnySpeed = 5.f; + Vector3 bunnyPosition = Vector3(0, 0, 0); + Vector3 bunnyTarget = Vector3(0, 0, 0); + Vector3 hopIncrement = Vector3(0, 0, 0); + + f32 numHops = 0; + f32 hopCount = 0; + f32 bunnyHopAnimationTimer = 0.f; + f32 stateTimer = 0.f; + f32 bunnyRotation = 0.f; + f32 targetRotation = 0.f; + + Mesh3d bunnyMesh; + GrassRenderer grassRenderer; + Renderer2d renderer2d; + RectangularGradient *background; + + void load(WebglContext *); + void update(f32 dtSeconds); + void render(); + void unload(); }; #endif |
