Skip to content

Instantly share code, notes, and snippets.

@shonumi
Created October 8, 2016 20:17
Show Gist options
  • Save shonumi/a73f737106c1eb3bd748178a23259fd7 to your computer and use it in GitHub Desktop.
Save shonumi/a73f737106c1eb3bd748178a23259fd7 to your computer and use it in GitHub Desktop.
Don't lose this.
#include <iostream>
#include <string>
#include <cmath>
#include <SDL2/SDL.h>
#include "common.h"
struct affine_data
{
double dx;
double dy;
double dmx;
double dmy;
double xref;
double yref;
double unit_slope_x;
double unit_slope_y;
};
struct vmt_data
{
double a[2];
double b[2];
double c[2];
double d[2];
double width;
double height;
u8 overflow;
};
u32 get_pixel_pos(int x, int y)
{
u32 result_pos = (y * 640) + x;
return result_pos;
}
//Calculates a texel position from the given screen coordinates
void get_texel_pos(affine_data& matrix, double screen_x, double screen_y, double& tx, double& ty)
{
if((matrix.dx == 0) || (matrix.dmy == 0)) { tx = -1.0; ty = -1.0; return; }
double tx_comp, ty_comp = 0.0;
//Shift by SX and SY before doing anything else
screen_x -= matrix.xref;
screen_y -= matrix.yref;
//Solve for TX first
if(matrix.dmx == 0)
{
tx = screen_x * (1 / matrix.dx);
ty_comp = (matrix.dmy);
ty = (screen_y - (matrix.dy * tx)) / ty_comp;
}
//Solve for TY first
else if(matrix.dy == 0)
{
ty = screen_y * (1 / matrix.dmy);
tx_comp = matrix.dx;
tx = (screen_x - (matrix.dmx * ty)) / tx_comp;
}
//Solve for TY first (system of equations)
else
{
tx_comp = (0 - matrix.dmx) * (1 / matrix.dx);
ty_comp = (matrix.dy * tx_comp) + (matrix.dmy);
ty = screen_y / ty_comp;
tx = ty * tx_comp;
tx += (screen_x * matrix.unit_slope_x);
ty += (screen_x * matrix.unit_slope_y);
}
}
//Calculates a screen pixel position from the given texture coordinates
void get_screen_pixel(affine_data& matrix, double tx, double ty, double& screen_x, double& screen_y)
{
screen_x = (matrix.dx * tx) + (matrix.dmx * ty) + matrix.xref;
screen_y = (matrix.dy * tx) + (matrix.dmy * ty) + matrix.yref;
}
void calculate_vmt(affine_data& matrix, vmt_data& current_vmt)
{
//Find Points A, B, C, and D
get_screen_pixel(matrix, 0, 0, current_vmt.a[0], current_vmt.a[1]);
get_screen_pixel(matrix, 640, 0, current_vmt.b[0], current_vmt.b[1]);
get_screen_pixel(matrix, 640, 480, current_vmt.c[0], current_vmt.c[1]);
get_screen_pixel(matrix, 0, 480, current_vmt.d[0], current_vmt.d[1]);
current_vmt.overflow = 0;
//Check for left overflow
if((current_vmt.a[0] < 0) || (current_vmt.b[0] < 0) || (current_vmt.c[0] < 0) || (current_vmt.d[0] < 0))
{
std::cout<<"VMT LEFT OVERFLOW\n";
current_vmt.overflow |= 0x1;
}
//Check for right overflow
if((current_vmt.a[0] > 640) || (current_vmt.b[0] > 640) || (current_vmt.c[0] > 640) || (current_vmt.d[0] > 640))
{
std::cout<<"VMT RIGHT OVERFLOW\n";
current_vmt.overflow |= 0x2;
}
//Check for top overflow
if((current_vmt.a[1] < 0) || (current_vmt.b[1] < 0) || (current_vmt.c[1] < 0) || (current_vmt.d[1] < 0))
{
std::cout<<"VMT TOP OVERFLOW\n";
current_vmt.overflow |= 0x4;
}
//Check for bottom overflow
if((current_vmt.a[1] > 480) || (current_vmt.b[1] > 480) || (current_vmt.c[1] > 480) || (current_vmt.d[1] > 480))
{
std::cout<<"VMT BOTTOM OVERFLOW\n";
current_vmt.overflow |= 0x8;
}
//Calculate width
double vmt_min_x, vmt_max_x = 0;
if((current_vmt.a[0] <= current_vmt.b[0]) && (current_vmt.a[0] <= current_vmt.c[0]) && (current_vmt.a[0] <= current_vmt.d[0])) { vmt_min_x = current_vmt.a[0]; }
else if((current_vmt.b[0] <= current_vmt.a[0]) && (current_vmt.b[0] <= current_vmt.c[0]) && (current_vmt.b[0] <= current_vmt.d[0])) { vmt_min_x = current_vmt.b[0]; }
else if((current_vmt.c[0] <= current_vmt.a[0]) && (current_vmt.c[0] <= current_vmt.b[0]) && (current_vmt.c[0] <= current_vmt.d[0])) { vmt_min_x = current_vmt.c[0]; }
else if((current_vmt.d[0] <= current_vmt.a[0]) && (current_vmt.d[0] <= current_vmt.b[0]) && (current_vmt.d[0] <= current_vmt.c[0])) { vmt_min_x = current_vmt.d[0]; }
if((current_vmt.a[0] >= current_vmt.b[0]) && (current_vmt.a[0] >= current_vmt.c[0]) && (current_vmt.a[0] >= current_vmt.d[0])) { vmt_max_x = current_vmt.a[0]; }
else if((current_vmt.b[0] >= current_vmt.a[0]) && (current_vmt.b[0] >= current_vmt.c[0]) && (current_vmt.b[0] >= current_vmt.d[0])) { vmt_max_x = current_vmt.b[0]; }
else if((current_vmt.c[0] >= current_vmt.a[0]) && (current_vmt.c[0] >= current_vmt.b[0]) && (current_vmt.c[0] >= current_vmt.d[0])) { vmt_max_x = current_vmt.c[0]; }
else if((current_vmt.d[0] >= current_vmt.a[0]) && (current_vmt.d[0] >= current_vmt.b[0]) && (current_vmt.d[0] >= current_vmt.c[0])) { vmt_max_x = current_vmt.d[0]; }
current_vmt.width = vmt_max_x - vmt_min_x;
//Calculate height
double vmt_min_y, vmt_max_y = 0;
if((current_vmt.a[1] <= current_vmt.b[1]) && (current_vmt.a[1] <= current_vmt.c[1]) && (current_vmt.a[1] <= current_vmt.d[1])) { vmt_min_y = current_vmt.a[1]; }
else if((current_vmt.b[1] <= current_vmt.a[1]) && (current_vmt.b[1] <= current_vmt.c[1]) && (current_vmt.b[1] <= current_vmt.d[1])) { vmt_min_y = current_vmt.b[1]; }
else if((current_vmt.c[1] <= current_vmt.a[1]) && (current_vmt.c[1] <= current_vmt.b[1]) && (current_vmt.c[1] <= current_vmt.d[1])) { vmt_min_y = current_vmt.c[1]; }
else if((current_vmt.d[1] <= current_vmt.a[1]) && (current_vmt.d[1] <= current_vmt.b[1]) && (current_vmt.d[1] <= current_vmt.c[1])) { vmt_min_y = current_vmt.d[1]; }
if((current_vmt.a[1] >= current_vmt.b[1]) && (current_vmt.a[1] >= current_vmt.c[1]) && (current_vmt.a[1] >= current_vmt.d[1])) { vmt_max_y = current_vmt.a[1]; }
else if((current_vmt.b[1] >= current_vmt.a[1]) && (current_vmt.b[1] >= current_vmt.c[1]) && (current_vmt.b[1] >= current_vmt.d[1])) { vmt_max_y = current_vmt.b[1]; }
else if((current_vmt.c[1] >= current_vmt.a[1]) && (current_vmt.c[1] >= current_vmt.b[1]) && (current_vmt.c[1] >= current_vmt.d[1])) { vmt_max_y = current_vmt.c[1]; }
else if((current_vmt.d[1] >= current_vmt.a[1]) && (current_vmt.d[1] >= current_vmt.b[1]) && (current_vmt.d[1] >= current_vmt.c[1])) { vmt_max_y = current_vmt.d[1]; }
current_vmt.height = vmt_max_y - vmt_min_y;
std::cout<<"VMT WIDTH-> " << current_vmt.width << "\n";
std::cout<<"VMT HEIGHT-> " << current_vmt.height << "\n";
//Calculate the unit slope
if((matrix.dx != 0) && (matrix.dy != 0) && (matrix.dmx != 0) && (matrix.dmy != 0))
{
double ty_0 = 0.0;
double ty_1 = 1.0;
double tx_0 = (0 - (matrix.dmy * ty_0)) / matrix.dy;
double tx_1 = (0 - (matrix.dmy * ty_1)) / matrix.dy;
matrix.unit_slope_x = tx_0 - tx_1;
matrix.unit_slope_y = ty_0 - ty_1;
double sx_0 = (matrix.dx * tx_0) + (matrix.dmx * ty_0);
double sx_1 = (matrix.dx * tx_1) + (matrix.dmx * ty_1);
double x_dist = sx_0 - sx_1;
matrix.unit_slope_x /= x_dist;
matrix.unit_slope_y /= x_dist;
}
}
int main(int argc, char* args[])
{
//Initialize SDL
if(SDL_Init(SDL_INIT_EVERYTHING) == -1)
{
std::cout<<"Error - Could not init SDL\n";
return -1;
}
//Grab texture file name and open it as a BMP
//Make sure this is 640x480
std::string texture_file = args[1];
SDL_Surface* texture = NULL;
texture = SDL_LoadBMP(texture_file.c_str());
if(texture == NULL)
{
std::cout<<"Error - Failed to load texture file " << texture_file << "\n";
return -1;
}
//Setup window + final screen texture
SDL_Window* window = SDL_CreateWindow("Affine Test", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, 0);
SDL_Surface* final_screen = SDL_GetWindowSurface(window);
//Set up screen buffer
u32 screen_buffer[0x4B000];
for(u32 x = 0; x < 0x4B000; x++) { screen_buffer[x] = 0xFF000000; }
//Set up texture buffer
u32 texture_buffer[0x4B000];
if(SDL_MUSTLOCK(texture)){ SDL_LockSurface(texture); }
u8* in_pixel_data = (u8*)texture->pixels;
for(u32 x = 0, y = 0; x < 0x4B000; x++, y+=3)
{
u32 temp = (0xFF000000 | (in_pixel_data[y+2] << 16) | (in_pixel_data[y+1] << 8) | (in_pixel_data[y]));
texture_buffer[x] = temp;
}
if(SDL_MUSTLOCK(texture)){ SDL_UnlockSurface(texture); }
//Set up affine coordinate buffer
double affine_coords[640][2];
for(u32 x = 0; x < 640; x++) { affine_coords[x][0] = affine_coords[x][1] = 0.0; }
//Setup affine matrix and VMT
affine_data m_matrix;
vmt_data m_vmt;
m_matrix.dx = 0.5;
m_matrix.dy = 0.1;
m_matrix.dmx = 0.1;
m_matrix.dmy = 0.5;
m_matrix.xref = 360.0;
m_matrix.yref = 120.0;
calculate_vmt(m_matrix, m_vmt);
bool wrap = true;
//For each scanline calculate affine coordinates
for(u32 y = 0; y < 480; y++)
{
double tx_0, tx_1 = 0.0;
double ty_0, ty_1 = 0.0;
double temp_sx, temp_sy = 0.0;
double vmt_x_factor, vmt_y_factor = 0.0;
double ftx_0, fty_0 = 0.0;
//Calculate first and second left-most pixels on this scanline
get_texel_pos(m_matrix, 0, y, tx_0, ty_0);
get_texel_pos(m_matrix, 1, y, tx_1, ty_1);
//Calculate the slope
double x_diff = tx_1 - tx_0;
double y_diff = ty_1 - ty_0;
if(wrap && m_vmt.overflow)
{
//When wrapping, modulo the SX and SY coordinates
temp_sx = m_matrix.xref;
temp_sy = m_matrix.yref;
m_matrix.xref = fmod(m_matrix.xref, 640);
m_matrix.yref = fmod(m_matrix.yref, 480);
//Calculate adjustments to SX and SY coordinates
vmt_x_factor = m_vmt.width / 640;
if(vmt_x_factor < 1) { vmt_x_factor = 1; }
vmt_y_factor = m_vmt.height / 480;
if(vmt_y_factor < 1) { vmt_y_factor = 1; }
if(m_vmt.overflow & 0x1) { m_matrix.xref += (640 * vmt_x_factor); }
else if(m_vmt.overflow & 0x2) { m_matrix.xref -= (640 * vmt_x_factor); }
get_texel_pos(m_matrix, 0, y, ftx_0, fty_0);
m_matrix.xref = temp_sx;
m_matrix.yref = temp_sy;
}
//Save affine coordinates
for(u32 x = 0; x < 640; x++)
{
//No wrapping
if(!wrap || !m_vmt.overflow)
{
affine_coords[x][0] = tx_0;
affine_coords[x][1] = ty_0;
tx_0 += x_diff;
ty_0 += y_diff;
}
//Wrapping
else
{
if((tx_0 >= 0) && (tx_0 < 640) && (ty_0 >= 0) && (ty_0 < 480))
{
affine_coords[x][0] = tx_0;
affine_coords[x][1] = ty_0;
}
else
{
affine_coords[x][0] = ftx_0;
affine_coords[x][1] = fty_0;
}
ftx_0 += x_diff;
fty_0 += y_diff;
tx_0 += x_diff;
ty_0 += y_diff;
}
}
//Apply affine transformation to final buffer
for(u32 x = 0; x < 640; x++)
{
u32 final_pos = get_pixel_pos(x, y);
u32 tex_pos;
s32 new_x = affine_coords[x][0];
s32 new_y = affine_coords[x][1];
if((new_x >= 0) && (new_x < 640) && (new_y >= 0) && (new_y < 480))
{
tex_pos = get_pixel_pos(new_x, new_y);
screen_buffer[final_pos] = texture_buffer[tex_pos];
}
}
}
//Copy buffer to final screen
if(SDL_MUSTLOCK(final_screen)){ SDL_LockSurface(final_screen); }
u32* out_pixel_data = (u32*)final_screen->pixels;
for(u32 x = 0; x < 0x4B000; x++) { out_pixel_data[x] = screen_buffer[x]; }
if(SDL_MUSTLOCK(final_screen)){ SDL_UnlockSurface(final_screen); }
//Draw window
if(SDL_UpdateWindowSurface(window) != 0)
{
std::cout<<"Error - Could not blit\n";
return -1;
}
SDL_SaveBMP(final_screen, "pp.bmp");
SDL_Delay(2000);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment