Created
October 8, 2016 20:17
-
-
Save shonumi/a73f737106c1eb3bd748178a23259fd7 to your computer and use it in GitHub Desktop.
Don't lose this.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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