<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Cubos Engine</title><link href="https://cubos.riscadoa.com/" rel="alternate"/><link href="https://cubos.riscadoa.com/feeds/all.atom.xml" rel="self"/><id>https://cubos.riscadoa.com/</id><updated>2025-10-21T17:00:00+01:00</updated><entry><title>The First Cubos Jam</title><link href="https://cubos.riscadoa.com/blog/the-first-cubos-jam/" rel="alternate"/><published>2025-10-21T17:00:00+01:00</published><updated>2025-10-21T17:00:00+01:00</updated><author><name>Cubos Team</name></author><id>tag:cubos.riscadoa.com,2025-10-21:/blog/the-first-cubos-jam/</id><summary type="html">&lt;p&gt;The Cubos Team hosted the first Cubos Jam! This game jam was open to the general public and was focused on our engine.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;From September 12th to September 21st, 2025, the first game jam focused on the Cubos Engine took place, with the theme &amp;quot;Retro&amp;quot;.
This was a major milestone for the Cubos team, which we had been planning for a long time.
The idea behind it was to see how people would use the engine to create games, and to gather feedback on the engine and its tools.&lt;/p&gt;
&lt;p&gt;The engine is still, of course, very hard to use, due to the lack of a proper editor and a scripting language.
Participants had to code their games in C++, which is, as usual, not the most user friendly language.
Even so, we still were able to attract 13 participants, which is a great start for a first-time event.
Four of the five teams were able to submit a game by the end of the event, which you can check out on the &lt;a href="https://itch.io/jam/cubos-jam/entries"&gt;Cubos Jam page&lt;/a&gt;.&lt;/p&gt;
&lt;div class="m-imagegrid m-container-inflate"&gt;
&lt;div&gt;
&lt;figure style="width: 27.752%"&gt;
&lt;a href="https://cubos.riscadoa.com/images/blog/meta/cubos-jam-2025/retrospective-penguin.png"&gt;&lt;img src="https://cubos.riscadoa.com/images/blog/meta/cubos-jam-2025/retrospective-penguin.png" /&gt;&lt;figcaption&gt;Restrospective Penguin&lt;/figcaption&gt;
&lt;/a&gt;
&lt;/figure&gt;
&lt;figure style="width: 24.176%"&gt;
&lt;a href="https://cubos.riscadoa.com/images/blog/meta/cubos-jam-2025/rider-or-block.png"&gt;&lt;img src="https://cubos.riscadoa.com/images/blog/meta/cubos-jam-2025/rider-or-block.png" /&gt;&lt;figcaption&gt;Rider or Block&lt;/figcaption&gt;
&lt;/a&gt;
&lt;/figure&gt;
&lt;figure style="width: 24.894%"&gt;
&lt;a href="https://cubos.riscadoa.com/images/blog/meta/cubos-jam-2025/space-runner.png"&gt;&lt;img src="https://cubos.riscadoa.com/images/blog/meta/cubos-jam-2025/space-runner.png" /&gt;&lt;figcaption&gt;Space Runner&lt;/figcaption&gt;
&lt;/a&gt;
&lt;/figure&gt;
&lt;figure style="width: 23.177%"&gt;
&lt;a href="https://cubos.riscadoa.com/images/blog/meta/cubos-jam-2025/sushi-chef-shuffle.png"&gt;&lt;img src="https://cubos.riscadoa.com/images/blog/meta/cubos-jam-2025/sushi-chef-shuffle.png" /&gt;&lt;figcaption&gt;Sushi Chef Shuffle&lt;/figcaption&gt;
&lt;/a&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The jam itself was remote, with an in-person opening and closing event. At the opening event, we held a quick workshop to help participants get started with the engine.
At the closing event, we had a small ceremony to showcase the games made during the jam.&lt;/p&gt;
&lt;p&gt;The workshop guided the participants through the process of installing the engine and compiling a template project we provided.
This required setting up CMake and a C++ compiler (Visual Studio on Windows). Installing the engine and compiling the project itself wasn't a very smooth experience.
In the future we plan to improve this process by providing pre-compiled binaries and installers for the engine, as well as better setup documentation.&lt;/p&gt;
&lt;figure class="m-figure"&gt;
&lt;img src="https://cubos.riscadoa.com/images/blog/meta/cubos-jam-2025/workshop.jpg" /&gt;
&lt;figcaption&gt;Cubos Jam workshop, at Instituto Superior Técnico, Lisbon.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;After the jam we gathered a lot of useful feedback from the participants. Overall, they saw the potential of the engine, but also highlighted many areas that need improvement. Namely:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Tooling&lt;/strong&gt;: the engine is still very hard to use for non-programmers, particularly artists, due to the lack of an editor and proper asset management tools.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Documentation&lt;/strong&gt;: although the features are extensively documented, there are few examples and guides, which makes it hard for new users to get started.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scripting&lt;/strong&gt;: integrating a language such as Lua (which is happening right now) would greatly reduce the barrier to entry for new users.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Physics&lt;/strong&gt;: add capsule shape collisions, and expose a nicer interface for handling multiple types of bodies.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We plan to hold more jams in the future, after improving the engine and its tools. We hope the experience will become more accessible and enjoyable for future participants.&lt;/p&gt;
&lt;figure class="m-figure"&gt;
&lt;img src="https://cubos.riscadoa.com/images/blog/meta/cubos-jam-2025/gathering.jpg" /&gt;
&lt;figcaption&gt;Cubos Jam closing event, at the Gaming Hub, Lisbon.&lt;/figcaption&gt;
&lt;/figure&gt;
</content><category term="Meta"/><category term="Game Jam"/></entry><entry><title>Roll Racers</title><link href="https://cubos.riscadoa.com/blog/roll-racers/" rel="alternate"/><published>2025-06-19T22:00:00+01:00</published><updated>2025-06-19T22:00:00+01:00</updated><author><name>Cubos Team</name></author><id>tag:cubos.riscadoa.com,2025-06-19:/blog/roll-racers/</id><summary type="html">&lt;p&gt;We once again made a game in a week for the Coffee N' Jam with Cubos, and this time, with a lot of physics!&lt;/p&gt;
</summary><content type="html">&lt;section id="the-theme"&gt;
&lt;h2&gt;The Theme&lt;/h2&gt;
&lt;p&gt;From 2nd of May to the 10th of May, &lt;a href="https://cubos.riscadoa.com/blog/author/tiago-antunes/"&gt;&amp;#64;fallenatlas&lt;/a&gt;, &lt;a href="https://cubos.riscadoa.com/blog/author/tomas-gouveia/"&gt;&amp;#64;tomas7770&lt;/a&gt;, &lt;a href="https://cubos.riscadoa.com/blog/author/francisco-catarino/"&gt;&amp;#64;fkatar&lt;/a&gt;, &lt;a href="https://cubos.riscadoa.com/blog/author/pedro-simoes/"&gt;&amp;#64;PedroSimoes24&lt;/a&gt;, &lt;a href="https://cubos.riscadoa.com/blog/author/ricardo-antunes/"&gt;&amp;#64;RiscadoA&lt;/a&gt;, &lt;a href="https://cubos.riscadoa.com/blog/author/joao-manita/"&gt;&amp;#64;joaomanita&lt;/a&gt; and &lt;a href="https://cubos.riscadoa.com/blog/author/miguel-canais/"&gt;&amp;#64;mcanais&lt;/a&gt; participated in the Coffee N' Jam, a game jam organized by GameDev Técnico, with our engine, &lt;strong&gt;Cubos&lt;/strong&gt;.
The goal was to create a game in a week, this time with theme &amp;quot;Blackout&amp;quot; - the huge power outage in the Iberian Peninsula had happened the prior week, and just like during the Covid lockdown, people rushed to the supermarkets to buy toilet paper.&lt;/p&gt;
&lt;p&gt;We thought it would be funny to make a game about racing to the supermarket to buy toilet paper - and that's what we ended up doing.
Our priorities were to test new features of the engine, and to make a fun local multiplayer game.
This idea was a great opportunity to fully use the physics and collision plugins in a game for the first time.&lt;/p&gt;
&lt;p&gt;The game is called &lt;em&gt;Roll Racers&lt;/em&gt;, and you can play it on its &lt;a href="https://riscadoa.itch.io/roll-racers"&gt;itch.io page&lt;/a&gt;.
It has a web build and a downloadable version for both Windows and Linux.
You can also check out its source code in our &lt;a href="https://github.com/GameDevTecnico/cubos-demo"&gt;demo repository&lt;/a&gt;.&lt;/p&gt;
&lt;figure class="m-figure"&gt;
&lt;img src="https://cubos.riscadoa.com/images/blog/demo/coffee-n-jam-2025/cover.png" /&gt;
&lt;figcaption&gt;The game supports up to 4 players, each having a different car model.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class="m-figure"&gt;
&lt;img src="https://cubos.riscadoa.com/images/blog/demo/coffee-n-jam-2025/image1.png" /&gt;
&lt;figcaption&gt;A player holding toilet paper, racing back to the start line to score points.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;We had a lot of fun making the game, and we fixed a ton of bugs and missing features in the engine as we went along.
Since the jam was a week long, we had some more time to work on the game than in the &lt;a href="https://cubos.riscadoa.com/blog/the-ondisseia-of-cubos/"&gt;previous jam&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="voxel-modeling"&gt;
&lt;h2&gt;Voxel Modeling&lt;/h2&gt;
&lt;p&gt;One major problem we had was that we didn't have any artists in the team, only programmers.
We ended up going full programmer art mode, and we used, as usual, &lt;a href="https://ephtracy.github.io/"&gt;MagicaVoxel&lt;/a&gt; for this purpose.
&lt;a href="https://cubos.riscadoa.com/blog/author/pedro-simoes/"&gt;&amp;#64;PedroSimoes24&lt;/a&gt; focused on making the buildings and the road models, &lt;a href="https://cubos.riscadoa.com/blog/author/ricardo-antunes/"&gt;&amp;#64;RiscadoA&lt;/a&gt; focused on the cars and some tiles and &lt;a href="https://cubos.riscadoa.com/blog/author/miguel-canais/"&gt;&amp;#64;mcanais&lt;/a&gt; made a few props.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="map-generation"&gt;
&lt;h2&gt;Map Generation&lt;/h2&gt;
&lt;p&gt;To make our jobs easier, the map is divided into tiles. Each tile is 68x68 voxels wide. Tiles can be road tiles, building tiles or park tiles.
&lt;a href="https://cubos.riscadoa.com/blog/author/ricardo-antunes/"&gt;&amp;#64;RiscadoA&lt;/a&gt; and &lt;a href="https://cubos.riscadoa.com/blog/author/miguel-canais/"&gt;&amp;#64;mcanais&lt;/a&gt; worked on the map generation, which is done procedurally. First, road tiles are placed in a river-like fashion, and then, building tiles are placed around them.&lt;/p&gt;
&lt;p&gt;To make the map prettier we also added some park tiles with trees and bushes, which are randomly placed adjacent to the road tiles.
For an extra challenge, we added a 'river' tile where the road has a big hole in the middle, and the players have to jump over it by going fast enough on the ramps.&lt;/p&gt;
&lt;p&gt;We also have special start and end tiles, the end tile being the supermarket, where the toilet paper is placed.
The map regenerates every time a round ends, i.e., when a player brings the toilet paper back to the start line.&lt;/p&gt;
&lt;figure class="m-figure"&gt;
&lt;img alt="Supermarket" src="https://cubos.riscadoa.com/images/blog/demo/coffee-n-jam-2025/supermarket.png" /&gt;
&lt;figcaption&gt;Supermarket tile, where the toilet paper can be picked up.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/section&gt;
&lt;section id="physics-and-collisions"&gt;
&lt;h2&gt;Physics and Collisions&lt;/h2&gt;
&lt;p&gt;For this game, we decided to go all in on physics. &lt;a href="https://cubos.riscadoa.com/blog/author/tiago-antunes/"&gt;&amp;#64;fallenatlas&lt;/a&gt; and &lt;a href="https://cubos.riscadoa.com/blog/author/ricardo-antunes/"&gt;&amp;#64;RiscadoA&lt;/a&gt; developed physically realistic cars.
The cars are essentially composed of a box shape, representing the chasis, and then four empty child entities representing each of the wheels, with origin on the point where the suspension connects to the car. When turning, these wheel entities are rotated on the y-axis.
Then, the forces for each individual wheel are calculated and applied on the connection point with the chasis, for realistic behaviour. These forces include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The suspension force, which lifts the car up and makes it react realistically to the ground bellow.&lt;/li&gt;
&lt;li&gt;The steering force, which simulates the cars steering with tire friction and splip angle.&lt;/li&gt;
&lt;li&gt;The acceleration force, which applies acceleration and breaking to the cars. The acceleration forces are applied to different wheels, depending if the car is front wheel drive or rear wheel drive, affecting it's handling.&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class="m-figure"&gt;
&lt;img src="https://cubos.riscadoa.com/images/blog/demo/coffee-n-jam-2025/going-over-sidewalk.png" /&gt;
&lt;figcaption&gt;Since each wheel is simulated independently, the cars tilts when going over the sidewalk.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;However, if the cars are not on the ground, we do not want to calculate any forces besides gravity. Therefore, we perform a raycast from the wheel origin to the ground.
In case it hits bellow the max distance, we calculate the forces, and particularly, we use it's hitpoint for determining the strength of the suspension force.&lt;/p&gt;
&lt;p&gt;As mentioned previously, the map is divided into tiles. As it's much fast and accurate than trying to put collision shapes manually,
we decided to use voxel collision shapes for every tile. This worked surprisingly well, and the framerate didn't drop as much as we where expecting given how many voxel shapes we had,
and the little optimization we currently have for collisions. It's worth noting that we were taking advantage of the collision layers and masks to determine which shapes should detect collision between themselves, which lead to better performance in this scenario.&lt;/p&gt;
&lt;p&gt;But this is where things went a little wrong... As we tried to put the car on the map, we noticed that the car would just completly fall through the tile.
After finding the root issue, &lt;a href="https://cubos.riscadoa.com/blog/author/tiago-antunes/"&gt;&amp;#64;fallenatlas&lt;/a&gt; started working quickly on fixing it. The issue had two parts. The first was related to the component matching on the penetration constraint solving, where we were missing some checks for certain components, leading to the solving being done in the opposite way, effectively pulling bodies to each other, instead of pushing them apart.
The second issue was related to the local contact points calculation for voxel shapes, in the narrow phase, where the points were always relative to the center of the entity rather than relative to their relative box center (since the voxel shape is composed of multiple boxes). This lead to the penetration distance being miscalculated in the penetration constraint solving, and therefore, the impulses applied to each entity were incorrect.&lt;/p&gt;
&lt;p&gt;Since, we were at it, we wanted to use as much physics features as we wanted, so &lt;a href="https://cubos.riscadoa.com/blog/author/joao-manita/"&gt;&amp;#64;joaomanita&lt;/a&gt; experimented with using the new Distance Constraint to attach the paper roll to the car, and having it be dragged along.
However, the team felt like it was too hard to steal the toilet paper, since it moved around too much, and discarted this idea.&lt;/p&gt;
&lt;p&gt;On the last day, a comical situation happened when we where putting ramps on the map. We (&lt;a href="https://cubos.riscadoa.com/blog/author/ricardo-antunes/"&gt;&amp;#64;RiscadoA&lt;/a&gt;) tried to rotate one of the ramps, by rotating it, and then putting it as a child entity of another entity with the same rotation applied to it. This lead to a huge framerate drop, which took as a couple of hours to figure out.
The exact reason for why this happened is still unknown, but we'll be investigating it in the future.&lt;/p&gt;
&lt;figure class="m-figure"&gt;
&lt;img src="https://cubos.riscadoa.com/images/blog/demo/coffee-n-jam-2025/image2.png" /&gt;
&lt;figcaption&gt;A player can steal another player's toilet paper by hitting them with their car.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/section&gt;
&lt;section id="ui"&gt;
&lt;h2&gt;UI&lt;/h2&gt;
&lt;p&gt;This game was also the first time UI was used on a demo. &lt;a href="https://cubos.riscadoa.com/blog/author/tomas-gouveia/"&gt;&amp;#64;tomas7770&lt;/a&gt; picked up this task, and made multiple menus and screens for the game.
We wanted to briefly explain the game mechanics to the players, so we added a title screen with some information.&lt;/p&gt;
&lt;figure class="m-figure"&gt;
&lt;img alt="Title screen" src="https://cubos.riscadoa.com/images/blog/demo/coffee-n-jam-2025/title-screen.png" /&gt;
&lt;figcaption&gt;The &amp;quot;Press Space/A ...&amp;quot; text color is animated, fading in and out.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Additionally, the players can choose how many players will play the game by pressing the spacebar or the &amp;quot;A&amp;quot; on the gamepad for each of them.
Instructions were added to the top of the screen.&lt;/p&gt;
&lt;figure class="m-figure"&gt;
&lt;img alt="Start screen" src="https://cubos.riscadoa.com/images/blog/demo/coffee-n-jam-2025/start.png" /&gt;
&lt;figcaption&gt;After advancing, the players must choose the number of players.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In game, it was important to show the players their score and how it changed over time, to make the game more competitive.
We decided to add a score indicator to the top of the screen, which shows the score of each player.&lt;/p&gt;
&lt;figure class="m-figure"&gt;
&lt;img alt="Score indicators" src="https://cubos.riscadoa.com/images/blog/demo/coffee-n-jam-2025/mid-game.png" /&gt;
&lt;figcaption&gt;Points are gained by holding the toilet paper.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Finally, at the end of the game, we wanted to reward the players with a scoreboard, showing the score of each player.&lt;/p&gt;
&lt;figure class="m-figure"&gt;
&lt;img alt="Scoreboard" src="https://cubos.riscadoa.com/images/blog/demo/coffee-n-jam-2025/scoreboard.png" /&gt;
&lt;figcaption&gt;The text color helps associate the players with their scores.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The UI ended up working well during the jam. There were only three issues:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Our UI and ImGui were not compatible - this was easily fixed by ensuring they were drawn in the correct order.&lt;/li&gt;
&lt;li&gt;There is no support for text wrapping or newlines, so we had to create a different text element for each line.&lt;/li&gt;
&lt;li&gt;The text contents only updated when its respective component was reinserted - simply modifying it wasn't enough. We're still unsure on how to handle these scenarios, but the most likely solution is to make the component unmodifiable and thus force the user to reinsert it.&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="audio"&gt;
&lt;h2&gt;Audio&lt;/h2&gt;
&lt;p&gt;On the last day of the jam &lt;a href="https://cubos.riscadoa.com/blog/author/ricardo-antunes/"&gt;&amp;#64;RiscadoA&lt;/a&gt; decided to focus on adding audio to the game.
Previously, when making &lt;a href="https://cubos.riscadoa.com/blog/the-ondisseia-of-cubos/"&gt;Ondisseia&lt;/a&gt;, we had a lot of trouble with adding sound effects.
They didn't play reliably and the audio system was a bit of a mess.&lt;/p&gt;
&lt;p&gt;To use audio in &lt;em&gt;Roll Racers&lt;/em&gt;, we ended up fixing all of the problems we previously had, which means that we can now rely on it for future projects.
We added a few sound effects to the game, such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Car engine sound, which changes pitch and gain based on RPM and gear.&lt;/li&gt;
&lt;li&gt;Pickup sound when picking up toilet paper.&lt;/li&gt;
&lt;li&gt;Steal sound when stealing toilet paper from other players.&lt;/li&gt;
&lt;li&gt;Fireworks sound when winning the game.&lt;/li&gt;
&lt;li&gt;Power cut sound when the race starts.&lt;/li&gt;
&lt;li&gt;Honking sound when honking the horn.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While the audio still sounded a bit janky, it was very satisfying to hear sound being used so extensively in a Cubos game for the first time.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="ecs-troubles"&gt;
&lt;h2&gt;ECS Troubles&lt;/h2&gt;
&lt;p&gt;At the end of the jam, the game's performance was very bad.
A bit of profiling quickly showed that a lot of time was being spent iterating over tens of thousands of relation tables - the ones introduced in the &lt;a href="https://cubos.riscadoa.com/blog/hello-relations/"&gt;relations development note&lt;/a&gt;.
This was a waste of time, as only less than 15 actually contained entities.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://cubos.riscadoa.com/blog/author/ricardo-antunes/"&gt;&amp;#64;RiscadoA&lt;/a&gt; quickly fixed this by adding logic to clean up unused relation tables every frame.
This immediately improved performance by a lot - on one machine, the framerate went from 40FPS to stable 240FPS.
In the future, this logic should probably be improved to only clean up the tables when they're not used for a while, to avoid wasting time recreating tables constantly.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="whats-next"&gt;
&lt;h2&gt;Whats Next?&lt;/h2&gt;
&lt;p&gt;We are very happy with the results of this jam - this demo is and will continue to be a great testbed for the engine, as it really pushes it to its limits.
After the jam ended, we met to discuss what went wrong and what issues we should prioritize fixing.
One of the biggest problems continues to be the lack of proper tooling for the engine - this will be the focus for the next few months.&lt;/p&gt;
&lt;/section&gt;
</content><category term="Demo"/><category term="Demo"/><category term="Game Jam"/><category term="Coffee N' Jam"/></entry><entry><title>Release 0.7</title><link href="https://cubos.riscadoa.com/blog/release-07/" rel="alternate"/><published>2025-06-19T00:00:00+01:00</published><updated>2025-06-19T00:00:00+01:00</updated><author><name>Cubos Team</name></author><id>tag:cubos.riscadoa.com,2025-06-19:/blog/release-07/</id><summary type="html">&lt;p&gt;A new ImGui theme and inspector, proper support for hierarchies in scenes, distance physics constraints and tons of other improvements.&lt;/p&gt;
</summary><content type="html">&lt;section id="new-0-7-features"&gt;
&lt;h2&gt;New 0.7 Features&lt;/h2&gt;
&lt;p&gt;This blog post covers the most important changes, but you can check out the full changelog in our &lt;a href="https://github.com/GameDevTecnico/cubos/blob/main/CHANGELOG.md"&gt;repository&lt;/a&gt;.&lt;/p&gt;
&lt;section id="on-the-engine"&gt;
&lt;h3&gt;On the Engine&lt;/h3&gt;
&lt;section id="overhauled-imgui-inspector-with-hook-support-riscadoa"&gt;
&lt;h4&gt;Overhauled ImGui Inspector with Hook Support &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/ricardo-antunes/"&gt;&amp;#64;RiscadoA&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;Before 0.7, to inspect a &lt;em&gt;Cubos&lt;/em&gt; value in the ImGui inspector, you used the &lt;code&gt;DataInspector&lt;/code&gt; resource.
This resource provided methods to show and edit types through ImGui, but had no state whatsoever, which was a bit counterintuitive.
Additionally, it was not possible to externally override the behavior of the inspector, which made it impossible to customize the way certain types were displayed or edited.
This meant that, for instance, if the game developer wanted to display a custom UI on the inspector for a component of theirs, it would not be possible.&lt;/p&gt;
&lt;p&gt;This previous implementation was replaced by a new &lt;code&gt;ImGuiInspector&lt;/code&gt; system argument. This can be used in any ImGui system. For example:&lt;/p&gt;
&lt;pre class="m-code"&gt;&lt;span class="n"&gt;cubos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;inspect all velocities&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tagged&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;imguiTag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;([](&lt;/span&gt;&lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Entity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Velocity&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;velocities&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ImGuiInspector&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;inspector&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;velocity&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;velocities&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;inspector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;edit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;velocity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Velocity of &amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;to_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ent&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;This new inspector allows overriding its behavior for specific types by using the &lt;code&gt;ImGuiInspector::hook&lt;/code&gt; method.
This paved the way for us to enhance the inspector with new features such as displaying quaternions as Euler angles, breaking down matrices into translation, rotation and scale, and more.
Additionally, support was added for mask and enum types using their respective reflection traits.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="distance-physics-constraint-joaomanita"&gt;
&lt;h4&gt;Distance Physics Constraint &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/joao-manita/"&gt;&amp;#64;joaomanita&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;In version 0.7, we began adding physics constraints that users can set and customize. Previously, only the penetration constraint was available, which was used exclusively for collision solving.
For now, we've added the distance constraint, which ensures either a fixed distance between two physics bodies, or a distance range (similar to a rope).
To add this constraint:&lt;/p&gt;
&lt;pre class="m-code"&gt;&lt;span class="n"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;relate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ent1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ent2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="n"&gt;DistanceConstraint&lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;isRigid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                                       &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;minDistance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.0F&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                                       &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;maxDistance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;5.0F&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                                       &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;localAnchor1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;0.0F&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.0F&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.0F&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;                                       &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;localAnchor2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;0.0F&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.0F&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.0F&lt;/span&gt;&lt;span class="p"&gt;}});&lt;/span&gt;

