Here is the relevant code:
Code: Select all
// My Python Mode 7 renderer reimplemented in C++. It changes the plane in place, so no return is required.
uint8_t* render_m7(uint8_t* img, uint16_t w, uint16_t h, float x, float y, float rotation)
{
// Store the cosine and sine of the angle for increased render speeds, as a pointer dereference is way faster than a floating point calculation.
float rot_cos = cos(rot);
float rot_sin = sin(rot);
// In MicroPython, fast locals were used in place of globals because local lookup speed was
// faster. In C++, globals are just allocated on heap, which isn't incredibly fast, but is
// way faster than even local lookups in Python.
// A list of bit masks was declared here, but for C++ I'll use a switch statement for masking.
// TODO: Perform bit masking later on.
// Finally, we create the renderer viewport.
// ...more important in the software Python renderer. Not needed here, as this step was previously done.
uint32_t img_sz = w * h;
// Rendering time has begun. Create an int to keep track of the y iteration through the resulting image.
uint16_t iy = 0;
// Store the FOV. This is only set once.
uint32_t py = fov;
// Iterate through every single scanline on the screen.
for (int32_t y = 0; y < render_height + YRES_DIV_2; y++)
{
// ix is reset every scanline.
uint16_t ix = 0;
// Only set this once per y iteration because its value does not change per x iteration.
int32_t pz = y + horizon;
// Prevent Guru Meditation Error: Core 1 panic'ed! (IntegerDivideByZero)
if (pz == 0)
{
pz = 1;
}
float sy = py / pz;
// Precalculate the multiplication for the rotation vectors to save a few CPU cycles (and allow them to be used by the audio renderer).
float sy_sin = (sy * rot_sin);
float sy_cos = (sy * rot_cos);
// Iterate over every pixel in the scanline.
int32_t neg_xres = (XRES_DIV_2 * -1);
for (int32_t x = neg_xres; x <= XRES_DIV_2; x++)
{
int32_t px = x;
// Other half of the projection system.
float sx = px / pz;
// Optimized the trig out of this, now just does multiplication.
float sxp = (sx * rot_cos) - sy_sin;
float syp = (sx * rot_sin) + sy_cos;
int32_t imgx = sxp * scaling + g_x;
int32_t imgy = syp * scaling + g_y;
// Get a specific bit position and access it.
// o_byte_val is the current byte that the pixel resides in.
// o_bit_val is the bit that corresponds to the pixel.
//int32_t img_loc = (((imgx + 1) * (imgy + 1)) - 1);
//int16_t wnd_loc = (((ix + 1) * (iy + 1)) - 1);
//int32_t o_byte_val = img_loc / 8;
//int8_t o_bit_val = img_loc % 8;
//int32_t n_byte_val = wnd_loc / 8;
//int8_t n_bit_val = wnd_loc % 8;
size_t o_index = (imgx + imgy) >> 3;
uint32_t o_offset = 7 - (imgx & 0x07);
size_t n_index = (ix * iy) >> 3;
uint16_t n_offset = 7 - (ix & 0x07);
// px_val stores the fetched pixel value.
uint8_t px_val = 0;
// Now we access the pixel, and XOR it into the byte, after bit shifting it to make life easier.
// Since this is hard even for me to read, I'm going to explain it here (first in pseudocode-like language, then in English).
// pixel_value = ( preimage[orignal_byte_value] & (1 << original_bit_value)) >> original_bit_value;
// What this does is grab a single pixel value from an array. It will look up the byte, and then use a bit mask to grab one bit, and then right bit shift it down so that it is now a bool.
// rendered_plane[new_byte_value] |= (pixel_value << new_bit_value)
// This sets a bit at the targeted plane byte (via the use of a unary XOR operator), and left bit shifts the bool up to the actual pixel location.
if ((imgx * imgy) - 1 < img_sz) {
//printf("img_pos %i img x %i img y %i \n", img_loc, imgx, imgy);
//printf("IMG_BYTE_VAL %i BYTE_VAL %i BIT_VAL %i \n", img[o_byte_val], o_byte_val, o_bit_val);
//px_val = (img[o_byte_val] & (0b1 << (0x8 - o_bit_val))) >> o_bit_val;
px_val = (img[o_index] >> (o_offset)) & 0x01;
//printf("px %i \n", px_val);
//wndw_ptr[n_byte_val] |= (px_val << (0x8 - n_bit_val));
//printf("wnd_pos %i ix %i iy %i \n", wnd_loc, ix, iy);
//printf("WND_BYTE_VAL %i BYTE_VAL %i BIT_VAL %i \n", wndw_ptr[n_byte_val], n_byte_val, n_bit_val);
}
else {
px_val = 0;
}
wndw_ptr[n_index] = (wndw_ptr[n_index] & ~(0x01 << n_offset)) | (px_val << n_offset);
// Increment the image x location.
ix++;
}
// Increment the image y location.
iy++;
}
// No need to return anything, as the image is in a higher scope and is edited by a pointer.
return wndw_ptr;
}