Updates on the C terminal rendering engine
Initial
After writing the first blog post, I built upon the rendering pipeline inside the engine. Here are some of the improvements!
Custom blender mesh importer
Using this custom blender script on a mesh generates code for a custom header file that can be inserted inside the main function. The generated header keeps descriptions for its vertex and face indexes plus their face normals. A rundown of how the script operates is down below.
![](https://static.l-m.dev/cs/image-suzanne-blender.png)
the blender script alongside suzanne with only quad faces
![](https://static.l-m.dev/cs/image-suzanne-internal.png)
![](https://static.l-m.dev/cs/image-suzanne-data.png)
Initial rendering with a snippet of the generated code, the original generated mesh file is 2385 lines long!
Script rundown
- Places the initial definitions with the name of the mesh
- Reads the face and vertex count and inserts them
- Loops through every vertex
- Reads position vector
- Reads the vertex index
- Inserts the correct definitions
- Loops through every face
- Checks if the face contains 4 vertices, stop execution if it does not
- Reads the vertex indexes
- Reads the face normal vector
- Inserts the correct definitions along with the default debug colour
- Copies the final header string to the clipboard
i = 0
for vt in bm.verts:
returnstring += f"{mesh_name}.v[{i}].p = (VECTOR){{{round(vt.co.x, decimals)},{round(vt.co.y, decimals)},{round(vt.co.z, decimals)}}};\n"
# bmonkey.v[0].p = (VECTOR){ 9.3274 , -9.6862 , 2.1144 };
# bmonkey.v[1].p = (VECTOR){ -9.3274 , -9.6862 , 2.1144 };
# bmonkey.v[2].p = (VECTOR){ 10.6598 , -8.0206 , 0.6154 };
# bmonkey.v[3].p = (VECTOR){ -10.6598 , -8.0206 , 0.6154 };
# bmonkey.v[4].p = (VECTOR){ 11.6592 , -5.6887 , -0.2174 };
# bmonkey.v[5].p = (VECTOR){ -11.6592 , -5.6887 , -0.2174 };
# .............. ........... ....... ... ...... ... ...... ..
i+=1
returnstring += "\n"
Example of the per vertex code
for fs in bm.faces:
if len(fs.verts) != 4:
print("face is not a quad!")
failed = True
continue
returnstring += f"{mesh_name}.f[{fs.index}].v[0] = {fs.verts[0].index}; "
returnstring += f"{mesh_name}.f[{fs.index}].v[1] = {fs.verts[1].index}; "
returnstring += f"{mesh_name}.f[{fs.index}].v[2] = {fs.verts[2].index}; "
returnstring += f"{mesh_name}.f[{fs.index}].v[3] = {fs.verts[3].index}; \n"
# bmonkey .f[0] .v[0] = 46;
# bmonkey .f[0] .v[1] = 0;
# bmonkey .f[0] .v[2] = 2;
# bmonkey .f[0] .v[3] = 44;
returnstring += f" {mesh_name}.f[{fs.index}].normal = (VECTOR){{{round(fs.normal.x,decimals)},{round(fs.normal.y,decimals)},{round(fs.normal.z,decimals)}}}; \n"
returnstring += f" {mesh_name}.f[{fs.index}].debugcolour = DEFAULT; \n\n"
# bmonkey .f[0] .normal = (VECTOR){ 0.665 , -0.7194 , -0.2008 };
# bmonkey .f[0] .debugcolour = DEFAULT;
Example of the per face code
Working depth buffer and Self occlusion
After importing a quad mesh with a hole cut out of it, I noticed something unusual. Faces that should be occluded, were showing! This was not an issue before as I rendered cubes and spheres that did not contain any holes or crevasses.
![](https://static.l-m.dev/cs/image-rendered-no-occlusion.png)
Although backface culling was implemented, faces that were facing the camera were still being shown even if they should be covered by other parts of the mesh.
Implementing a working depth buffer was long overdue. I used per face depth information to discard pixels that behind already existing pixels. Ensuring faces behind by other faces will not show up.
![](https://static.l-m.dev/cs/image-rendered-with-occlusion.png)
![](https://static.l-m.dev/cs/image-rendered-blender.png)
Fragment to Pixel code
void copyattributes(ATTRIBUTE frag, int x, int y){
if (x < 0 || y < 0 || x >= WIDTH || y >= HEIGHT)
return; //! out of bounds
if (framebuffer[y][x].depth < frag.depth) return;
// ignore pixel if it is occluded!
framebuffer[y][x].colour = frag.colour;
framebuffer[y][x].depth = frag.depth;
framebuffer[y][x].normal = frag.normal;
}
Forward
I eventually want to port this to V, where I can introduce new features such as vertex and fragment shaders and smooth shading without the many downfalls of a pure C based program.
Until then, this project will be put to sleep. I thank you for reading!