&lt;span class="n"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;relate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ent1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ent2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="n"&gt;DistanceConstraint&lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;isRigid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                                       &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fixedDistance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;5.0F&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                                       &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;localAnchor1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;0.0F&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.0F&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.0F&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;                                       &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;localAnchor2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;0.0F&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.0F&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.0F&lt;/span&gt;&lt;span class="p"&gt;}});&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;Where isRigid indicates whether it's a fixed distance or a range constraint, and the local anchor values specify the points on each body where the constraint is applied.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="voxel-mesh-frustum-culling-mkuritsu"&gt;
&lt;h4&gt;Voxel Mesh Frustum Culling &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/rodrigo-correia/"&gt;&amp;#64;mkuritsu&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;In this release we used the intersection utilities from the previous release to perform frustum culling on rendered voxel meshes.
Previously, every voxel mesh loaded into the engine would have its data sent to the GPU for rendering, regardless of whether they were visible. This resulted in wasted resources, and that's exactly what this feature addresses.
With frustum culling, only meshes within the camera's visible area are sent to the GPU for drawing, while others are ignored, potentially improving performance in scenes with a large number of objects.&lt;/p&gt;
&lt;p&gt;Frustum culling is performed automatically with any type of camera—whether it's an &lt;a href="https://docs.cubos.riscadoa.com/structcubos_1_1engine_1_1OrthographicCamera.html"&gt;OrthographicCamera&lt;/a&gt; or a &lt;a href="https://docs.cubos.riscadoa.com/structcubos_1_1engine_1_1PerspectiveCamera.html"&gt;PerspectiveCamera&lt;/a&gt;. This means no manual intervention is required to benefit from it.
Additionally, a debug option has been added to the &lt;a href="https://docs.cubos.riscadoa.com/structcubos_1_1engine_1_1Camera.html"&gt;Camera&lt;/a&gt; component, allowing you to freeze (stop) updates to the camera's frustum, keeping it in the last calculated state.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="aabb-and-collider-rework-fallenatlas"&gt;
&lt;h4&gt;AABB and Collider Rework &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/tiago-antunes/"&gt;&amp;#64;fallenatlas&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;In this release, we began reworking the Colliders to make them more streamlined and easier to understand.
Previously, we had the &lt;code&gt;Collider&lt;/code&gt; component, which contained the AABB of the Collision Shape as well as a transform to apply an offset to the AABB.
This component has now been renamed to &lt;code&gt;ColliderAABB&lt;/code&gt; to clarify its purpose, and the transform has been removed, as it only affected the AABB and not the collision shape itself.&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;Collider&lt;/code&gt; now represents the owner entity of a collision shape. This distinction is important, as we plan to support collision shapes composed of multiple shapes from child entities of the owner entity. We need an easy way to distinguish them in the hierarchy.
The offset of collision shapes will be determined by the entity's transform when the collision shape is part of a child entity. Otherwise, it is assumed to be centered. This functionality has not been implemented yet, but will begin rolling out in future releases.&lt;/p&gt;
&lt;p&gt;Additionally, we now automatically add the &lt;code&gt;ColliderAABB&lt;/code&gt; when a collision shape is added.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="voxel-shape-inertia-fallenatlas"&gt;
&lt;h4&gt;Voxel Shape Inertia &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/tiago-antunes/"&gt;&amp;#64;fallenatlas&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;Up until now, we had voxel shapes that collided with each other and with boxes. However, the inertia matrix for these shapes was calculated based on the size of the voxel grid, which led to inaccuracies, particularly for shapes with many empty spaces.
Now, we use a custom algorithm that treats each voxel as a point and sums the inertia for each point, relative to the center of mass, to compute the total inertia matrix.
Currently, we only support uniform density, meaning each point has a mass equal to the total mass divided by the number of occupied voxels in the grid.
You can check the implementation of the functions defined &lt;a href="https://docs.cubos.riscadoa.com/inertia_8hpp.html"&gt;here&lt;/a&gt; for more details.&lt;/p&gt;
&lt;p&gt;One important note is that, while we calculate the inertia tensor for this shape, we do not guarantee it will be symmetric, even though it should be by definition.
The matrix is guaranteed to be symmetric only when the shape itself is symmetric. However, in all cases, it’s possible to obtain a symmetric matrix by finding the principal axis of inertia.
This is a minor optimization that makes the inertia more readable, though it's not a priority.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="new-scene-format-riscadoa"&gt;
&lt;h4&gt;New Scene Format &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/ricardo-antunes/"&gt;&amp;#64;RiscadoA&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;With the introduction of relations in the ECS, particularly the &lt;cite&gt;ChildOf&lt;/cite&gt; relation, hierarchies became essential in Cubos scenes.
This posed a problem, as the previous scene format didn’t have an intuitive way to represent hierarchies—scenes were simply flat lists of entities with no parent-child relationships.
To address this, we designed a new scene format where each scene is a JSON file describing an entity, its components and relations, as well as its children, recursively.
It looks like this:&lt;/p&gt;
&lt;pre class="m-code"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;inherit&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ba19170e-65a0-47cd-9bd2-b4ea9447fe22&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;game::Player&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;#camera&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;cubos::engine::PerspectiveCamera&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;fovY&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;#gun&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;inherit&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;6cb09eea-4156-4a75-b0ae-488aac843e05&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;This scene represents an entity that inherits from another scene with the ID &lt;code&gt;ba19170e-65a0-47cd-9bd2-b4ea9447fe22&lt;/code&gt;, meaning we're modifying an existing scene.```
To this base entity, a &lt;code&gt;game::Player&lt;/code&gt; component and two child entities, &lt;code&gt;camera&lt;/code&gt; and &lt;code&gt;gun&lt;/code&gt;, are added.
The &lt;code&gt;camera&lt;/code&gt; entity also receives a &lt;code&gt;cubos::engine::PerspectiveCamera&lt;/code&gt; component, with a field &lt;code&gt;fovY&lt;/code&gt; set to 90, and the &lt;code&gt;gun&lt;/code&gt; entity inherits from another scene, with the ID &lt;code&gt;6cb09eea-4156-4a75-b0ae-488aac843e05&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This format is much more flexible than the previous one, as it allows us to easily expand upon existing scenes by inheriting from them, while also representing hierarchies in a natural way.
The inheritance system has been especially useful in our recent demos, such as for setting different player skins by inheriting from a base player scene and overriding the models used.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="custom-imgui-styling-riscadoa"&gt;
&lt;h4&gt;Custom ImGui Styling &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/ricardo-antunes/"&gt;&amp;#64;RiscadoA&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;In this release we've finally customized the ImGui theme colors and font to match the Cubos brand guidelines.
This change makes the engine's tools UI look more professional and consistent with the overall look of our website and brand.&lt;/p&gt;
&lt;img class="m-image" src="https://cubos.riscadoa.com/images/blog/release/0-7/imgui-sample-theme.png" /&gt;
&lt;/section&gt;
&lt;section id="optional-render-picker-tomas7770"&gt;
&lt;h4&gt;Optional Render Picker &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/tomas-gouveia/"&gt;&amp;#64;tomas7770&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;In the &lt;a href="https://cubos.riscadoa.com/blog/release-06"&gt;previous release's blog post&lt;/a&gt;, we discussed some of the major performance bottlenecks in the graphics renderer. One of them was SSAO, which was made optional to allow disabling it on lower-end devices. The other was the render picker.&lt;/p&gt;
&lt;p&gt;Although work to make the render picker optional had already started, it wasn’t completed due to deadline constraints and some lingering bugs.&lt;/p&gt;
&lt;p&gt;With this release, it's now possible to remove the &lt;a href="https://docs.cubos.riscadoa.com/structcubos_1_1engine_1_1RenderPicker.html"&gt;RenderPicker&lt;/a&gt; component from a render target without breaking the graphics system. While this disables certain features, such as selecting entities with the mouse cursor, in most cases, those features aren't crucial, and the performance cost of render picking is too high to ignore.&lt;/p&gt;
&lt;p&gt;On one particular machine, disabling render picking in our Airships demo running at 1080p on integrated graphics resulted in a jump from 30 FPS to nearly 60 FPS!
This shows how this change makes it much more feasible to run Cubos games on lower-end hardware.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="center-render-voxel-pivot-kuukitenshi"&gt;
&lt;h4&gt;Center Render Voxel Pivot &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/laura-cunha/"&gt;&amp;#64;kuukitenshi&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;Previously, the pivot point for voxel rendering was at the bottom-left corner of each voxel grid. Now, it’s at the center!&lt;/p&gt;
&lt;p&gt;This change makes aligning voxel meshes with other entities much easier, as there's no longer a need to manually adjust offsets.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="on-the-core"&gt;
&lt;h3&gt;On the Core&lt;/h3&gt;
&lt;section id="math-vector-reflection-trait-riscadoa"&gt;
&lt;h4&gt;Math Vector Reflection Trait &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/ricardo-antunes/"&gt;&amp;#64;RiscadoA&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;One issue that had been bothering us for a while was the lack of proper support for math types in the reflection system. This led to the serialization and inspector systems being unaware of them, treating them as simple structs.
To address this, we've added a new reflection trait, &lt;code&gt;VectorTrait&lt;/code&gt;, which identifies types that represent math vectors, such as all the &lt;cite&gt;glm&lt;/cite&gt; vector types.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="entity-destruction-observers-kuukitenshi"&gt;
&lt;h4&gt;Entity Destruction Observers &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/laura-cunha/"&gt;&amp;#64;kuukitenshi&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;Previously, detecting when an entity was being destroyed required manually checking its destruction state. Now, we have an observable for entity destruction that triggers automatically when an entity is destroyed.&lt;/p&gt;
&lt;p&gt;This makes handling such events easier, as we can now simply register a callback that’s invoked upon destruction. It’s especially useful for cleaning up resources or performing actions across systems without needing to repeatedly check if an entity is being destroyed.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="query-match-counting-galaxycrush"&gt;
&lt;h4&gt;Query Match Counting &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/joao-pereira/"&gt;&amp;#64;GalaxyCrush&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;Previously, to count matches in queries, we had to use a for loop to count them after the query was executed.&lt;/p&gt;
&lt;p&gt;Now we added a new &lt;code&gt;count&lt;/code&gt; method to the &lt;a href="https://docs.cubos.riscadoa.com/classcubos_1_1core_1_1ecs_1_1Query.html"&gt;Query&lt;/a&gt; class, which directly returns the number of matches in the query, eliminating the need for a separate loop outside the class.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="wrapper-reflection-trait-srgesus-riscadoa"&gt;
&lt;h4&gt;Wrapper Reflection Trait &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/gabriel-ferreira/"&gt;&amp;#64;SrGesus&lt;/a&gt;, &lt;a href="https://cubos.riscadoa.com/blog/author/ricardo-antunes/"&gt;&amp;#64;RiscadoA&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;A recurring pattern in our codebase are single field wrapper structs, such as &lt;code&gt;Position&lt;/code&gt;, &lt;code&gt;Rotation&lt;/code&gt; and &lt;code&gt;Scale&lt;/code&gt;.
We use these types to give meaning to the data they contain (in this case, math vectors and quaternions), and to distinguish them in the &lt;em&gt;Entity Component System&lt;/em&gt;.
To reflect these types, we previously relied  the &lt;code&gt;FieldsTrait&lt;/code&gt;, as it describes &lt;code&gt;struct&lt;/code&gt; like types.&lt;/p&gt;
&lt;p&gt;When reflection consumers such as the &lt;cite&gt;JSON&lt;/cite&gt; serializer interacted with these types, they would usually treat &lt;code&gt;FieldsTrait&lt;/code&gt; types with a single field as wrappers.
In the case of &lt;cite&gt;JSON&lt;/cite&gt;, this led to shorter more readable output, e.g., instead of:&lt;/p&gt;
&lt;pre class="m-code"&gt;&lt;span class="nt"&gt;&amp;quot;cubos::engine::Position&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;vec&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;x&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;y&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;z&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;3.0&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;It would just output:&lt;/p&gt;
&lt;pre class="m-code"&gt;&lt;span class="nt"&gt;&amp;quot;cubos::engine::Position&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;x&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;y&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;z&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;3.0&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;However, this behavior became problematic, as not &lt;em&gt;all&lt;/em&gt; struct types with a single field should be treated as wrappers.
For instance, the &lt;code&gt;cubos::engine::PerspectiveCamera&lt;/code&gt; component holds a single field &lt;code&gt;fovY&lt;/code&gt;.
Thus, when serialized to JSON, it would output:&lt;/p&gt;
&lt;pre class="m-code"&gt;&lt;span class="nt"&gt;&amp;quot;cubos::engine::PerspectiveCamera&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;90.0&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;This, of course, is not readable at all. Even worse, if someone were to add a new field to the &lt;code&gt;PerspectiveCamera&lt;/code&gt; component, it would break the serialization format.
image the component now has a new field called &lt;code&gt;farClip&lt;/code&gt;. How would the JSON deserializer know how to deserialize the above JSON?&lt;/p&gt;
&lt;p&gt;To prevent issues like these while still allowing the wrapper behavior for single field structs, we introduced a new reflection trait called &lt;code&gt;WrapperTrait&lt;/code&gt;.
&lt;code&gt;FieldsTrait&lt;/code&gt; types with single fields are now no longer automatically treated as wrappers, and instead, only types with the &lt;code&gt;WrapperTrait&lt;/code&gt; will be serialized in the shorter format.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="on-the-tools"&gt;
&lt;h3&gt;On the Tools&lt;/h3&gt;
&lt;section id="tesseratos-menu-bar-jdbaracho"&gt;
&lt;h4&gt;Tesseratos Menu Bar &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/joao-baracho/"&gt;&amp;#64;jdbaracho&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;Tesseratos now features a menu bar that lets users enable or disable each tool.&lt;/p&gt;
&lt;p&gt;This change brings Tesseratos a step closer to being a fully functional editor, making it easier for more people to build games using &lt;strong&gt;Cubos&lt;/strong&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="auto-scroll-toggle-on-the-console-r-camacho"&gt;
&lt;h4&gt;Auto-scroll Toggle on the Console &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/r-camacho/"&gt;&amp;#64;R-Camacho&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;Previously, we had to scroll manually to view the latest debug messages in the console.&lt;/p&gt;
&lt;p&gt;Now, a new checkbox allows us toggle auto-scroll. When enabled, the console will automatically scroll to display the most recent messages as they appear.&lt;/p&gt;
&lt;img class="m-image" src="https://cubos.riscadoa.com/images/blog/release/0-7/console-auto-scroll.png" /&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="next-steps"&gt;
&lt;h2&gt;Next Steps&lt;/h2&gt;
&lt;p&gt;On the next release we'll be introducing a &lt;em&gt;Lua&lt;/em&gt; scripting plugin, backed by C API bindings to Cubos.
Our goal is to be able to host a game jam focused on our engine by October 2025, and we believe that having a scripting language will be essential for that.&lt;/p&gt;
&lt;p&gt;As usual, one of the focus areas of the team is improving the tooling. The tools team has received a great influx of new contributors, and we're excited to see what they will bring to the table.&lt;/p&gt;
&lt;p&gt;You can check out the full list of things we want to get done in the &lt;a href="https://github.com/GameDevTecnico/cubos/milestone/31"&gt;milestone&lt;/a&gt; for the next release.&lt;/p&gt;
&lt;/section&gt;
</content><category term="Release"/></entry><entry><title>The "Ondisseia" of Cubos</title><link href="https://cubos.riscadoa.com/blog/the-ondisseia-of-cubos/" rel="alternate"/><published>2025-05-16T10:00:00+01:00</published><updated>2025-05-16T10:00:00+01:00</updated><author><name>Cubos Team</name></author><id>tag:cubos.riscadoa.com,2025-05-16:/blog/the-ondisseia-of-cubos/</id><summary type="html">&lt;p&gt;Cubos participation in the Azul Game Jam - the achievements, lessons taken, and much more.&lt;/p&gt;
</summary><content type="html">&lt;section id="the-jam"&gt;
&lt;h2&gt;The Jam&lt;/h2&gt;
&lt;p&gt;Around a month ago, &lt;a href="https://cubos.riscadoa.com/blog/author/miguel-canais/"&gt;&amp;#64;mcanais&lt;/a&gt;, &lt;a href="https://cubos.riscadoa.com/blog/author/nuno-baptista/"&gt;&amp;#64;NunoBaptista&lt;/a&gt;, &lt;a href="https://cubos.riscadoa.com/blog/author/ricardo-antunes/"&gt;&amp;#64;RiscadoA&lt;/a&gt; and &lt;a href="https://cubos.riscadoa.com/blog/author/ricardo-subtil/"&gt;&amp;#64;rsubtil&lt;/a&gt; joined forces to participate in the &lt;a href="https://www.linkedin.com/company/azulgamejam"&gt;Azul Game Jam&lt;/a&gt;.
This was the first edition of a Portuguese on-site game jam, where the aim was to join the Portuguese game dev community at the &lt;a href="https://lisboainnovation.com/hubs/aihub/"&gt;AI Hub&lt;/a&gt;, to make games during 48 hours.
We were a team of 4 - 3 programmers, 1 artist, and a lot of ideas.&lt;/p&gt;
&lt;figure class="m-figure"&gt;
&lt;img src="https://cubos.riscadoa.com/images/blog/demo/azul-jam-2025/CubosInAzul4.jpg" /&gt;
&lt;figcaption&gt;Cubos team participating in the Azul Game Jam - photo by Gonçalo Goulão.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The game is playable on the browser, and can be found at &lt;a href="https://riscadoa.itch.io/ondisseia"&gt;itch.io&lt;/a&gt;.&lt;/p&gt;
&lt;section id="game-development"&gt;
&lt;h3&gt;Game Development&lt;/h3&gt;
&lt;p&gt;It is challenging to work with such a barebones engine, but also really satisfying to have so much control over what is made.
This year's theme was waves, and we decided to make a sea battle game, where up to 4 players fight until the last standing.
The movement was really inspired on &lt;a href="https://cubos.riscadoa.com/blog/coffee-n-jam-2024/"&gt;Scraps vs Zombies&lt;/a&gt; demo, functioning in a grid wise manner,
where the player moves a tile at the time, in for possible directions.
To fulfill the jam theme, we've included a cellular automata wave that sweeps across the scene.
The player's boat slows down when sailing against this wave, or can catch up speed while riding it.
The waves are colossal, sometimes sweeping over the islands on the map.
With this game mechanic, we'd hoped to add an interesting twist to the genre of Battle Royale.&lt;/p&gt;
&lt;img class="m-image" src="https://cubos.riscadoa.com/images/blog/demo/azul-jam-2025/OndisseiaGameplay.gif" /&gt;
&lt;section id="voxel-art-nunobaptista"&gt;
&lt;h4&gt;Voxel Art &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/nuno-baptista/"&gt;&amp;#64;NunoBaptista&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;As an artist, I produce the voxel assets, including the animations, the tiles of the map, and the wave.
Currently, we don't have a voxel editor inside Cubos, so we used &lt;a href="https://ephtracy.github.io/"&gt;MagicaVoxel&lt;/a&gt; for this purpose.
For the animations, we've used a process very similar to pixel art animation, with a model for each frame.
Though not strictly necessary, the boat had several animated parts: the sail and the flag, plus the oarsmen and the oars.
The oarsmen also served as a game design purpose -to indicate the number of lives the player had, each time the player took damage,
one of the sailors would fly out of the boat. Some decoration was created to bring life to the game's world.
Plants, houses, and shipwrecks were designed and modeled, but in the end, never made it to the final cut.
Currently, each model has to be imported by hand, and even the meta files and ID have to be manually assigned.
The 2-day Jam was a very limited time frame for this level of polish.&lt;/p&gt;
&lt;figure class="m-figure"&gt;
&lt;img src="https://cubos.riscadoa.com/images/blog/demo/azul-jam-2025/Deco.png" /&gt;
&lt;figcaption&gt;Models to populate the game world.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/section&gt;
&lt;section id="playtest"&gt;
&lt;h4&gt;Playtest&lt;/h4&gt;
&lt;p&gt;The game was playtested during the jam's showcase, and it was well received by the community present.
A real sense of competition sprout in the players, and fierce fights unfold before our eyes.
The game was praised for its engaging gameplay and innovative art style. Some even comment that it locked like pixel art.
The voxel resolution and the camera perspective might have helped in this regard.&lt;/p&gt;
&lt;figure class="m-figure"&gt;
&lt;img src="https://cubos.riscadoa.com/images/blog/demo/azul-jam-2025/CubosInAzul5.jpg" /&gt;
&lt;figcaption&gt;Cubos showcasing its game in Azul Game Jam - photo by Gonçalo Goulão.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Few days later the Cubos team took the opportunity to showcase the game in a &lt;a href="https://www.eventbrite.pt/cc/game-break-3434859"&gt;Game Break&lt;/a&gt; event, at &lt;a href="https://unicornfactorylisboa.com/hubs/"&gt;Gaming Hub&lt;/a&gt;.
Developers from Portugal and beyond gather at these Game Break events every mouth at Gaming Hub to showcase their latest work, discuss ideas and network.
Ondisseia was again showcased in this last april edition, and again, the feedback was really positive. Some suggestions were made about the aiming system -
it seemed to some to be too complicated. Only when the player is sideways to other player, over a minimum of 5 chunks, is it possible to shoot arrows.
It was also suggested to use a free movement instead of the grid base one.
Maybe the most interesting suggestion was to try and make the game run in mobile (browser).&lt;/p&gt;
&lt;img class="m-image" src="https://cubos.riscadoa.com/images/blog/demo/azul-jam-2025/CubosGameBreak9.jpg" /&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="android-features"&gt;
&lt;h2&gt;Android Features&lt;/h2&gt;
&lt;p&gt;Cubos uses &lt;a href="https://emscripten.org/"&gt;Emscripten&lt;/a&gt; to export to WebGL.
We already had a build from Emscripten for Azul Game Jam, but the formatting was incompatible with itch.io. This build was made for a single web page and not to be embedded on Itch.io. Therefore, we strived to simplify the HTML and customize the CSS classes to our liking.&lt;/p&gt;
&lt;p&gt;There were problems with the way Emscripten controls the canvas. Apart from the usual fullscreen bottom, there was a Resize canvas and  Lock/hide mouse pointer, for resizing and hiding the mouse in fullscreen, respectively. We've opted to rewrite these functionalities in a new .JavaScript, but we aim to integrate these better in the future.&lt;/p&gt;
&lt;p&gt;After this step, we had a functional WebGL build on Itch.io, though no UI and functionalities for mobile devices had yet been included. The game would open, the sound would play, but there wasn't any interactivity.&lt;/p&gt;
&lt;figure class="m-figure"&gt;
&lt;img src="https://cubos.riscadoa.com/images/blog/demo/azul-jam-2025/AzulAndroid1.jpg" /&gt;
&lt;figcaption&gt;Screenshot of early test of the game in mobile.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;To support mobile devices better, new UI  elements were created with HTML and CSS. Still, there wasn't any way to connect them with the game code.  For this purpose, this JavaScript Function was created to send the bottom's interaction to the game:&lt;/p&gt;
&lt;pre class="m-code"&gt;&lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;simulateKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;keyCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;isDown&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;KeyboardEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isDown&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;keydown&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;keyup&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nl"&gt;keyCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;keyCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nl"&gt;which&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;keyCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nl"&gt;bubbles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nl"&gt;cancelable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;This combined with this HTML button:&lt;/p&gt;
&lt;pre class="m-code"&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;shoot&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ontouchstart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;simulateKey(32, true)&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ontouchend&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;simulateKey(32, false)&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;space&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;Allowed to interact with the game&lt;/p&gt;
&lt;figure class="m-figure"&gt;
&lt;img src="https://cubos.riscadoa.com/images/blog/demo/azul-jam-2025/AzulAndroid2.jpg" /&gt;
&lt;figcaption&gt;Screenshot of the game in a mobile browser.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Despite this progress, a new problem emerged; now we were stuck with these inputs on the PC.
To solve this issue, we added a function to hide the mobile control when no longer necessary:&lt;/p&gt;
&lt;pre class="m-code"&gt;&lt;span class="c1"&gt;// Detect if it&amp;#39;s a touch device (Android or otherwise)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;ontouchstart&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;maxTouchPoints&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;// Detect Android specifically&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;isAndroid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;Android&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;userAgent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isAndroid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// If it&amp;#39;s Android, display the touch controls&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;touch&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;controls&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;visibility&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;visible&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;fullscreen&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;btn&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;visibility&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;hidden&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;With this final touch, we were able to hide the extra UI when the game was running on a PC browser and enable the UI in a mobile device browser.
After the jam, we integrated these changes into the main branch of Cubos, so that all samples can be easily ported to mobile devices.&lt;/p&gt;
&lt;/section&gt;
</content><category term="Demo"/><category term="Demo"/><category term="Game Jam"/><category term="Azul Game Jam"/></entry><entry><title>Release 0.6</title><link href="https://cubos.riscadoa.com/blog/release-06/" rel="alternate"/><published>2025-03-12T00:00:00+00:00</published><updated>2025-03-12T00:00:00+00:00</updated><author><name>Cubos Team</name></author><id>tag:cubos.riscadoa.com,2025-03-12:/blog/release-06/</id><summary type="html">&lt;p&gt;A smaller update focused on performance, physics stability and better options for lower end machines.&lt;/p&gt;
</summary><content type="html">&lt;section id="new-0-6-features"&gt;
&lt;h2&gt;New 0.6 Features&lt;/h2&gt;
&lt;p&gt;This blog post covers the most important changes, but you can check out the full changelog in our &lt;a href="https://github.com/GameDevTecnico/cubos/blob/main/CHANGELOG.md"&gt;repository&lt;/a&gt;.&lt;/p&gt;
&lt;section id="on-the-engine"&gt;
&lt;h3&gt;On the Engine&lt;/h3&gt;
&lt;section id="physics-with-warm-starting-fallenatlas"&gt;
&lt;h4&gt;Physics with Warm-Starting &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/tiago-antunes/"&gt;&amp;#64;fallenatlas&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;Continuing the work from the previous release, we finished &lt;strong&gt;warm-starting&lt;/strong&gt; for our physics simulation. Warm-starting uses the information stored in the contact manifold between two bodies, especially the total impulses applied to each contact point in the previous frame, and applies it before solving begins.
It works based on the assumption that the impulses applied in the previous frame are a good estimate for the impulses that will also be applied in the current frame. Therefore, by applying them to the body before solving the collision constraint, the body should already be closer to its ideal position. Over time, these impulses will stabilize (since our solver is a &amp;quot;dirty&amp;quot; solver), resulting in less jitter.
This is particularly effective for stacks of bodies, where the bodies push against each other, creating instability. However, if they're already closer to their ideal position, this instability will be reduced. With warm-starting, we can stack more bodies, and when the stack isn't fully stable, it stays together for longer.&lt;/p&gt;
&lt;p&gt;Here you can see how, in our complex physics sample, the stack holds for much longer than before, although it still falls in the end.&lt;/p&gt;
&lt;img class="m-image" src="https://cubos.riscadoa.com/images/blog/release/0-6/complex_physics_sample_warm_starting.gif" /&gt;
&lt;p&gt;However, if we increase our physics update rate to 60 times per second (rather than our current 30), and the substeps performed in each update to 8 (instead of our current 4), we can see that a stack of 8 blocks holds mostly stable.&lt;/p&gt;
&lt;img class="m-image" src="https://cubos.riscadoa.com/images/blog/release/0-6/complex_physics_sample_1_stack.gif" /&gt;
&lt;p&gt;Unfortunately, our collisions are not optimized to the point where we can have 60 updates per second with many bodies, but the door is open for this upgrade in the future!&lt;/p&gt;
&lt;/section&gt;
&lt;section id="toggleable-ssao-and-resolution-scale-tomas7770"&gt;
&lt;h4&gt;Toggleable SSAO and Resolution Scale &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/tomas-gouveia/"&gt;&amp;#64;tomas7770&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;Some time ago, we profiled &lt;strong&gt;Cubos&lt;/strong&gt;'s graphics engine to determine the main performance bottlenecks. SSAO stood out as the
graphics plugin that took the most frame time. However, due to the way the rendering pipeline was implemented, disabling it to get
better performance wasn't possible without breaking rendering altogether.
Starting with this release, the &lt;a href="https://docs.cubos.riscadoa.com/structcubos_1_1engine_1_1SSAO.html"&gt;SSAO&lt;/a&gt; component can be removed
from a render target to disable this feature.&lt;/p&gt;
&lt;p&gt;In addition, we found that other game engines often default to rendering SSAO textures at half the width/height of the screen resolution,
which greatly improves performance without major loss in quality.
To implement this, we added a &lt;a href="https://docs.cubos.riscadoa.com/structcubos_1_1engine_1_1SSAO.html#ab4c8760eb1582559172373476ff04508"&gt;resolution scale&lt;/a&gt; option to the SSAO component,
which defaults to 0.5, but can be set to any value up to 2.0.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="conditional-shader-compilation-tomas7770"&gt;
&lt;h4&gt;Conditional Shader Compilation &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/tomas-gouveia/"&gt;&amp;#64;tomas7770&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;Another major bottleneck in the graphics renderer is the &lt;a href="https://docs.cubos.riscadoa.com/structcubos_1_1engine_1_1RenderPicker.html"&gt;render picker&lt;/a&gt;, which
allows finding out which entity is visible at a given pixel. As explained in the &lt;strong&gt;Modular Renderer&lt;/strong&gt; section of the &lt;a href="https://cubos.riscadoa.com/coffeen-jam-and-02.html"&gt;0.2 blog post&lt;/a&gt;,
we attempted to mitigate performance loss with Pixel Buffers and double-buffering. But despite this, it remains a demanding feature.&lt;/p&gt;
&lt;p&gt;Just like what happened with SSAO, disabling the render picker wasn't possible. Unlike the former, though, this one required the ability
to change shaders' source code at runtime. To solve this, we added new methods to the &lt;a href="https://docs.cubos.riscadoa.com/classcubos_1_1engine_1_1Shader.html"&gt;Shader&lt;/a&gt;
class to prepend &lt;code&gt;#define&lt;/code&gt; macros and recompile the shader.&lt;/p&gt;
&lt;pre class="m-code"&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;vs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;vsAsset&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;shaderStage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// unmodified shader&lt;/span&gt;
&lt;span class="n"&gt;vs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;vsAsset&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;RENDER_PICKER&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// shader with #define RENDER_PICKER&lt;/span&gt;
&lt;span class="n"&gt;vs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;vsAsset&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;MAX_CSM_SPLITS&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;5&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// shader with #define MAX_CSM_SPLITS 5&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;Unfortunately, the actual ability to toggle render picking didn't make it to this release, but it should make it to the next one!&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="on-the-core"&gt;
&lt;h3&gt;On the Core&lt;/h3&gt;
&lt;section id="added-frustum-geometry-and-intersection-utilities-mkuritsu"&gt;
&lt;h4&gt;Added Frustum geometry and intersection utilities &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/rodrigo-correia/"&gt;&amp;#64;mkuritsu&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;A new geometry object was added, the &lt;strong&gt;frustum&lt;/strong&gt;, and additionally, two new intersection functions to check if a &lt;a href="https://docs.cubos.riscadoa.com/structcubos_1_1core_1_1geom_1_1Box.html"&gt;box&lt;/a&gt; or a &lt;a href="https://docs.cubos.riscadoa.com/structcubos_1_1core_1_1geom_1_1Capsule.html"&gt;capsule&lt;/a&gt; intersects a frustum.
This new geometry utility will be useful for &lt;em&gt;frustum culling&lt;/em&gt; in a future release, potentially improving graphics performance when rendering large scenes with &lt;strong&gt;Cubos&lt;/strong&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="new-demo-sneak-peek"&gt;
&lt;h2&gt;New Demo Sneak Peek&lt;/h2&gt;
&lt;p&gt;We have been working on a new game made with &lt;strong&gt;Cubos&lt;/strong&gt;, and we're excited to share a sneak peek with you!
It's a competitive multiplayer game where you and your team pilot an airship and fight against other teams.
We plan to implement networked multiplayer, allowing you to play with friends online — this will be a first for &lt;strong&gt;Cubos&lt;/strong&gt;!
Stay tuned for more details after the 0.7 release blog post!&lt;/p&gt;
&lt;img class="m-image" src="https://cubos.riscadoa.com/images/blog/release/0-6/airships_sneak_peek.png" /&gt;
&lt;/section&gt;
&lt;section id="next-steps"&gt;
&lt;h2&gt;Next Steps&lt;/h2&gt;
&lt;p&gt;In the next release, which should be out by the end of April, we're planning to work on the following features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A menu bar and a sensible default layout for &lt;strong&gt;Tesseratos&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;A proper scene editor for &lt;strong&gt;Tesseratos&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Automated inertia computation for arbitrary voxel shapes.&lt;/li&gt;
&lt;li&gt;Frustum culling when rendering.&lt;/li&gt;
&lt;li&gt;Various other performance improvements in the renderer.&lt;/li&gt;
&lt;li&gt;Better integration of mouse input with the high-level input system.&lt;/li&gt;
&lt;li&gt;And many other new features and bug fixes!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the meantime, we'll also be participating in the &lt;a href="https://azulgamejam.com/"&gt;Azul Jam&lt;/a&gt;, a game jam happening over the weekend of March 28nd.
We're excited to see what we can come up with in just 48 hours! A blog post about our experience will be published sometime after the jam.&lt;/p&gt;
&lt;p&gt;You can check out the full list of things we want to get done in the &lt;a href="https://github.com/GameDevTecnico/cubos/milestone/30"&gt;milestone&lt;/a&gt; for the next release.&lt;/p&gt;
&lt;/section&gt;
</content><category term="Release"/><category term="Release"/></entry><entry><title>Release 0.5</title><link href="https://cubos.riscadoa.com/blog/release-05/" rel="alternate"/><published>2025-01-27T00:00:00+00:00</published><updated>2025-01-27T00:00:00+00:00</updated><author><name>Cubos Team</name></author><id>tag:cubos.riscadoa.com,2025-01-27:/blog/release-05/</id><summary type="html">&lt;p&gt;From Voxel collisions, point light shadows and an audio plugin, to web support, text rendering, and FXAA.&lt;/p&gt;
</summary><content type="html">&lt;section id="new-0-5-features"&gt;
&lt;h2&gt;New 0.5 Features&lt;/h2&gt;
&lt;p&gt;This blog post covers the most important changes in the 0.5 release - we've added a lot of new features, such point light shadows, audio support, MSDF text rendering and more!
We've released 0.5 some time ago, but with the exam season and the holidays, we only got around to writing this blog post now.
As usual, you can check out the full changelog in our &lt;a href="https://github.com/GameDevTecnico/cubos/blob/main/CHANGELOG.md"&gt;repository&lt;/a&gt;.&lt;/p&gt;
&lt;section id="on-the-editor"&gt;
&lt;h3&gt;On the Editor&lt;/h3&gt;
&lt;section id="hold-mode-for-debug-camera-jdbaracho"&gt;
&lt;h4&gt;Hold mode for Debug Camera &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/joao-baracho/"&gt;&amp;#64;jdbaracho&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;For debugging purposes, we have a debug camera which allows the user to move around the scene and inspect it.
Previous this camera could only be controlled by locking the camera to the mouse with a toggle.
This was inconvenient when also using the mouse to interact with the editor UI, so we added a new mode where the camera can be rotated by holding a key or mouse button (e.g. the middle mouse button).&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="on-the-engine"&gt;
&lt;h3&gt;On the Engine&lt;/h3&gt;
&lt;section id="point-light-shadows-tomas7770"&gt;
&lt;h4&gt;Point Light Shadows &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/tomas-gouveia/"&gt;&amp;#64;tomas7770&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;Over the past couple of releases, &lt;strong&gt;shadows&lt;/strong&gt; have been gradually introduced to the engine's graphics
renderer, starting with spotlight shadows, and then directional ones. Naturally, the next step was to
implement shadows for point lights, finally completing support for the most common types of lights.&lt;/p&gt;
&lt;p&gt;Point shadows are enabled by adding a &lt;a href="https://docs.cubos.riscadoa.com/structcubos_1_1engine_1_1PointShadowCaster.html"&gt;PointShadowCaster&lt;/a&gt;
component to the light entity.
They behave similarly to spot shadows, lacking all the configurable values of the more complex directional shadows.
You can read the previous blog posts for more detailed information (&lt;a href="https://cubos.riscadoa.com/03-is-here.html"&gt;0.3&lt;/a&gt;, &lt;a href="https://cubos.riscadoa.com/04-is-out.html"&gt;0.4&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;In terms of implementation, point shadows are also very similar to spot shadows, using a shared atlas texture
that stores shadow maps for every point light in a quadtree structure. However, because point lights cast light in
all directions, there are 6 atlas textures, one for each direction of a cube.&lt;/p&gt;
&lt;p&gt;The final result of our shadows engine can be seen in the &lt;a href="https://github.com/GameDevTecnico/cubos/tree/main/engine/samples/render/shadows"&gt;Shadows sample&lt;/a&gt;.
Below is a screenshot of the sample with all light types casting shadows. Some tweaks have been done to better highlight the shadows.&lt;/p&gt;
&lt;img class="m-image" src="https://cubos.riscadoa.com/images/blog/release/0-5/shadows_sample.png" /&gt;
&lt;/section&gt;
&lt;section id="audio-support-through-the-audio-plugin-dageus-diogomsmiranda"&gt;
&lt;h4&gt;Audio Support through the Audio Plugin  &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/joao-nogueira/"&gt;&amp;#64;Dageus&lt;/a&gt;, &lt;a href="https://cubos.riscadoa.com/blog/author/diogo-miranda/"&gt;&amp;#64;diogomsmiranda&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;This release is a significant milestone for the Cubos engine, as it finally introduces Audio functionality!&lt;/p&gt;
&lt;p&gt;The new &lt;code&gt;audioPlugin&lt;/code&gt; leverages the previously implemented &lt;code&gt;Audio Context&lt;/code&gt; to manage audio playback. It introduces two new components:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;AudioSource&lt;/code&gt;: Attach this component to entities to play audio files in supported formats such as FLAC, MP3 and WAV.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AudioListener&lt;/code&gt;: Attach this component to an entity to capture audio from the scene.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With the plugin, you can now register listeners, and control audio sources with play, pause, and stop functionality. You can also customize audio playback by modifying the &lt;code&gt;AudioSource&lt;/code&gt; properties for each entity, adjusting settings such as volume, pitch, and looping behavior to tailor the sound to your needs.
Due to restrictions in our audio backend, you can only have up to &lt;strong&gt;three active listeners&lt;/strong&gt; at a time. For practical examples of how to use these new audio components (&lt;code&gt;AudioPlay&lt;/code&gt;, &lt;code&gt;AudioPause&lt;/code&gt;, &lt;code&gt;AudioStop&lt;/code&gt;), check out our &lt;a href="https://github.com/GameDevTecnico/cubos/blob/main/engine/samples/audio/main.cpp"&gt;Audio Sample&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="method-to-save-settings-to-files-srgesus"&gt;
&lt;h4&gt;Method to save settings to files &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/gabriel-ferreira/"&gt;&amp;#64;SrGesus&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;Settings&lt;/code&gt; resource was missing some way to interact with its corresponding file,
as it was limited to a startup system that would load the file from a command line
argument with no way for posterior changes to persist which was inconvenient.&lt;/p&gt;
&lt;p&gt;Now there are two new simple methods - &lt;code&gt;save(path, indentation)&lt;/code&gt; and
&lt;code&gt;load(path)&lt;/code&gt; - to save and load the &lt;code&gt;Settings&lt;/code&gt; to and from
&lt;code&gt;cubos::core::data::FileSystem&lt;/code&gt; paths. E.g:&lt;/p&gt;
&lt;pre class="m-code"&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/file.json&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/file_backup.json&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/pre&gt;
&lt;/section&gt;
&lt;section id="collision-detection-between-voxelcollisionshapes-joaomanita"&gt;
&lt;h4&gt;Collision detection between VoxelCollisionShapes &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/joao-manita/"&gt;&amp;#64;joaomanita&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;The engine was only able to able to detect collisons between boxes, now we can add a &lt;a href="https://docs.cubos.riscadoa.com/classcubos_1_1engine_1_1VoxelCollisionShape.html"&gt;VoxelCollisionShape&lt;/a&gt;
to decompose a voxel shape into multiple box collision shapes.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="anti-aliasing-using-the-fxaa-technique-kuukitenshi"&gt;
&lt;h4&gt;Anti-aliasing using the FXAA technique &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/laura-cunha/"&gt;&amp;#64;kuukitenshi&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;We introduced the FXAA (Fast Approximate Anti-Aliasing) technique to reduce jagged edges in the rendered images.
As you can see in the images below, FXAA smooths out the visuals without the performance cost of traditional anti-aliasing methods.&lt;/p&gt;
&lt;img-comparison-slider&gt;
&lt;figure class="before" slot="first"&gt;
&lt;img src="https://cubos.riscadoa.com/images/blog/release/0-5/cars_before.png" style="width: 100%" /&gt;
&lt;figcaption&gt;FXAA Off&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class="after" slot="second"&gt;
&lt;img src="https://cubos.riscadoa.com/images/blog/release/0-5/cars_after.png" style="width: 100%" /&gt;
&lt;figcaption&gt;FXAA On&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/img-comparison-slider&gt;
&lt;/section&gt;
&lt;section id="ui-text-element-using-msdf-for-text-rendering-mkuritsu"&gt;
&lt;h4&gt;UI text element using MSDF for text rendering &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/rodrigo-correia/"&gt;&amp;#64;mkuritsu&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;A significant addition to the Cubos engine was the support for text rendering in the UI, which will greatly improve the capabilities
for communicating with the player in future games developed with the engine.&lt;/p&gt;
&lt;p&gt;This new features comes in the &lt;cite&gt;uiTextPlugin&lt;/cite&gt;, which includes the &lt;cite&gt;UIText&lt;/cite&gt; component, containing all the data needed
to draw text on the screen. The plugin also adds two new asset types:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Font&lt;/strong&gt;: holds the raw font data to be used to create font atlases (.ttf and .otf files supported).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;FontAtlas&lt;/strong&gt;: maps characters to their glyph on a given font, with different configurable properties.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In order to draw the text on the screen this plugins uses Multi Signed Distante Fields (MSDFs) with the help from &lt;a href="https://github.com/Chlumsky/msdfgen"&gt;msdfgen&lt;/a&gt;
and &lt;a href="https://github.com/Chlumsky/msdf-atlas-gen"&gt;msdf-atlas-gen&lt;/a&gt;, and &lt;a href="https://freetype.org/"&gt;FreeType&lt;/a&gt; to load different font formats. Below is a screenshot
taken from the UI sample available in the engine showcasing the text rendering in action.&lt;/p&gt;
&lt;img class="m-image" src="https://cubos.riscadoa.com/images/blog/release/0-5/ui-text.png" /&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Currently only the ASCII charset is supported, UTF-8 support will be added in a future release.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="enable-assets-to-be-identified-in-the-code-by-their-file-path-galaxycrush"&gt;
&lt;h4&gt;Enable assets to be identified in the code by their file path &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/joao-pereira/"&gt;&amp;#64;GalaxyCrush&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;Assets could only be identified by their UUID, which can be hard to keep track of for human developers.
Now, they are able to be identified by their path, thus making them easier to handle when developing projects with Cubos.
E.g., you can now do:&lt;/p&gt;
&lt;pre class="m-code"&gt;&lt;span class="n"&gt;Asset&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Scene&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SceneAsset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AnyAsset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/path/to/asset&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;instead of:&lt;/p&gt;
&lt;pre class="m-code"&gt;&lt;span class="n"&gt;Asset&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Scene&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SceneAsset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AnyAsset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;uuid&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/pre&gt;
&lt;/section&gt;
&lt;section id="option-to-use-shadow-normal-offset-bias-algorithm-galaxycrush"&gt;
&lt;h4&gt;Option to use Shadow Normal Offset Bias algorithm &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/joao-pereira/"&gt;&amp;#64;GalaxyCrush&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;We noticed that the shadows had some &lt;a href="https://digitalrune.github.io/DigitalRune-Documentation/html/3f4d959e-9c98-4a97-8d85-7a73c26145d7.htm#Acne"&gt;shadow acne&lt;/a&gt; and/or &lt;a href="https://digitalrune.github.io/DigitalRune-Documentation/html/3f4d959e-9c98-4a97-8d85-7a73c26145d7.htm#PeterPanning"&gt;peter panning&lt;/a&gt; artifacts in some samples. So, to improve it, we implemented the option of using the Shadow Normal Offset Bias algorithm, which improved the quality of the shadows and reduced these artifacts. To use the algorithm, the user simply has to assign a value to the normalOffsetScale field in the light casters &lt;a href="https://docs.cubos.riscadoa.com/structcubos_1_1engine_1_1ShadowCaster.html"&gt;ShadowCaster&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="resource-to-easily-configure-constants-in-the-physics-solver-gcesilva"&gt;
&lt;h4&gt;Resource to easily configure constants in the physics solver &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/goncalo-silva/"&gt;&amp;#64;GCeSilva&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;The physics solver previously included hardcoded constants, which were not easily accessible or modifiable.
A new resource has been added which allows the user to configure these constants, making it easier to tune and debug, even at runtime.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="contact-caching-for-collision-between-box-shapes-fallenatlas"&gt;
&lt;h4&gt;Contact caching for collision between box shapes &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/tiago-antunes/"&gt;&amp;#64;fallenatlas&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;We added contact caching as a first step towards implementing warm-starting. Contact caching consists of keeping information about collisions that were happening in the previous physics update
so that in the next update we can trace them back. This information includes identifiers for each collision manifold and the corrections applied to each contact point. Due to limitations in the current implementation of
collisions between entities, this is currently only supported for box shapes.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="on-the-core"&gt;
&lt;h3&gt;On the Core&lt;/h3&gt;
&lt;section id="a-proper-cmake-configuration-riscadoa"&gt;
&lt;h4&gt;A Proper CMake Configuration &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/ricardo-antunes/"&gt;&amp;#64;RiscadoA&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;While functional, our &lt;a href="https://cmake.org/"&gt;CMake&lt;/a&gt; build configuration was incomplete in terms of features and poorly organized.
As the engine evolved, we recognized the need to install it as a system-wide library for use in other projects.
Before, we always simply added the engine repository as a &lt;code&gt;git&lt;/code&gt; submodule in our projects, which required maintaining a local copy of the entire codebase for each project - an inneficient approach.
To address this, we decided to clean up the CMake configuration, and add installation and &lt;code&gt;find_package&lt;/code&gt; support. As a result, you can now install the whole Cubos project, and to use it in another project, you only need to include the following lines in your &lt;code&gt;CMakeLists.txt&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="m-code"&gt;&lt;span class="nb"&gt;find_package&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;cubos&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;REQUIRED&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;target_link_libraries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;your_game&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;PRIVATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;cubos::engine&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;One big advantage of this is that we can now also install Quadrados and Tesseratos, the tools we use for making games with Cubos, system-wide, as you would with any other application.
As of now, Cubos has only been packaged for NixOS, but we haven't contributed it to the Nixpkgs repository yet. We plan to do so in the future, and also to package it for other distributions.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="web-support-with-emscripten-riscadoa-luishfonseca"&gt;
&lt;h4&gt;Web Support with Emscripten &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/ricardo-antunes/"&gt;&amp;#64;RiscadoA&lt;/a&gt;, &lt;a href="https://cubos.riscadoa.com/blog/author/luis-fonseca/"&gt;&amp;#64;luishfonseca&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;We've had a pull request open since 0.2 for porting Cubos to the web with &lt;a href="https://emscripten.org/"&gt;Emscripten&lt;/a&gt;, but it was constantly postponed due to some blocking issues being present, and us being focused on other features.
Now, we finally made the final push to get it working, and now Cubos can be compiled to WebAssembly and ran in the browser!&lt;/p&gt;
&lt;p&gt;This is an important feature, as for game jams, for example, it's handy to be able to share your game with others without them having to download anything, and also to be able to play it on any device.
It's a good way to showcase the engine to people who might be interested in using it, but are reluctant to download it.
We plan on embedding each sample on their respective documentation page, but that will be left for a future release.&lt;/p&gt;
&lt;p&gt;One of the major challenges was guaranteeing compatibility with WebGL2. Fortunately, the rendering code is built on top of a rendering API abstraction layer.
We had to make some changes to this layer to guarantee we cover strictly the intersection between the features of OpenGL 3.3 and WebGL2, but we managed to do it without much hassle.
As of now, this means we're making some performance sacrifices, as we're not using advanced features even when they're available. We plan to address this in the future.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="next-steps"&gt;
&lt;h2&gt;Next Steps&lt;/h2&gt;
&lt;p&gt;In the next release, which should be out by February 6th, we're planning to work on the following features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Toggleable gravity on the physics plugin.&lt;/li&gt;
&lt;li&gt;Warm starting in physics solver.&lt;/li&gt;
&lt;li&gt;Graphics performance improvements.&lt;/li&gt;
&lt;li&gt;Transparent voxel rendering.&lt;/li&gt;
&lt;li&gt;Particle system.&lt;/li&gt;
&lt;li&gt;Editor UX improvements, making it very useful.&lt;/li&gt;
&lt;li&gt;New scene editor.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We also developed a demo showcasing the new features of this release, which you will be able to check out pretty soon here.&lt;/p&gt;
&lt;p&gt;You can check out the full list of stuff we want to get done in the &lt;a href="https://github.com/GameDevTecnico/cubos/milestone/29"&gt;milestone&lt;/a&gt; for the next release.&lt;/p&gt;
&lt;/section&gt;
</content><category term="Release"/><category term="Release"/></entry><entry><title>Release 0.4</title><link href="https://cubos.riscadoa.com/blog/release-04/" rel="alternate"/><published>2024-11-05T00:00:00+00:00</published><updated>2024-11-05T00:00:00+00:00</updated><author><name>Cubos Team</name></author><id>tag:cubos.riscadoa.com,2024-11-05:/blog/release-04/</id><summary type="html">&lt;p&gt;Rotating rigid bodies, cascading shadow maps, a standalone editor application, better telemetry and much more!&lt;/p&gt;
</summary><content type="html">&lt;section id="new-0-4-features"&gt;
&lt;h2&gt;New 0.4 Features&lt;/h2&gt;
&lt;p&gt;This blog post covers the most important changes, but you can check out the full changelog in our &lt;a href="https://github.com/GameDevTecnico/cubos/blob/main/CHANGELOG.md"&gt;repository&lt;/a&gt;.&lt;/p&gt;
&lt;section id="on-the-editor"&gt;
&lt;h3&gt;On the Editor&lt;/h3&gt;
&lt;section id="project-management-and-debugging-riscadoa"&gt;
&lt;h4&gt;Project Management and Debugging &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/ricardo-antunes/"&gt;&amp;#64;RiscadoA&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;In this release, we were finally able to make our editor, &lt;strong&gt;Tesseratos&lt;/strong&gt;, a standalone application!
Previously, the editor was a part of the engine itself, which meant if you made changes to the game's code, you had to recompile the editor as well, and restart it.
Now the editor has its own executable, and through it, you can open projects, edit their assets, and even debug them - all without having to restart the editor when recompiling the game.
At this time, the UX is still a bit rough, as we've focused on making it work first.&lt;/p&gt;
&lt;p&gt;One major hurdle we had to overcome was how to make the editor aware of the project's data types, such as its components and resources.
Before, they were part of the same application, and thus, the editor could simply access them directly.
Now, with the editor being a separate application, we needed a way to share this information across different processes.
You can read more about it in the &amp;quot;Type Client and Type Server&amp;quot; section at the end of this post, but in short, with the new reflection functionalities, it is now possible to share type data across different processes, through, for example, a socket.&lt;/p&gt;
&lt;p&gt;In practice, this means that games made with Cubos can now be launched with a debug flag which makes them act as a server at a given port.
Then, the editor is able to connect to this server and extract all of the required debug information.
We also make use of this new connection to allow the editor to send debugging commands to the game, such as pausing, resuming, and stepping the game's execution.&lt;/p&gt;
&lt;p&gt;So, essentially, we've added two new tools to the editor:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A debugger tool, which allows you to connect to a running game and debug it (pause, resume, step, etc.).&lt;/li&gt;
&lt;li&gt;A project manager tool, which allows you to open projects, edit their assets, and launch them.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can see them in action in the video below:&lt;/p&gt;
&lt;iframe width="560" height="340" style="display: block; margin: auto" src="https://www.youtube.com/embed/Pu40BjmmW2U" title="" frameBorder="0"   allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"  allowFullScreen&gt;&lt;/iframe&gt;&lt;/section&gt;
&lt;section id="voxel-model-importing-tool-scarface1809"&gt;
&lt;h4&gt;Voxel Model Importing Tool &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/joao-santos/"&gt;&amp;#64;Scarface1809&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;In this release, we've added a highly useful plugin to &lt;strong&gt;Tesseratos&lt;/strong&gt;: a &lt;strong&gt;Voxel Model Importing Tool&lt;/strong&gt;. With this new tool, you can now import &lt;cite&gt;.qb&lt;/cite&gt; files directly within the editor, bypassing the need for the external conversion tool &lt;strong&gt;Quadrados&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Previously, importing voxel models required converting &lt;cite&gt;.qb&lt;/cite&gt; files into &lt;cite&gt;.pal&lt;/cite&gt; and &lt;cite&gt;.grd&lt;/cite&gt; files using &lt;strong&gt;Quadrados&lt;/strong&gt;, a CLI tool, before they could be used in the engine. Now, with the &lt;strong&gt;Voxel Model Importer Plugin&lt;/strong&gt;, the process has been simplified, acting as an interface to handle the conversion process within the editor.&lt;/p&gt;
&lt;p&gt;The tool allows you to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Assign names to the &lt;cite&gt;.pal&lt;/cite&gt; and &lt;cite&gt;.grd&lt;/cite&gt; files.&lt;/li&gt;
&lt;li&gt;Choose the file paths where the &lt;cite&gt;.grd&lt;/cite&gt; files and the &lt;cite&gt;.pal&lt;/cite&gt; file should be saved.&lt;/li&gt;
&lt;li&gt;Specify how many &lt;cite&gt;.grd&lt;/cite&gt; files supported by the &lt;cite&gt;.qb&lt;/cite&gt; file you want to import.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With this tool, importing voxel models into your projects is now faster and easier, allowing for a smoother workflow and less reliance on &lt;strong&gt;Quadrados&lt;/strong&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="on-the-engine"&gt;
&lt;h3&gt;On the Engine&lt;/h3&gt;
&lt;section id="physics-with-rotation-fallenatlas"&gt;
&lt;h4&gt;Physics with Rotation &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/tiago-antunes/"&gt;&amp;#64;fallenatlas&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;We have &lt;strong&gt;rotation&lt;/strong&gt;!&lt;/p&gt;
&lt;img class="m-image" src="https://cubos.riscadoa.com/images/blog/release/0-4/complex_physics_sample_rotations.gif" /&gt;
&lt;p&gt;Since the start of the physics plugin, we always treated rigid bodies as simple particles.
This is because it made the calculations more intuitive, and it reduced the potential amount of mistakes we could make when first starting out. It also made debugging - of penetration solving for example - easier.
With the particle behaviour stabilized, we decided it was time to expand the plugin to consider rotation, and for that we had to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Add Collision Manifold and Collision Points computation.&lt;/li&gt;
&lt;li&gt;Add Components for Rotation.&lt;/li&gt;
&lt;li&gt;Expand the Integrator and Solver to compute rotations.&lt;/li&gt;
&lt;/ul&gt;
&lt;section id="collision-manifold-and-collision-points"&gt;
&lt;h5&gt;Collision Manifold and Collision Points&lt;/h5&gt;
&lt;p&gt;Previously, since everything was a particle, we simply computed if there was a collision using SAT, which also returned the normal along which the penetration was the smallest, along with that penetration value.
For rotations we need a &lt;code&gt;ContactManifold&lt;/code&gt; relation, that holds all information about the collision. A &lt;em&gt;Contact Manifold&lt;/em&gt; is effectively a 2D polygon that details the surface of contact between the 2 bodies.
In computer physics the bodies inter-penetrate each other, which does not happen in the real world. The manifold is the aproximation of the real contact surface the bodies would have.
This polygon is described by its vertices, in the form of &lt;em&gt;Contact Points&lt;/em&gt;, all of which contain their position and penetration in &lt;code&gt;ContactPointData&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To compute the manifold we use the Sutherland Hodgman algorithm. Our current implementation was mostly inspired by this &lt;a href="https://research.ncl.ac.uk/game/mastersdegree/gametechnologies/previousinformation/physics5collisionmanifolds/2017%20Tutorial%205%20-%20Collision%20Manifolds.pdf"&gt;tutorial&lt;/a&gt;, which you can check out, to see how it works.&lt;/p&gt;
&lt;pre class="m-code"&gt;&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;ContactPointData&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;cubos&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ecs&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Entity&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;///&amp;lt; Entity to which the normal is relative to.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;glm&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vec3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;globalOn1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="c1"&gt;///&amp;lt; Position on the entity of the contact in global coordinates.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;glm&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vec3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;globalOn2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="c1"&gt;///&amp;lt; Position on the other entity of the contact in global coordinates.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;glm&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vec3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;localOn1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="c1"&gt;///&amp;lt; Position on the entity of the contact in local coordinates.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;glm&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vec3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;localOn2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="c1"&gt;///&amp;lt; Position on the other entity of the contact in local coordinates.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;penetration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;               &lt;/span&gt;&lt;span class="c1"&gt;///&amp;lt; Penetration of the contact point. Always positive.&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;ContactManifold&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;cubos&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ecs&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Entity&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;///&amp;lt; Entity to which the normal is relative to.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;glm&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vec3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;normal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;                     &lt;/span&gt;&lt;span class="c1"&gt;///&amp;lt; A contact normal shared by all contacts in this manifold,&lt;/span&gt;
&lt;span class="w"&gt;                                          &lt;/span&gt;&lt;span class="c1"&gt;///&amp;lt; expressed in the local space of the first entity.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ContactPointData&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;points&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;///&amp;lt; Contact points of this manifold.&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;With this change, our narrow phase firstly checks for collision with SAT, since it's cheaper, and only then computes the manifold.&lt;/p&gt;
&lt;p&gt;The collisions sample was also enhanced so we could visualize this information.&lt;/p&gt;
&lt;img class="m-image" src="https://cubos.riscadoa.com/images/blog/release/0-4/collision_sample_manifold_demo.gif" /&gt;
&lt;/section&gt;
&lt;section id="new-components"&gt;
&lt;h5&gt;New Components&lt;/h5&gt;
&lt;p&gt;The next thing to add were Components to support rotations, such as &lt;code&gt;Inertia&lt;/code&gt;, &lt;code&gt;Torque&lt;/code&gt;, and &lt;code&gt;AngularVelocity&lt;/code&gt;, just to name a few.
To facilitate the creation of entities with box collision shapes, we also added automatic computation of the inertia tensor for the &lt;code&gt;BoxCollisionShape&lt;/code&gt;, which is performed upon adding the &lt;code&gt;Inertia&lt;/code&gt; component and every time &lt;code&gt;Mass&lt;/code&gt; or the dimentions of the shape are changed.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="expand-the-integrator-and-solver"&gt;
&lt;h5&gt;Expand the Integrator and Solver&lt;/h5&gt;
&lt;p&gt;The last step was to add rotation in the Integrator and &lt;code&gt;PenetrationConstraint&lt;/code&gt; Solving.&lt;/p&gt;
&lt;p&gt;The integrator simply takes the &lt;code&gt;AngularImpulse&lt;/code&gt; and &lt;code&gt;Torque&lt;/code&gt; components and handles them similarly to the linear movement components.
We want to note that the &lt;code&gt;Force&lt;/code&gt; component also holds a torque vector, in our current design.
This is because the method we supply to apply a force on a point of the body belongs to &lt;code&gt;Force&lt;/code&gt;, and that method may also apply some torque, if the force isn't aligned with the center of mass.
This hidden torque vector is also used in the Integration phase.&lt;/p&gt;
&lt;pre class="m-code"&gt;&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Force&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;addForceOnPoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;glm&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vec3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;force&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;glm&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vec3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;localPoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;glm&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vec3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;centerOfMass&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;mForce&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;force&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;mTorque&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;glm&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cross&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;localPoint&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;centerOfMass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;force&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;private&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;glm&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vec3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mForce&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;0.0F&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.0F&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.0F&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;glm&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vec3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mTorque&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;0.0F&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.0F&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.0F&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;PenetrationConstraint&lt;/code&gt; is now solved for each contact point, using their local anchor for relative velocity, separation, and angular velocity calculation at each step.
This involved changing the constraint itself to also have a number of &lt;code&gt;PenetrationConstraintPointData&lt;/code&gt;, which are the same as the manifold points but with extra information to keep track of the constraint solving.
The constraint is now created when the &lt;code&gt;ContactManifold&lt;/code&gt; relation is present between 2 entities, instead of &lt;code&gt;CollidingWith&lt;/code&gt;.&lt;/p&gt;
&lt;pre class="m-code"&gt;&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;PenetrationConstraintPointData&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;initialSeparation&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;///&amp;lt; The separation of the contact point. Negative separation indicates&lt;/span&gt;
&lt;span class="w"&gt;                             &lt;/span&gt;&lt;span class="c1"&gt;///&amp;lt; penetration.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;normalSpeed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;///&amp;lt; The relative velocity of the bodies along the normal at the contact point the begging of&lt;/span&gt;
&lt;span class="w"&gt;                       &lt;/span&gt;&lt;span class="c1"&gt;///&amp;lt; the collision.&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;glm&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vec3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;localAnchor1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;///&amp;lt; The local contact point relative to the center of mass of the first body.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;glm&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vec3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;localAnchor2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;///&amp;lt; The local contact point relative to the center of mass of the second body.&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;/// Store fixed world-space anchors.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;/// This improves rolling behavior for shapes like balls and capsules. Used for restitution and friction.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;glm&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vec3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fixedAnchor1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;///&amp;lt; The world-space contact point relative to the center of mass of the first body.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;glm&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vec3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fixedAnchor2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;///&amp;lt; The world-space contact point relative to the center of mass of the second body.&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// separation&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;normalMass&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;///&amp;lt; Mass to use for normal impulse calculation.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;normalImpulse&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;///&amp;lt; Accumulated impulse for separation.&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// friction&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;frictionMass1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;///&amp;lt; Mass to use for friction impulse calculation along the first tangent..&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;frictionMass2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;///&amp;lt; Mass to use for friction impulse calculation along the second tangent..&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;frictionImpulse1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;///&amp;lt; Accumulated impulse for friction along the first tangent.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;frictionImpulse2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;///&amp;lt; Accumulated impulse for friction along the second tangent.&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;PenetrationConstraint&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;cubos&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ecs&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Entity&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;///&amp;lt; Entity to which the normal is relative to.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;glm&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vec3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;normal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="c1"&gt;///&amp;lt; Normal of contact on the surface of the entity.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;friction&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;                  &lt;/span&gt;&lt;span class="c1"&gt;///&amp;lt; Friction of the constraint.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;restitution&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;               &lt;/span&gt;&lt;span class="c1"&gt;///&amp;lt; Restitution coefficient of the constraint.&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PenetrationConstraintPointData&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;points&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;///&amp;lt; Contact points in the contact manifold.&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;And that's it for this very short summary of the physics changes.
Our implementation is still far from perfect. As you might have seen in the sample clip, some movements still don't look very natural, and the boxes should have stayed on top of each other, which is something we'll be working on over the next releases.&lt;/p&gt;
&lt;p&gt;To wrap up, as we promised in the previous release blog post, we've added the &lt;code&gt;PhysicsMaterial&lt;/code&gt; component, which allows users to fine tune the friction and bounciness behaviour of each body. Check it out in our documentation!&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="cascading-shadow-maps-tomas7770"&gt;
&lt;h4&gt;Cascading Shadow Maps &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/tomas-gouveia/"&gt;&amp;#64;tomas7770&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;In the previous release, we introduced &lt;strong&gt;shadows&lt;/strong&gt;.
As shown in the release blog post, this had a significant impact on the quality of Cubos' graphics.
However, due to the complexity and time it took to implement this feature, we were only able to support
spot lights, the type of light for which it's easiest to cast shadows. This obviously restricted the
scenarios where shadows were visible. For this release we aimed to extend shadows support
to directional lights, leaving only point light shadows to be implemented.&lt;/p&gt;
&lt;p&gt;Here are some comparison screenshots showing the difference directional shadows can make.
Try dragging the slider in the middle to compare the before and after!&lt;/p&gt;
&lt;img-comparison-slider&gt;
&lt;figure class="before" slot="first"&gt;
&lt;img src="https://cubos.riscadoa.com/images/blog/release/0-4/scraps_vs_zombies_csm_before.png" style="width: 100%" /&gt;
&lt;figcaption&gt;Before&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class="after" slot="second"&gt;
&lt;img src="https://cubos.riscadoa.com/images/blog/release/0-4/scraps_vs_zombies_csm_after.png" style="width: 100%" /&gt;
&lt;figcaption&gt;After&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/img-comparison-slider&gt;
&lt;img-comparison-slider&gt;
&lt;figure class="before" slot="first"&gt;
&lt;img src="https://cubos.riscadoa.com/images/blog/release/0-4/cars_csm_before.png" style="width: 100%" /&gt;
&lt;figcaption&gt;Before&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class="after" slot="second"&gt;
&lt;img src="https://cubos.riscadoa.com/images/blog/release/0-4/cars_csm_after.png" style="width: 100%" /&gt;
&lt;figcaption&gt;After&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/img-comparison-slider&gt;
&lt;img-comparison-slider&gt;
&lt;figure class="before" slot="first"&gt;
&lt;img src="https://cubos.riscadoa.com/images/blog/release/0-4/surfers_csm_before.png" style="width: 100%" /&gt;
&lt;figcaption&gt;Before&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class="after" slot="second"&gt;
&lt;img src="https://cubos.riscadoa.com/images/blog/release/0-4/surfers_csm_after.png" style="width: 100%" /&gt;
&lt;figcaption&gt;After&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/img-comparison-slider&gt;
&lt;p&gt;As you can see, this adds a whole new level of depth to the engine's graphics.&lt;/p&gt;
&lt;p&gt;Similarly to spot shadows, directional shadows can be enabled by adding a component to the respective light,
in this case a &lt;a href="https://docs.cubos.riscadoa.com/structcubos_1_1engine_1_1DirectionalShadowCaster.html"&gt;DirectionalShadowCaster&lt;/a&gt;.
The &lt;a href="https://github.com/GameDevTecnico/cubos/tree/main/engine/samples/render/shadows"&gt;Shadows sample&lt;/a&gt; has been updated to show this.&lt;/p&gt;
&lt;p&gt;Because directional shadows are more complex than spot shadows, there are more options that can be configured,
namely the maximum and minimum distances for which shadows are casted (directional lights have an unlimited
range, but shadows have a limited range), the distances at which the shadow quality level drops (&amp;quot;splits&amp;quot;), and the resolution
of the shadow map. You may want to tweak the distance values for better results: a lower value will result in
better shadow quality at the expense of range, and vice-versa.&lt;/p&gt;
&lt;p&gt;Due to a technical limitation in our renderer, only one directional light can cast shadows at a given time;
other directional shadow casters will simply be ignored.&lt;/p&gt;
&lt;p&gt;The way directional shadows are implemented is similar to spot shadows. One of the main differences is
that, because of directional lights' unlimited range, a technique known as &lt;a href="https://learnopengl.com/Guest-Articles/2021/CSM"&gt;Cascaded Shadow Mapping&lt;/a&gt;
is employed, rendering the world from a point which varies with the camera's position, and at multiple
distances to balance quality with draw range. This means that instead of just a single shadow map texture
for the light, there is a texture for each camera, multiplied by the number of distances at which the world is rendered.
It's worth noting that each directional shadow caster has its own textures, instead of using a shared shadow atlas like spot casters.
Below are screenshots of the directional shadow map used to draw shadows in &lt;em&gt;Scraps vs Zombies&lt;/em&gt;, as shown earlier.&lt;/p&gt;
&lt;div class="m-row"&gt;
&lt;div class="m-col-m-4"&gt;
&lt;img class="m-image" src="https://cubos.riscadoa.com/images/blog/release/0-4/cascaded_shadow_map_1.png" /&gt;
&lt;/div&gt;
&lt;div class="m-col-m-4"&gt;
&lt;img class="m-image" src="https://cubos.riscadoa.com/images/blog/release/0-4/cascaded_shadow_map_2.png" /&gt;
&lt;/div&gt;
&lt;div class="m-col-m-4"&gt;
&lt;img class="m-image" src="https://cubos.riscadoa.com/images/blog/release/0-4/cascaded_shadow_map_3.png" /&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="input-axis-deadzones-kuukitenshi"&gt;
&lt;h4&gt;Input Axis Deadzones &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/laura-cunha/"&gt;&amp;#64;kuukitenshi&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;Previously, dealing with input sources that exhibited drift, like older gamepad joysticks, required developers to manually filter out noise from input data.&lt;/p&gt;
&lt;p&gt;In this release, input deadzones can now be configured directly within the bindings asset, allowing players to adjust it in the settings for their controllers to filter out unwanted noise.&lt;/p&gt;
&lt;p&gt;This enhancement significantly simplifies input handling and ensures a smoother and more reliable gameplay experience, especially for games that heavily rely on precise controller input.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="ortographic-cameras-mkuritsu"&gt;
&lt;h4&gt;Ortographic Cameras &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/rodrigo-correia/"&gt;&amp;#64;mkuritsu&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;Previously we only had support for perspective cameras in Cubos, and additionally, perspective matrix computations were duplicated all over the code.
To address this, we've added a new generic &lt;code&gt;Camera&lt;/code&gt; component that holds the projection matrix of the current camera in use.
Now, the &lt;code&gt;PerspectiveCamera&lt;/code&gt; component is only used to fill in the &lt;code&gt;Camera&lt;/code&gt; component with the correct projection matrix, and code needing the projection matrix can simply query the &lt;code&gt;Camera&lt;/code&gt; component.&lt;/p&gt;
&lt;p&gt;With this, we decoupled the camera type from the rest of code, allowing us to add a new &lt;code&gt;OrtographicCamera&lt;/code&gt; component which uses an ortographic projection instead of a perspective one.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="on-the-core"&gt;
&lt;h3&gt;On the Core&lt;/h3&gt;
&lt;section id="spans-for-profiling-and-tracing-roby2014"&gt;
&lt;h4&gt;Spans for Profiling and Tracing &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/roberto-carlos/"&gt;&amp;#64;roby2014&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;In our ongoing efforts to improve metrics and address performance issues, we are excited to announce the implementation of a new feature for telemetry: Tracing.
This addition will significantly enhance our ability to monitor and understand the execution flow of applications built with &lt;strong&gt;Cubos&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Tracing allows developers to track the execution of their code by creating spans that represent specific periods of execution.
This capability makes it easier to log messages and visualize the flow of an application, providing valuable insights into performance and behaviour.&lt;/p&gt;
&lt;p&gt;In &lt;strong&gt;Cubos&lt;/strong&gt;, tracing is facilitated through a set of macros defined in &lt;code&gt;core/tel/tracing.hpp&lt;/code&gt;:
- &lt;code&gt;CUBOS_SPAN_TRACE&lt;/code&gt;
- &lt;code&gt;CUBOS_SPAN_DEBUG&lt;/code&gt;
- &lt;code&gt;CUBOS_SPAN_INFO&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;From now on, all telemetry components share the same logging level. You can set it by using &lt;code&gt;cubos::core::tel::level&lt;/code&gt; method.
This means that, as an example, trace spans will only be registered if the logger level is set to trace.
We also moved the other components to the &lt;code&gt;tel&lt;/code&gt; namespace (metrics and logging).&lt;/p&gt;
&lt;p&gt;Here's a simple code snippet on how it works and its output:&lt;/p&gt;
&lt;pre class="m-code"&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;cubos&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;tel&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Level&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;CUBOS_SPAN_TRACE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;this_wont_exist!&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// wont exist because trace &amp;lt; debug&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;CUBOS_INFO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;hello from root span!&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;CUBOS_SPAN_INFO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;main_span&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// With this macro, a new RAII guard is created. When dropped, exits the span.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// This indicates that we are in the span for the current lexical scope.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Logs and metrics from here will be associated with &amp;#39;main&amp;#39; span.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;CUBOS_INFO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;hello!&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;CUBOS_SPAN_DEBUG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;other_scope&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;CUBOS_INFO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;hello again!&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;SpanManager&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;manual_span&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cubos&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;tel&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Level&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;CUBOS_INFO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;entered a manual span&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;SpanManager&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;CUBOS_INFO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;after exit manual span&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;/pre&gt;
&lt;pre class="m-code"&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;16&lt;/span&gt;:03:31.966&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;main.cpp:20&lt;span class="w"&gt; &lt;/span&gt;main&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;thread11740&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;info:&lt;span class="w"&gt; &lt;/span&gt;hello&lt;span class="w"&gt; &lt;/span&gt;from&lt;span class="w"&gt; &lt;/span&gt;root&lt;span class="w"&gt; &lt;/span&gt;span!
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;16&lt;/span&gt;:03:31.967&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;main.cpp:26&lt;span class="w"&gt; &lt;/span&gt;main&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;thread11740:main_span&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;info:&lt;span class="w"&gt; &lt;/span&gt;hello!
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;16&lt;/span&gt;:03:31.967&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;main.cpp:29&lt;span class="w"&gt; &lt;/span&gt;main&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;thread11740:main_span:other_scope&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;info:&lt;span class="w"&gt; &lt;/span&gt;hello&lt;span class="w"&gt; &lt;/span&gt;again!
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;16&lt;/span&gt;:03:31.968&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;main.cpp:34&lt;span class="w"&gt; &lt;/span&gt;main&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;thread11740:main_span:other_scope:manual_span&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;info:&lt;span class="w"&gt; &lt;/span&gt;entered&lt;span class="w"&gt; &lt;/span&gt;a&lt;span class="w"&gt; &lt;/span&gt;manual&lt;span class="w"&gt; &lt;/span&gt;span
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;16&lt;/span&gt;:03:31.969&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;main.cpp:37&lt;span class="w"&gt; &lt;/span&gt;main&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;thread11740:main_span:other_scope&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;info:&lt;span class="w"&gt; &lt;/span&gt;after&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;exit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;manual&lt;span class="w"&gt; &lt;/span&gt;span&lt;/pre&gt;
&lt;p&gt;Looking ahead, we aim to develop a &lt;strong&gt;Tesseratos&lt;/strong&gt; plugin that will allow developers to debug and view all possible spans and their execution times
(e.g: &lt;a class="m-link-wrap" href="https://github.com/bwrsandman/imgui-flame-graph"&gt;https://github.com/bwrsandman/imgui-flame-graph&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;This UI will enable developers to interact with the tracing data, providing a comprehensive view of the entire game flow.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="swapping-openal-for-miniaudio-diogomsmiranda-dageus"&gt;
&lt;h4&gt;Swapping OpenAL for Miniaudio &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/diogo-miranda/"&gt;&amp;#64;diogomsmiranda&lt;/a&gt;, &lt;a href="https://cubos.riscadoa.com/blog/author/joao-nogueira/"&gt;&amp;#64;Dageus&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;This release marks the beginning of an exciting new chapter for Cubos: the Audio Plugin.
But before we could start working on the plugin itself, we had to make some changes to the audio backend.
Previously, we were using OpenAL for audio, which no longer aligned with our vision for Cubos, so we decided to switch to &lt;a href="https://miniaud.io"&gt;miniaudio.h&lt;/a&gt;, a lightweight, single-file audio library easy to integrate and use while also sharing the same license as Cubos.
We implemented a new &lt;code&gt;AudioContext&lt;/code&gt; abstraction on the core library, which hides the underlying audio library from the rest of the engine.&lt;/p&gt;
&lt;p&gt;In the next release of &lt;strong&gt;Cubos&lt;/strong&gt;, we're planning to have a fully functional audio plugin, which will allow you to play sounds and music in your games!&lt;/p&gt;
&lt;/section&gt;
&lt;section id="type-client-and-type-server-riscadoa"&gt;
&lt;h4&gt;Type Client and Type Server &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/ricardo-antunes/"&gt;&amp;#64;RiscadoA&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;To separate &lt;strong&gt;Tesseratos&lt;/strong&gt; from the engine into a separate process, we needed a way to share type data across different processes.
This is because the editor needs to know about the project's data types, such as its components and resources, to be able to do work with them, such as showing them in the editor's UI, or editing scene assets.&lt;/p&gt;
&lt;p&gt;To solve this problem, we've added the &lt;code&gt;TypeServer&lt;/code&gt; and &lt;code&gt;TypeClient&lt;/code&gt; classes to the core's reflection module.
The editor contains a &lt;code&gt;TypeClient&lt;/code&gt;, the game contains a &lt;code&gt;TypeServer&lt;/code&gt; and both communicate through an abstract stream, which currently is implemented using a TCP socket.&lt;/p&gt;
&lt;p&gt;Our reflection system is based on the concept of &amp;quot;traits&amp;quot;. A trait is a piece of metadata that describes a type, such as its name, its fields, and its methods.
Both the &lt;code&gt;TypeClient&lt;/code&gt; and the &lt;code&gt;TypeServer&lt;/code&gt; allow registering serialization and deserialization functions for each trait, so that they can be sent across the stream.&lt;/p&gt;
&lt;p&gt;The protocol itself is relatively simple:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The client - the editor - sends a list with the names of the traits it supports.&lt;/li&gt;
&lt;li&gt;The client sends a list with the types it already knows about (for example, primitive types like &lt;code&gt;int&lt;/code&gt; and &lt;code&gt;float&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;The server - the game - sends a list with the types that the client doesn't know about yet. This includes:
- The type name.
- A list of the serialized traits that describe the type.
- The serialized default value for the type, if it has one.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Most of the complexity of this system is on the client side, as it needs to figure out the memory layout of the types it receives from the server.
We do this by basing the memory layout on one of the received traits.
For example, if the server sends a trait that describes an object type with fields, then the client will create a new type which stores these fields in memory.
If the trait describes an array type, then the client will create a new type that stores an array in memory, and so on.&lt;/p&gt;
&lt;p&gt;One major limitation of the current implementation is that it lacks support for traits like &lt;code&gt;NullableTrait&lt;/code&gt;.
This trait contains a function which determines if a value is null or not, and another to make a value null.
How can we pass a function through a stream? We can't. One thing we could do would be to communicate again with the server whenever the client needs to know whether a value is null or not.
For now, we've decided to simply ignore these traits, but we'll eventually need to tackle this issue, as it would allow for better UX in the editor.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="stacktraces-on-crashes-riscadoa"&gt;
&lt;h4&gt;Stacktraces on Crashes &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/ricardo-antunes/"&gt;&amp;#64;RiscadoA&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;Previously, when the engine crashed, it would simply print an error message to the console, and that was it.
We would not get a lot of information about the crash's context, other than the previous log messages.
To debug a crash, we usually spun up a debugger and tried to reproduce it.
This way we could get a stack trace, but it was a bit cumbersome, and when the crash happened in a release build or in a different environment, we were out of luck.&lt;/p&gt;
&lt;p&gt;In order to make our lives easier, we've integrated &lt;a href="https://github.com/jeremy-rifkin/cpptrace"&gt;cpptrace&lt;/a&gt; into the core library.
Now, whenever the engine aborts, it prints a pretty stack trace to &lt;code&gt;stderr&lt;/code&gt;, which includes the function names, the file names, and the line numbers of the functions that were called.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="new-team-organization"&gt;
&lt;h2&gt;New Team Organization&lt;/h2&gt;
&lt;p&gt;We're currently undergoing a major reorganization of the team itself, as it has grown significantly over the past few months.
We have now over 20 developers working on the project, and we're planning to expand even further.&lt;/p&gt;
&lt;p&gt;Previusly, we all met weekly to showcase what each member had been working, and to discuss the project's direction.
Of course, with such a large team, these meetings were becoming increasingly long and unproductive.&lt;/p&gt;
&lt;p&gt;So, we decided to split the big team into smaller teams, each with its own focus. As of now, we have the following teams:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Community&lt;/strong&gt;: responsible for managing the more meta aspects of the project, such as the blog, the Discord server, and the social media.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Graphics&lt;/strong&gt;: responsible for any graphics-related features, such as voxels, UI and gizmos rendering.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Physics&lt;/strong&gt;: responsible for the physics and collisions plugins.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tools&lt;/strong&gt;: responsible for &lt;strong&gt;Tesseratos&lt;/strong&gt;, &lt;strong&gt;Quadrados&lt;/strong&gt; and all other kinds of tooling.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Wildcard&lt;/strong&gt;: catch-all team for any other kind of feature that doesn't fit in the other teams.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now each team meets weekly to discuss their progress and plans, and we have a big meeting less frequently to discuss the project as a whole.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="next-steps"&gt;
&lt;h2&gt;Next Steps&lt;/h2&gt;
&lt;p&gt;In the next release, which should be out by the end of November, we're planning to work on the following features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Scene editing through the new standalone editor, &lt;strong&gt;Tesseratos&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;An Audio plugin, using the new Audio Context abstraction we've added in this release.&lt;/li&gt;
&lt;li&gt;Point light shadows.&lt;/li&gt;
&lt;li&gt;Anti-aliasing.&lt;/li&gt;
&lt;li&gt;MSDF text rendering on our UI plugin.&lt;/li&gt;
&lt;li&gt;Reduce shadow artifacts such as shadow acne and peter panning.&lt;/li&gt;
&lt;li&gt;Toggleable gravity on the physics plugin.&lt;/li&gt;
&lt;li&gt;Voxel collision shapes, where the collision shape is the same as the voxel model.&lt;/li&gt;
&lt;li&gt;Contact point caching to avoid recomputing the collision manifolds every frame.&lt;/li&gt;
&lt;li&gt;Saving settings after changing them in the UI.&lt;/li&gt;
&lt;li&gt;An &lt;code&gt;Active&lt;/code&gt; component to enable and disable all kinds of behaviors in entities.&lt;/li&gt;
&lt;li&gt;Refactoring the whole CMake configuration to support installation and packaging.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Additionally, we're planning to work on a new game project using our engine - now with online multiplayer support!&lt;/p&gt;
&lt;p&gt;You can check out the full list of stuff we want to get done in the &lt;a href="https://github.com/GameDevTecnico/cubos/milestone/28"&gt;milestone&lt;/a&gt; for the next release.&lt;/p&gt;
&lt;/section&gt;
</content><category term="Release"/><category term="Release"/></entry><entry><title>A New Brand Identity</title><link href="https://cubos.riscadoa.com/blog/a-new-brand-identity/" rel="alternate"/><published>2024-09-09T17:00:00+01:00</published><updated>2024-09-09T17:00:00+01:00</updated><author><name>Nuno Baptista</name></author><id>tag:cubos.riscadoa.com,2024-09-09:/blog/a-new-brand-identity/</id><summary type="html">&lt;p&gt;Introducing a new brand identity for Cubos.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Cubos had grown out of its cocoon and needed new wings to fly out of its embryonic phase.
The new visual identity aimed at providing a more cohesive and professional look to an increasingly defined and completed game engine.
A new logo was designed, alongside a set of visual rules and principals that form Cubos new identity.&lt;/p&gt;
&lt;p&gt;The process started with the logo.
The team wished to keep the 3D aspect of the former logo, but with a more slick and modern twist.
Furthermore, as the project was aimed at a software (a game engine), an icon was necessary for the application, the branding the launch screen, etc.
A stylized wordmark, like the previous version, was insufficient. So we set to design the icon.
At first, the design followed a more intellectual and less literal approach, adapting an isometric view with 3 sides to establish a deconstructed view of a Cube.
The concept of this design was to incorporate the movement and change in the symbol, while representing the assembly purpose of a game engine.
In this case, the assembly of a cube, with is faces at the moment they are sliding into place.&lt;/p&gt;
&lt;div class="m-imagegrid m-container-inflate"&gt;
&lt;div&gt;
&lt;figure style="width: 70.686%"&gt;
&lt;a href="https://cubos.riscadoa.com/images/blog/meta/brand-identity-2024/prototype_logo_1_horizontal.png"&gt;&lt;img src="https://cubos.riscadoa.com/images/blog/meta/brand-identity-2024/prototype_logo_1_horizontal.png" /&gt;&lt;figcaption&gt;First design - horizontal layout&lt;/figcaption&gt;
&lt;/a&gt;
&lt;/figure&gt;
&lt;figure style="width: 29.314%"&gt;
&lt;a href="https://cubos.riscadoa.com/images/blog/meta/brand-identity-2024/prototype_logo_1_vertical.png"&gt;&lt;img src="https://cubos.riscadoa.com/images/blog/meta/brand-identity-2024/prototype_logo_1_vertical.png" /&gt;&lt;figcaption&gt;First design - vertical layout&lt;/figcaption&gt;
&lt;/a&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The team received this design with mixed feelings.
The logo had little of the 3D aspect that made their previous logo and that also constitutes the main selling point of the engine - voxel base game engine.
Therefore, we jumped to a second design, that retained the original while representing a shape made of literal isometric cubes.
The final shape was a 3-armed spiral, inspired by the Keltic symbol the Trisquel, without copying the Trisquel GNU/Linux software.&lt;/p&gt;
&lt;div class="m-imagegrid m-container-inflate"&gt;
&lt;div&gt;
&lt;figure style="width: 70.684%"&gt;
&lt;a href="https://cubos.riscadoa.com/images/blog/meta/brand-identity-2024/prototype_logo_2_horizontal.png"&gt;&lt;img src="https://cubos.riscadoa.com/images/blog/meta/brand-identity-2024/prototype_logo_2_horizontal.png" /&gt;&lt;figcaption&gt;Second design - horizontal layout&lt;/figcaption&gt;
&lt;/a&gt;
&lt;/figure&gt;
&lt;figure style="width: 29.316%"&gt;
&lt;a href="https://cubos.riscadoa.com/images/blog/meta/brand-identity-2024/prototype_logo_2_vertical.png"&gt;&lt;img src="https://cubos.riscadoa.com/images/blog/meta/brand-identity-2024/prototype_logo_2_vertical.png" /&gt;&lt;figcaption&gt;Second design - vertical layout&lt;/figcaption&gt;
&lt;/a&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Unfortunately, to some members of the team, the new icon reminded them of some unpleasant symbol.
Consequently, we're back to square one. A brainstorming session ensued.
Various shapes were tested and debated, but the icons kept getting too complex and unrecognizable.&lt;/p&gt;
&lt;div class="m-imagegrid m-container-inflate"&gt;
&lt;div&gt;
&lt;figure style="width: 100.000%"&gt;
&lt;a href="https://cubos.riscadoa.com/images/blog/meta/brand-identity-2024/prototype_logo_3.png"&gt;&lt;img src="https://cubos.riscadoa.com/images/blog/meta/brand-identity-2024/prototype_logo_3.png" /&gt;&lt;figcaption&gt;Logo brainstorming&lt;/figcaption&gt;
&lt;/a&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Eventually, we arrived at an icon that was composed of cubes, that did not bring any bad connotations and that was relatively simple and memorable.
As it can be seen in the experiment above, the spiral/movement idea was forsaken in favor of a greater emphasis in the assembly aspect.
Three cubes merged to form something more complex, suggesting ideas of union and strength.&lt;/p&gt;
&lt;div class="m-imagegrid m-container-inflate"&gt;
&lt;div&gt;
&lt;figure style="width: 71.605%"&gt;
&lt;a href="https://cubos.riscadoa.com/images/blog/meta/brand-identity-2024/prototype_logo_4_horizontal.png"&gt;&lt;img src="https://cubos.riscadoa.com/images/blog/meta/brand-identity-2024/prototype_logo_4_horizontal.png" /&gt;&lt;figcaption&gt;Final design - horizontal layout&lt;/figcaption&gt;
&lt;/a&gt;
&lt;/figure&gt;
&lt;figure style="width: 28.395%"&gt;
&lt;a href="https://cubos.riscadoa.com/images/blog/meta/brand-identity-2024/prototype_logo_4_vertical.png"&gt;&lt;img src="https://cubos.riscadoa.com/images/blog/meta/brand-identity-2024/prototype_logo_4_vertical.png" /&gt;&lt;figcaption&gt;Final design - vertical layout&lt;/figcaption&gt;
&lt;/a&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Parallel to the icon design process was the font selection.
The goals we set at the start were that the font had to transmit a feel of modernity and a certain degree of formality, since those were the goals of this redesign, while conveying the slickness and dynamism of an innovative product.
Other concerns that were considered were the versatility and rights of the chosen font.
To address this last point, we opted for google fonts library, a largely integrated and open-source collection of great fonts, that complements the open-source aspect of the project.
The fonts selected were Russo One for the logo mark and displays/headers, and Roboto font family for text body.
Russo One conveys energy and strength while maintaining a dynamic feel to it, while Roboto is a well-established in the space of tech and innovation.&lt;/p&gt;
&lt;p&gt;About the logo's colors, it can be observed that these remained virtually unchanged thought out the design process.
The contrast of neutral colors with a cyan blue, that aim to represent knowledge and innovation, was well received.
Additional colors were added to the pallet for software and marketing purposes.
Speaking of which, a &lt;a href="/images/brand.pdf"&gt;Brand Guidelines Document&lt;/a&gt; was enacted to specify how the brand and marketing material should be designed and to which principles it should adhere.&lt;/p&gt;
&lt;div class="m-imagegrid m-container-inflate"&gt;
&lt;div&gt;
&lt;figure style="width: 25.011%"&gt;
&lt;a href="https://cubos.riscadoa.com/images/blog/meta/brand-identity-2024/brand_guidelines_1.png"&gt;&lt;img src="https://cubos.riscadoa.com/images/blog/meta/brand-identity-2024/brand_guidelines_1.png" /&gt;&lt;div&gt;
&lt;/div&gt;
&lt;/a&gt;
&lt;/figure&gt;
&lt;figure style="width: 25.011%"&gt;
&lt;a href="https://cubos.riscadoa.com/images/blog/meta/brand-identity-2024/brand_guidelines_2.png"&gt;&lt;img src="https://cubos.riscadoa.com/images/blog/meta/brand-identity-2024/brand_guidelines_2.png" /&gt;&lt;div&gt;
&lt;/div&gt;
&lt;/a&gt;
&lt;/figure&gt;
&lt;figure style="width: 25.029%"&gt;
&lt;a href="https://cubos.riscadoa.com/images/blog/meta/brand-identity-2024/brand_guidelines_3.png"&gt;&lt;img src="https://cubos.riscadoa.com/images/blog/meta/brand-identity-2024/brand_guidelines_3.png" /&gt;&lt;div&gt;
&lt;/div&gt;
&lt;/a&gt;
&lt;/figure&gt;
&lt;figure style="width: 24.948%"&gt;
&lt;a href="https://cubos.riscadoa.com/images/blog/meta/brand-identity-2024/brand_guidelines_4.png"&gt;&lt;img src="https://cubos.riscadoa.com/images/blog/meta/brand-identity-2024/brand_guidelines_4.png" /&gt;&lt;div&gt;
&lt;/div&gt;
&lt;/a&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
</content><category term="Meta"/><category term="Design"/></entry><entry><title>Release 0.3</title><link href="https://cubos.riscadoa.com/blog/release-03/" rel="alternate"/><published>2024-09-09T16:00:00+01:00</published><updated>2024-09-09T16:00:00+01:00</updated><author><name>Cubos Team</name></author><id>tag:cubos.riscadoa.com,2024-09-09:/blog/release-03/</id><summary type="html">&lt;p&gt;Raycasting, shadows, profiling tools, networking utilities, and more!&lt;/p&gt;
</summary><content type="html">&lt;p&gt;This blog post came a bit late due to the summer break, but we're back with a new release!
Features-wise, this release was a tad smaller than the previous one as we spent a lot of time working on non-code related stuff, such as rebranding and marketing the project.&lt;/p&gt;
&lt;section id="new-0-3-features"&gt;
&lt;h2&gt;New 0.3 Features&lt;/h2&gt;
&lt;p&gt;This blog post covers the most important changes, but you can check out the full changelog in our &lt;a href="https://github.com/GameDevTecnico/cubos/blob/main/CHANGELOG.md"&gt;repository&lt;/a&gt;.&lt;/p&gt;
&lt;section id="on-the-editor"&gt;
&lt;h3&gt;On the Editor&lt;/h3&gt;
&lt;section id="groundwork-for-a-standalone-editor-riscadoa"&gt;
&lt;h4&gt;Groundwork for a Standalone Editor &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/ricardo-antunes/"&gt;&amp;#64;RiscadoA&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;The original main goal for this version was to get a first prototype of our standalone editor working.
Sadly, we didn't manage to get it done in time, as it was a bigger task than we anticipated.&lt;/p&gt;
&lt;p&gt;The initial approach, to which we dedicated most of the time, was to have two instances of Cubos running at the same time, on the &lt;strong&gt;same process&lt;/strong&gt; - one for the editor and one for the game.
The game instance would be running within the editor instance, and would be launched by loading the game's code from a shared library (such as a DLL).
With this separation, it would be possible for the editor to interact with the game, pause it, restart it and so on, even across game code changes, without having to restart the editor.&lt;/p&gt;
&lt;p&gt;So, with this in mind, we restructured some parts of the code base to get it flexible enough to support this kind of setup.
Namely, it is now possible to step Cubos instances, reset them, restart them, and other similar operations.
We also added cross platform support for loading Cubos instances from shared libraries, so that we could load the game from the editor.&lt;/p&gt;
&lt;p&gt;This all seems, at first, perfect, but after a team discussion, we realized that this approach of having two Cubos instances in the same process is not ideal at all.
Any game crash would bring down the editor, any memory leak in the game would affect the editor, and infinite loops in the game would freeze the editor.
Since solving the halting problem isn't in our roadmap, we decided that we should discard this approach, and instead try to keep the &lt;strong&gt;editor and the game in two separate processes&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;So, for the next release, we'll be focusing on following this new approach. It will be a big task, and it will be a lot more difficult, as it will require a lot of inter-process communication, but it will be worth it in the end.
It will allows us to have a stable editor, whose performance is not affected (that much at least) by the game, and vice-versa.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="on-the-engine"&gt;
&lt;h3&gt;On the Engine&lt;/h3&gt;
&lt;section id="a-new-physics-solver-fallenatlas"&gt;
&lt;h4&gt;A New Physics Solver &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/tiago-antunes/"&gt;&amp;#64;fallenatlas&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;Upon discovering some potential issues with regards to NVidia's patent of XPBD, some problems with how XPBD handled friction, and stability,
we decided that this would be the ideal moment to switch to another solver.
Recently, Erin Catto shared his &lt;a href="https://box2d.org/posts/2024/02/solver2d/"&gt;comparison of multiple solvers&lt;/a&gt;, and the results of TGS Soft seemed quite promising, so we decided to give it a try... And at this point, our simulation is already more stable than before!&lt;/p&gt;
&lt;p&gt;In simple terms, it differs from XPBD in the sense that it uses impulses instead of changing the position of the bodies directly, however, like XPBD it also uses substeps. On top of this it also uses soft constraints, which is what Soft stands for in it's name.&lt;/p&gt;
&lt;p&gt;Since these changes were mostly in internal systems, from a user stand point, everything works the same way.&lt;/p&gt;
&lt;p&gt;Additionally, we added Friction and Bounciness. For now, these are a predefined value for every body, but will be customizable in the next release with a &lt;code&gt;PhysicsMaterial&lt;/code&gt;.&lt;/p&gt;
&lt;img class="m-image" src="https://cubos.riscadoa.com/images/blog/release/0-3/complex_physics_sample_tgs_soft.gif" /&gt;
&lt;/section&gt;
&lt;section id="raycasting-diogomsmiranda"&gt;
&lt;h4&gt;Raycasting &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/diogo-miranda/"&gt;&amp;#64;diogomsmiranda&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;Raycasting is a commonly used tool in game development which Cubos was lacking until now - it is a technique used to determine the intersection of a ray with an object in a scene.
This allows for a wide range of applications, such as shooting mechanics, object selection, and more.&lt;/p&gt;
&lt;p&gt;This new utility was implemented as a system argument, which means it can be used in any system, by simply adding it as an argument:&lt;/p&gt;
&lt;pre class="m-code"&gt;&lt;span class="n"&gt;cubos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;raycast&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;([](&lt;/span&gt;&lt;span class="n"&gt;Raycast&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;raycast&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// raycast from the origin to -50,0,0&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Raycast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fire&lt;/span&gt;&lt;span class="p"&gt;({{&lt;/span&gt;&lt;span class="mf"&gt;0.0F&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;0.0F&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;0.0F&lt;/span&gt;&lt;span class="p"&gt;},{&lt;/span&gt;&lt;span class="mf"&gt;-50.0F&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;0.0F&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;0.0F&lt;/span&gt;&lt;span class="p"&gt;}});&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;CUBOS_INFO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Hit entity {} at point {}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hit&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hit&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;To implement this, we had to handle the two types of colliders that we currently have in Cubos: boxes and capsules.&lt;/p&gt;
&lt;section id="intersection-with-a-box"&gt;
&lt;h5&gt;Intersection with a Box&lt;/h5&gt;
&lt;p&gt;The intersection test with a box is based on the Cyrus-Beck algorithm, which is a line clipping algorithm that is used to find the intersection of a line segment with a convex polygon.&lt;/p&gt;
&lt;p&gt;We can easily define a box by the minimum and maximum values of x,y,z and the ray by its origin and the direction.&lt;/p&gt;
&lt;p&gt;A ray is defined then by the line formula:&lt;/p&gt;
&lt;div class="m-math"&gt;
&lt;svg style="width: 20.195em; height: 1.107em;" viewBox="97.335067 -10.626787 193.872922 10.626798"&gt;
&lt;title&gt;
point = ray.origin + t * ray.direction
&lt;/title&gt;
&lt;defs&gt;
&lt;path id='eq1-g0-3' d='M3.287671-5.104857C3.299626-5.272229 3.299626-5.559153 2.988792-5.559153C2.797509-5.559153 2.642092-5.403736 2.677958-5.248319V-5.092902L2.84533-3.239851L1.315068-4.351681C1.207472-4.411457 1.183562-4.435367 1.099875-4.435367C.932503-4.435367 .777086-4.267995 .777086-4.100623C.777086-3.90934 .896638-3.861519 1.016189-3.801743L2.713823-2.988792L1.06401-2.187796C.872727-2.092154 .777086-2.044334 .777086-1.865006S.932503-1.530262 1.099875-1.530262C1.183562-1.530262 1.207472-1.530262 1.506351-1.75741L2.84533-2.725778L2.666002-.71731C2.666002-.466252 2.881196-.406476 2.976837-.406476C3.120299-.406476 3.299626-.490162 3.299626-.71731L3.120299-2.725778L4.65056-1.613948C4.758157-1.554172 4.782067-1.530262 4.865753-1.530262C5.033126-1.530262 5.188543-1.697634 5.188543-1.865006C5.188543-2.044334 5.080946-2.10411 4.937484-2.175841C4.220174-2.534496 4.196264-2.534496 3.251806-2.976837L4.901619-3.777833C5.092902-3.873474 5.188543-3.921295 5.188543-4.100623S5.033126-4.435367 4.865753-4.435367C4.782067-4.435367 4.758157-4.435367 4.459278-4.208219L3.120299-3.239851L3.287671-5.104857Z'/&gt;
&lt;path id='eq1-g2-43' d='M4.770112-2.761644H8.069738C8.237111-2.761644 8.452304-2.761644 8.452304-2.976837C8.452304-3.203985 8.249066-3.203985 8.069738-3.203985H4.770112V-6.503611C4.770112-6.670984 4.770112-6.886177 4.554919-6.886177C4.327771-6.886177 4.327771-6.682939 4.327771-6.503611V-3.203985H1.028144C.860772-3.203985 .645579-3.203985 .645579-2.988792C.645579-2.761644 .848817-2.761644 1.028144-2.761644H4.327771V.537983C4.327771 .705355 4.327771 .920548 4.542964 .920548C4.770112 .920548 4.770112 .71731 4.770112 .537983V-2.761644Z'/&gt;
&lt;path id='eq1-g2-61' d='M8.069738-3.873474C8.237111-3.873474 8.452304-3.873474 8.452304-4.088667C8.452304-4.315816 8.249066-4.315816 8.069738-4.315816H1.028144C.860772-4.315816 .645579-4.315816 .645579-4.100623C.645579-3.873474 .848817-3.873474 1.028144-3.873474H8.069738ZM8.069738-1.649813C8.237111-1.649813 8.452304-1.649813 8.452304-1.865006C8.452304-2.092154 8.249066-2.092154 8.069738-2.092154H1.028144C.860772-2.092154 .645579-2.092154 .645579-1.876961C.645579-1.649813 .848817-1.649813 1.028144-1.649813H8.069738Z'/&gt;
&lt;path id='eq1-g1-58' d='M2.199751-.573848C2.199751-.920548 1.912827-1.159651 1.625903-1.159651C1.279203-1.159651 1.0401-.872727 1.0401-.585803C1.0401-.239103 1.327024 0 1.613948 0C1.960648 0 2.199751-.286924 2.199751-.573848Z'/&gt;
&lt;path id='eq1-g1-97' d='M3.598506-1.422665C3.53873-1.219427 3.53873-1.195517 3.371357-.968369C3.108344-.633624 2.582316-.119552 2.020423-.119552C1.530262-.119552 1.255293-.561893 1.255293-1.267248C1.255293-1.924782 1.625903-3.263761 1.853051-3.765878C2.259527-4.60274 2.82142-5.033126 3.287671-5.033126C4.076712-5.033126 4.23213-4.052802 4.23213-3.957161C4.23213-3.945205 4.196264-3.789788 4.184309-3.765878L3.598506-1.422665ZM4.363636-4.483188C4.23213-4.794022 3.90934-5.272229 3.287671-5.272229C1.936737-5.272229 .478207-3.526775 .478207-1.75741C.478207-.573848 1.171606 .119552 1.984558 .119552C2.642092 .119552 3.203985-.394521 3.53873-.789041C3.658281-.083686 4.220174 .119552 4.578829 .119552S5.224408-.095641 5.439601-.526027C5.630884-.932503 5.798257-1.661768 5.798257-1.709589C5.798257-1.769365 5.750436-1.817186 5.678705-1.817186C5.571108-1.817186 5.559153-1.75741 5.511333-1.578082C5.332005-.872727 5.104857-.119552 4.614695-.119552C4.267995-.119552 4.244085-.430386 4.244085-.669489C4.244085-.944458 4.27995-1.075965 4.387547-1.542217C4.471233-1.841096 4.531009-2.10411 4.62665-2.450809C5.068991-4.244085 5.176588-4.674471 5.176588-4.746202C5.176588-4.913574 5.045081-5.045081 4.865753-5.045081C4.483188-5.045081 4.387547-4.62665 4.363636-4.483188Z'/&gt;
&lt;path id='eq1-g1-99' d='M4.674471-4.495143C4.447323-4.495143 4.339726-4.495143 4.172354-4.351681C4.100623-4.291905 3.969116-4.112578 3.969116-3.921295C3.969116-3.682192 4.148443-3.53873 4.375592-3.53873C4.662516-3.53873 4.985305-3.777833 4.985305-4.25604C4.985305-4.829888 4.435367-5.272229 3.610461-5.272229C2.044334-5.272229 .478207-3.56264 .478207-1.865006C.478207-.824907 1.123786 .119552 2.343213 .119552C3.969116 .119552 4.99726-1.147696 4.99726-1.303113C4.99726-1.374844 4.925529-1.43462 4.877709-1.43462C4.841843-1.43462 4.829888-1.422665 4.722291-1.315068C3.957161-.298879 2.82142-.119552 2.367123-.119552C1.542217-.119552 1.279203-.836862 1.279203-1.43462C1.279203-1.853051 1.482441-3.012702 1.912827-3.825654C2.223661-4.387547 2.86924-5.033126 3.622416-5.033126C3.777833-5.033126 4.435367-5.009215 4.674471-4.495143Z'/&gt;
&lt;path id='eq1-g1-100' d='M6.01345-7.998007C6.025405-8.045828 6.049315-8.117559 6.049315-8.177335C6.049315-8.296887 5.929763-8.296887 5.905853-8.296887C5.893898-8.296887 5.308095-8.249066 5.248319-8.237111C5.045081-8.225156 4.865753-8.201245 4.65056-8.18929C4.351681-8.16538 4.267995-8.153425 4.267995-7.938232C4.267995-7.81868 4.363636-7.81868 4.531009-7.81868C5.116812-7.81868 5.128767-7.711083 5.128767-7.591532C5.128767-7.519801 5.104857-7.424159 5.092902-7.388294L4.363636-4.483188C4.23213-4.794022 3.90934-5.272229 3.287671-5.272229C1.936737-5.272229 .478207-3.526775 .478207-1.75741C.478207-.573848 1.171606 .119552 1.984558 .119552C2.642092 .119552 3.203985-.394521 3.53873-.789041C3.658281-.083686 4.220174 .119552 4.578829 .119552S5.224408-.095641 5.439601-.526027C5.630884-.932503 5.798257-1.661768 5.798257-1.709589C5.798257-1.769365 5.750436-1.817186 5.678705-1.817186C5.571108-1.817186 5.559153-1.75741 5.511333-1.578082C5.332005-.872727 5.104857-.119552 4.614695-.119552C4.267995-.119552 4.244085-.430386 4.244085-.669489C4.244085-.71731 4.244085-.968369 4.327771-1.303113L6.01345-7.998007ZM3.598506-1.422665C3.53873-1.219427 3.53873-1.195517 3.371357-.968369C3.108344-.633624 2.582316-.119552 2.020423-.119552C1.530262-.119552 1.255293-.561893 1.255293-1.267248C1.255293-1.924782 1.625903-3.263761 1.853051-3.765878C2.259527-4.60274 2.82142-5.033126 3.287671-5.033126C4.076712-5.033126 4.23213-4.052802 4.23213-3.957161C4.23213-3.945205 4.196264-3.789788 4.184309-3.765878L3.598506-1.422665Z'/&gt;
&lt;path id='eq1-g1-101' d='M2.139975-2.773599C2.462765-2.773599 3.275716-2.797509 3.849564-3.012702C4.758157-3.359402 4.841843-4.052802 4.841843-4.267995C4.841843-4.794022 4.387547-5.272229 3.598506-5.272229C2.343213-5.272229 .537983-4.136488 .537983-2.008468C.537983-.753176 1.255293 .119552 2.343213 .119552C3.969116 .119552 4.99726-1.147696 4.99726-1.303113C4.99726-1.374844 4.925529-1.43462 4.877709-1.43462C4.841843-1.43462 4.829888-1.422665 4.722291-1.315068C3.957161-.298879 2.82142-.119552 2.367123-.119552C1.685679-.119552 1.327024-.657534 1.327024-1.542217C1.327024-1.709589 1.327024-2.008468 1.506351-2.773599H2.139975ZM1.566127-3.012702C2.080199-4.853798 3.21594-5.033126 3.598506-5.033126C4.124533-5.033126 4.483188-4.722291 4.483188-4.267995C4.483188-3.012702 2.570361-3.012702 2.068244-3.012702H1.566127Z'/&gt;
&lt;path id='eq1-g1-103' d='M4.040847-1.518306C3.993026-1.327024 3.969116-1.279203 3.813699-1.099875C3.323537-.466252 2.82142-.239103 2.450809-.239103C2.056289-.239103 1.685679-.549938 1.685679-1.374844C1.685679-2.008468 2.044334-3.347447 2.307347-3.88543C2.654047-4.554919 3.19203-5.033126 3.694147-5.033126C4.483188-5.033126 4.638605-4.052802 4.638605-3.981071L4.60274-3.813699L4.040847-1.518306ZM4.782067-4.483188C4.62665-4.829888 4.291905-5.272229 3.694147-5.272229C2.391034-5.272229 .908593-3.634371 .908593-1.853051C.908593-.609714 1.661768 0 2.426899 0C3.060523 0 3.622416-.502117 3.837609-.74122L3.574595 .334745C3.407223 .992279 3.335492 1.291158 2.905106 1.709589C2.414944 2.199751 1.960648 2.199751 1.697634 2.199751C1.338979 2.199751 1.0401 2.175841 .74122 2.080199C1.123786 1.972603 1.219427 1.637858 1.219427 1.506351C1.219427 1.315068 1.075965 1.123786 .812951 1.123786C.526027 1.123786 .215193 1.362889 .215193 1.75741C.215193 2.247572 .705355 2.438854 1.721544 2.438854C3.263761 2.438854 4.064757 1.446575 4.220174 .800996L5.547198-4.554919C5.583064-4.698381 5.583064-4.722291 5.583064-4.746202C5.583064-4.913574 5.451557-5.045081 5.272229-5.045081C4.985305-5.045081 4.817933-4.805978 4.782067-4.483188Z'/&gt;
&lt;path id='eq1-g1-105' d='M3.383313-1.709589C3.383313-1.769365 3.335492-1.817186 3.263761-1.817186C3.156164-1.817186 3.144209-1.78132 3.084433-1.578082C2.773599-.490162 2.283437-.119552 1.888917-.119552C1.745455-.119552 1.578082-.155417 1.578082-.514072C1.578082-.836862 1.721544-1.195517 1.853051-1.554172L2.689913-3.777833C2.725778-3.873474 2.809465-4.088667 2.809465-4.315816C2.809465-4.817933 2.450809-5.272229 1.865006-5.272229C.765131-5.272229 .32279-3.53873 .32279-3.443088C.32279-3.395268 .37061-3.335492 .454296-3.335492C.561893-3.335492 .573848-3.383313 .621669-3.550685C.908593-4.554919 1.362889-5.033126 1.829141-5.033126C1.936737-5.033126 2.139975-5.021171 2.139975-4.638605C2.139975-4.327771 1.984558-3.93325 1.888917-3.670237L1.052055-1.446575C.980324-1.255293 .908593-1.06401 .908593-.848817C.908593-.310834 1.279203 .119552 1.853051 .119552C2.952927 .119552 3.383313-1.625903 3.383313-1.709589ZM3.287671-7.460025C3.287671-7.639352 3.144209-7.854545 2.881196-7.854545C2.606227-7.854545 2.295392-7.591532 2.295392-7.280697C2.295392-6.981818 2.546451-6.886177 2.689913-6.886177C3.012702-6.886177 3.287671-7.197011 3.287671-7.460025Z'/&gt;
&lt;path id='eq1-g1-110' d='M2.462765-3.502864C2.486675-3.574595 2.785554-4.172354 3.227895-4.554919C3.53873-4.841843 3.945205-5.033126 4.411457-5.033126C4.889664-5.033126 5.057036-4.674471 5.057036-4.196264C5.057036-3.514819 4.566874-2.15193 4.327771-1.506351C4.220174-1.219427 4.160399-1.06401 4.160399-.848817C4.160399-.310834 4.531009 .119552 5.104857 .119552C6.216687 .119552 6.635118-1.637858 6.635118-1.709589C6.635118-1.769365 6.587298-1.817186 6.515567-1.817186C6.40797-1.817186 6.396015-1.78132 6.336239-1.578082C6.06127-.597758 5.606974-.119552 5.140722-.119552C5.021171-.119552 4.829888-.131507 4.829888-.514072C4.829888-.812951 4.961395-1.171606 5.033126-1.338979C5.272229-1.996513 5.774346-3.335492 5.774346-4.016936C5.774346-4.734247 5.355915-5.272229 4.447323-5.272229C3.383313-5.272229 2.82142-4.519054 2.606227-4.220174C2.570361-4.901619 2.080199-5.272229 1.554172-5.272229C1.171606-5.272229 .908593-5.045081 .705355-4.638605C.490162-4.208219 .32279-3.490909 .32279-3.443088S.37061-3.335492 .454296-3.335492C.549938-3.335492 .561893-3.347447 .633624-3.622416C.824907-4.351681 1.0401-5.033126 1.518306-5.033126C1.793275-5.033126 1.888917-4.841843 1.888917-4.483188C1.888917-4.220174 1.769365-3.753923 1.685679-3.383313L1.350934-2.092154C1.303113-1.865006 1.171606-1.327024 1.111831-1.111831C1.028144-.800996 .896638-.239103 .896638-.179328C.896638-.011955 1.028144 .119552 1.207472 .119552C1.350934 .119552 1.518306 .047821 1.613948-.131507C1.637858-.191283 1.745455-.609714 1.80523-.848817L2.068244-1.924782L2.462765-3.502864Z'/&gt;
&lt;path id='eq1-g1-111' d='M5.451557-3.287671C5.451557-4.423412 4.710336-5.272229 3.622416-5.272229C2.044334-5.272229 .490162-3.550685 .490162-1.865006C.490162-.729265 1.231382 .119552 2.319303 .119552C3.90934 .119552 5.451557-1.601993 5.451557-3.287671ZM2.331258-.119552C1.733499-.119552 1.291158-.597758 1.291158-1.43462C1.291158-1.984558 1.578082-3.203985 1.912827-3.801743C2.450809-4.722291 3.120299-5.033126 3.610461-5.033126C4.196264-5.033126 4.65056-4.554919 4.65056-3.718057C4.65056-3.239851 4.399502-1.960648 3.945205-1.231382C3.455044-.430386 2.797509-.119552 2.331258-.119552Z'/&gt;
&lt;path id='eq1-g1-112' d='M.514072 1.518306C.430386 1.876961 .382565 1.972603-.107597 1.972603C-.251059 1.972603-.37061 1.972603-.37061 2.199751C-.37061 2.223661-.358655 2.319303-.227148 2.319303C-.071731 2.319303 .095641 2.295392 .251059 2.295392H.765131C1.016189 2.295392 1.625903 2.319303 1.876961 2.319303C1.948692 2.319303 2.092154 2.319303 2.092154 2.10411C2.092154 1.972603 2.008468 1.972603 1.80523 1.972603C1.255293 1.972603 1.219427 1.888917 1.219427 1.793275C1.219427 1.649813 1.75741-.406476 1.829141-.681445C1.960648-.3467 2.283437 .119552 2.905106 .119552C4.25604 .119552 5.71457-1.637858 5.71457-3.395268C5.71457-4.495143 5.092902-5.272229 4.196264-5.272229C3.431133-5.272229 2.785554-4.531009 2.654047-4.363636C2.558406-4.961395 2.092154-5.272229 1.613948-5.272229C1.267248-5.272229 .992279-5.104857 .765131-4.65056C.549938-4.220174 .382565-3.490909 .382565-3.443088S.430386-3.335492 .514072-3.335492C.609714-3.335492 .621669-3.347447 .6934-3.622416C.872727-4.327771 1.099875-5.033126 1.578082-5.033126C1.853051-5.033126 1.948692-4.841843 1.948692-4.483188C1.948692-4.196264 1.912827-4.076712 1.865006-3.861519L.514072 1.518306ZM2.582316-3.730012C2.666002-4.064757 3.000747-4.411457 3.19203-4.578829C3.323537-4.698381 3.718057-5.033126 4.172354-5.033126C4.698381-5.033126 4.937484-4.507098 4.937484-3.88543C4.937484-3.311582 4.60274-1.960648 4.303861-1.338979C4.004981-.6934 3.455044-.119552 2.905106-.119552C2.092154-.119552 1.960648-1.147696 1.960648-1.195517C1.960648-1.231382 1.984558-1.327024 1.996513-1.3868L2.582316-3.730012Z'/&gt;
&lt;path id='eq1-g1-114' d='M4.65056-4.889664C4.27995-4.817933 4.088667-4.554919 4.088667-4.291905C4.088667-4.004981 4.315816-3.90934 4.483188-3.90934C4.817933-3.90934 5.092902-4.196264 5.092902-4.554919C5.092902-4.937484 4.722291-5.272229 4.124533-5.272229C3.646326-5.272229 3.096389-5.057036 2.594271-4.327771C2.510585-4.961395 2.032379-5.272229 1.554172-5.272229C1.08792-5.272229 .848817-4.913574 .705355-4.65056C.502117-4.220174 .32279-3.502864 .32279-3.443088C.32279-3.395268 .37061-3.335492 .454296-3.335492C.549938-3.335492 .561893-3.347447 .633624-3.622416C.812951-4.339726 1.0401-5.033126 1.518306-5.033126C1.80523-5.033126 1.888917-4.829888 1.888917-4.483188C1.888917-4.220174 1.769365-3.753923 1.685679-3.383313L1.350934-2.092154C1.303113-1.865006 1.171606-1.327024 1.111831-1.111831C1.028144-.800996 .896638-.239103 .896638-.179328C.896638-.011955 1.028144 .119552 1.207472 .119552C1.338979 .119552 1.566127 .035866 1.637858-.203238C1.673724-.298879 2.116065-2.10411 2.187796-2.379078C2.247572-2.642092 2.319303-2.893151 2.379078-3.156164C2.426899-3.323537 2.47472-3.514819 2.510585-3.670237C2.546451-3.777833 2.86924-4.363636 3.16812-4.62665C3.311582-4.758157 3.622416-5.033126 4.112578-5.033126C4.303861-5.033126 4.495143-4.99726 4.65056-4.889664Z'/&gt;
&lt;path id='eq1-g1-116' d='M2.402989-4.805978H3.502864C3.730012-4.805978 3.849564-4.805978 3.849564-5.021171C3.849564-5.152677 3.777833-5.152677 3.53873-5.152677H2.486675L2.929016-6.898132C2.976837-7.065504 2.976837-7.089415 2.976837-7.173101C2.976837-7.364384 2.82142-7.47198 2.666002-7.47198C2.570361-7.47198 2.295392-7.436115 2.199751-7.053549L1.733499-5.152677H.609714C.37061-5.152677 .263014-5.152677 .263014-4.925529C.263014-4.805978 .3467-4.805978 .573848-4.805978H1.637858L.848817-1.649813C.753176-1.231382 .71731-1.111831 .71731-.956413C.71731-.394521 1.111831 .119552 1.78132 .119552C2.988792 .119552 3.634371-1.625903 3.634371-1.709589C3.634371-1.78132 3.58655-1.817186 3.514819-1.817186C3.490909-1.817186 3.443088-1.817186 3.419178-1.769365C3.407223-1.75741 3.395268-1.745455 3.311582-1.554172C3.060523-.956413 2.510585-.119552 1.817186-.119552C1.458531-.119552 1.43462-.418431 1.43462-.681445C1.43462-.6934 1.43462-.920548 1.470486-1.06401L2.402989-4.805978Z'/&gt;
&lt;path id='eq1-g1-121' d='M3.144209 1.338979C2.82142 1.793275 2.355168 2.199751 1.769365 2.199751C1.625903 2.199751 1.052055 2.175841 .872727 1.625903C.908593 1.637858 .968369 1.637858 .992279 1.637858C1.350934 1.637858 1.590037 1.327024 1.590037 1.052055S1.362889 .681445 1.183562 .681445C.992279 .681445 .573848 .824907 .573848 1.41071C.573848 2.020423 1.08792 2.438854 1.769365 2.438854C2.964882 2.438854 4.172354 1.338979 4.507098 .011955L5.678705-4.65056C5.69066-4.710336 5.71457-4.782067 5.71457-4.853798C5.71457-5.033126 5.571108-5.152677 5.391781-5.152677C5.284184-5.152677 5.033126-5.104857 4.937484-4.746202L4.052802-1.231382C3.993026-1.016189 3.993026-.992279 3.897385-.860772C3.658281-.526027 3.263761-.119552 2.689913-.119552C2.020423-.119552 1.960648-.777086 1.960648-1.099875C1.960648-1.78132 2.283437-2.701868 2.606227-3.56264C2.737733-3.90934 2.809465-4.076712 2.809465-4.315816C2.809465-4.817933 2.450809-5.272229 1.865006-5.272229C.765131-5.272229 .32279-3.53873 .32279-3.443088C.32279-3.395268 .37061-3.335492 .454296-3.335492C.561893-3.335492 .573848-3.383313 .621669-3.550685C.908593-4.554919 1.362889-5.033126 1.829141-5.033126C1.936737-5.033126 2.139975-5.033126 2.139975-4.638605C2.139975-4.327771 2.008468-3.981071 1.829141-3.526775C1.243337-1.960648 1.243337-1.566127 1.243337-1.279203C1.243337-.143462 2.056289 .119552 2.654047 .119552C3.000747 .119552 3.431133 .011955 3.849564-.430386L3.861519-.418431C3.682192 .286924 3.56264 .753176 3.144209 1.338979Z'/&gt;
&lt;/defs&gt;
&lt;g id='eq1-page1'&gt;
&lt;use x='97.335067' y='-2.324596' xlink:href='#eq1-g1-112'/&gt;
&lt;use x='103.21021' y='-2.324596' xlink:href='#eq1-g1-111'/&gt;
&lt;use x='108.837647' y='-2.324596' xlink:href='#eq1-g1-105'/&gt;
&lt;use x='112.83108' y='-2.324596' xlink:href='#eq1-g1-110'/&gt;
&lt;use x='119.818685' y='-2.324596' xlink:href='#eq1-g1-116'/&gt;
&lt;use x='127.366674' y='-2.324596' xlink:href='#eq1-g2-61'/&gt;
&lt;use x='139.792155' y='-2.324596' xlink:href='#eq1-g1-114'/&gt;
&lt;use x='145.392629' y='-2.324596' xlink:href='#eq1-g1-97'/&gt;
&lt;use x='151.537573' y='-2.324596' xlink:href='#eq1-g1-121'/&gt;
&lt;use x='157.674225' y='-2.324596' xlink:href='#eq1-g1-58'/&gt;
&lt;use x='160.925886' y='-2.324596' xlink:href='#eq1-g1-111'/&gt;
&lt;use x='166.553323' y='-2.324596' xlink:href='#eq1-g1-114'/&gt;
&lt;use x='172.153797' y='-2.324596' xlink:href='#eq1-g1-105'/&gt;
&lt;use x='176.147229' y='-2.324596' xlink:href='#eq1-g1-103'/&gt;
&lt;use x='182.181485' y='-2.324596' xlink:href='#eq1-g1-105'/&gt;
&lt;use x='186.174918' y='-2.324596' xlink:href='#eq1-g1-110'/&gt;
&lt;use x='195.819187' y='-2.324596' xlink:href='#eq1-g2-43'/&gt;
&lt;use x='207.580502' y='-2.324596' xlink:href='#eq1-g1-116'/&gt;
&lt;use x='214.464325' y='-2.324596' xlink:href='#eq1-g0-3'/&gt;
&lt;use x='223.098595' y='-2.324596' xlink:href='#eq1-g1-114'/&gt;
&lt;use x='228.699069' y='-2.324596' xlink:href='#eq1-g1-97'/&gt;
&lt;use x='234.844013' y='-2.324596' xlink:href='#eq1-g1-121'/&gt;
&lt;use x='240.980665' y='-2.324596' xlink:href='#eq1-g1-58'/&gt;
&lt;use x='244.232326' y='-2.324596' xlink:href='#eq1-g1-100'/&gt;
&lt;use x='250.315019' y='-2.324596' xlink:href='#eq1-g1-105'/&gt;
&lt;use x='254.308451' y='-2.324596' xlink:href='#eq1-g1-114'/&gt;
&lt;use x='259.908925' y='-2.324596' xlink:href='#eq1-g1-101'/&gt;
&lt;use x='265.334365' y='-2.324596' xlink:href='#eq1-g1-99'/&gt;
&lt;use x='270.372353' y='-2.324596' xlink:href='#eq1-g1-116'/&gt;
&lt;use x='274.599513' y='-2.324596' xlink:href='#eq1-g1-105'/&gt;
&lt;use x='278.592945' y='-2.324596' xlink:href='#eq1-g1-111'/&gt;
&lt;use x='284.220383' y='-2.324596' xlink:href='#eq1-g1-110'/&gt;
&lt;/g&gt;
&lt;/svg&gt;&lt;/div&gt;
&lt;p&gt;Being t a scalar value that represents the distance from the ray's origin to the point.&lt;/p&gt;
&lt;p&gt;Our objective is to find t, and check if the point is in the &amp;quot;right&amp;quot; side of the ray (the side that the ray is pointing to).&lt;/p&gt;
&lt;p&gt;For that we can rearrange the previous formula to:&lt;/p&gt;
&lt;div class="m-math"&gt;
&lt;svg style="width: 20.597em; height: 1.245em;" viewBox="95.40479 -11.955168 197.733475 11.955168"&gt;
&lt;title&gt;
t = (point - ray.origin) / ray.direction
&lt;/title&gt;
&lt;defs&gt;
&lt;path id='eq2-g0-0' d='M7.878456-2.749689C8.081694-2.749689 8.296887-2.749689 8.296887-2.988792S8.081694-3.227895 7.878456-3.227895H1.41071C1.207472-3.227895 .992279-3.227895 .992279-2.988792S1.207472-2.749689 1.41071-2.749689H7.878456Z'/&gt;
&lt;path id='eq2-g2-40' d='M3.88543 2.905106C3.88543 2.86924 3.88543 2.84533 3.682192 2.642092C2.486675 1.43462 1.817186-.537983 1.817186-2.976837C1.817186-5.296139 2.379078-7.292653 3.765878-8.703362C3.88543-8.810959 3.88543-8.834869 3.88543-8.870735C3.88543-8.942466 3.825654-8.966376 3.777833-8.966376C3.622416-8.966376 2.642092-8.105604 2.056289-6.933998C1.446575-5.726526 1.171606-4.447323 1.171606-2.976837C1.171606-1.912827 1.338979-.490162 1.960648 .789041C2.666002 2.223661 3.646326 3.000747 3.777833 3.000747C3.825654 3.000747 3.88543 2.976837 3.88543 2.905106Z'/&gt;
&lt;path id='eq2-g2-41' d='M3.371357-2.976837C3.371357-3.88543 3.251806-5.36787 2.582316-6.75467C1.876961-8.18929 .896638-8.966376 .765131-8.966376C.71731-8.966376 .657534-8.942466 .657534-8.870735C.657534-8.834869 .657534-8.810959 .860772-8.607721C2.056289-7.400249 2.725778-5.427646 2.725778-2.988792C2.725778-.669489 2.163885 1.327024 .777086 2.737733C.657534 2.84533 .657534 2.86924 .657534 2.905106C.657534 2.976837 .71731 3.000747 .765131 3.000747C.920548 3.000747 1.900872 2.139975 2.486675 .968369C3.096389-.251059 3.371357-1.542217 3.371357-2.976837Z'/&gt;
&lt;path id='eq2-g2-61' d='M8.069738-3.873474C8.237111-3.873474 8.452304-3.873474 8.452304-4.088667C8.452304-4.315816 8.249066-4.315816 8.069738-4.315816H1.028144C.860772-4.315816 .645579-4.315816 .645579-4.100623C.645579-3.873474 .848817-3.873474 1.028144-3.873474H8.069738ZM8.069738-1.649813C8.237111-1.649813 8.452304-1.649813 8.452304-1.865006C8.452304-2.092154 8.249066-2.092154 8.069738-2.092154H1.028144C.860772-2.092154 .645579-2.092154 .645579-1.876961C.645579-1.649813 .848817-1.649813 1.028144-1.649813H8.069738Z'/&gt;
&lt;path id='eq2-g1-58' d='M2.199751-.573848C2.199751-.920548 1.912827-1.159651 1.625903-1.159651C1.279203-1.159651 1.0401-.872727 1.0401-.585803C1.0401-.239103 1.327024 0 1.613948 0C1.960648 0 2.199751-.286924 2.199751-.573848Z'/&gt;
&lt;path id='eq2-g1-61' d='M5.128767-8.524035C5.128767-8.53599 5.200498-8.715318 5.200498-8.739228C5.200498-8.88269 5.080946-8.966376 4.985305-8.966376C4.925529-8.966376 4.817933-8.966376 4.722291-8.703362L.71731 2.546451C.71731 2.558406 .645579 2.737733 .645579 2.761644C.645579 2.905106 .765131 2.988792 .860772 2.988792C.932503 2.988792 1.0401 2.976837 1.123786 2.725778L5.128767-8.524035Z'/&gt;
&lt;path id='eq2-g1-97' d='M3.598506-1.422665C3.53873-1.219427 3.53873-1.195517 3.371357-.968369C3.108344-.633624 2.582316-.119552 2.020423-.119552C1.530262-.119552 1.255293-.561893 1.255293-1.267248C1.255293-1.924782 1.625903-3.263761 1.853051-3.765878C2.259527-4.60274 2.82142-5.033126 3.287671-5.033126C4.076712-5.033126 4.23213-4.052802 4.23213-3.957161C4.23213-3.945205 4.196264-3.789788 4.184309-3.765878L3.598506-1.422665ZM4.363636-4.483188C4.23213-4.794022 3.90934-5.272229 3.287671-5.272229C1.936737-5.272229 .478207-3.526775 .478207-1.75741C.478207-.573848 1.171606 .119552 1.984558 .119552C2.642092 .119552 3.203985-.394521 3.53873-.789041C3.658281-.083686 4.220174 .119552 4.578829 .119552S5.224408-.095641 5.439601-.526027C5.630884-.932503 5.798257-1.661768 5.798257-1.709589C5.798257-1.769365 5.750436-1.817186 5.678705-1.817186C5.571108-1.817186 5.559153-1.75741 5.511333-1.578082C5.332005-.872727 5.104857-.119552 4.614695-.119552C4.267995-.119552 4.244085-.430386 4.244085-.669489C4.244085-.944458 4.27995-1.075965 4.387547-1.542217C4.471233-1.841096 4.531009-2.10411 4.62665-2.450809C5.068991-4.244085 5.176588-4.674471 5.176588-4.746202C5.176588-4.913574 5.045081-5.045081 4.865753-5.045081C4.483188-5.045081 4.387547-4.62665 4.363636-4.483188Z'/&gt;
&lt;path id='eq2-g1-99' d='M4.674471-4.495143C4.447323-4.495143 4.339726-4.495143 4.172354-4.351681C4.100623-4.291905 3.969116-4.112578 3.969116-3.921295C3.969116-3.682192 4.148443-3.53873 4.375592-3.53873C4.662516-3.53873 4.985305-3.777833 4.985305-4.25604C4.985305-4.829888 4.435367-5.272229 3.610461-5.272229C2.044334-5.272229 .478207-3.56264 .478207-1.865006C.478207-.824907 1.123786 .119552 2.343213 .119552C3.969116 .119552 4.99726-1.147696 4.99726-1.303113C4.99726-1.374844 4.925529-1.43462 4.877709-1.43462C4.841843-1.43462 4.829888-1.422665 4.722291-1.315068C3.957161-.298879 2.82142-.119552 2.367123-.119552C1.542217-.119552 1.279203-.836862 1.279203-1.43462C1.279203-1.853051 1.482441-3.012702 1.912827-3.825654C2.223661-4.387547 2.86924-5.033126 3.622416-5.033126C3.777833-5.033126 4.435367-5.009215 4.674471-4.495143Z'/&gt;
&lt;path id='eq2-g1-100' d='M6.01345-7.998007C6.025405-8.045828 6.049315-8.117559 6.049315-8.177335C6.049315-8.296887 5.929763-8.296887 5.905853-8.296887C5.893898-8.296887 5.308095-8.249066 5.248319-8.237111C5.045081-8.225156 4.865753-8.201245 4.65056-8.18929C4.351681-8.16538 4.267995-8.153425 4.267995-7.938232C4.267995-7.81868 4.363636-7.81868 4.531009-7.81868C5.116812-7.81868 5.128767-7.711083 5.128767-7.591532C5.128767-7.519801 5.104857-7.424159 5.092902-7.388294L4.363636-4.483188C4.23213-4.794022 3.90934-5.272229 3.287671-5.272229C1.936737-5.272229 .478207-3.526775 .478207-1.75741C.478207-.573848 1.171606 .119552 1.984558 .119552C2.642092 .119552 3.203985-.394521 3.53873-.789041C3.658281-.083686 4.220174 .119552 4.578829 .119552S5.224408-.095641 5.439601-.526027C5.630884-.932503 5.798257-1.661768 5.798257-1.709589C5.798257-1.769365 5.750436-1.817186 5.678705-1.817186C5.571108-1.817186 5.559153-1.75741 5.511333-1.578082C5.332005-.872727 5.104857-.119552 4.614695-.119552C4.267995-.119552 4.244085-.430386 4.244085-.669489C4.244085-.71731 4.244085-.968369 4.327771-1.303113L6.01345-7.998007ZM3.598506-1.422665C3.53873-1.219427 3.53873-1.195517 3.371357-.968369C3.108344-.633624 2.582316-.119552 2.020423-.119552C1.530262-.119552 1.255293-.561893 1.255293-1.267248C1.255293-1.924782 1.625903-3.263761 1.853051-3.765878C2.259527-4.60274 2.82142-5.033126 3.287671-5.033126C4.076712-5.033126 4.23213-4.052802 4.23213-3.957161C4.23213-3.945205 4.196264-3.789788 4.184309-3.765878L3.598506-1.422665Z'/&gt;
&lt;path id='eq2-g1-101' d='M2.139975-2.773599C2.462765-2.773599 3.275716-2.797509 3.849564-3.012702C4.758157-3.359402 4.841843-4.052802 4.841843-4.267995C4.841843-4.794022 4.387547-5.272229 3.598506-5.272229C2.343213-5.272229 .537983-4.136488 .537983-2.008468C.537983-.753176 1.255293 .119552 2.343213 .119552C3.969116 .119552 4.99726-1.147696 4.99726-1.303113C4.99726-1.374844 4.925529-1.43462 4.877709-1.43462C4.841843-1.43462 4.829888-1.422665 4.722291-1.315068C3.957161-.298879 2.82142-.119552 2.367123-.119552C1.685679-.119552 1.327024-.657534 1.327024-1.542217C1.327024-1.709589 1.327024-2.008468 1.506351-2.773599H2.139975ZM1.566127-3.012702C2.080199-4.853798 3.21594-5.033126 3.598506-5.033126C4.124533-5.033126 4.483188-4.722291 4.483188-4.267995C4.483188-3.012702 2.570361-3.012702 2.068244-3.012702H1.566127Z'/&gt;
&lt;path id='eq2-g1-103' d='M4.040847-1.518306C3.993026-1.327024 3.969116-1.279203 3.813699-1.099875C3.323537-.466252 2.82142-.239103 2.450809-.239103C2.056289-.239103 1.685679-.549938 1.685679-1.374844C1.685679-2.008468 2.044334-3.347447 2.307347-3.88543C2.654047-4.554919 3.19203-5.033126 3.694147-5.033126C4.483188-5.033126 4.638605-4.052802 4.638605-3.981071L4.60274-3.813699L4.040847-1.518306ZM4.782067-4.483188C4.62665-4.829888 4.291905-5.272229 3.694147-5.272229C2.391034-5.272229 .908593-3.634371 .908593-1.853051C.908593-.609714 1.661768 0 2.426899 0C3.060523 0 3.622416-.502117 3.837609-.74122L3.574595 .334745C3.407223 .992279 3.335492 1.291158 2.905106 1.709589C2.414944 2.199751 1.960648 2.199751 1.697634 2.199751C1.338979 2.199751 1.0401 2.175841 .74122 2.080199C1.123786 1.972603 1.219427 1.637858 1.219427 1.506351C1.219427 1.315068 1.075965 1.123786 .812951 1.123786C.526027 1.123786 .215193 1.362889 .215193 1.75741C.215193 2.247572 .705355 2.438854 1.721544 2.438854C3.263761 2.438854 4.064757 1.446575 4.220174 .800996L5.547198-4.554919C5.583064-4.698381 5.583064-4.722291 5.583064-4.746202C5.583064-4.913574 5.451557-5.045081 5.272229-5.045081C4.985305-5.045081 4.817933-4.805978 4.782067-4.483188Z'/&gt;
&lt;path id='eq2-g1-105' d='M3.383313-1.709589C3.383313-1.769365 3.335492-1.817186 3.263761-1.817186C3.156164-1.817186 3.144209-1.78132 3.084433-1.578082C2.773599-.490162 2.283437-.119552 1.888917-.119552C1.745455-.119552 1.578082-.155417 1.578082-.514072C1.578082-.836862 1.721544-1.195517 1.853051-1.554172L2.689913-3.777833C2.725778-3.873474 2.809465-4.088667 2.809465-4.315816C2.809465-4.817933 2.450809-5.272229 1.865006-5.272229C.765131-5.272229 .32279-3.53873 .32279-3.443088C.32279-3.395268 .37061-3.335492 .454296-3.335492C.561893-3.335492 .573848-3.383313 .621669-3.550685C.908593-4.554919 1.362889-5.033126 1.829141-5.033126C1.936737-5.033126 2.139975-5.021171 2.139975-4.638605C2.139975-4.327771 1.984558-3.93325 1.888917-3.670237L1.052055-1.446575C.980324-1.255293 .908593-1.06401 .908593-.848817C.908593-.310834 1.279203 .119552 1.853051 .119552C2.952927 .119552 3.383313-1.625903 3.383313-1.709589ZM3.287671-7.460025C3.287671-7.639352 3.144209-7.854545 2.881196-7.854545C2.606227-7.854545 2.295392-7.591532 2.295392-7.280697C2.295392-6.981818 2.546451-6.886177 2.689913-6.886177C3.012702-6.886177 3.287671-7.197011 3.287671-7.460025Z'/&gt;
&lt;path id='eq2-g1-110' d='M2.462765-3.502864C2.486675-3.574595 2.785554-4.172354 3.227895-4.554919C3.53873-4.841843 3.945205-5.033126 4.411457-5.033126C4.889664-5.033126 5.057036-4.674471 5.057036-4.196264C5.057036-3.514819 4.566874-2.15193 4.327771-1.506351C4.220174-1.219427 4.160399-1.06401 4.160399-.848817C4.160399-.310834 4.531009 .119552 5.104857 .119552C6.216687 .119552 6.635118-1.637858 6.635118-1.709589C6.635118-1.769365 6.587298-1.817186 6.515567-1.817186C6.40797-1.817186 6.396015-1.78132 6.336239-1.578082C6.06127-.597758 5.606974-.119552 5.140722-.119552C5.021171-.119552 4.829888-.131507 4.829888-.514072C4.829888-.812951 4.961395-1.171606 5.033126-1.338979C5.272229-1.996513 5.774346-3.335492 5.774346-4.016936C5.774346-4.734247 5.355915-5.272229 4.447323-5.272229C3.383313-5.272229 2.82142-4.519054 2.606227-4.220174C2.570361-4.901619 2.080199-5.272229 1.554172-5.272229C1.171606-5.272229 .908593-5.045081 .705355-4.638605C.490162-4.208219 .32279-3.490909 .32279-3.443088S.37061-3.335492 .454296-3.335492C.549938-3.335492 .561893-3.347447 .633624-3.622416C.824907-4.351681 1.0401-5.033126 1.518306-5.033126C1.793275-5.033126 1.888917-4.841843 1.888917-4.483188C1.888917-4.220174 1.769365-3.753923 1.685679-3.383313L1.350934-2.092154C1.303113-1.865006 1.171606-1.327024 1.111831-1.111831C1.028144-.800996 .896638-.239103 .896638-.179328C.896638-.011955 1.028144 .119552 1.207472 .119552C1.350934 .119552 1.518306 .047821 1.613948-.131507C1.637858-.191283 1.745455-.609714 1.80523-.848817L2.068244-1.924782L2.462765-3.502864Z'/&gt;
&lt;path id='eq2-g1-111' d='M5.451557-3.287671C5.451557-4.423412 4.710336-5.272229 3.622416-5.272229C2.044334-5.272229 .490162-3.550685 .490162-1.865006C.490162-.729265 1.231382 .119552 2.319303 .119552C3.90934 .119552 5.451557-1.601993 5.451557-3.287671ZM2.331258-.119552C1.733499-.119552 1.291158-.597758 1.291158-1.43462C1.291158-1.984558 1.578082-3.203985 1.912827-3.801743C2.450809-4.722291 3.120299-5.033126 3.610461-5.033126C4.196264-5.033126 4.65056-4.554919 4.65056-3.718057C4.65056-3.239851 4.399502-1.960648 3.945205-1.231382C3.455044-.430386 2.797509-.119552 2.331258-.119552Z'/&gt;
&lt;path id='eq2-g1-112' d='M.514072 1.518306C.430386 1.876961 .382565 1.972603-.107597 1.972603C-.251059 1.972603-.37061 1.972603-.37061 2.199751C-.37061 2.223661-.358655 2.319303-.227148 2.319303C-.071731 2.319303 .095641 2.295392 .251059 2.295392H.765131C1.016189 2.295392 1.625903 2.319303 1.876961 2.319303C1.948692 2.319303 2.092154 2.319303 2.092154 2.10411C2.092154 1.972603 2.008468 1.972603 1.80523 1.972603C1.255293 1.972603 1.219427 1.888917 1.219427 1.793275C1.219427 1.649813 1.75741-.406476 1.829141-.681445C1.960648-.3467 2.283437 .119552 2.905106 .119552C4.25604 .119552 5.71457-1.637858 5.71457-3.395268C5.71457-4.495143 5.092902-5.272229 4.196264-5.272229C3.431133-5.272229 2.785554-4.531009 2.654047-4.363636C2.558406-4.961395 2.092154-5.272229 1.613948-5.272229C1.267248-5.272229 .992279-5.104857 .765131-4.65056C.549938-4.220174 .382565-3.490909 .382565-3.443088S.430386-3.335492 .514072-3.335492C.609714-3.335492 .621669-3.347447 .6934-3.622416C.872727-4.327771 1.099875-5.033126 1.578082-5.033126C1.853051-5.033126 1.948692-4.841843 1.948692-4.483188C1.948692-4.196264 1.912827-4.076712 1.865006-3.861519L.514072 1.518306ZM2.582316-3.730012C2.666002-4.064757 3.000747-4.411457 3.19203-4.578829C3.323537-4.698381 3.718057-5.033126 4.172354-5.033126C4.698381-5.033126 4.937484-4.507098 4.937484-3.88543C4.937484-3.311582 4.60274-1.960648 4.303861-1.338979C4.004981-.6934 3.455044-.119552 2.905106-.119552C2.092154-.119552 1.960648-1.147696 1.960648-1.195517C1.960648-1.231382 1.984558-1.327024 1.996513-1.3868L2.582316-3.730012Z'/&gt;
&lt;path id='eq2-g1-114' d='M4.65056-4.889664C4.27995-4.817933 4.088667-4.554919 4.088667-4.291905C4.088667-4.004981 4.315816-3.90934 4.483188-3.90934C4.817933-3.90934 5.092902-4.196264 5.092902-4.554919C5.092902-4.937484 4.722291-5.272229 4.124533-5.272229C3.646326-5.272229 3.096389-5.057036 2.594271-4.327771C2.510585-4.961395 2.032379-5.272229 1.554172-5.272229C1.08792-5.272229 .848817-4.913574 .705355-4.65056C.502117-4.220174 .32279-3.502864 .32279-3.443088C.32279-3.395268 .37061-3.335492 .454296-3.335492C.549938-3.335492 .561893-3.347447 .633624-3.622416C.812951-4.339726 1.0401-5.033126 1.518306-5.033126C1.80523-5.033126 1.888917-4.829888 1.888917-4.483188C1.888917-4.220174 1.769365-3.753923 1.685679-3.383313L1.350934-2.092154C1.303113-1.865006 1.171606-1.327024 1.111831-1.111831C1.028144-.800996 .896638-.239103 .896638-.179328C.896638-.011955 1.028144 .119552 1.207472 .119552C1.338979 .119552 1.566127 .035866 1.637858-.203238C1.673724-.298879 2.116065-2.10411 2.187796-2.379078C2.247572-2.642092 2.319303-2.893151 2.379078-3.156164C2.426899-3.323537 2.47472-3.514819 2.510585-3.670237C2.546451-3.777833 2.86924-4.363636 3.16812-4.62665C3.311582-4.758157 3.622416-5.033126 4.112578-5.033126C4.303861-5.033126 4.495143-4.99726 4.65056-4.889664Z'/&gt;
&lt;path id='eq2-g1-116' d='M2.402989-4.805978H3.502864C3.730012-4.805978 3.849564-4.805978 3.849564-5.021171C3.849564-5.152677 3.777833-5.152677 3.53873-5.152677H2.486675L2.929016-6.898132C2.976837-7.065504 2.976837-7.089415 2.976837-7.173101C2.976837-7.364384 2.82142-7.47198 2.666002-7.47198C2.570361-7.47198 2.295392-7.436115 2.199751-7.053549L1.733499-5.152677H.609714C.37061-5.152677 .263014-5.152677 .263014-4.925529C.263014-4.805978 .3467-4.805978 .573848-4.805978H1.637858L.848817-1.649813C.753176-1.231382 .71731-1.111831 .71731-.956413C.71731-.394521 1.111831 .119552 1.78132 .119552C2.988792 .119552 3.634371-1.625903 3.634371-1.709589C3.634371-1.78132 3.58655-1.817186 3.514819-1.817186C3.490909-1.817186 3.443088-1.817186 3.419178-1.769365C3.407223-1.75741 3.395268-1.745455 3.311582-1.554172C3.060523-.956413 2.510585-.119552 1.817186-.119552C1.458531-.119552 1.43462-.418431 1.43462-.681445C1.43462-.6934 1.43462-.920548 1.470486-1.06401L2.402989-4.805978Z'/&gt;
&lt;path id='eq2-g1-121' d='M3.144209 1.338979C2.82142 1.793275 2.355168 2.199751 1.769365 2.199751C1.625903 2.199751 1.052055 2.175841 .872727 1.625903C.908593 1.637858 .968369 1.637858 .992279 1.637858C1.350934 1.637858 1.590037 1.327024 1.590037 1.052055S1.362889 .681445 1.183562 .681445C.992279 .681445 .573848 .824907 .573848 1.41071C.573848 2.020423 1.08792 2.438854 1.769365 2.438854C2.964882 2.438854 4.172354 1.338979 4.507098 .011955L5.678705-4.65056C5.69066-4.710336 5.71457-4.782067 5.71457-4.853798C5.71457-5.033126 5.571108-5.152677 5.391781-5.152677C5.284184-5.152677 5.033126-5.104857 4.937484-4.746202L4.052802-1.231382C3.993026-1.016189 3.993026-.992279 3.897385-.860772C3.658281-.526027 3.263761-.119552 2.689913-.119552C2.020423-.119552 1.960648-.777086 1.960648-1.099875C1.960648-1.78132 2.283437-2.701868 2.606227-3.56264C2.737733-3.90934 2.809465-4.076712 2.809465-4.315816C2.809465-4.817933 2.450809-5.272229 1.865006-5.272229C.765131-5.272229 .32279-3.53873 .32279-3.443088C.32279-3.395268 .37061-3.335492 .454296-3.335492C.561893-3.335492 .573848-3.383313 .621669-3.550685C.908593-4.554919 1.362889-5.033126 1.829141-5.033126C1.936737-5.033126 2.139975-5.033126 2.139975-4.638605C2.139975-4.327771 2.008468-3.981071 1.829141-3.526775C1.243337-1.960648 1.243337-1.566127 1.243337-1.279203C1.243337-.143462 2.056289 .119552 2.654047 .119552C3.000747 .119552 3.431133 .011955 3.849564-.430386L3.861519-.418431C3.682192 .286924 3.56264 .753176 3.144209 1.338979Z'/&gt;
&lt;/defs&gt;
&lt;g id='eq2-page1'&gt;
&lt;use x='95.40479' y='-2.988792' xlink:href='#eq2-g1-116'/&gt;
&lt;use x='102.952779' y='-2.988792' xlink:href='#eq2-g2-61'/&gt;
&lt;use x='115.37826' y='-2.988792' xlink:href='#eq2-g2-40'/&gt;
&lt;use x='119.930586' y='-2.988792' xlink:href='#eq2-g1-112'/&gt;
&lt;use x='125.805729' y='-2.988792' xlink:href='#eq2-g1-111'/&gt;
&lt;use x='131.433166' y='-2.988792' xlink:href='#eq2-g1-105'/&gt;
&lt;use x='135.426598' y='-2.988792' xlink:href='#eq2-g1-110'/&gt;
&lt;use x='142.414204' y='-2.988792' xlink:href='#eq2-g1-116'/&gt;
&lt;use x='149.298027' y='-2.988792' xlink:href='#eq2-g0-0'/&gt;
&lt;use x='161.253188' y='-2.988792' xlink:href='#eq2-g1-114'/&gt;
&lt;use x='166.853661' y='-2.988792' xlink:href='#eq2-g1-97'/&gt;
&lt;use x='172.998606' y='-2.988792' xlink:href='#eq2-g1-121'/&gt;
&lt;use x='179.135257' y='-2.988792' xlink:href='#eq2-g1-58'/&gt;
&lt;use x='182.386919' y='-2.988792' xlink:href='#eq2-g1-111'/&gt;
&lt;use x='188.014356' y='-2.988792' xlink:href='#eq2-g1-114'/&gt;
&lt;use x='193.61483' y='-2.988792' xlink:href='#eq2-g1-105'/&gt;
&lt;use x='197.608262' y='-2.988792' xlink:href='#eq2-g1-103'/&gt;
&lt;use x='203.642518' y='-2.988792' xlink:href='#eq2-g1-105'/&gt;
&lt;use x='207.63595' y='-2.988792' xlink:href='#eq2-g1-110'/&gt;
&lt;use x='214.623556' y='-2.988792' xlink:href='#eq2-g2-41'/&gt;
&lt;use x='219.175882' y='-2.988792' xlink:href='#eq2-g1-61'/&gt;
&lt;use x='225.028872' y='-2.988792' xlink:href='#eq2-g1-114'/&gt;
&lt;use x='230.629345' y='-2.988792' xlink:href='#eq2-g1-97'/&gt;
&lt;use x='236.77429' y='-2.988792' xlink:href='#eq2-g1-121'/&gt;
&lt;use x='242.910941' y='-2.988792' xlink:href='#eq2-g1-58'/&gt;
&lt;use x='246.162603' y='-2.988792' xlink:href='#eq2-g1-100'/&gt;
&lt;use x='252.245296' y='-2.988792' xlink:href='#eq2-g1-105'/&gt;
&lt;use x='256.238728' y='-2.988792' xlink:href='#eq2-g1-114'/&gt;
&lt;use x='261.839201' y='-2.988792' xlink:href='#eq2-g1-101'/&gt;
&lt;use x='267.264641' y='-2.988792' xlink:href='#eq2-g1-99'/&gt;
&lt;use x='272.30263' y='-2.988792' xlink:href='#eq2-g1-116'/&gt;
&lt;use x='276.52979' y='-2.988792' xlink:href='#eq2-g1-105'/&gt;
&lt;use x='280.523222' y='-2.988792' xlink:href='#eq2-g1-111'/&gt;
&lt;use x='286.150659' y='-2.988792' xlink:href='#eq2-g1-110'/&gt;
&lt;/g&gt;
&lt;/svg&gt;&lt;/div&gt;
&lt;p&gt;If the point is in the right side of the ray, then the intersection point is the point that is closest to the ray's origin.&lt;/p&gt;
&lt;p&gt;Now, the only thing that we still need to account is, that most of the times, we have 2 intersection points, one going in, and one going out.&lt;/p&gt;
&lt;p&gt;For this we can change the way we use this formulas by instead of using the point, we use the minimum and maximum values of the box.&lt;/p&gt;
&lt;p&gt;If both our t's make sense, then we have an intersection.&lt;/p&gt;
&lt;p&gt;Here is an excerpt taken from the &lt;code&gt;Raycast&lt;/code&gt; class:&lt;/p&gt;
&lt;pre class="m-code"&gt;&lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;intersects&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cubos&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Raycast&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Ray&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ray&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cubos&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;geom&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Box&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;box&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;glm&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vec3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;corners&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;glm&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vec3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;corners&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tMinX&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tMaxX&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tMinY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tMaxY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tMinZ&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tMaxZ&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// find the maximum of the min&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tMin&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tMinX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tMaxX&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tMinY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tMaxY&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tMinZ&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tMaxZ&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// find the minimum of the max&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tMax&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tMinX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tMaxX&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tMinY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tMaxY&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tMinZ&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tMaxZ&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tMax&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tMin&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tMax&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;-1.0F&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tMin&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.0F&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tMax&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tMin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;&lt;/pre&gt;
&lt;/section&gt;
&lt;section id="intersection-with-a-capsule"&gt;
&lt;h5&gt;Intersection with a Capsule&lt;/h5&gt;
&lt;p&gt;The intersection with a capsule is more straight forward than the collision with a box, as we can separate a capsule into 3 parts,
a cylinder and the two spheres at the ends.&lt;/p&gt;
&lt;p&gt;We then can check for a point of intersection by checking if the ray intersects the cylinder, and if it doesn't, we check if it intersects the spheres.&lt;/p&gt;
&lt;p&gt;We can determine both intersections by simply subbing the the ray's equation for x and z in the cylinder and sphere equations, and then solving it for t.&lt;/p&gt;
&lt;p&gt;Code excerpt from raycast.cpp for the cylinder intersection:&lt;/p&gt;
&lt;pre class="m-code"&gt;&lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;intersects&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cubos&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Raycast&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Ray&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ray&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;top&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// We are gonna use the quadratic equation made by subbing the ray equation into the cylinder equation&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// The cylinder equation is:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// x^2 + z^2 = r^2&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// The ray equation is:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// x = x0 + t * dx&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// z = z0 + t * dz&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2.0F&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;discriminant&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;4.0F&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;discriminant&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;-1.0F&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// no intersection with the cylinder&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;discriminant&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;2.0F&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;discriminant&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;2.0F&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.0F&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.0F&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;-1.0F&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// no valid intersection&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bottom&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;top&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;-1.0F&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// intersection is outside the finite cylinder&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;&lt;/pre&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="spot-light-shadows-tomas7770"&gt;
&lt;h4&gt;Spot Light Shadows &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/tomas-gouveia/"&gt;&amp;#64;tomas7770&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;Our graphics renderer has received a new major feature in this release: shadows!&lt;/p&gt;
&lt;img class="m-image" src="https://cubos.riscadoa.com/images/blog/release/0-3/cover.png" /&gt;
&lt;p&gt;It should go without saying that this feature has a big impact on the visuals of games
developed with Cubos. We've tried it on &lt;em&gt;Scraps vs Zombies&lt;/em&gt; and the result is stunning!
It's an important step towards the kind of appealing graphics that we hope to achieve.&lt;/p&gt;
&lt;p&gt;For the time being, shadows support is limited to spot lights. To enable them, all you need
to do is add a &lt;a href="https://docs.cubos.riscadoa.com/structcubos_1_1engine_1_1SpotShadowCaster.html"&gt;SpotShadowCaster&lt;/a&gt; component to the spot lights for which you want shadows to be cast,
as shown in the &lt;a href="https://github.com/GameDevTecnico/cubos/tree/main/engine/samples/render/shadows"&gt;Shadows sample&lt;/a&gt;.
Both hard and soft shadows are supported, with a configurable &lt;a href="https://docs.cubos.riscadoa.com/structcubos_1_1engine_1_1ShadowCaster.html#a450e1d2c732f1c38403a428eb0536660"&gt;blurRadius&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Behind the scenes, this works by rendering the world from each light's perspective to determine which parts
are occluded, and making these parts unlit. A large texture known as the &amp;quot;shadow atlas&amp;quot; holds this information for
every light in a quadtree structure, reducing expensive texture switching. Finally, soft shadows are implemented
as a post-processing step that effectively blurs out the shadows.
Below is a screenshot of the shadow atlas with 5 spot lights. Lines have been drawn separating
the areas of the atlas reserved for each light.&lt;/p&gt;
&lt;img class="m-image" src="https://cubos.riscadoa.com/images/blog/release/0-3/shadow_atlas.png" /&gt;
&lt;/section&gt;
&lt;section id="initial-ui-plugin-diogomendonc-a"&gt;
&lt;h4&gt;Initial UI Plugin &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/diogo-mendonca/"&gt;&amp;#64;DiogoMendonc-a&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;Cubos now has a UI system!&lt;/p&gt;
&lt;p&gt;Add a &lt;a href="https://docs.cubos.riscadoa.com/structcubos_1_1engine_1_1UICanvas.html"&gt;UICanvas&lt;/a&gt; to your Render Target, and set a &lt;a href="https://docs.cubos.riscadoa.com/structcubos_1_1engine_1_1UIElement.html"&gt;UIElement&lt;/a&gt; as its child. The UIElement will determine where the entity is drawn, and other UI components you add to the entity, such as a UIImage, will determine what it is that is drawn.
For this initial version of the plugin, there are only two types of elements: &lt;a href="https://docs.cubos.riscadoa.com/structcubos_1_1engine_1_1UIColorRect.html"&gt;UIColorRect&lt;/a&gt;, which simply fills the element with a solid colour, and &lt;a href="https://docs.cubos.riscadoa.com/structcubos_1_1engine_1_1UIImage.html"&gt;UIImage&lt;/a&gt;, which draws an image asset.&lt;/p&gt;
&lt;p&gt;To make using the UIElement easier, there are also a number of components meant to dynamically change its size: &lt;a href="https://docs.cubos.riscadoa.com/structcubos_1_1engine_1_1UIHorizontalStretch.html"&gt;UIHorizontalStretch&lt;/a&gt; and &lt;a href="https://docs.cubos.riscadoa.com/structcubos_1_1engine_1_1UIVerticalStretch.html"&gt;UIVerticalStretch&lt;/a&gt; will make the element expand to its parent's size.&lt;/p&gt;
&lt;p&gt;One more problem that was tackled was the question of how to handle different aspect ratios. As it stands, Cubos now has five different settings for how to handle that, that can be read in detail on &lt;a href="https://docs.cubos.riscadoa.com/examples-engine-ui.html"&gt;the sample page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Finally, there is a &lt;a href="https://docs.cubos.riscadoa.com/structcubos_1_1engine_1_1UINativeAspectRatio.html"&gt;UINativeAspectRatio&lt;/a&gt; that, when paired with an UIImage, will ensure that the UIElement will retain the proportions of the original source file.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="on-the-core"&gt;
&lt;h3&gt;On the Core&lt;/h3&gt;
&lt;section id="metrics-roby2014"&gt;
&lt;h4&gt;Metrics &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/roberto-carlos/"&gt;&amp;#64;roby2014&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;We are excited to introduce the new metrics and profiling utilities!
This started becoming a priority since we detected lots of performance issues in the last Game Jam we participated.&lt;/p&gt;
&lt;p&gt;These tools are designed to help track performance and gather valuable insights about code execution and data.&lt;/p&gt;
&lt;p&gt;How it looks:&lt;/p&gt;
&lt;pre class="m-code"&gt;&lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;compute&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;CUBOS_PROFILE&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// after the scope ends, a new metric `compute` will be added, with the duration of this scope&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;myFunction&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;/// simulate profiling loop&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1337&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// simulate frame by calling a function that does work..&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;compute&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// register some metrics, this could be FPS, entities count, ....&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;CUBOS_METRIC&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;count&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;Currently, metrics can be accessed manually through the singleton class. However, we plan to
integrate these metrics into our editor for a more streamlined experience.&lt;/p&gt;
&lt;p&gt;Learn more about it on our &lt;a href="https://docs.cubos.riscadoa.com/examples-core-metrics.html"&gt;metrics documentation&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="networking-utilities-roby2014"&gt;
&lt;h4&gt;Networking Utilities &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/roberto-carlos/"&gt;&amp;#64;roby2014&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;In the 0.3 release, Cubos finally has networking! This is a big step forward for our engine, bringing
powerful networking utilities such as &lt;a href="https://docs.cubos.riscadoa.com/classcubos_1_1core_1_1net_1_1Address.html"&gt;Address&lt;/a&gt;,
&lt;a href="https://docs.cubos.riscadoa.com/classcubos_1_1core_1_1net_1_1UdpSocket.html"&gt;UdpSocket&lt;/a&gt;, &lt;a href="https://docs.cubos.riscadoa.com/classcubos_1_1core_1_1net_1_1TcpListener.html"&gt;TcpListener&lt;/a&gt;,
and &lt;a href="https://docs.cubos.riscadoa.com/classcubos_1_1core_1_1net_1_1TcpStream.html"&gt;TcpStream&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Creating an UDP client and sending a message is as simple as:&lt;/p&gt;
&lt;pre class="m-code"&gt;&lt;span class="n"&gt;UdpSocket&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Address&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;LocalHost&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Hello, I&amp;#39;m a Cubos UDP client!&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;strlen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Address&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;server.com&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8081&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;Learn more about it on our &lt;a href="https://docs.cubos.riscadoa.com/examples-core-networking.html"&gt;networking documentation&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="next-steps"&gt;
&lt;h2&gt;Next Steps&lt;/h2&gt;
&lt;p&gt;Although this was a smaller release, we managed to get some important features done!
In the next release, which should be out by the end of this month, we're planning to add:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;standalone editor application&lt;/strong&gt;. Our tools are currently integrated into the games themselves which is not ideal.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Audio support&lt;/strong&gt;, as there's no sound at all in the engine right now.&lt;/li&gt;
&lt;li&gt;Actual &lt;strong&gt;voxel collisions&lt;/strong&gt;, as we currently only check the bounding boxes.&lt;/li&gt;
&lt;li&gt;Basic &lt;strong&gt;rigidbody physics&lt;/strong&gt;, with rotation and friction.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Shadows&lt;/strong&gt; for other light types.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tracing and spans&lt;/strong&gt;, for better tracking of program execution.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can check out the full list of stuff we want to get done in the &lt;a href="https://github.com/GameDevTecnico/cubos/milestone/27"&gt;milestone&lt;/a&gt; for the next release.&lt;/p&gt;
&lt;p&gt;We're currently a team of 13 people, and &lt;strong&gt;we're looking to expand&lt;/strong&gt;! If you're interested in joining us, or just want to learn more about the project, join our &lt;a href="https://discord.gg/WjTtcNTRqD"&gt;Discord server&lt;/a&gt;!&lt;/p&gt;
&lt;/section&gt;
</content><category term="Release"/><category term="Release"/></entry><entry><title>Release 0.2</title><link href="https://cubos.riscadoa.com/blog/release-02/" rel="alternate"/><published>2024-05-21T10:00:00+01:00</published><updated>2024-05-21T10:00:00+01:00</updated><author><name>Cubos Team</name></author><id>tag:cubos.riscadoa.com,2024-05-21:/blog/release-02/</id><summary type="html">&lt;p&gt;From ECS observers and fixed-time step support to better editing tools, a new modular rendering pipeline, and the first physics constraints solvers.&lt;/p&gt;
</summary><content type="html">&lt;section id="new-0-2-features"&gt;
&lt;h2&gt;New 0.2 Features&lt;/h2&gt;
&lt;p&gt;Our focus on this release was to continue working on improving and implementing base functionality, such as rendering and physics, and also to improve on the tools available to the developer.
We've compiled here some of the most important changes (if you're curious, you can check out the full changelog in our &lt;a href="https://github.com/GameDevTecnico/cubos/blob/main/CHANGELOG.md"&gt;repository&lt;/a&gt;).&lt;/p&gt;
&lt;section id="on-the-editor"&gt;
&lt;h3&gt;On the Editor&lt;/h3&gt;
&lt;section id="transform-gizmo-upgrades-diogomendonc-a"&gt;
&lt;h4&gt;Transform Gizmo Upgrades &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/diogo-mendonca/"&gt;&amp;#64;DiogoMendonc-a&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;The biggest change was the addition of a rotation gizmo, so now you can rotate entities using your mouse!&lt;/p&gt;
&lt;p&gt;We also added a toggle that allows changing between using global or local space with the Transform Gizmo,
and a new type of gizmo, a &lt;code&gt;Rotated Box&lt;/code&gt;, which, unlike the old boz gizmos, does not need to be axis-aligned.&lt;/p&gt;
&lt;p&gt;Finally, the transform gizmo is now always rendered at the same size, regardless of the selected entity's distance to the camera.
This should hopefully make it easier to use the tool when moving entities either very far away, or very close.&lt;/p&gt;
&lt;img class="m-image" src="https://cubos.riscadoa.com/images/blog/release/0-2/transform_gizmo.gif" /&gt;
&lt;/section&gt;
&lt;section id="world-inspector-overhaul-diogomsmiranda"&gt;
&lt;h4&gt;World Inspector Overhaul &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/diogo-miranda/"&gt;&amp;#64;diogomsmiranda&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;The World inspector has been on our radar for quite some time now, as a tool that could be improved.
This release we particularly aimed on making it better by focusing on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Making it easier to find the entities you are looking for.&lt;/li&gt;
&lt;li&gt;Displaying more information about the entities in the scene.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To tackle the first point we've added a search bar that allows you to filter the entities in the scene, either by their name, or by their components. e.g:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Searching for &amp;quot;player&amp;quot; will show all entities that have &amp;quot;player&amp;quot; in their name.&lt;/li&gt;
&lt;li&gt;Searching for &amp;quot;Transform&amp;quot; will show all entities that have a &lt;code&gt;Transform&lt;/code&gt; component.&lt;/li&gt;
&lt;li&gt;Searching for &amp;quot;player, Transform&amp;quot; will show all entities that have a &lt;code&gt;Transform&lt;/code&gt; component and contain &amp;quot;player&amp;quot; in their name.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To tackle the second point on our list we added an hierarchy view that shows the entities in the scene in a tree-like structure representing the &lt;code&gt;ChildOf&lt;/code&gt; relation between entities.&lt;/p&gt;
&lt;img class="m-image" src="https://cubos.riscadoa.com/images/blog/release/0-2/world_inspector.gif" /&gt;
&lt;/section&gt;
&lt;section id="sub-scene-importing-in-scene-editor-teres4"&gt;
&lt;h4&gt;Sub-scene importing in scene editor &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/teresa-matos/"&gt;&amp;#64;teres4&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;Previously our scene editor was lacking support for importing sub-scenes.
This meant that developers had to manually open the scene file and paste the sub-scene UUID in its import section.
To save time, we've added a popup that shows all the importable sub-scenes, and allows the developer to select the one they want to import.&lt;/p&gt;
&lt;img class="m-image" src="https://cubos.riscadoa.com/images/blog/release/0-2/scene_editor.gif" /&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="on-the-engine"&gt;
&lt;h3&gt;On the Engine&lt;/h3&gt;
&lt;section id="physics-improvements-fallenatlas"&gt;
&lt;h4&gt;Physics Improvements &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/tiago-antunes/"&gt;&amp;#64;fallenatlas&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;This update we introduced the &lt;code&gt;Solver&lt;/code&gt;, which works to satisfy the physics constraints applied to entities (e.g. Spring, Fixed distance between entities, etc.).&lt;/p&gt;
&lt;p&gt;This means that the developers can now give entities any number of constraints, either engine defined or their own, and the solver will handle their expected interaction.
As of now, we have one constraint, internal to the engine, the &lt;code&gt;PenetrationConstraint&lt;/code&gt;, which separates bodies that are inside each other, and comes in to replace our previous collision solving system.&lt;/p&gt;
&lt;p&gt;With the ability to have repeating systems, it was possible to introduce the physics substeps loop, which
will perform the physics update in multiple smaller steps. This is important to increase the convergence rate of the solver and preservation of energy, by reducing the errors caused by approximations of the simulation.&lt;/p&gt;
&lt;p&gt;These changes should lead to more realistic behavior, as well as better consistency between systems with different performance levels.&lt;/p&gt;
&lt;img class="m-image" src="https://cubos.riscadoa.com/images/blog/release/0-2/complex_physics_sample.gif" /&gt;
&lt;/section&gt;
&lt;section id="modular-renderer-riscadoa-tomas7770"&gt;
&lt;h4&gt;Modular Renderer &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/ricardo-antunes/"&gt;&amp;#64;RiscadoA&lt;/a&gt;, &lt;a href="https://cubos.riscadoa.com/blog/author/tomas-gouveia/"&gt;&amp;#64;tomas7770&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;Before this update, our graphics renderer was very monolithic, with a lot of the code being held in a single file. This posed some problems,
namely that if we wanted to implement new rendering methods in the future (e.g. raytracing), we would end up with tons of duplicate code.
It also didn't fit well with our ECS design, since things such as the renderer and its active cameras were just global resources.&lt;/p&gt;
&lt;p&gt;To overcome this, we've restructured the renderer, splitting it into &lt;a href="https://docs.cubos.riscadoa.com/group__render-plugins.html"&gt;several components with their respective plugins&lt;/a&gt;. The most important ones
are perhaps &lt;code&gt;RenderTarget&lt;/code&gt;, representing something that can be drawn to, and &lt;code&gt;PerspectiveCamera&lt;/code&gt;, which draws to render targets using perspective projection.
Entities with these components are related using a &lt;code&gt;DrawsTo&lt;/code&gt; relation. There are also components that individually enable various parts of the renderer,
such as deferred shading, or effects like bloom and SSAO. This separation opens up possibilities for more customizability from the user side,
and makes the renderer code easier to deal with by engine developers.&lt;/p&gt;
&lt;p&gt;We also used the opportunity to optimize the renderer, namely, by allocating a large GPU buffer for all the voxel meshes, which we then split into smaller buckets.
This way, we can draw all the voxel objects with fewer draw calls, and with way less context switches.
One other feature we improved greatly was entity picking - finding out which entity is visible at a given pixel. Previously, enabling it cut the framerate by more than half, which was unacceptable.
By using &lt;a href="https://www.khronos.org/opengl/wiki/Pixel_Buffer_Object"&gt;Pixel Buffers&lt;/a&gt; and double-buffering, we managed to make its performance impact negligible.&lt;/p&gt;
&lt;img class="m-image" src="https://cubos.riscadoa.com/images/blog/release/0-2/scraps_vs_zombies_new_renderer.png" /&gt;
&lt;p&gt;Additionally, we added a tone-mapping pass to the renderer, which makes better use of the HDR color space. It allows us to tune the exposure of the scene, and also to apply effects like bloom and SSAO in a more realistic way.
One side-effect of us changing the color-space is that now the colors in the game are a bit more washed out. We'll have to go back and tweak the colors in the game to make them look good again.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="on-the-core"&gt;
&lt;h3&gt;On the Core&lt;/h3&gt;
&lt;section id="clearer-plugin-dependencies-riscadoa"&gt;
&lt;h4&gt;Clearer Plugin Dependencies &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/ricardo-antunes/"&gt;&amp;#64;RiscadoA&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;One of the main features of &lt;strong&gt;Cubos&lt;/strong&gt; is its plugin system. We structure all of the engine and editor features as plugins, each with its own set of components, systems, and resources.
The developer ultimately chooses which plugins to include in their game, and may also create their own plugins.&lt;/p&gt;
&lt;p&gt;Plugins may depend on each other. One example is the &lt;a href="https://docs.cubos.riscadoa.com/group__physics-plugin.html"&gt;physics plugin&lt;/a&gt;, which depends on the &lt;a href="https://docs.cubos.riscadoa.com/group__collisions-plugin.html"&gt;collisions plugin&lt;/a&gt;.
Previously, when a plugin was included, all of its dependencies were automatically included. It was also possible to include a plugin twice - the second time, the plugin would simply be ignored.&lt;/p&gt;
&lt;p&gt;While this seems useful, this led to some problems. Since a plugin could be included more than once, there isn't a single place where its safe to make decisions about how a given plugin should behave.
This was particularly problematic with the new modular renderer plugins.&lt;/p&gt;
&lt;p&gt;Imagine a scenario where we have two plugins A and B, which implement different rendering methods.
A and B are incompatible. Adding both should be an error, as it's impossible to have both rendering methods active at the same time.
Additionally, A and B both add on a plugin C, and make decisions about how C should behave. If both A and B are included, which one should have the final say?
How do we specify the dependency between them without coupling their code?&lt;/p&gt;
&lt;p&gt;Our solution to this was to distinguish between &lt;em&gt;depending&lt;/em&gt; and &lt;em&gt;including&lt;/em&gt; a plugin. When a plugin X depends on a plugin Y, adding X without Y being present leads to an error.
When a plugin X includes a plugin Y, including X will also include Y. But, if a plugin is included twice, an error occurs.
This way, we get a clean plugin hierarchy, which is easier to reason about:&lt;/p&gt;
&lt;pre class="m-code"&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;myPlugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Cubos&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cubos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;cubos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;depends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;renderVoxelsPlugin&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Don&amp;#39;t care about internals, just need the plugin&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;cubos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mySubPlugin&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Include a sub-plugin, which becomes part of my plugin&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;One issue with this solution is that you would now have to manually include all plugins you need when making a game.
Of course, this is not ideal, and thus, we've added a new &lt;a href="https://docs.cubos.riscadoa.com/group__defaults-plugin.html"&gt;defaults plugin&lt;/a&gt; to the engine that includes all the plugins that are necessary for a basic game to run.
If the developer wants to configure the engine in a different way, they can simply write their own alternative defaults plugin.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="observers-riscadoa"&gt;
&lt;h4&gt;Observers &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/ricardo-antunes/"&gt;&amp;#64;RiscadoA&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;Very often while working with &lt;strong&gt;Cubos&lt;/strong&gt;, we find the need to react to the addition and removal of components in entities.
One use case, for example, is setting up the AABB of an entity when we add a collider to it.
Previously, we had to manually implement this logic, for example, by adding a flag to the collider which was initially &lt;code&gt;false&lt;/code&gt;, and having a system initializing all colliders with the flag set to &lt;code&gt;false&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To solve this problem in a more elegant way, we added a new special kind of system: &lt;em&gt;observers&lt;/em&gt;. Observers are essentially systems which instead of running on a specific schedule, run immediately after a certain change occurs in the ECS (akin to callbacks).&lt;/p&gt;
&lt;p&gt;We also found another unexpected use for this feature. Complex plugins with lots of components, such as the physics plugin, where previously cumbersome to work with, as all the required components had to be manually added to each entity we wanted physics to apply to.
With observers, we instead now add a single component &lt;code&gt;PhysicsBundle&lt;/code&gt;. An observer picks up this addition, and immediately replaces this component with all of the required components for the physics plugin to work.&lt;/p&gt;
&lt;p&gt;If you're interested in learning more about this feature and ECS in general on our engine, check out our &lt;a href="https://docs.cubos.riscadoa.com/features-ecs.html"&gt;ECS feature guide&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="introducing-inheritance-in-reflection-roby2014"&gt;
&lt;h4&gt;Introducing Inheritance in Reflection &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/roberto-carlos/"&gt;&amp;#64;roby2014&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Cubos&lt;/strong&gt; has a powerful &lt;a href="https://docs.cubos.riscadoa.com/group__core-reflection.html"&gt;reflection system&lt;/a&gt; to examine and interact with a structures and types at runtime
It works by attaching metadata to types, which we call &lt;em&gt;traits&lt;/em&gt;. These traits can be queried at runtime to get information about the type.
For example, we provide a &lt;code&gt;FieldsTrait&lt;/code&gt;, which you can add to a type to expose its fields to the reflection system.&lt;/p&gt;
&lt;p&gt;One thing we were missing was information about inheritance relationships between types.
We've added a new trait, &lt;code&gt;InheritsTrait&lt;/code&gt;, which allows you to specify that a type inherits from another type.&lt;/p&gt;
&lt;p&gt;This will be particularly useful for serialization, as right now we have to manually add the fields of the base class to the derived class. Now, serializers can check if this trait is present and fallback to the base class if necessary.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="repeating-systems-and-fixed-step-updates-joaomanita"&gt;
&lt;h4&gt;Repeating Systems and Fixed-Step Updates &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/joao-manita/"&gt;&amp;#64;joaomanita&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;Certain plugins, like the physics plugin, required some of their systems to run multiple times per frame so that they could make more accurate
aproximations. In addition, some of them need to be executed in ordered groups. For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The integration system needs to run before the velocity update system.&lt;/li&gt;
&lt;li&gt;The velocity update system needs to run before the force clear system.&lt;/li&gt;
&lt;li&gt;The force clear system needs to run before the impulse clear system.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To allow repeating groups such as these, we added a new feature to the ECS tags: the repeat while modifier. This makes the systems tagged by it repeat until a certain condition becomes false.&lt;/p&gt;
&lt;pre class="m-code"&gt;&lt;span class="n"&gt;cubos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;groupTag&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;repeatWhile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;myCondition&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;cubos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;integrate physics&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;tagged&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;groupTag&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="cm"&gt;/* ... */&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;cubos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;update velocity&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;tagged&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;groupTag&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="cm"&gt;/* ... */&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;cubos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;clear forces&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;tagged&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;groupTag&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="cm"&gt;/* ... */&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;Its also possible to make repeating subgroups by tagging the subgroup with the parent tag.&lt;/p&gt;
&lt;pre class="m-code"&gt;&lt;span class="n"&gt;cubos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subgroupTag&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;tagged&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;groupTag&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;repeatWhile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;myOtherCondition&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;This way the subgroup's systems will repeat a total of n x m times, where n is the number of times the parent tag repeats and m is the number of times the subgroup tag repeats.&lt;/p&gt;
&lt;p&gt;With this implemented, it was easy to implement the fixed-step plugin on the engine side. It simply adds a tag that makes systems repeat as needed according to
the &lt;code&gt;DeltaTime&lt;/code&gt; passed, avoiding variance due to different frame rates and more/less powerful PCs.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="moving-away-from-the-old-serialization-dacops"&gt;
&lt;h4&gt;Moving Away from the Old Serialization &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/david-pires/"&gt;&amp;#64;Dacops&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;a href="https://docs.cubos.riscadoa.com/group__core-data-ser.html"&gt;Serialization&lt;/a&gt; is a crucial part of the game engine that allows for the saving of any Cubos game components as well as then loading them in.
Previously, serialization was done through a series of functions, both for reading and writing, that could be overloaded for each type.
On the previous (and first) release, we introduced a reflection system, that allowed for the inspection of types at runtime. This was an essential step to make the inspectors on the editor side work.&lt;/p&gt;
&lt;p&gt;But with this new system, we now had two sources of truth for the types, the reflection system and the serialization system.
To solve this, we decided to change the serialization system to use the reflection system, and thus, not force the developer to write serialization code for each type.&lt;/p&gt;
&lt;p&gt;On this release, we've finally finished this transition, by replacing serialization by direct stream manipulation in places where performance was more of a concern, such as voxel object saving/loading; and by replacing the serialization methods by reflection in other places, such as the input bindings.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="next-steps"&gt;
&lt;h2&gt;Next Steps&lt;/h2&gt;
&lt;p&gt;We're very happy with the progress we've made on this release. The engine is becoming more and more powerful, and we were able to create a full game with it in a week!&lt;/p&gt;
&lt;p&gt;For the next release, which should be out in July, we'll be focusing on expanding the engine's capabilities, as it is currently missing some key features, such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;standalone editor application&lt;/strong&gt;. Our tools are currently integrated into the games themselves which is not ideal.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Audio support&lt;/strong&gt;, as there's no sound at all in the engine right now.&lt;/li&gt;
&lt;li&gt;An initial &lt;strong&gt;UI plugin&lt;/strong&gt;, which can render images, text and handle buttons.&lt;/li&gt;
&lt;li&gt;Actual &lt;strong&gt;voxel collisions&lt;/strong&gt;, as we currently only check the bounding boxes.&lt;/li&gt;
&lt;li&gt;Basic &lt;strong&gt;rigidbody physics&lt;/strong&gt;, with rotation and friction.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Shadows, transparency&lt;/strong&gt; and other rendering features.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Raycasting&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Networking&lt;/strong&gt; utilities, to allow for online multiplayer games.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Web-support&lt;/strong&gt;, so that games can be played in the browser.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;C API&lt;/strong&gt;, so that we can use the engine from other languages (scripts!).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Better editor tools&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can check out the full list of stuff we want to get done in the &lt;a href="https://github.com/GameDevTecnico/cubos/milestone/26"&gt;milestone&lt;/a&gt; for the next release.&lt;/p&gt;
&lt;p&gt;We're currently a team of 10 people, and &lt;strong&gt;we're looking to expand&lt;/strong&gt;! If you're interested in joining us, or just want to learn more about the project, join our &lt;a href="https://discord.gg/WjTtcNTRqD"&gt;Discord server&lt;/a&gt;!&lt;/p&gt;
&lt;/section&gt;
</content><category term="Release"/><category term="Release"/></entry><entry><title>Coffee N' Jam 2024</title><link href="https://cubos.riscadoa.com/blog/coffee-n-jam-2024/" rel="alternate"/><published>2024-05-21T09:00:00+01:00</published><updated>2024-05-21T09:00:00+01:00</updated><author><name>Cubos Team</name></author><id>tag:cubos.riscadoa.com,2024-05-21:/blog/coffee-n-jam-2024/</id><summary type="html">&lt;p&gt;We made a game in a week with Cubos, and managed to win two awards!&lt;/p&gt;
</summary><content type="html">&lt;p&gt;From 26th of April to 3rd of May, &lt;a href="https://cubos.riscadoa.com/blog/author/ricardo-antunes/"&gt;&amp;#64;RiscadoA&lt;/a&gt; and &lt;a href="https://cubos.riscadoa.com/blog/author/joao-manita/"&gt;&amp;#64;joaomanita&lt;/a&gt; participated in the Coffee N' Jam, a game jam organized by GameDev Técnico, with our engine, &lt;strong&gt;Cubos&lt;/strong&gt;.
The goal was to create a game in a week, with the theme &amp;quot;Apocalypse&amp;quot; - and we had a blast!&lt;/p&gt;
&lt;p&gt;The game jam was a great opportunity to test the engine in a real game development scenario, and we learned a lot from it.
The game is called &lt;em&gt;Scraps vs Zombies&lt;/em&gt;, and you can download it from its &lt;a href="https://riscadoa.itch.io/scraps-vs-zombies"&gt;itch.io page&lt;/a&gt;.
You can also check out its source code in our &lt;a href="https://github.com/GameDevTecnico/cubos-demo"&gt;demo repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We managed to win the &amp;quot;Best Gameplay&amp;quot; and &amp;quot;People's Choice&amp;quot; awards, which we're very proud of!&lt;/p&gt;
&lt;div class="m-image"&gt;
&lt;a href="https://riscadoa.itch.io/scraps-vs-zombies"&gt;&lt;img src="https://cubos.riscadoa.com/images/blog/demo/coffee-n-jam-2024/cover.png" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;We found a lot of bugs and missing features during the jam. Additionally, we had performance become a problem for the first time.
One particular area that is in dire need of development is Audio, which we've been ignoring so far - people expect to hear something when they play a game!
We'll be working on fixing these issues in the next release, and we'll also be profiling the engine to find out where the bottlenecks are.&lt;/p&gt;
</content><category term="Demo"/><category term="Demo"/><category term="Game Jam"/><category term="Coffee N' Jam"/></entry><entry><title>Hello Relations</title><link href="https://cubos.riscadoa.com/blog/hello-relations/" rel="alternate"/><published>2024-01-31T16:30:00+00:00</published><updated>2024-01-31T16:30:00+00:00</updated><author><name>Ricardo Antunes</name></author><id>tag:cubos.riscadoa.com,2024-01-31:/blog/hello-relations/</id><summary type="html">&lt;p&gt;Adding entity relations to our ECS&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Hello! This past month I've been tinkering with the &lt;em&gt;ECS&lt;/em&gt;, which arguably is the core foundation of &lt;strong&gt;Cubos&lt;/strong&gt;.
We structure all of our engine code around it. You can learn more about the concept in our &lt;a href="https://docs.cubos.riscadoa.com/features-ecs.html"&gt;ECS guide&lt;/a&gt;!&lt;/p&gt;
&lt;section id="the-problem"&gt;
&lt;h2&gt;The Problem&lt;/h2&gt;
&lt;p&gt;The ECS is a very powerful tool, but it's not without its limitations. One of the most common issues is that it's hard to represent relations between entities.
Components are used to associate pieces of data to specific entities. This is great for stuff like position, velocity, health, etc.
They can also be used to tag entities as belonging to a certain group. E.g., we could have a &lt;code&gt;Poisonous&lt;/code&gt; component.
We can then query all entities with a given set of components, and apply some logic to them:&lt;/p&gt;
&lt;pre class="m-code"&gt;&lt;span class="n"&gt;cubos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;integrate velocity naively&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt;&lt;span class="p"&gt;([](&lt;/span&gt;&lt;span class="n"&gt;Position&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Velocity&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;velocity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;velocity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;But what if we want to associate data to not one entity, but to a pair of entities?&lt;/p&gt;
&lt;section id="parent-child-hierarchies"&gt;
&lt;h3&gt;Parent-Child Hierarchies&lt;/h3&gt;
&lt;p&gt;One classical example are parent-child relations. In a standard ECS, we could technically add a &lt;code&gt;Parent&lt;/code&gt; component to each entity, which would contain a reference to the parent entity.
This way we could build hierarchies and easily discover the parent of an entity simply by accessing its &lt;code&gt;Parent&lt;/code&gt; component.&lt;/p&gt;
&lt;pre class="m-code"&gt;&lt;span class="n"&gt;cubos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;print parents&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt;&lt;span class="p"&gt;([](&lt;/span&gt;&lt;span class="n"&gt;Entity&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Parent&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="n"&gt;CUBOS_INFO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Entity {} has parent {}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;But what if we wanted to find all children of a given entity? There are two possible solutions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Query all entities with a &lt;code&gt;Parent&lt;/code&gt; component and check if their parent is the entity you're looking for.&lt;/li&gt;
&lt;li&gt;Add a &lt;code&gt;Children&lt;/code&gt; component to each entity, which contains a list of references to all of its children.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The first option is very slow and inefficient, as it requires a full scan of all entities with a &lt;code&gt;Parent&lt;/code&gt; component.
The second option means having to store a heap allocated list of references in the component, ruining any cache locality you might have had.
Not only that, you would also have to keep the &lt;code&gt;Children&lt;/code&gt; component in sync with the &lt;code&gt;Parent&lt;/code&gt; component, which is a pain to do.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="physics-constraints"&gt;
&lt;h3&gt;Physics Constraints&lt;/h3&gt;
&lt;p&gt;Another use case we had trouble with at Cubos was physics constraints. Eventually we will want to add support for adding constraints between entities such as springs, hinges, etc.
Where do we store the data for these constraints? For example, a spring constraint needs to store the two entities it's connecting, as well as the spring constant and rest length.
One possible solution would be to represent the constraint itself as a separate entity which stores this data, and the references to the two entities it's connecting.
This isn't a bad solution, but we can do better!&lt;/p&gt;
&lt;/section&gt;
&lt;section id="collision-pairs"&gt;
&lt;h3&gt;Collision Pairs&lt;/h3&gt;
&lt;p&gt;This was kind of the tipping point for us. The collision plugins works in two phases: broadphase and narrowphase.&lt;/p&gt;
&lt;p&gt;The broadphase is collision shape agnostic - it doesn't care whether the entities are spheres, boxes, or whatever. If finds pairs of entities whose AABBs intersect.
The narrowphase, on the other hand, is collision shape aware. It takes the pairs generated by the broadphase, and checks if the real shapes are actually colliding.&lt;/p&gt;
&lt;p&gt;On our original solution, we stored the pairs generated by the broadphase in a resource (used to share data between systems), which the narrowphase systems then read.
This meant filtering the pairs generated by the broadphase by shape, which was not only inefficient but also a mess to implement. Lots of code duplication and boilerplate, which makes it hard to read and maintain.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="adding-r-to-ecs"&gt;
&lt;h2&gt;Adding R to ECS&lt;/h2&gt;
&lt;p&gt;While searching for possible solutions to our troubles, I came across a &lt;a href="https://ajmmertens.medium.com/why-it-is-time-to-start-thinking-of-games-as-databases-e7971da33ac3"&gt;post by Sander Mertens&lt;/a&gt;, the author of &lt;a href="https://github.com/SanderMertens/flecs"&gt;Flecs&lt;/a&gt;.
I really recommend reading his series of posts on relations if you're interested in the topic. It has been a great source of inspiration and a tremendous help in understanding the concept.&lt;/p&gt;
&lt;p&gt;The idea is to add a new kind of 'component', which we'll call... &lt;em&gt;relations&lt;/em&gt;! Like components, relations are pieces of data associated to entities.
However, unlike components, relations belong to pairs of entities, instead of a single entity.&lt;/p&gt;
&lt;section id="how-does-it-look-like"&gt;
&lt;h3&gt;How does it look like?&lt;/h3&gt;
&lt;p&gt;Creating and removing relations is very similar to creating and removing components. We just pass one extra argument, which is the second entity in the pair.&lt;/p&gt;
&lt;pre class="m-code"&gt;&lt;span class="n"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;relate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;alice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ChildOf&lt;/span&gt;&lt;span class="p"&gt;{});&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="c1"&gt;// Alice is now a child of Bob&lt;/span&gt;
&lt;span class="n"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unrelate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;alice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ChildOf&lt;/span&gt;&lt;span class="p"&gt;{});&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Alice is no longer a child of Bob&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;But the real power of relations comes from querying them. We can query all relations of a certain type, and apply filters on both entities in the pair.&lt;/p&gt;
&lt;pre class="m-code"&gt;&lt;span class="n"&gt;cubos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;access car wheels&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt;&lt;span class="p"&gt;([](&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Wheel&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wheel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ChildOf&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Car&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;car&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="c1"&gt;// Do something with the wheel and its parent car.&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;The query above, for example, will match all pairs of entities where the first entity has a &lt;code&gt;Wheel&lt;/code&gt; component, the second entity has a &lt;code&gt;Car&lt;/code&gt; component, and the first entity is a child of the second entity.&lt;/p&gt;
&lt;p&gt;This also allows us to write the narrowphase collision checks in a much more elegant manner:&lt;/p&gt;
&lt;pre class="m-code"&gt;&lt;span class="n"&gt;cubos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;box vs sphere&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt;&lt;span class="p"&gt;([](&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;box&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PotentiallyCollidingWith&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Sphere&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sphere&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shapesIntersect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;box&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sphere&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="c1"&gt;// Do something with it.&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;We also allow the user to customize the behavior of relation types with two extra options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Symmetry&lt;/strong&gt;: Whether the relation type is symmetric or not.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tree&lt;/strong&gt;: Whether instances of the relation type should form a tree.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Symmetry is particularly important for collision relations. While the direction of some relations, such as &lt;code&gt;ChildOf&lt;/code&gt;, matter, the direction of others, such as &lt;code&gt;CollidingWith&lt;/code&gt;, should not.
For example, a relation between &lt;em&gt;foo and bar&lt;/em&gt; will also be identifiable through &lt;em&gt;bar and foo&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Tree relations are used to specify that each entity can only have at most one outgoing instance of that relation type, and that cycles are not allowed.
This is useful for parent-child relations: &lt;code class="m-code"&gt;cmds.relate(a, b, ChildOf{})&lt;/code&gt; would remove any previous relations of type &lt;code&gt;ChildOf&lt;/code&gt; from &lt;code&gt;a&lt;/code&gt; to other entity.&lt;/p&gt;
&lt;p&gt;This is great, but how do we store these relations tightly in memory while also allowing for fast queries?&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="implementation"&gt;
&lt;h2&gt;Implementation&lt;/h2&gt;
&lt;p&gt;Although I was heavily inspired by Flecs, I decided to implement relations in a slightly different way.
Both Flecs and our ECS are Archetype based, which means that entities are grouped into archetypes based on their components.
An archetype is the set of all entities which have exactly the same set of components.&lt;/p&gt;
&lt;p&gt;To query entities with a given set of components, we just need to find the archetypes which contain those components, and then iterate over all entities in those archetypes.
The operation of finding the archetypes may be slow, but it can be cached.&lt;/p&gt;
&lt;p&gt;Flecs handles relations by creating different archetypes for each relation target. For example, with four entities with some data we would get, in this case, three archetypes:&lt;/p&gt;
&lt;table class="m-table m-frame"&gt;
&lt;caption&gt;Archetype A&lt;/caption&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Entity&lt;/th&gt;
&lt;th&gt;Health&lt;/th&gt;
&lt;th&gt;Player&lt;/th&gt;
&lt;th&gt;ChildOf(4)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;()&lt;/td&gt;
&lt;td&gt;()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td&gt;()&lt;/td&gt;
&lt;td&gt;()&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="m-table m-frame"&gt;
&lt;caption&gt;Archetype B&lt;/caption&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Entity&lt;/th&gt;
&lt;th&gt;Health&lt;/th&gt;
&lt;th&gt;ChildOf(4)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;75&lt;/td&gt;
&lt;td&gt;()&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="m-table m-frame"&gt;
&lt;caption&gt;Archetype C&lt;/caption&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Entity&lt;/th&gt;
&lt;th&gt;Health&lt;/th&gt;
&lt;th&gt;ChildOf(5)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;80&lt;/td&gt;
&lt;td&gt;()&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Where &lt;code&gt;Health&lt;/code&gt; and &lt;code&gt;Player&lt;/code&gt; are components, and &lt;code&gt;ChildOf&lt;/code&gt; is a relation type. As you may have noticed, although entities 3 and 4 have the same data types, since they have different parents, they end up in different archetypes.&lt;/p&gt;
&lt;p&gt;While this allows for very fast queries for children of the same entity, it also means that data will be heavily fragmented in memory for relations with many different targets.
It also means that adding a new relation to an entity will require moving it to another archetype, which can get expensive.
This makes this approach unsuitable for our use case, as we want to be able to add and remove relations very frequently (e.g., collision pairs).&lt;/p&gt;
&lt;section id="sparse-relation-tables"&gt;
&lt;h3&gt;Sparse Relation Tables&lt;/h3&gt;
&lt;p&gt;Instead of touching the archetypes, we store relations in separate tables, which we call &lt;em&gt;sparse relation tables&lt;/em&gt;.
Each sparse relation table stores all relations of a given type whose entities belong to a given pair of archetypes&lt;/p&gt;
&lt;p&gt;We store relations in separate tables. For each pair of archetypes and relation type, we create a table which stores all relations of that type between entities in those archetypes.
With the entities of the previous example, we would get only two archetypes:&lt;/p&gt;
&lt;table class="m-table m-frame"&gt;
&lt;caption&gt;Archetype A&lt;/caption&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Entity&lt;/th&gt;
&lt;th&gt;Health&lt;/th&gt;
&lt;th&gt;Player&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td&gt;()&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="m-table m-frame"&gt;
&lt;caption&gt;Archetype B&lt;/caption&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Entity&lt;/th&gt;
&lt;th&gt;Health&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;75&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;80&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The relations would be stored in two separate tables: one for relations between archetype A and archetype B, and another for relations between archetype B and B.&lt;/p&gt;
&lt;table class="m-table m-frame"&gt;
&lt;caption&gt;A to B&lt;/caption&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;From&lt;/th&gt;
&lt;th&gt;To&lt;/th&gt;
&lt;th&gt;ChildOf&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;()&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="m-table m-frame"&gt;
&lt;caption&gt;B to B&lt;/caption&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;From&lt;/th&gt;
&lt;th&gt;To&lt;/th&gt;
&lt;th&gt;ChildOf&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;()&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;This means that to query over a given relation, we just need to find all sparse relation tables for that relation type, and whose archetypes match the query filters.
This result, once again, can be cached. Feel free to take a look at the code on &lt;em&gt;GitHub&lt;/em&gt; if you're interested in the details!&lt;/p&gt;
&lt;/section&gt;
&lt;section id="symmetric-relations"&gt;
&lt;h3&gt;Symmetric Relations&lt;/h3&gt;
&lt;p&gt;Implementing symmetric relations was actually really easy. On all operations, we simply sort the entities in the pair by their ID, such that the entity with the lowest ID is always the first one.
This way, we can guarantee that the same relation will always be stored in the same table, regardless of the order in which the entities are passed to the operation.&lt;/p&gt;
&lt;p&gt;It also took some tuning on the query side to look for both orders of the pair, but it didn't take much effort.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="tree-relations"&gt;
&lt;h3&gt;Tree Relations&lt;/h3&gt;
&lt;p&gt;Tree relations were a bit trickier to implement. The main issue was wanting to provide a fast way to perform BFS on the tree.
Allowing traversal from top to bottom or bottom to top would allow us to easily implement parent-child transform updates, and I wanted to make sure that the components were laid out in memory in a way that would allow for fast traversal.&lt;/p&gt;
&lt;p&gt;To achieve this, I changed the sparse relation tables to not only be indexed by type and archetype pair, but also by their &lt;em&gt;depth&lt;/em&gt;.
Relations are then stored in the table corresponding to the depth of their destination entity.
For example, parent-child relations whose parent has no parent are stored at depth 0, and relations whose parent has a parent but no grandparent are stored at depth 1.&lt;/p&gt;
&lt;p&gt;To traverse the tree from bottom to top or vice versa we just need to store the cached sparse relation tables by their depth, and voila!&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="what-s-next"&gt;
&lt;h2&gt;What's next?&lt;/h2&gt;
&lt;p&gt;Regarding relations, there isn't anything else really blocking in the near future.
It would be cool to extend the query system to support queries with more than two targets, and implement some sort of entity destruction policy for relations (e.g., destroy all children when destroying a parent).
But these are not essential features, and can be added later on.&lt;/p&gt;
&lt;p&gt;With this out of the way, my focus will now shift to the renderer plugin. I've been wanting to tackle ray tracing for a while now, and I think it's time to give it a shot.
We'll also be working on a new demo soon, so stay tuned for that!&lt;/p&gt;
&lt;/section&gt;
</content><category term="Dev Notes"/><category term="Core"/><category term="ECS"/></entry><entry><title>Hello World</title><link href="https://cubos.riscadoa.com/blog/hello-world/" rel="alternate"/><published>2023-11-10T10:06:00+00:00</published><updated>2023-11-10T10:06:00+00:00</updated><author><name>Cubos Team</name></author><id>tag:cubos.riscadoa.com,2023-11-10:/blog/hello-world/</id><summary type="html">&lt;p&gt;This is Cubos!&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Hello everyone, and welcome to the very first post of the Cubos engine blog. This is where we'll be posting updates and information about Cubos
Let's start today by talking about what Cubos is!&lt;/p&gt;
&lt;p&gt;Cubos is an open-source game engine with two core features: everything is made out of voxels, and the engine is built on an entity component system.
You can find the &lt;a href="https://github.com/GameDevTecnico/cubos"&gt;source right here&lt;/a&gt;.
Up to this point, our work has built a powerful core and an array of plugins that give Cubos a lot of the necessary functionalities to make a complete game.
We have also been working on a suite of tools called Tesseratos that you can plug into any game you're making with Cubos to help with development.
It provides, among other things, editors for scenes, entities, palette.&lt;/p&gt;
&lt;p&gt;So, what are we working on?&lt;/p&gt;
&lt;p&gt;Right now, our current goal is to have an engine that can feasibly be used in game jams.
This means building some important engine plugins that we are missing, such as the physics plugin, and to create more tools to allow swift development, like a transform gizmo so you can move objects around with your mouse.&lt;/p&gt;
&lt;p&gt;Thank you for your interest in our engine.
Come with us, as we keep you updated on the developments, adventures and trials Cubos goes through.&lt;/p&gt;
&lt;p&gt;See you soon!&lt;/p&gt;
</content><category term="Meta"/></entry></feed>