diff options
author | 2025-02-07 01:11:35 -0500 | |
---|---|---|
committer | 2025-02-07 01:11:35 -0500 | |
commit | ff07cfa1d2cb0260ca2fcafef5cf5c856720d7ce (patch) | |
tree | cab6daf2bfe022c6a1008af7a4cf1dc2e9532ee1 | |
parent | 828fac9c385568c5d00bbe9f386f7e6151b9cf0c (diff) |
Fri Feb 7 01:11:35 AM EST 2025
-rw-r--r-- | content/blog/mons_i/base.png | bin | 0 -> 223768 bytes | |||
-rw-r--r-- | content/blog/mons_i/first_triangle.png | bin | 0 -> 3480 bytes | |||
-rw-r--r-- | content/blog/mons_i/index.md | 247 | ||||
-rw-r--r-- | content/blog/mons_i/mesh.png | bin | 0 -> 81687 bytes | |||
-rw-r--r-- | content/blog/mons_i/mons.png | bin | 0 -> 1724 bytes | |||
-rw-r--r-- | content/blog/mons_i/normalmap.png | bin | 0 -> 792980 bytes | |||
-rw-r--r-- | content/blog/mons_i/texture.png | bin | 0 -> 72688 bytes | |||
-rw-r--r-- | templates/blog-page.html | 2 |
8 files changed, 248 insertions, 1 deletions
diff --git a/content/blog/mons_i/base.png b/content/blog/mons_i/base.png Binary files differnew file mode 100644 index 0000000..60a4d0f --- /dev/null +++ b/content/blog/mons_i/base.png diff --git a/content/blog/mons_i/first_triangle.png b/content/blog/mons_i/first_triangle.png Binary files differnew file mode 100644 index 0000000..16d0992 --- /dev/null +++ b/content/blog/mons_i/first_triangle.png diff --git a/content/blog/mons_i/index.md b/content/blog/mons_i/index.md new file mode 100644 index 0000000..16c2721 --- /dev/null +++ b/content/blog/mons_i/index.md @@ -0,0 +1,247 @@ ++++ +title = "mons (the mountain) - part i: i got sidetracked writing c for a week" +date = 2025-02-06 ++++ + +## the valley + +so i've been working for a while on a large game project and was really starting to get burnt out + +it's been my primary focus since graduating from school and progress started slowing to a crawl + +it was clear to me that i needed a break from the project to recharge a little bit + +so i decided to try and start a project i've been fantasizing about for a long time: + + + +mons is a monumental undertaking, an excercise in insanity, and my personal code sanctuary + +the goal of the project is pretty abstract, i'm just coding whatever i feel like, but doing it with + +## a couple rules + +### rule 1: c99 + +no rust :( + +i've been writing rust almost every day for the last 3 years, it's time to get scary + +c is like dead simple which is why i think it will be a pretty meditative experience + +by meditative i mean mind-numbingly tedious but i'm not in a rush here + +i think the absolute worst part of this is the lack of namespaces but i'll live + +### rule 2: extremely limited dependencies + +it's time to get real + +if i'm undertaking this journey in ~~masochism~~ voluntary discomfort (only true stoic sigmas get this one) i might as well be learning something while i do it + +this would be "no dependencies" but like i kind of want to make some fun stuff at some point + +so i have 3 big exceptions + +i can use stdlib and posix shit (at my discretion) + +i can use "important" libraries + +i don't really have a good description for what makes a library "important" but think hardware stuff like opengl/vulkan or xlib + +the last thing is that i can use freely available implementations of algorithms i have no chance in hell of understanding, like [mikktspace](http://www.mikktspace.com/) + +i just don't see much value in doing that myself + +## the foot + +### mons_math + +starting out from nothing i decided my first goal would be to spin up a basic 3d graphical application + +i decided to start with opengl, i have experience with both it and vulkan and opengl is significantly less work to get started with + +before i could even render a basic triangle though, i needed (wanted) a way to represent and manipulate basic linear algebra types such as vectors, matrices, and quaternions + +so on the first day i just sat on my couch and typed up a library for exactly that + +``` +mons_math/src/ +├── mat2.c +├── mat3.c +├── mat4.c +├── quat.c +├── util.c +├── vec2.c +├── vec3.c +└── vec4.c +``` +(i won't get into implementation details too much in this post in the interest of brevity, but i'll have all this up on my [git server](https://git.exvacuum.dev) soon) + +i found writing the matrix and vector operations super satisfying in spite of the repetition for different dimensionalities + +i do wish i had kept in mind that opengl is column-major for matrices, since i wrote mine as row-major like a normal person, but it might honestly be for the best, as it makes using them much more intuitive based on the stuff i learned in math class + +once i had this library i was quickly able to spin up a basic 3d app using opengl, along with xlib and glx for windowing and input under x11 + + + +i had always used [glfw](https://www.glfw.org/) for windowing so it was interesting to see how similar it was using xlib (although there were some tricky differences, like windows being created without a depth buffer by default) + +### auto-embedding shader files + +i set up my project to automatically embed shaders in my code which was pretty cool + +basically there isn't a c mechanism similar to rust's lovely [`include_bytes`](https://doc.rust-lang.org/std/macro.include_bytes.html) macro, so the best you can really do is generate a constant byte array and store it in a header file + +luckily i learned how to use the [xxd](https://linux.die.net/man/1/xxd) tool to automatically generate such a header file, and then added a script to automatically embed all files located in my project's `embed` folder and place generated headers in the corresponding `include/embedded` location + +``` +embed/ +└── shaders + ├── basic.frag.glsl + └── basic.vert.glsl +include/ +├── ... +├── embedded +│ └── shaders +│ ├── basic.frag.glsl.h +│ └── basic.vert.glsl.h +└── ... +``` + +then i added it as a fake target in my cmake project and made the main project target depend on it + +### mons_qoi + +not satisfied with a simple solid color, i decided i needed to load images + +obviously i wasn't allowed to use an existing loader like [stbi](https://github.com/nothings/stb/blob/master/stb_image.h) + +i did take a quick look at the png and jpeg specifications since these are by far and away the most popular texture image formats out there + +my lazy ass was not about to write a loader for either of those formats (for now) + +for now i decided to look for something easy to implement but like actually respectable (so not [ppm](https://netpbm.sourceforge.net/doc/ppm.html)), and boy oh boy + +enter [qoi](https://qoiformat.org/) + +this shit descended from the heavens i swear to god + +qoi, or the quite ok image format, is extremely simple and extremely fast to encode and decode, and offers pretty decent lossless compression + +my decoder is around 200 lines of c i think, which is a bit more than the [reference implementation](https://github.com/phoboslab/qoi) i believe but i tried my best to not refer to it for fun + +once i had that i was able to just pop the loaded image data into opengl textures + + + +### mons_json + +up to this point i was using hardcoded vertex buffers and i wanted to be able to load more complex geometry + +i considered using [obj](https://www.loc.gov/preservation/digital/formats/fdd/fdd000507.shtml) for my models but i'm a big fan of [gltf](https://www.khronos.org/Gltf) so i decided to just jump the gun and go for a gltf loader right off the bat + +gltf is stored as [json](https://www.json.org/json-en.html) data so i knew if i wanted to have a shot i would need a flexible, dynamic json solution + +```c +struct mons_json_value; +union mons_json_entry; + +typedef struct mons_json_array { + struct mons_json_value *values; + unsigned int len; +} mons_json_array; + +typedef union mons_json_value_data { + char *string; + float number; + mons_hashmap object; + mons_json_array array; + bool boolean; + void *null; +} mons_json_value_data; + +typedef enum mons_json_value_type { + MONS_JSON_STRING, + MONS_JSON_NUMBER, + MONS_JSON_OBJECT, + MONS_JSON_ARRAY, + MONS_JSON_BOOL, + MONS_JSON_NULL, +} mons_json_value_type; + +typedef struct mons_json_value { + mons_json_value_type type; + mons_json_value_data data; +} mons_json_value; +``` + +i discovered a cool pattern here where a combination of an enum and a union (never actually used one before this project) allowed me to store a variety of types in a `mons_json_value` while being aware of which type it actually was + +### mons_hashmap + +the performance of my json structures depended on my ability to store key-value pairs in an efficient-to-access way + +obvious choice was a hashmap data structure + +i just implemented this from memory from my data structures class back in school + +```c +typedef struct mons_hashmap_pair { + char *key; + void *value; + struct mons_hashmap_pair *next; +} mons_hashmap_pair; + +typedef struct mons_hashmap { + mons_hashmap_pair **data; + unsigned int bucket_count; + unsigned int member_size; + unsigned int len; +} mons_hashmap; +``` + +this hashmap is basically just an array of linked lists that pairs get shoved into based on the hash of the key + +for the hash function i just went with the [djb2 hash function](http://www.cse.yorku.ca/~oz/hash.html) since it worked well for me in the past + +### mons_gltf + +at long last, i was able to dig into the gltf format + +[the specification](https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html) is quite deep and i focused on implementing basic mesh features first, opting to do things like animations, morph targets, scenes, node hierarchies, etc. later + +i actually found the process of implementing the loader pretty chill, i especially enjoyed learning how the data was stored in the binary buffers and how to use accessors to read it + +after about a day of work i managed to get mesh primitives out of the gltf files and into my in-memory model structure + + + +(i was using [this model](https://polyhaven.com/a/marble_bust_01) for testing) + +the textures that came with the model were in jpeg format, which i obviously could not yet load, and the gltf spec doesn't support formats other than jpeg and png + +so i said "To hell with the spec!" and switched the image mimetypes to the (illegal) `image/qoi` and converted the textures using [imagemagick](https://imagemagick.org/index.php) anyway + +then my textures loaded beautifully + + + +at this point i was getting pretty tired lol + +i was ready to take a break from my break + +but i wanted to leave off in a really satisfying place so i decided to implement normal mapping as one last thing + +as i previously mentioned i would use the [mikktspace](http://www.mikktspace.com) algorithm to do my tangent calculations + + + +and with that i decided i was ready to go back and get some work done on something actually important to me + +this whole process took me about a week and was a pretty chill experience + +thanks for reading this far, i hope it's not too brutal + +\- silas diff --git a/content/blog/mons_i/mesh.png b/content/blog/mons_i/mesh.png Binary files differnew file mode 100644 index 0000000..aedf3a1 --- /dev/null +++ b/content/blog/mons_i/mesh.png diff --git a/content/blog/mons_i/mons.png b/content/blog/mons_i/mons.png Binary files differnew file mode 100644 index 0000000..6aeb835 --- /dev/null +++ b/content/blog/mons_i/mons.png diff --git a/content/blog/mons_i/normalmap.png b/content/blog/mons_i/normalmap.png Binary files differnew file mode 100644 index 0000000..c295595 --- /dev/null +++ b/content/blog/mons_i/normalmap.png diff --git a/content/blog/mons_i/texture.png b/content/blog/mons_i/texture.png Binary files differnew file mode 100644 index 0000000..94e071c --- /dev/null +++ b/content/blog/mons_i/texture.png diff --git a/templates/blog-page.html b/templates/blog-page.html index 61f99d8..6c4afa5 100644 --- a/templates/blog-page.html +++ b/templates/blog-page.html @@ -5,6 +5,6 @@ <h1 class="title"> {{ page.title }} </h1> -<p class="subtitle"><strong>{{ page.date }}</strong></p> + <p class="subtitle"><strong>{{ page.date }} - reading time: {{ page.reading_time }} minutes</strong></p> {{ page.content | safe }} {% endblock content %} |