<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
     xmlns:content="http://purl.org/rss/1.0/modules/content/"
     xmlns:dc="http://purl.org/dc/elements/1.1/"
     xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Enno Games Blog</title>
    <link>https://ennogames.com/blog</link>
    <atom:link href="https://ennogames.com/rss.xml" rel="self" type="application/rss+xml" />
    <description>Unity, Blender, Maya and game engine notes by Daniel Phillips.</description>
    <language>en-us</language>
    <lastBuildDate>Tue, 04 Aug 2026 00:27:15 GMT</lastBuildDate>
    <item>
      <title>Blender Geometry Nodes - Texel Density Modifier</title>
      <link>https://ennogames.com/blog/blender-geometry-nodes-texel-density-modifier</link>
      <guid isPermaLink="true">https://ennogames.com/blog/blender-geometry-nodes-texel-density-modifier</guid>
      <pubDate>Fri, 10 Mar 2023 12:00:00 GMT</pubDate>
      <dc:creator>Daniel Phillips</dc:creator>
      <description>I wanted a texel density modifer for Blender so I made one with geometry nodes. It updates in real-time as you model and is applied when exporting.</description>
      <content:encoded><![CDATA[<p>I wanted a texel density modifer for <a href="https://www.blender.org/">Blender</a> so I made one with geometry nodes.  It updates in real-time as you model and is applied when exporting.</p>
<p><div class="text-align-center"><img src="https://ennogames.com/blog-images/blender-geometry-nodes-texel-density/texel_density.gif" alt="Texel Density Modifier"></div></p>
<p> </p>
<p> </p>
<p>Also check out my <a href="https://ennogames.com/blog/blender-geometry-nodes-auto-uv-unwrap-and-rotate-modifier">uv unwrap modifier</a></link>.  They make a great combo.</p>
<p><div class="text-align-center"><img src="https://ennogames.com/blog-images/blender-geometry-nodes-texel-density/unwrap_and_texel_density.gif" alt="Texel Density Modifier"></div></p>
<p> </p>
<p> </p>
<h1>How to Use</h1>
<p>Select a mesh and add a &quot;Geometry Nodes&quot; modifier.</p>
<p><img src="https://ennogames.com/blog-images/blender-geometry-nodes-texel-density/modifier.jpg" alt="Texel Density Settings"></p>
<p>Select &quot;GN_Texel_Density&quot;.  Set the texture size, the texel density and the UV map name.</p>
<p><img src="https://ennogames.com/blog-images/blender-geometry-nodes-texel-density/texel_density_settings.jpg" alt="Texel Density Settings"></p>
<p>If you check the &quot;apply modifiers&quot; checkbox when exporting to fbx then you can export with texel density without having to apply your modifiers.</p>
<p><img src="https://ennogames.com/blog-images/blender-geometry-nodes-texel-density/apply_modifiers.jpg" alt="Texel Density Settings"></p>
<p> </p>
<p> </p>
<h1>Asset Library</h1>
<p>If you use the asset library you can find it under the &quot;Enno/uv&quot; folder.</p>
<p><img src="https://ennogames.com/blog-images/blender-geometry-nodes-texel-density/asset_library.jpg" alt="Texel Density Settings"></p>
<p> </p>
<p> </p>
<h1>How to get it</h1>
<p>Grab it from <a href="https://dan335.gumroad.com/l/blender-geometry-nodes-texel-density-modifier">Gumroad</a>.  MIT license.</p>
<p> </p>
<p> </p>
<p>If you're looking for a texel density addon I recommend <a href="https://mrven.gumroad.com/l/CEIOR">Texel Density Checker</a>.  If you would like to know more about texel density check out this <a href="https://www.beyondextent.com/deep-dives/deepdive-texeldensity">article by Timothy Dries</a>.</p>
]]></content:encoded>
      <category>blender</category>
      <category>geometry-nodes</category>
    </item>
    <item>
      <title>Blender Geometry Nodes - Auto UV Unwrap and Rotate Modifier</title>
      <link>https://ennogames.com/blog/blender-geometry-nodes-auto-uv-unwrap-and-rotate-modifier</link>
      <guid isPermaLink="true">https://ennogames.com/blog/blender-geometry-nodes-auto-uv-unwrap-and-rotate-modifier</guid>
      <pubDate>Thu, 09 Mar 2023 12:00:00 GMT</pubDate>
      <dc:creator>Daniel Phillips</dc:creator>
      <description>Geometry nodes modifier to unwrap a mesh with the option to rotate the uv islands to a world space direction.</description>
      <content:encoded><![CDATA[<p>Geometry nodes modifier to unwrap a mesh with the option to rotate the uv islands to a world space direction.  Great for things like rocks with drips where the UVs need to be vertical.</p>
<p>This modifier combined with my <a href="https://ennogames.com/blog/blender-geometry-nodes-auto-uv-unwrap-and-rotate-modifier">uv unwrap modifier</a></link> makes procedural geometry exportable without applying the modifiers, creating UVs and setting texel density each time you export. Real-time on meshes under around 10k triangles.  Requires Blender version 3.5 beta or higher.</p>
<p><div class="text-align-center"><img src="https://ennogames.com/blog-images/blender-geometry-nodes-texel-density/unwrap_and_texel_density.gif" alt="Texel Density Modifier"></div></p>
<p> </p>
<p> </p>
<h1>How to Use</h1>
<p>Select a mesh and add a &quot;Geometry Nodes&quot; modifier.</p>
<p><img src="https://ennogames.com/blog-images/blender-geometry-nodes-texel-density/modifier.jpg" alt="Texel Density Settings"></p>
<p>Select &quot;GN_Unwrapper&quot;.</p>
<p><img src="https://ennogames.com/blog-images/blender-geometry-nodes-unwrapper/unwrapper_settings.jpg" alt="Texel Density Modifier"></p>
<h2>Seams</h2>
<p>There are four ways to define uv seams.  Sharp edges, edge angle and material border.  If you select more than one then they will be combined.</p>
<ul>
<li>
<p>Sharp Edges - Use sharp edges as uv seams.  If you check the &quot;clear sharp edges&quot; checkbox then the sharp edges that you define will be removed by the modifer so that they do not create sharp edges and are only used for uv seams.  There is currently no way to get seams in geometry nodes so uv seams cannot be used.  Sharp edges can be used as a work-around.</p>
</li>
<li>
<p>Edge Angle - Use edge angle for uv seams.  The edge angle is in degrees.  Unfortuantly this does not work as well as Blender's smart unwrap.  Smart unwrap does more than only use edge angle.</p>
</li>
<li>
<p>Material Border - Use the border of the defined material as a uv seam.</p>
</li>
<li>
<p>Attribute - Used an edge boolean attribute to define uv seams.  Geometry nodes do not have access to uv seams.  One way to get around this is to use a python script to copy the uv seams into an attribute.  A script to do this can be found at <a href="https://blender.stackexchange.com/a/269344">https://blender.stackexchange.com/a/269344</a></p>
</li>
</ul>
<p> </p>
<p> </p>
<h2>Other Settings</h2>
<ul>
<li>
<p>Limit to Material - Use this to limit which faces will be unwrapped.  Unwrapping will be limited to faces with the defined material.</p>
</li>
<li>
<p>UV Map Name - Set this to the name of the UV map attribute.  Default is &quot;UVMap&quot;.</p>
</li>
</ul>
<p> </p>
<p> </p>
<h2>Rotate UVs to face up in world space (or any other direction)</h2>
<p>If you want all of the uvs to be oriented in the same direction for example you are modeling walls and want drips on the walls to all be vertical then check the &quot;Rotate UVs to World Direction&quot; and set the &quot;UV Rotation World Direction&quot;.  Set to 0,0,1 for UVs to be vertical.</p>
<p><img src="https://ennogames.com/blog-images/blender-geometry-nodes-unwrapper/point_up.png" alt="Texel Density Modifier"></p>
<p> </p>
<p> </p>
<h2>Pack UVs</h2>
<p>Use this to pack UVs into 0 - 1 space.  Settings for this can be found in the <a href="https://docs.blender.org/manual/en/latest/modeling/geometry_nodes/uv/pack_uv_islands.html">Blender Docs</a></p>
<p> </p>
<p> </p>
<h1>Asset Library</h1>
<p>If you use the asset library you can find it under the &quot;Enno/uv&quot; folder.</p>
<p><img src="https://ennogames.com/blog-images/blender-geometry-nodes-unwrapper/asset_browser_unwrapper.jpg" alt="Texel Density Settings"></p>
<p> </p>
<p> </p>
<h1>How to get it</h1>
<p>Grab it from <a href="https://dan335.gumroad.com/l/blender-geometry-nodes-auto-uv-unwrap-and-rotate-modifier">Gumroad</a>.  MIT license.</p>
<p> </p>
<p> </p>
<p>Also check out my <a href="https://ennogames.com/blog/blender-geometry-nodes-texel-density-modifier">texel density modifier</a></link>.</p>
]]></content:encoded>
      <category>blender</category>
      <category>geometry-nodes</category>
    </item>
    <item>
      <title>Third Time is the Charm</title>
      <link>https://ennogames.com/blog/third-time-is-the-charm</link>
      <guid isPermaLink="true">https://ennogames.com/blog/third-time-is-the-charm</guid>
      <pubDate>Mon, 28 Nov 2022 12:00:00 GMT</pubDate>
      <dc:creator>Daniel Phillips</dc:creator>
      <description>I've been doing a lot of woodworking lately. I enjoy making physical things for a change and getting away from the computer.</description>
      <content:encoded><![CDATA[<p>I've been doing a lot of woodworking lately.  I enjoy making physical things for a change and getting away from the computer.  One noticable difference between working on a computer versus on physical objects is how much harder it is to fix mistakes or iterate on a design.  I've been finding that the first time I make something it is full of mistakes and design things that could be done better.  The second time I make something I can usually fix most of those but it's still not perfect.  The third time making something is when it finally comes out great.</p>
<p>I recently read <a href="https://amzn.to/3Ug5y8P">Build by Tony Fadell</a> after seeing him on the <a href="https://www.youtube.com/watch?v=4oDZyOf6CW4">Lex Fridman Podcast</a>.  In it there is a chapter titled &quot;Three Generations&quot; where he talks about how it usually takes three generations to get a product right.  He is responsible for building the iPod, iPhone and Nest Thermostat and uses those as examples.</p>
]]></content:encoded>
      <category>woodworking</category>
    </item>
    <item>
      <title>Should You Switch from Maya to Blender?</title>
      <link>https://ennogames.com/blog/should-you-switch-from-maya-to-blender</link>
      <guid isPermaLink="true">https://ennogames.com/blog/should-you-switch-from-maya-to-blender</guid>
      <pubDate>Tue, 15 Nov 2022 12:00:00 GMT</pubDate>
      <dc:creator>Daniel Phillips</dc:creator>
      <description>Yes. Don't even waste your time reading the rest of this blog post. Just do it.</description>
      <content:encoded><![CDATA[<p>Yes.  Don't even waste your time reading the rest of this blog post.  Just do it.</p>
<h2>Why?</h2>
<p>I first learned <a href="https://www.autodesk.com/products/maya/overview?term=1-YEAR&amp;tab=subscription">Maya</a> during college in 2000 and after 22 years using Maya I know it quite well.  The idea of throwing that built up knowlage away and switching to <a href="https://www.blender.org/">Blender</a> did not sound appealing to me for a long time.  For years co-workers told me how much they liked it (although some warned that it was far from perfect).  Finally, a few months ago I made the jump and am very glad that I did.</p>
<p>It's hard to put into words why Blender is better.  It is far from perfect.  Maybe it's similar to creating an image using oil paint versus using a paint program on a computer.  People create amazing images with both but what you can do using paint is vastly limited compared to using a computer.  Or maybe a typewriter versus a word processor.  Maya has all the tools to create games or movies and some are much better than what is currently in Blender but Blender has tools that breed creativity.</p>
<p>Oh yeah and Blender is also <a href="https://github.com/blender/blender">open-source and free</a>.</p>
]]></content:encoded>
      <category>maya</category>
      <category>blender</category>
      <category>3D</category>
      <category>modeling</category>
    </item>
    <item>
      <title>Why Rust and Bevy are awesome.</title>
      <link>https://ennogames.com/blog/why-rust-and-bevy-are-awesome</link>
      <guid isPermaLink="true">https://ennogames.com/blog/why-rust-and-bevy-are-awesome</guid>
      <pubDate>Sat, 05 Nov 2022 12:00:00 GMT</pubDate>
      <dc:creator>Daniel Phillips</dc:creator>
      <description>I recently stumbled upon Bevy, &quot;a refreshingly simple data-driven game engine built in Rust)&quot; and started making a simple game with it to check it out.</description>
      <content:encoded><![CDATA[<p>I recently stumbled upon <a href="https://bevyengine.org/">Bevy</a>, &quot;a refreshingly simple data-driven game engine built in <a href="https://en.wikipedia.org/wiki/Rust_(programming_language)">Rust</a>&quot; and started making a simple game with it to check it out.  Bevy is built with <a href="https://en.wikipedia.org/wiki/Rust_(programming_language)">Rust</a> and uses <a href="https://en.wikipedia.org/wiki/Entity_component_system">ECS</a> so it should be fast, probably much faster than even <a href="https://learn.unity.com/tutorial/entity-component-system">Unity's ECS</a>.  While I didn't finish I am totally sold on Bevy and Rust.  They are awesome and I can't wait to see Bevy mature and grow.</p>
<h2>What is Rust and why is it awesome?</h2>
<h2>People like Rust</h2>
<p>According to <a href="https://survey.stackoverflow.co/2022/#section-most-loved-dreaded-and-wanted-programming-scripting-and-markup-languages">Stack Overflow's developer survey</a> Rust has been the most loved programming language since 2016.  I made sure to include C# which Unity uses.  C and C++ were so far down I had to cut them off.</p>
<p><div class="text-align-center"><img src="https://ennogames.com/blog-images/rust-is-awesome-and-why-godot-and-bevy-are-going-to-win/most-loved-programming-languages-2021.jpg" alt="Most loved programming languages of 2021."></div></p>
<h2>Rust is <em>FAST</em></h2>
<p>Rust is about as fast as C and C++ and maybe twice as fast as C# in this test.</p>
<p><div class="text-align-center"><img src="https://ennogames.com/blog-images/rust-is-awesome-and-why-godot-and-bevy-are-going-to-win/Barplot_language_speeds_(Benchmarks_Game_Mandelbrot).svg.png" alt="Performance comparison of programming languages."></div></p>
<h2>Rust doesn't crash</h2>
<p>One of Rust's main benefits is being memory safe.  About 70% of the bugs in <a href="https://www.zdnet.com/article/microsoft-70-percent-of-all-security-bugs-are-memory-safety-issues/">Microsft products</a> and <a href="https://www.zdnet.com/article/chrome-70-of-all-security-bugs-are-memory-safety-issues/">Chrome</a> are because of memory errors.  Here's an example of one.</p>
<pre><code class="language-c">int main() 
{ 
   char str1[20]; 
 
   printf(&quot;Enter name: &quot;); 
   scanf(&quot;%s&quot;, str1); 
 
   return(0); 
} 
</code></pre>
<p>This C code will blow up if someone enters more than 20 characters.  There's no warning or error when you write it and it compiles fine.  With Rust however this would not only give you an error it would probably suggest how to fix it.</p>
<p>Another Rust feature is safe concurrency.  Here's an example in C that runs with no crash but the result will be... who knows.</p>
<pre><code class="language-c">int x = 0, y = 0; 
void thread1() {  
  x = 1; 
  if (0 == y) x = 2; 
} 
 
void thread2() { 
  y = 1; 
  if (0 == x) y = 2; 
} 
</code></pre>
<p>Rust fixes this problem because of the way it handles ownership and it's borrow checker.  Concurrency is much easier in Rust.</p>
<!-- 
Find sum of squares in an array.

```rust
fn sum_of_squares(input: &[i32]) -> i32 {
    input.iter()
         .map(|i| i * i)
         .sum()
}
```

The above code runs synchronously meaning it looks at each number in the array one at a time and adds them up.

Running this in parallel with the [rayon](https://docs.rs/rayon/latest/rayon/) package is easy.

```rust
use rayon::prelude::*;

fn sum_of_squares(input: &[i32]) -> i32 {
    input.par_iter()
         .map(|i| i * i)
         .sum()
}
``` -->
<h2>There's more</h2>
<p>Not only is it possible to write a Rust program that is guarenteed to not leak memory or crash but Rust also has great tools built around it.  For example it's errors messages not only tell you what's wrong but in a lot of cases teaches you how to fix it and can even fix it for you.</p>
<p><div class="text-align-center"><img src="https://ennogames.com/blog-images/rust-is-awesome-and-why-godot-and-bevy-are-going-to-win/clippy.png" alt="Rust Clippy suggestion."></div></p>
<p>Other features include a <a href="https://doc.rust-lang.org/cargo/">package manager</a> similar to <a href="https://sambleckley.com/writing/crates.html">Javascript but better</a>.  It can run on <a href="https://doc.rust-lang.org/nightly/rustc/platform-support.html">just about anything</a> even in browsers faster than Javascript by compiling to WebAssembly.</p>
<p>Some links to learn more about Rust.</p>
<ul>
<li><a href="https://stackoverflow.blog/2020/06/05/why-the-developers-who-use-rust-love-it-so-much">Why the developers who use Rust love it so much.</a></li>
<li><a href="https://tourofrust.com/">Tour of Rust</a></li>
<li><a href="https://doc.rust-lang.org/book/">The Rust Programming Language Book</a></li>
<li><a href="https://doc.rust-lang.org/stable/rust-by-example/">Rust by example</a></li>
<li><a href="https://cheats.rs/">Rust cheat sheet</a></li>
</ul>
<!-- What is Bevy and is it awesome?
===
Now that we know what Rust is what the heck is a Bevy?  

10 years from now.
===
 -->]]></content:encoded>
      <category>programming</category>
      <category>game-dev</category>
      <category>rust</category>
      <category>godot</category>
      <category>bevy</category>
    </item>
    <item>
      <title>Building a Way Too Complicated Mailbox</title>
      <link>https://ennogames.com/blog/building-a-way-too-complicated-mailbox</link>
      <guid isPermaLink="true">https://ennogames.com/blog/building-a-way-too-complicated-mailbox</guid>
      <pubDate>Sun, 03 Jul 2022 12:00:00 GMT</pubDate>
      <dc:creator>Daniel Phillips</dc:creator>
      <description>I moved into a new house recently and the existing mailbox was falling apart and needed to be replaced.</description>
      <content:encoded><![CDATA[<p>I moved into a new house recently and the existing mailbox was falling apart and needed to be replaced.  I've also been learning about wood working and wanted to try building something a little complicated and maybe a little different.  Here's the design I came up with.</p>
<p><div class="text-align-center"><img src="https://ennogames.com/blog-images/building-a-way-too-complicated-mailbox/mailbox-3d-03.jpg" alt="Mailbox Design 3D"></div></p>
<p>I wanted to do it with joinery instead of screws which meant some of the pieces had to be cut into some pretty odd shapes.</p>
<p><div class="text-align-center"><img src="https://ennogames.com/blog-images/building-a-way-too-complicated-mailbox/mailbox-3d-04.jpg" alt="Mailbox Piece"></div></p>
<p>My first attempt to make some of the cuts was with a japanese hand saw, some chisels and a mallet.  Videos on youtube make it look so easy but trying to get straight cuts was very hard.  After a lot of research I bought a <a href="https://amzn.to/3yFcftB">Dewalt Table Saw</a>.</p>
<p><div class="text-align-center"><a href="https://amzn.to/3yFcftB"><img src="https://ennogames.com/blog-images/building-a-way-too-complicated-mailbox/dewalt_table_saw.jpg" alt="Dewalt Table Saw"></a></div></p>
<p>With my new table saw I was able to straight complicated cuts pretty easily.  Here are most of the pieces.</p>
<p><div class="text-align-center"><img src="https://ennogames.com/blog-images/building-a-way-too-complicated-mailbox/PXL_20220612_171508565.jpg" alt="Mailbox Pieces"></div></p>
<p>Test fitting them together.  I think it's going to work!</p>
<p><div class="text-align-center"><img src="https://ennogames.com/blog-images/building-a-way-too-complicated-mailbox/PXL_20220612_171829249.jpg" alt="Mailbox Test Fitting"></div></p>
<p>Shots of gluing some pieces together.  The holes in the pieces are for dowels which I used instead of screws.</p>
<p><div class="text-align-center"><img src="https://ennogames.com/blog-images/building-a-way-too-complicated-mailbox/PXL_20220624_221856536.jpg" alt="Mailbox Gluing"></div></p>
<p><div class="text-align-center"><img src="https://ennogames.com/blog-images/building-a-way-too-complicated-mailbox/PXL_20220624_222800470.jpg" alt="Mailbox Gluing"></div></p>
<p><div class="text-align-center"><img src="https://ennogames.com/blog-images/building-a-way-too-complicated-mailbox/PXL_20220624_223028337.jpg" alt="Mailbox Gluing"></div></p>
<p><div class="text-align-center"><img src="https://ennogames.com/blog-images/building-a-way-too-complicated-mailbox/PXL_20220624_232702314.jpg" alt="Mailbox Gluing"></div></p>
<p><div class="text-align-center"><img src="https://ennogames.com/blog-images/building-a-way-too-complicated-mailbox/PXL_20220625_183629699.jpg" alt="Mailbox Gluing"></div></p>
<p><div class="text-align-center"><img src="https://ennogames.com/blog-images/building-a-way-too-complicated-mailbox/PXL_20220626_160336198.jpg" alt="Mailbox Gluing"></div></p>
<p>Next step was lots of sanding.</p>
<p><div class="text-align-center"><img src="https://ennogames.com/blog-images/building-a-way-too-complicated-mailbox/PXL_20220627_223107694.jpg" alt="Mailbox Sanding"></div></p>
<p>Here's the finished post.</p>
<p><div class="text-align-center"><img src="https://ennogames.com/blog-images/building-a-way-too-complicated-mailbox/PXL_20220627_224921046.jpg" alt="Mailbox Finished"></div></p>
<p><div class="text-align-center"><img src="https://ennogames.com/blog-images/building-a-way-too-complicated-mailbox/PXL_20220627_224858049.jpg" alt="Mailbox Finished"></div></p>
<p>I didn't have a piece of wood wide enough for the mailbox base so I glued two together with dowels in between.</p>
<p><div class="text-align-center"><img src="https://ennogames.com/blog-images/building-a-way-too-complicated-mailbox/PXL_20220628_224140702.jpg" alt="Mailbox Gluing"></div></p>
<p>Next I sprayed it all down with some clear sealant, attached the box and numbers.</p>
<p><div class="text-align-center"><img src="https://ennogames.com/blog-images/building-a-way-too-complicated-mailbox/PXL_20220703_165940100.jpg" alt="Mailbox"></div></p>
<p><div class="text-align-center"><img src="https://ennogames.com/blog-images/building-a-way-too-complicated-mailbox/PXL_20220703_171229625.jpg" alt="Mailbox"></div></p>
<p><div class="text-align-center"><img src="https://ennogames.com/blog-images/building-a-way-too-complicated-mailbox/PXL_20220703_171237070.jpg" alt="Mailbox"></div></p>
<p>Done!</p>
<p><div class="text-align-center"><img src="https://ennogames.com/blog-images/building-a-way-too-complicated-mailbox/PXL_20220703_173521909.jpg" alt="Mailbox"></div></p>
<p><div class="text-align-center"><img src="https://ennogames.com/blog-images/building-a-way-too-complicated-mailbox/PXL_20220703_172559071.jpg" alt="Mailbox"></div></p>
<p><div class="text-align-center"><img src="https://ennogames.com/blog-images/building-a-way-too-complicated-mailbox/PXL_20220703_172613767.jpg" alt="Mailbox"></div></p>
<p>Until I finally saw it in the ground I was pretty worried about how it was going to look but I think it turned out great.  If I did it over again I would change the design a bit.  Maybe for the next house.  Time to start the next project!</p>
]]></content:encoded>
      <category>woodworking</category>
      <category>home-improvement</category>
    </item>
    <item>
      <title>Creating Clouds for Sky Fleet</title>
      <link>https://ennogames.com/blog/creating-clouds-for-sky-fleet</link>
      <guid isPermaLink="true">https://ennogames.com/blog/creating-clouds-for-sky-fleet</guid>
      <pubDate>Thu, 30 Jun 2022 12:00:00 GMT</pubDate>
      <dc:creator>Daniel Phillips</dc:creator>
      <description>Figuring out the clouds for Sky Fleet was a journey but I'm pretty happy with the final solution.</description>
      <content:encoded><![CDATA[<p><div class="text-align-center"><img src="https://ennogames.com/blog-images/skyfleet-clouds/2021-07-13_14-14-59_4.gif" alt=""></div></p>
<p>Figuring out the clouds for <a href="https://store.steampowered.com/app/1347540/Sky_Fleet/">Sky Fleet</a> was a journey but I'm pretty happy with the final solution.  Here is how it works and the many failures along the way.</p>
<p>My first idea was to use simple meshes for the clouds.  I believe I booleaned together some spheres then cleaned them up to get these shapes.</p>
<p><div class="text-align-center"><img src="https://ennogames.com/blog-images/skyfleet-clouds/2020-05-2_21-08-23.gif" alt=""></div></p>
<p>They thought they looked pretty cool but knew I needed a better solution.  I stumbled upon a video by <a href="https://www.youtube.com/c/SebastianLague">Sebastian Lague</a> on youtube about clouds that gave me some ideas.  Sebastian's videos are amazing if you've never seen them.</p>
<p><div class="embed-responsive embed-responsive-16by9"><iframe class="embed-responsive-item youtube-player" type="text/html" width="640" height="390" src="https://www.youtube.com/embed/4QOcCGI6xOU" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe></div></p>
<p>Watching this video led me down a rabbit hole of cloud rendering shaders.  My attempt didn't turn out that great and I finally decided that I wanted a styalized look for the clouds.</p>
<p><div class="text-align-center"><img src="https://ennogames.com/blog-images/skyfleet-clouds/2020-06-02_19-24-43.gif" alt=""></div></p>
<p>Instead of focusing on the rendering I decided to try to come up with better shapes for the clouds.  In Sebastian's video he talks about using worley noise.  I combined worley and simplex noise to come up with the shapes below.</p>
<p><div class="text-align-center"><img src="https://ennogames.com/blog-images/skyfleet-clouds/cloudNoise.png" alt=""></div></p>
<p>This gave me much better shapes for the clouds but they looked way too solid I thought.</p>
<p><div class="text-align-center"><img src="https://ennogames.com/blog-images/skyfleet-clouds/2020-07-05_01-43-40.gif" alt=""></div></p>
<p>The first thing I tried was duplicating the meshes a couple times and expanding them.  The inside mesh is opaque and the outer meshes are transparent.</p>
<p><div class="text-align-center"><img src="https://ennogames.com/blog-images/skyfleet-clouds/2020-12-21_13-26-33.gif" alt=""></div></p>
<p><div class="text-align-center"><img src="https://ennogames.com/blog-images/skyfleet-clouds/2021-01-02_14-17-46.gif" alt=""></div></p>
<p>After watching more youtube videos I tried this technique and was pretty happy with the result.</p>
<p><div class="embed-responsive embed-responsive-16by9"><iframe class="embed-responsive-item youtube-player" type="text/html" width="640" height="390" src="https://www.youtube.com/embed/iASMFba7GeI" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe></div></p>
<p><div class="text-align-center"><img src="https://ennogames.com/blog-images/skyfleet-clouds/2021-04-07_19-49-27.gif" alt=""></div></p>
<p><div class="text-align-center"><img src="https://ennogames.com/blog-images/skyfleet-clouds/2021-04-07_19-53-07.gif" alt=""></div></p>
<p>Looks pretty good I think but a little too crisp.  After some research I found <a href="https://docs.unity3d.com/Manual/SL-AlphaToMask.html">alpha-to-coverage</a> which can be used to apply anti-aliasing to alpha test shaders.  Here's how the final clouds look in the game.</p>
<p><div class="text-align-center"><img src="https://ennogames.com/blog-images/skyfleet-clouds/2021-07-13_14-14-59_4.gif" alt=""></div></p>
<p>Since finishing Sky Fleet I got curious about clouds again and made a little cloudy planet shader.</p>
<p><div class="text-align-center"><img src="https://ennogames.com/blog-images/skyfleet-clouds/2021-12-04_12-35-08.gif" alt=""></div></p>
<p>Here's another way to do fake clouds by Oakleaf</p>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Smacked together some quad-based fake <a href="https://twitter.com/hashtag/volumetric?src=hash&amp;ref_src=twsrc%5Etfw">#volumetric</a> clouds in a couple of days. Ended up doing a simple depth sort to keep the quads in correct order, at 500 quads a dumb depth sort + vertex buffer reconstruction takes about 2 ms. Buffers are nice. <a href="https://twitter.com/hashtag/GameMaker?src=hash&amp;ref_src=twsrc%5Etfw">#GameMaker</a> <a href="https://twitter.com/hashtag/gamedev?src=hash&amp;ref_src=twsrc%5Etfw">#gamedev</a> <a href="https://t.co/gbaRILUs6N">pic.twitter.com/gbaRILUs6N</a></p>&mdash; Oakleaf (@Oakleaff_) <a href="https://twitter.com/Oakleaff_/status/1561797039805157378?ref_src=twsrc%5Etfw">August 22, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
]]></content:encoded>
      <category>sky-fleet</category>
      <category>programming</category>
      <category>clouds</category>
    </item>
    <item>
      <title>Select Faces on Object by Material in Maya</title>
      <link>https://ennogames.com/blog/select-faces-on-object-by-material-in-maya</link>
      <guid isPermaLink="true">https://ennogames.com/blog/select-faces-on-object-by-material-in-maya</guid>
      <pubDate>Tue, 28 Jun 2022 12:00:00 GMT</pubDate>
      <dc:creator>Daniel Phillips</dc:creator>
      <description>Selecting faces on an object in Maya by material is easy. Hold down right mouse button on the material then click &quot;Select Objects with Material.&quot; This…</description>
      <content:encoded><![CDATA[<p>Selecting faces on an object in Maya by material is easy.  Hold down right mouse button on the material then click &quot;Select Objects with Material.&quot;  This selects faces on all objects in the scene however.  Here's a script to select faces on a single object by it's material.  Select object then shift select material in hypershade then run script.</p>
<pre><code class="language-mel">string $sel[] = `ls -sl`;
if (size($sel) &lt; 2 || size($sel) &gt; 2) {
    error(&quot;Select an object, shift-select one material in Hypershade, then run this script&quot;);
}
string $object = $sel[0];
string $mat = $sel[1];
string $objects[];
hyperShade -o $mat;

select -tgl $objects;
$objects=`ls -sl`;

select -cl;

for($i=0; $i&lt;size($objects); $i++)
{
    string $temp = `match &quot;^.*\\.&quot; $objects[$i]`;
    if (size($temp) &gt; 0) {
        string $check = `substring $temp 1 (size($temp) - 1)`;
        if($check == $object)
        select -tgl $objects[$i];
    }
}
</code></pre>
]]></content:encoded>
      <category>maya</category>
      <category>mel</category>
      <category>programming</category>
    </item>
    <item>
      <title>Give each vert a random vertex color script in Maya</title>
      <link>https://ennogames.com/blog/give-each-vert-a-random-vertex-color-script-in-maya</link>
      <guid isPermaLink="true">https://ennogames.com/blog/give-each-vert-a-random-vertex-color-script-in-maya</guid>
      <pubDate>Sat, 28 May 2022 12:00:00 GMT</pubDate>
      <dc:creator>Daniel Phillips</dc:creator>
      <description>Here's a mel script to set the green channel to a random value. Select a mesh or meshes then run it.</description>
      <content:encoded><![CDATA[<p>Here's a mel script to set the green channel to a random value.  Select a mesh or meshes then run it.  Change the numbers after &quot;rand&quot; to constrain the values.  -g can be -rgb with 3 numbers after it to set all rgb channels.</p>
<pre><code class="language-mel">string $listOfObjects[] = `ls -sl`;

int $i;
for( $i = 0; $i &lt; size($listOfObjects); $i++ )
{
	int $eval[] = `polyEvaluate -v`;
	int $numVertices = $eval[0];
	int $v;
	for( $v = 0; $v &lt; $numVertices; $v++ )
	{
	    float $rand = `rand 0.0 1.0`;
		polyColorPerVertex -g $rand ($listOfObjects[$i] + &quot;.vtx[&quot; + $v + &quot;]&quot;);
	}
}
</code></pre>
]]></content:encoded>
      <category>maya</category>
      <category>mel</category>
      <category>programming</category>
    </item>
    <item>
      <title>Game Programming Links</title>
      <link>https://ennogames.com/blog/game-programming-links</link>
      <guid isPermaLink="true">https://ennogames.com/blog/game-programming-links</guid>
      <pubDate>Wed, 16 Mar 2022 12:00:00 GMT</pubDate>
      <dc:creator>Daniel Phillips</dc:creator>
      <description>Interesting links related to game programming. How to RTS Terrain Topology Algorithms Shaders Shader Tutorials by Inigo Quilez Unity Shaders Cheat Sheet…</description>
      <content:encoded><![CDATA[<p>Interesting links related to game programming.</p>
<ul>
<li><a href="https://howtorts.github.io/">How to RTS</a></li>
<li><a href="https://github.com/Scrawk/Terrain-Topology-Algorithms">Terrain Topology Algorithms</a></li>
</ul>
<p>Shaders</p>
<ul>
<li><a href="https://iquilezles.org/articles/">Shader Tutorials by Inigo Quilez</a></li>
<li><a href="https://gist.github.com/Split82/d1651403ffb05e912d9c3786f11d6a44">Unity Shaders Cheat Sheet</a></li>
<li><a href="https://youtu.be/x4ufs1OzPIw">Ned Makes Games' Foilage Shader</a></li>
<li><a href="https://teodutra.com/unity/shaders/urp/graphics/2020/05/18/From-Built-in-to-URP/">From Build-In to URP - Unity</a></li>
<li><a href="https://github.com/phi-lira/UniversalShaderExamples">URP Shader Examples by Filipe Lira</a></li>
<li><a href="https://gist.github.com/phi-lira/225cd7c5e8545be602dca4eb5ed111ba">URP Shader Template by Filipe Lira</a></li>
<li><a href="https://github.com/Cyanilux/URP_ShaderCodeTemplates">URP Shader Templates by Cyan</a></li>
<li><a href="https://cyangamedev.wordpress.com/2020/10/06/watercolour-shader-experiments/">Watercolor Shader for URP by Cyan</a></li>
<li><a href="https://valeriomarty.medium.com/raymarched-volumetric-lighting-in-unity-urp-e7bc84d31604">Raymarched Volumetric Lighting in URP</a></li>
<li><a href="https://kelvinvanhoorn.com/2022/03/17/skybox-tutorial-part-1/">Skybox Shader by Kelvin van Hoorn</a></li>
<li><a href="http://halisavakis.com/archive/">Harry Alisavakis' Shaders</a></li>
<li><a href="https://github.com/Auburn/FastNoiseLite/blob/master/HLSL/FastNoiseLite.hlsl">Fast Noise Lite</a></li>
<li><a href="https://thebookofshaders.com/">The Book of Shaders</a></li>
</ul>
<p>Clouds</p>
<ul>
<li><a href="https://github.com/clayjohn/godot-volumetric-cloud-demo">godot-volumetric-cloud-demo</a></li>
</ul>
<p>Trees</p>
<ul>
<li><a href="https://www.patreon.com/posts/freebie-quick-68635397">Quick Procedural Bush</a></li>
</ul>
<p>Grass</p>
<ul>
<li><a href="https://twitter.com/HarryAlisavakis/status/1551496728238592001">Grass Bending</a></li>
</ul>
<p>Camera Shake</p>
<ul>
<li><a href="https://www.youtube.com/watch?v=tu-Qe66AvtY">Math for Game Programmers: Juicing Your Cameras With Math</a></li>
</ul>
<p>Math</p>
<ul>
<li><a href="https://easings.net/">Easings.net</a></li>
</ul>
]]></content:encoded>
      <category>game dev</category>
      <category>programming</category>
      <category>unity</category>
      <category>shaders</category>
    </item>
    <item>
      <title>Fade Opaque Object using Dithering</title>
      <link>https://ennogames.com/blog/fade-opaque-object-using-dithering</link>
      <guid isPermaLink="true">https://ennogames.com/blog/fade-opaque-object-using-dithering</guid>
      <pubDate>Fri, 11 Mar 2022 12:00:00 GMT</pubDate>
      <dc:creator>Daniel Phillips</dc:creator>
      <description>Dithering is an option to make an object transparent or fade in/out without using transparency. Here's a function to dither without using a texture.</description>
      <content:encoded><![CDATA[<p>Dithering is an option to make an object transparent or fade in/out without using transparency.</p>
<p>Here's  a function to dither without using a texture.</p>
<pre><code class="language-hlsl">float dithering(float2 pos) {
    pos *= _ScreenParams.xy;

    float DITHER_THRESHOLDS[16] =
    {
        1.0 / 17.0,  9.0 / 17.0,  3.0 / 17.0, 11.0 / 17.0,
        13.0 / 17.0,  5.0 / 17.0, 15.0 / 17.0,  7.0 / 17.0,
        4.0 / 17.0, 12.0 / 17.0,  2.0 / 17.0, 10.0 / 17.0,
        16.0 / 17.0,  8.0 / 17.0, 14.0 / 17.0,  6.0 / 17.0
    };

    int index = (int(pos.x) % 4) * 4 + int(pos.y) % 4;
    return DITHER_THRESHOLDS[index];
}
</code></pre>
<p>Here's a complete unity unlit shader that fades out over a distance. (untested)</p>
<pre><code class="language-hlsl">Shader &quot;Unlit/NewUnlitShader&quot;
{
    Properties
    {
        _MainTex (&quot;Texture&quot;, 2D) = &quot;white&quot; {}
    }
    SubShader
    {
        Tags { &quot;RenderType&quot;=&quot;Opaque&quot; }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog

            #include &quot;UnityCG.cginc&quot;

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;

                // need these
                float4 screenPosition : TEXCOORD1;
                float ditherAmount : TEXCOORD2;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            float maxDistance;
            float fadeDistance;

            float dithering(float2 pos) {
                pos *= _ScreenParams.xy;

                float DITHER_THRESHOLDS[16] =
                {
                    1.0 / 17.0,  9.0 / 17.0,  3.0 / 17.0, 11.0 / 17.0,
                    13.0 / 17.0,  5.0 / 17.0, 15.0 / 17.0,  7.0 / 17.0,
                    4.0 / 17.0, 12.0 / 17.0,  2.0 / 17.0, 10.0 / 17.0,
                    16.0 / 17.0,  8.0 / 17.0, 14.0 / 17.0,  6.0 / 17.0
                };

                int index = (int(pos.x) % 4) * 4 + int(pos.y) % 4;
                return DITHER_THRESHOLDS[index];
            }

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                UNITY_TRANSFER_FOG(o,o.vertex);

                // pass to fragment shader
                o.screenPosition = ComputeScreenPos(o.vertex);

                float dstToCamera = distance(_WorldSpaceCameraPos, v.vertex);
                float fadeStart = maxDistance - fadeDistance;
                o.ditherAmount = clamp(((dstToCamera - fadeStart) / maxDistance) / (fadeStart / maxDistance), 0, 1);

                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
                fixed4 col = tex2D(_MainTex, i.uv);
                // apply fog
                UNITY_APPLY_FOG(i.fogCoord, col);

                // fade by dithering
                float2 screenUV = IN.screenPosition.xy / IN.screenPosition.w;
                clip(dithering(screenUV - i.ditherAmount);

                return col;
            }
            ENDCG
        }
    }
}
</code></pre>
]]></content:encoded>
      <category>shaders</category>
      <category>unity</category>
      <category>game-dev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Random Numbers inside Unity Jobs</title>
      <link>https://ennogames.com/blog/random-numbers-inside-unity-jobs</link>
      <guid isPermaLink="true">https://ennogames.com/blog/random-numbers-inside-unity-jobs</guid>
      <pubDate>Thu, 24 Feb 2022 12:00:00 GMT</pubDate>
      <dc:creator>Daniel Phillips</dc:creator>
      <description>Unity's job system is fast but because it's multithreaded using random numbers inside a job is tricky. Here's one way to do it.</description>
      <content:encoded><![CDATA[<p>Unity's job system is fast but because it's multithreaded using random numbers inside a job is tricky.  Here's one way to do it.</p>
<pre><code class="language-csharp">using UnityEngine;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;


public class Obj {
    NativeArray&lt;Unity.Mathematics.Random&gt; randomNumberGenerators;

    void Awake() {
        randomNumberGenerators = new NativeArray&lt;Unity.Mathematics.Random&gt;(Unity.Jobs.LowLevel.Unsafe.JobsUtility.MaxJobThreadCount, Allocator.Persistent);

        for (int i = 0; i &lt; randomNumberGenerators.Length; i++) {
            randomNumberGenerators[i] = new Unity.Mathematics.Random((uint)UnityEngine.Random.Range(1, int.MaxValue));
        }
    }

    public void RunJob() {
        NativeArray&lt;float3&gt; positions = new NativeArray&lt;float3&gt;(1000, Allocator.TempJob);

        ObjJob job = new ObjJob {
            randomNumberGenerators = randomNumberGenerators,
            positions = positions
        };

        job.Schedule(positions.Length, 32).Complete();

        // do something with random positions

        positions.Dispose();
    }
}


[BurstCompile]
struct ObjJob : IJobParallelFor
{
    [Unity.Collections.LowLevel.Unsafe.NativeDisableContainerSafetyRestriction] public NativeArray&lt;Unity.Mathematics.Random&gt; randomNumberGenerators;
	[Unity.Collections.LowLevel.Unsafe.NativeSetThreadIndex] int threadId;
    [WriteOnly] public NativeArray&lt;float3&gt; positions;

    public void Execute(int i) {
        Unity.Mathematics.Random random = randomNumberGenerators[threadId];

        positions[i] = new float3(random.NextFloat(), random.NextFloat(), random.NextFloat());

        randomNumberGenerators[threadId] = random;
    }
}
</code></pre>
]]></content:encoded>
      <category>game-dev</category>
      <category>programming</category>
      <category>unity</category>
    </item>
    <item>
      <title>Frustum Culling with Unity Jobs</title>
      <link>https://ennogames.com/blog/frustum-culling-with-unity-jobs</link>
      <guid isPermaLink="true">https://ennogames.com/blog/frustum-culling-with-unity-jobs</guid>
      <pubDate>Fri, 11 Feb 2022 12:00:00 GMT</pubDate>
      <dc:creator>Daniel Phillips</dc:creator>
      <description>Here's a fast way to find out which objects are visible to the camera. Use Unity's job system to test if each object's bounding box is inside the camera's…</description>
      <content:encoded><![CDATA[<p>Here's a fast way to find out which objects are visible to the camera.  Use Unity's job system to test if each object's bounding box is inside the camera's frustum planes.</p>
<p>First here's the code for the job.</p>
<pre><code class="language-csharp">using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Burst;

[BurstCompile]
struct CullingJob : IJobParallelForFilter
{
    [ReadOnly] public NativeArray&lt;float4&gt; frustumPlanes;
    [ReadOnly] public NativeArray&lt;float3&gt; positions;
    [ReadOnly] public NativeArray&lt;float3&gt; extents;


    public bool Execute(int i) {
        return FrustumContainsBox(positions[i] - extents[i], positions[i] + extents[i]);
    }


    float DistanceToPlane( float4 plane, float3 position )
    {
        return math.dot(plane.xyz, position) + plane.w;
    }

    bool FrustumContainsBox(float3 bboxMin, float3 bboxMax) {
        float3 pos;

        for (int i = 0; i &lt; 6; i++) {
            pos.x = frustumPlanes[i].x &gt; 0 ? bboxMax.x : bboxMin.x;
            pos.y = frustumPlanes[i].y &gt; 0 ? bboxMax.y : bboxMin.y;
            pos.z = frustumPlanes[i].z &gt; 0 ? bboxMax.z : bboxMin.z;

            if (DistanceToPlane(frustumPlanes[i], pos) &lt; 0) {
                return false;
            }
        }

        return true;
    }
}
</code></pre>
<p>Here is the code to setup and run the job.</p>
<pre><code class="language-csharp">using UnityEngine;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;

public class OjbectCuller : MonoBehaviour {
    Plane[] frustumPlanes;
    NativeArray&lt;float4&gt; frustumPlanesNative;
    public Transform[] objects;
    public Camera playerCamera;


    void Awake() {
        frustumPlanesNative = new NativeArray&lt;float4&gt;(6, Allocator.Persistent);
    }


    void Update() {
        // put object bounds into native arrays
        NativeArray&lt;float3&gt; objectPositions = new NativeArray&lt;float3&gt;(objects.Length, Allocator.TempJob);
        NativeArray&lt;float3&gt; objectExtents = new NativeArray&lt;float3&gt;(objects.Length, Allocator.TempJob);

        for (int i = 0; i &lt; objects.Length; i++) {
            objectPositions[i] = objects[i].bounds.center;
            objectExtents[i] = objects[i].bounds.extents;
        }

        // get camera frustum planes
        frustumPlanes = GeometryUtility.CalculateFrustumPlanes(playerCamera);

        // put planes into native array for job
        for (int i = 0; i &lt; 6; i++) {
            frustumPlanesNative[i] = new float4(frustumPlanes[i].normal.x, frustumPlanes[i].normal.y, frustumPlanes[i].normal.z, frustumPlanes[i].distance);
        }

        // this will hold indexes of visible objects when job is complete
        NativeList&lt;int&gt; visibleIndexes = new NativeList&lt;int&gt;(Allocator.TempJob);

        FoilageCellCulling filterJob = new FoilageCellCulling()
        {        
            frustumPlanes = frustumPlanesNative,
            positions = objectPositions,
            extents = objectExtents
        };

        filterJob.ScheduleAppend(visibleIndexes, numObjects, 32).Complete();

        // do something with visible objects
        for (int i = 0; i &lt; visibleIndexes.Length; i++) {
            // DrawObject(objectPositions[visibleIndexes[i]], objectExtents[visibleIndexes[i]]);
        }

        objectPositions.Dispose();
        objectExtents.Dispose();
    }


    void OnDestroy() {
        frustumPlanesNative.Dispose();
    }
}
</code></pre>
]]></content:encoded>
      <category>game-dev</category>
      <category>programming</category>
      <category>unity</category>
    </item>
    <item>
      <title>3D and 2D Coordinates to 1D Indexes</title>
      <link>https://ennogames.com/blog/3d-and-2d-coordinates-to-1d-indexes</link>
      <guid isPermaLink="true">https://ennogames.com/blog/3d-and-2d-coordinates-to-1d-indexes</guid>
      <pubDate>Thu, 03 Feb 2022 12:00:00 GMT</pubDate>
      <dc:creator>Daniel Phillips</dc:creator>
      <description>Going from coordinates to indexes and back can be very useful. For example textures store pixel info as a 1d array so if you want to sample a pixel at x…</description>
      <content:encoded><![CDATA[<p>Going from coordinates to indexes and back can be very useful.  For example textures store pixel info as a 1d array so if you want to sample a pixel at x and y you need to convert your coordinates to the pixel index.</p>
<p>Here's how to turn 3d and 2d coordinates into 1d indexes.</p>
<pre><code class="language-csharp">int ThreeToOne (int x, int y, int z, int xSize, int ySize, int zSize) {
    return x + y * xSize + z * xSize * ySize;
}
</code></pre>
<pre><code class="language-csharp">int TwoToOne (int x, int y, int xSize, int ySize) {
    return x + y * xSize;
}
</code></pre>
<p>Here's how to turn the 1d indexes back into coordinates.</p>
<pre><code class="language-csharp">int3 OneToThree (int index, int xSize, ySize, zSize) {
    int3 result = new int3(0, 0, 0);
    result.x = index % xSize;
    result.y = (index / xSize) % ySize;
    result.z = index / (xSize * ySize);
    return result;
}
</code></pre>
<pre><code class="language-csharp">int2 OneToTwo (int index, int xSize, ySize) {
    int2 result;
    result.x = index % xSize;
    result.y = index / xSize;
    return result;
}
</code></pre>
<p>One example where this is useful is setting pixels in a Unity Texture2D.</p>
<pre><code class="language-csharp">void FillTextureWithBlack (Texture2D texture) {
    Color[] colors = new Color[texture.width * texture.height];

    for (int x = 0; x &lt; texture.width; x++) {
        for (int y = 0; y &lt; texture.height; y++) {
            colors[x + y * texture.width] = new Color(0f, 0f, 0f);
        }
    }

    texture.SetPixels(colors);
    texture.Apply();
}
</code></pre>
<p>Here's another example where going from indexes to coordinates is useful.  Of course this could be made faster with texture.GetPixels()</p>
<pre><code class="language-csharp">Color GetAverageColorOfTexture (Texture2D texture) {
    Vector3 averageColor = new Vector3(0f, 0f, 0f);

    for (int i = 0; i &lt; texture.width * texture.height; i++) {
        int x = i % texture.width;
        int y = i / texture.width;

        Color color = texture.GetPixel(x, y);
        averageColor += new Vector3(color.r, color.g, color.b);
    }

    averageColor /= (float)(texture.width * texture.height);
    return new Color(averageColor.x, averageColor.y, averageColor.z, 1f);
}
</code></pre>
]]></content:encoded>
      <category>programming</category>
      <category>game-dev</category>
      <category>unity</category>
    </item>
    <item>
      <title>Bilinear vs Bicubic Interpolation and How to Get Height of Terrain in Unity from a Heightmap</title>
      <link>https://ennogames.com/blog/bilinear-vs-bicubic-interpolation-and-how-to-get-height-of-terrain-in-unity-from-a-heightmap</link>
      <guid isPermaLink="true">https://ennogames.com/blog/bilinear-vs-bicubic-interpolation-and-how-to-get-height-of-terrain-in-unity-from-a-heightmap</guid>
      <pubDate>Thu, 03 Feb 2022 12:00:00 GMT</pubDate>
      <dc:creator>Daniel Phillips</dc:creator>
      <description>Here's how to sample between pixels of a texture using bilinear interpolation and bicubic interpolation.</description>
      <content:encoded><![CDATA[<p>Here's how to sample between pixels of a texture using <a href="https://en.wikipedia.org/wiki/Bilinear_interpolation">bilinear interpolation</a> and <a href="https://en.wikipedia.org/wiki/Bicubic_interpolation">bicubic interpolation</a>.  These can be used for scaling images or sampling the value of an image in-between pixels.</p>
<p>Bilinear averages the colors of surrounding pixels producing medium quality results.  Bicubic is slower but more precise and produces smoother gradations.</p>
<p>Bicubic example.</p>
<pre><code class="language-csharp">using UnityEngine;

public class BicubicInterpolator {
    public static float BicubicInterpolated (float x, float y, Texture2D heightmap) {
        int row = Mathf.FloorToInt(x);
        int col = Mathf.FloorToInt(y);

        float remainderX = x - row;
        float remainderY = y - col;

        float[] p0 = new float[4];
        float[] p1 = new float[4];
        float[] p2 = new float[4];
        float[] p3 = new float[4];

        p0[0] = heightmap.GetPixel(row - 1, col - 1).r;
        p0[1] = heightmap.GetPixel(row - 1, col).r;
        p0[2] = heightmap.GetPixel(row - 1, col + 1).r;
        p0[3] = heightmap.GetPixel(row - 1, col + 2).r;

        p1[0] = heightmap.GetPixel(row, col - 1).r;
        p1[1] = heightmap.GetPixel(row, col).r;
        p1[2] = heightmap.GetPixel(row, col + 1).r;
        p1[3] = heightmap.GetPixel(row, col + 2).r;

        p2[0] = heightmap.GetPixel(row + 1, col - 1).r;
        p2[1] = heightmap.GetPixel(row + 1, col).r;
        p2[2] = heightmap.GetPixel(row + 1, col + 1).r;
        p2[3] = heightmap.GetPixel(row + 1, col + 2).r;

        p3[0] = heightmap.GetPixel(row + 2, col - 1).r;
        p3[1] = heightmap.GetPixel(row + 2, col).r;
        p3[2] = heightmap.GetPixel(row + 2, col + 1).r;
        p3[3] = heightmap.GetPixel(row + 2, col + 2).r;

        return BiCubicValue(p0, p1, p2, p3, remainderX, remainderY);
    }

    static float BiCubicValue(float[] p0, float[] p1, float[] p2, float[] p3, float x, float y) {
        float[] arr = new float[4];
        arr[0] = CubicValue(p0, y);
        arr[1] = CubicValue(p1, y);
        arr[2] = CubicValue(p2, y);
        arr[3] = CubicValue(p3, y);
        return CubicValue(arr, x);
    }

    static float CubicValue(float[] p, float x) {
        return p[1] + 0.5f * x * (p[2] - p[0] + x * (2.0f * p[0] - 5.0f * p[1] + 4.0f * p[2] - p[3] + x * (3.0f * (p[1] - p[2]) + p[3] - p[0])));
    }
}
</code></pre>
<p>Bilinear example</p>
<pre><code class="language-csharp">using UnityEngine;

public class BilinearInterpolator {
    public static float BicubicInterpolated (float x, float y, Texture2D heightmap) {
        int row = Mathf.FloorToInt(x);
        int col = Mathf.FloorToInt(y);

        float remainderX = x - row;
        float remainderY = y - col;

        float x1 = heightmap.GetPixel(row, col).r;
        float x2 = heightmap.GetPixel(row + 1, col).r;

        float y1 = heightmap.GetPixel(row, col + 1).r;
        float y2 = heightmap.GetPixel(row + 1, col + 1).r;

        return Mathf.Lerp(Mathf.Lerp(x1, x2, remainderX), Mathf.Lerp(y1, y2, remainderX), remainderY);
    }
}
</code></pre>
<p>If you need to get the height of Unity's terrain from only a heightmap you can use bicubic interpolation.</p>
<pre><code class="language-csharp">Vector3 GetPosition (float x, float z, Terrain terrain, Texture2D heightmap) {
    float x1 = x / terrain.terrainData.size.x * heightmap.width;
    float z1 = z / terrain.terrainData.size.z * heightmap.height;

    float y = BicubicInterpolated(x1, z1, heightmap) * terrain.terrainData.heightmapScale.y;

    return new Vector3(x, y, z);
}
</code></pre>
]]></content:encoded>
      <category>programming</category>
      <category>game-dev</category>
      <category>unity</category>
    </item>
  </channel>
</rss>
