You Are So Opaque

3 minute read

71 Days Until I Can Walk

Bit of a slow coding day today, but not without cause. In order to help me not become a giant couch potato while sitting around on one foot, I asked my dad to help me mount a pull-up bar on the wall of my room. It took much longer than expected, but it’s up there.

Pull up bar mounted on a wall

All of this to say that while I think transparency could have been achieved today, it’s not quite there yet. In fact, this is proving to be a little more involved than I expected.

Failing to Implement Transparency

In order for transparency to work, I have modified the ray cast algorithm so that instead of returning the distance to the nearest wall, it returns a list of walls struck by rays and their respective distances. If we come to a transparent spot in the closest wall, then we move to the next wall in the list and start rendering that instead. Some cool alpha-blending stuff will happen at this point too.

Getting all of this set up involved designing some new datastructures and slightly rethinking how I do ray casting. In fact, today is the first day where I butted up against a feature of Rust that really blocked me from doing what I wanted to do.

Modifying the searches for horizontal and vertical intersections so that they returned lists of walls instead of the first wall they encounter was trivially simple. However, now I have two vectors, I want to merge sort them into one interleaved list of walls. Because of how Rust handles ownership I really struggled to find a straightforward way to do this.

Initially I tried to implement a solution proposed on stackoverflow. This looked extremely promising, but required me to implement several traits on my Slice struct. Because Slice stores a TextureCode enum internally, these traits also needed to be implemented on TextureCode. This seemed like an overly laborious solution. Espectially considering that I was implementing equality on Slice such that it would only compare distance values. But that is not a true measure of equality and feels like something that could bite me later.

My second attempted solution was to fire the horizontal and vertical rays simultaneously, instead of working with two separate functions. As each ray encountered a surface, they would be inserted in sorted order into a list of slices. The code was very messy and ultimately broke the renderer, so I abandoned it. It is, however, a possible solution for the future that would save me from having to post-process the slices after the ray caster is finished firing rays.

Finally, I went for the lazy, stupid solution. I used a few loops to copy the slices into a new vector and then returned the sorted vector. I really hate that I have to copy data around instead of simply moving it between vectors. I’m not happy with this at all, but because of how much time I spent installing the pull-up bar today, I was rushing. At this stage, I’ve decided to park everything and come back to it tomorrow.

let hslices = self.find_horizontal_intersect(origin_x, origin_y, direction);
let vslices = self.find_vertical_intersect(origin_x, origin_y, direction);

let mut slices = Vec::new();
slices.reserve(hslices.len() + vslices.len());

let mut i = 0;
let mut j = 0;

while i < hslices.len() && j < vslices.len() {
    if hslices[i].distance < vslices[j].distance {
        slices.push(hslices[i]);
        i += 1;
    } else {
        slices.push(vslices[j]);
        j += 1;
    }
}

while i < hslices.len() {           
    slices.push(hslices[i]);
    i += 1;
}

while j < vslices.len() {
    slices.push(vslices[j]);
    j += 1;
}

This code is clearly not very “Rust”, but this is a good incentive to figure out how to write better Rust code.

A Couple Of Small Wins

What I did manage to get working is conditional collision detection on walls. I can make it so that the player can walk through certain walls and not others. I have also managed to factor in the alpha channel of a texture when rendering. This allowed me to install some door frames in walls that users will walk through. Once transparency is implemented, this will look less like clipping through a surface and more like actually walking through an open door frame. I’m quite happy about this at least.

The camera faces an open doorway and walks through. The scene behind the doorway is not visible until after the camera passes through.

Conclusion

I really hoped I would be working on floor and ceiling textures tomorrow, but it looks like the focus will be transparency instead. That’s fine, but I want to push quite hard to have this item off my desk by the end of tomorrow as I would really like to spend next week implementing a map editor.