Read up on the compiler changes in the monthly release notes.
Quick Shoutouts #
droaz
is still hard at work on the odin language server. Make sure to give it a try!
Heres a neat feature I saw they have recently added on the Discord.
Obiwan87
is back on adding Odin-Intellij support. Better tooling around odin is always a blessing!
Projects shared this month #
Project Name | Description |
---|---|
odin-m3d | M3D bindings |
hot-reload-template | odin / raylib Hot Realoding Template |
odin-aseprite | Handler for Aseprite files |
odin-al | OpenAL bindings |
ZTME | Simple Tile Editor |
audio-files | Play audio files from memory |
jamgine | sandbox / portfolio game graphics library |
duckdb-odin | duckdb bindings |
anima | 2D animation library |
ld55 | LD55 jam entry game |
Project Showcase #
We’re going with the same layout as last month, let’s see how it goes again.
Text Editor Experimentation #1 (Skytrias) #
Earlier this year I became a bit stuck with personal projects. During that time I got angry at Sublime for crashing very often.
This sparked an interest in making an odin specific editor. It didn’t have to do everything, but I wanted to give it a more serious try. Given that the odin core:odin
library exists to read odin files / packages, it looked like a pretty simple project.
Craving a Challenge #
Well you may have guessed it. I wanted to try something more difficult, so I decided to take one different idea for this project.
Non-Linear text editing is what I called it. Meaning not having to care about separating code into files. It would be done automatically and the editor would be more like an IDE.
Here is a a short preview of what I got working pretty quickly.
- Left: structs, procedures, etc + identifier
- Middle: buffer name + buffer content
- Right: Preview of the file output
What happened? #
You can see mostly everything is done already? By the time the project was ~1 week old. The thing is, getting raw text editors to work is actually quite simple. As soon as you can edit bytes and navigate around, you’re basically done.
One feature I really missed from 4coder was virtual whitespace, which automatically indents each line. Implementing this for odin was pretty simple.
The Rest #
While syntax highlighting was easy, auto-completion with code intelligence turned out to be more trouble. At some point I though about just using OLS LSP but by the time I had already lost interest in the project.
Here is the current state of the project.
- Split with individual buffer being slightly highlighted and can be navigated to/from.
- Toggle details of buffers
- Jump to Definition
- Copy/Paste
- Undo/Redo
- Versioned buffers in a database
Wrapping Up #
You can see it’s not that difficult to get started with one of these. Nowadays Text Editors mostly win through ecosystem and plugin capabilities.
Next month I’ll show you another approach to text editing odin specific code.
Maco’s Voxel Engine (maco) #
As a hobbyist game developer my quest is to build a game of impossible proportions. The first part of this quest is to create a procedurally generated game world with an interesting visual style.
Many different styles and techniques could potentially apply, however one important requirement is that I must be able to create all of the art by myself. I settled with the small voxel aesthetic which seems to be gaining some popularity among indie devs. The aesthetic hits a sweet spot between natural looking, detailed feel and abstract minimalism. I might try to describe this style as “pixel art but in 3D”.
One who wants to draw voxels fast must solve a few fundamental problems. Many of the problems are well known at this point and some solutions are available. Not all of the solutions are applicable to every situation however.
One such problem is how to triangulate the voxel space so that the GPU can draw millions of small cubes efficiently. I chose a technique which, I believe, the author of “Teardown” is also using, i.e. ray marching. Instead of drawing each face using classic rasterization, I draw a bounding volume and solve detailed geometry by ray marching into a 3D array.
Another example of a challenge faced by the voxel renderer is Moiré aliasing. This has been a constant bother from back when I started making my first prototypes. Since there are only 6 possible normal vectors on the scene, the geometry does not smooth itself naturally. Whenever a face starts approaching a subpixel size it will start to flicker in a very unpleasant way. The voxels are very small to begin with so there are flickering faces everywhere all the time.
This is one of the problems that doesn’t seem to have a widely accepted generic solution. One can only alleviate it by applying different measures depending on what’s acceptable and what’s available. I was hoping I could do without temporal antialiasing but in the end I had to implement my version of TAA. Nothing else seemed to get the job done.
Generally speaking I am a little hesitant to call my game’s backend an engine. It is little more than just the simplest thing that will hopefully allow me to do what I wanted to do. I am now approaching this milestone where I can finally start thinking about the content for change. This is all new and exciting to me.
Below is a short video showcasing the current state of the game. Everything in it is still a placeholder. There is only one tree model, one patch of grass and two decal bitmaps on the ground.
One cool aspect of the voxel style is that its possible to produce art directly from the code. The tree model is an example of this. It changes appearance every time the game is run as its generated algorithmically.
Performance-wise the engine can draw about 500 frames per second currently with a decent draw distance. This is not a bad result considering how much ray marching is taking place. Most of the performance budget is spent solving shadows, which is something I have been trying to optimize but I have been unable to figure out a breakthrough here. Either way I’m quite happy with what I have now and I’m looking forward to starting to build a virtual world using this engine.
Since this is the Odin newsletter I should probably also mention that the adoption of the Odin language to my workflow has been pivotal for making progress. Improved quality of life means there is more energy left to solve application problems and combat the horrors of our graphics APIs. A big thanks to Bill and all the maintainers of the language!
Highest Peak (Skol) #
I’ve been around Handmade Network for a while and heard about Odin there. I don’t remember the first project I’ve done with Odin but I’ve been using Odin as my go to language for about two years. I cannot tell you which part of the language I like the most but writing it is so natural and I don’t have to battle it, it’s a blast. It’s not a recent thing either, writing in Odin felt correct right from the start.
It’s my peak #
This was supposed to be my submission for a two week game jam with horror, aliens and crop circles themes. Before the jam I was working on another game and I stripped it of everything relating to that game and I was left with the barebones. So in the beginning, I had controls, soloud library for sounds, debug ui and a d3d11 renderer. I wasn’t able to finish it in time, mostly because of the scope and I haven’t done any real art and music before. However most of what I implemented took as long as I thought they would and hey, at least that means I know how much I procrastinate.
In the end I had serialization, save games, hexagon tiles, tile editor, dialogue and event system, resource and food gathering, camping, and very simple battles (with aliens) and more. There are also crop circles around that if you do what’s necessary to complete them, they’ll give you weird abilities that you will retain. Your team consists of some people in the family territory and they might die, so uh, that’s a kind of horror right?
Anyways, the idea was to have a guy reliving the same time frame over and over and had recollection of what happened earlier. It’s a kind of a puzzle game where you try out different paths to either figure out the optimal pathing through the story, or obtain information to use on subsequent runs.
At first I didn’t want to have any dialogue but that requires more time and better art. As soon as I decided to have dialogue, it was obvious that simple switch cases and state flags would be too much for me to handle. Think about the case where you meet the same character you always meet in the fifth tile for the tenth time and the dialogue changes depending on what you have done that round and the rounds before that. Would have been a mess.
Condition - Response #
Some years ago I watched this talk and I wanted to do something similar since. Perfect time to do so!
I implemented a system that reads a text file and turns a group of conditions and responses into code that runs in a loop each time you move to another tile. It is much simpler from what they have done, but enough for my needs. Let’s see in an example.
# Meeting the medium after seeing the sniper
{
cond
Run_Met_Medium = 0
Loop_Met_Medium >= 2
Loop_Medium_Appears_At_Distance = 3
Info_Pack_Distance_From_Start = 3
Loop_Met_Sniper >= 1
Run_Met_Sniper <= 0
response
Run_Met_Medium +1
Loop_Met_Medium +1
Dial_Meet_Medium_Again_Met_Sniper__Player_Kid
Proc_rsp_inc_medium_distance_and_draw_medium
}
This is checked each time you move into a tile. There’s a character internally called Medium that can move around in between runs (spooky). What the above text says is that if you haven’t yet met the Medium in this run, but have met her at least twice before, and if she appears at a distance of 3 tiles from the starting tile, and if you are at that distance too, and if you have met the sniper at least once before but not yet this run, do the following response:
Increment the flags for meeting the Medium (so that you don’t meet her again this run as all Medium events begin with Run_Met_Medium = 0
), show the dialogue Meet_Medium_Again_Met_Sniper__Player_Kid
and call the proc rsp_inc_medium_distance_and_draw_medium
Might look complicated but very easy to write.
I have Run
and Loop
flags signaling things that happen on this run or had happened in earlier runs. The conditions tagged with Info
are things that are collected per new tile and don’t make sense to me as flags. Where your characters are, whether they are bored, the amount of hours since they last ate and so on go there. They are also collected from different structs so your characters' health could come from their entity structs, but the pack position comes from the pack struct etc. while the state flags are just two large arrays sitting in my main game struct.
From these files I generate an array containing all events before each compilation. This array is ordered by the amount of conditions, largest first. The more conditions an event has, the more specialized it is and it should be checked first in order to stop the less special/more common events from taking over. And that’s basically it. Text parsed, array generated, the ordered condition groups are run in a loop, if one has all conditions true, its responses are called. If more than one event can happen with the same amount of conditions, I choose randomly which one happens.
Also, I could do more than dialogue by using this system:
{
cond
Info_Tile_Event_Count = 0
Run_Last_Battle_In_Tiles >= 8
Info_Tile_Battle_Chance >= 0.1
response
Proc_rsp_roll_for_battle
}
If there are no events on that tile, we haven’t had a battle for at least 8 tiles and the tile could roll for battle, roll! Then I don’t have to add extra logic for checking for battle events.
Proc_rsp_do_more
#
Of course you could do a lot more and optimize it. This was meant to be for a game jam so I implemented it as quickly as possible. You could add an event editor, generate a binary file and read from there instead of a fixed array when things get larger, separate conditions into logical areas (grass tiles only check for things that could happen on grass tiles) and so on. There is also some cruft to it. For example, I haven’t talked about dialogues themselves but if a line changes in a dialogue, it has a different ID and the data is copied except for that line. If I implement an event editor, I could keep text ID’s instead of text itself and just add/remove ids for different lines.
Anyways, I’ll be completing this small game at some point. Will try to post updates if anything interesting happen.
chip8 emulator (Virror) #
Background #
I always loved playing emulated games as an older kid. As I never had any consoles when I was younger there was a whole world of games out here waiting to be played and I especially loved playing SNES games because of all the amazing RPGs there.
When I went to university and started learning programming, I was naturally drawn to how emulators actually worked and became really interested in creating one myself. After looking a bit on the internet, I found out that the easiest one to start with was chip8 so I started implementing that according to the specs. The result was truly a horrible mess of half working buggy code, but it could play a few games and I was still happy with that. After that I tried to make a Gameboy emulator but quickly ran into a wall and stopped and did not try to make anything more for years.
Maybe around 20 years later I talked to a friend at work, and he was also really interested in making emulators, so we decided to each try and make our own Gameboy emulator. With my extra years of experience that went much smoother, and I have been working on different emulators now and then since then. After my friend recommended me Odin and working on a bigger hobby project for a while I decided to kind of go back to my roots a bit and decided to create a chip8 emulator again as a smaller project.
Creating an emulator #
The basics workings of a simple emulator are actually pretty simple. A game consists of a large number of instructions. These instructions can be anything from adding two numbers, jump to another part of the game or draw a sprite to the screen. So, to emulate the chip8, you basically read the first 2 bytes from the game, look at he most significant nibble to decide what kind of instruction it is and then perform that instruction. Some instructions need a little bit more decoding than that since there is a total of 35 instructions, but its still very straight forward.
The screen on chip8 is very small, only 64x32 pixels big, and there are 2 instructions to write to it. One is to clear the entire screen and the other one is to draw a sprite at a certain location on the screen. The drawing is done using xor, so if a white pixel is being drawn on another white pixel, that pixel is turned black, and a flag is set to indicate a collision so the game can handle thing like player colliding and dying.
This is just a very brief glimpse into the world of emulation and here are tons of resources on the internet, and if anyone is interested feel free to PM me @virror on the Odin discord and make sure to join the “Emulator Development” discord.
I also want to give a shoutout to my friend David for helping me reignite my passion for emulators and also for recommending this amazing language that makes coding even more fun then it was before : )
My chip8 repo: https://github.com/virror/odin-chip
Asterboids (Beans) #
A very simple space-shooter with rogue-like elements. Loosely inspired by the classic arcade game, Asteroids. The game was meant to be a week-long project, with the goal of learning the Odin language and data-oriented-programming. I ended up spending a bit more time on it because I was learning a lot and having so much fun!
Project Breakdown #
Asterboids is a top-down space shooter where the player fights off waves of enemies from their spaceship. The enemies move in bird-like flocks, hence the name Asterboids.
There were three things I thought were particularly satisfying to develop: the smoke trails, enemy flocking simulation and rogue-like perk system.
Smoke Trails #
The original goal was to draw a trail behind the player when they dash. An approach I’ve gotten a lot of mileage out of in the past is to draw objects that should have a trail directly into a render texture. Rather than clearing the render texture each frame, it is drawn into another render texture using a shader that subtracts a little bit of alpha from the entire image. Using this double-buffered render texture aproach lets you create post-processing-like effects which accumulate over time.
In addition to fading out the trail map over time, I add a little bit of distortion by offsetting the uvs. This “moves” the pixels in render texture over time. For distortion I’m using simplex noise. Each noise sample is mapped between 0 and TAU, and then used to rotate a unit vector.
This is what the trail effect looks like with the displacement applied. The distortion “forces” are hackily visualized on the right of the screen.
I actually think it looks good as-is, but as a final step I wanted the smoke to disperse over time. Rather than adding a blur pass, I’m simply setting the filter-mode of the trail render-texture to BILINEAR. This “blurs” each sample, softening the smoke over time.
Flocking Simulation #
I wanted to find an interesting way for enemies to move. Boids seemed interesting, so I went with that!
I started with a naive n² implementation where each enemy checks its distance from every other enemy to find its neighbors. This worked, but I couldn’t have many boids before the frame-rate tanked. To speed it up I wanted to do some sort of spatial partitioning. I had recently watched a video on optimization using spatial hashgrids, and it seemed like a basic implementation would be quite straightforward, so I wrote my own.
Now that boids knew what cell they were in, they only had to check for boids in neighboring cells.
This was a lot faster, but I was interested giving multithreading a go. I found a job-system package on GitHub and threw it in my project. Rather than processing the simulation boid by boid as I was before, the boids are now simulated cell by cell. Each cell is simulated in its own job, and the job-system spreads that work across multiple threads automatically.
// Pseudo-code
for boid in boids
insert(grid, boid)
for cell in grid
add_job((cell) -> {
for boid in cell
flock(boid, cell)
})
run_jobs()
More boids, yay!
Gameplay Modifiers #
Whenever the player levels up they can pick one of three random level-up choices. Each choice is represented by a struct that stores a function for ensuring the modifier is valid given the current game state, and another for actually applying it to the game-state
Modifier :: struct {
name : cstring, // Name of the modifier. Shown in the level up gui
description : cstring, // Description of the modifier. Shown in the level up gui
is_valid : proc(game : ^Game) -> bool, // Function that can be called to check if a modifier is valid
on_choose : proc(game : ^Game), // Function that can be called to apply the modifier to the current game state
}
Each modifier is then authored in a big map so it’s quite easy to throw together interesting modifiers.
ModifierChoices := [ModifierType]Modifier {
.RangeFinder = {
name = "Range Finder",
description = "Installs a laser sight onto the player ship",
on_choose = proc(game : ^Game) {
add_action(&game.weapon.on_draw_weapon, proc(draw : ^bool, game : ^Game) {
rl.DrawLineV(...)
})
}
},
...
}
Summary #
I had quite a positive experience learning the basics of Odin and am looking forward to using it for more projects in the future. Odin provides a buttery-smooth entry into low-level programming and I appreciate the lessons its design taught me about writing simpler code. I think there’s some good bones here for a proper game so I may continue hacking on it in the future, but for now I’ve got other projects I need to get back to :)