Last active
December 7, 2016 16:38
-
-
Save croepha/2abaca90e03847c4f8d05a7e40685c5b to your computer and use it in GitHub Desktop.
work in progress software renderer for 3d
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
/* | |
watch -n.1 "clang++ --std=c++11 -g main.cpp -shared -o _target.so && mv _target.so target.so" | |
Ortho: http://i.imgur.com/bJ3VqNz.png | |
# clang++ --std=c++11 -g main.cpp -o target && ./target | |
*/ | |
#include <assert.h> | |
#include <stdio.h> | |
#include "linalg.h" | |
#include <math.h> | |
#include <time.h> | |
#define FOR_RANGE(index, count) for ((index) = 0; (index) < (count); (index)++) | |
using namespace linalg::aliases; | |
using namespace linalg; | |
struct GC { | |
unsigned char * render_buffer; | |
int width; | |
int height; | |
float near; | |
float far; | |
float4x4 projection; | |
}; | |
static const float4x4 IDENTITY = {{ 1, 0, 0, 0}, | |
{ 0, 1, 0, 0}, | |
{ 0, 0, 1, 0}, | |
{ 0, 0, 0, 1}}; | |
void draw_line(float2 p1, float2 p2, GC *gc) { | |
if (p1[0] == INFINITY || p1[0] == -INFINITY || isnan(p1[0]) || | |
p1[1] == INFINITY || p1[1] == -INFINITY || isnan(p1[1]) || | |
p2[0] == INFINITY || p2[0] == -INFINITY || isnan(p2[0]) || | |
p2[1] == INFINITY || p2[1] == -INFINITY || isnan(p2[1])) return; | |
float2 d = normalize(p2 - p1); | |
float2 last_p = p1; | |
float minx = p1[0]; | |
if (p2[0] < p1[0]) minx = p2[0]; | |
float miny = p1[1]; | |
if (p2[1] < p1[1]) miny = p2[1]; | |
float maxx = p1[0]; | |
if (p2[0] > p1[0]) maxx = p2[0]; | |
float maxy = p1[1]; | |
if (p2[1] > p1[1]) maxy = p2[1]; | |
for(;;) { | |
int x = last_p[0]; | |
int y = last_p[1]; | |
if (x >= 0 && y>=0 && x <gc->width && y < gc->height) { | |
*(gc->render_buffer + 4 * (x + gc->width * y) + 0) = 0x00; | |
*(gc->render_buffer + 4 * (x + gc->width * y) + 1) = 0x00; | |
*(gc->render_buffer + 4 * (x + gc->width * y) + 2) = 0x00; | |
*(gc->render_buffer + 4 * (x + gc->width * y) + 3) = 0xFF; | |
} | |
// else { printf("clip!\n");}; | |
last_p += d; | |
if ( (d[0]==0 && d[1]==0) || | |
d[0] == INFINITY || d[0] == -INFINITY || isnan(d[0]) || | |
d[1] == INFINITY || d[1] == -INFINITY || isnan(d[1]) || | |
last_p[0] < minx || | |
last_p[1] < miny || | |
last_p[0] > maxx || | |
last_p[1] > maxy | |
) break; | |
} | |
} | |
float4x4 look_at() { | |
// gluLookAt | |
float la_ey_x = 0.0f; | |
float la_ey_y = 0.0f; | |
float la_ey_z = 5.0f; | |
float la_cn_x = 0.0f; | |
float la_cn_y = 0.0f; | |
float la_cn_z = 0.0f; | |
float la_up_x = 0.0f; | |
float la_up_y = 1.0f; | |
float la_up_z = 0.0f; | |
float3 la_F = {la_cn_x - la_ey_x, la_cn_y - la_ey_y, la_cn_z - la_ey_z}; | |
float3 la_UP = {la_up_x, la_up_y, la_up_z}; | |
float3 la_f = normalize(la_F); | |
float3 la_UP_norm = normalize(la_UP); | |
// float3 la_s = la_f * la_UP_norm; | |
// float3 la_u = normalize(la_s) * la_f; | |
float3 la_s = cross(la_f, la_UP_norm); | |
float3 la_u = cross(normalize(la_s), la_f); | |
float4x4 ret = {{ la_s[0], la_s[1], la_s[2], 0}, | |
{ la_u[0], la_u[1], la_u[2], 0}, | |
{ -la_f[0], -la_f[1], -la_f[2], 0}, | |
{ 0, 0, 0, 1}}; | |
return transpose(ret); | |
} | |
float4x4 translate(float3 trans_p) { | |
float4x4 ret = {{ 1, 0, 0, trans_p[0]}, | |
{ 0, 1, 0, trans_p[1]}, | |
{ 0, 0, 1, trans_p[2]}, | |
{ 0, 0, 0, 1}}; | |
return transpose(ret); | |
} | |
float4x4 rotate(float3 r_p, float r_angle) { | |
r_p = normalize(r_p); | |
float r_c = cos(r_angle * M_PI / 180); | |
float r_s = sin(r_angle * M_PI / 180); | |
float4x4 ret; | |
ret = {{ powf(r_p.x, 2) * (1 - r_c) + r_c, r_p.x * r_p.y * (1 - r_c) - r_p.z * r_s, r_p.x * r_p.z * (1-r_c) + r_p.y * r_s, 0 }, | |
{ r_p.y * r_p.x * (1 - r_c) + r_p.z * r_s, powf(r_p.y, 2) * (1 - r_c) + r_c, r_p.y * r_p.z * (1-r_c) - r_p.x * r_s, 0 }, | |
{ r_p.x * r_p.z * (1 - r_c) - r_p.y * r_s, r_p.y * r_p.z * (1 - r_c) + r_p.x * r_s, powf(r_p.z, 2) * (1 - r_c) + r_c, 0 }, | |
{ 0, 0, 0, 1 }}; | |
return transpose(ret); | |
} | |
float4x4 ortho() { | |
float o_left = -2.0f; | |
float o_right = 2.0f; | |
float o_bottom = -2.0f; | |
float o_top = 2.0f; | |
float o_near = -2.0f; | |
float o_far = 2.0f; | |
float o_tx = (o_right + o_left) / (o_right - o_left); | |
float o_ty = (o_top + o_bottom) / (o_top - o_bottom); | |
float o_tz = (o_far + o_near) / (o_far - o_near); | |
float4x4 ret; | |
ret = {{ 2.0f / (o_right - o_left), 0, 0, o_tx}, | |
{ 0, 2.0f / (o_top - o_bottom), 0, o_ty}, | |
{ 0, 0, 2.0f / (o_far - o_near), o_tz}, | |
{ 0, 0, -1, 1}}; | |
return transpose(ret); | |
} | |
float4x4 perspective() { | |
// gluPerspective | |
float field_of_view = 40.0f; | |
float aspect_ratio = 1.0f; | |
float z_near = 10.0f; | |
float z_far = 100.000f; | |
// float f = cotangent(field_of_view / 2.0f); | |
float f = 1.0f / tan((float)(M_PI) * field_of_view / 360.0f); | |
float4x4 ret; | |
ret = {{ f/aspect_ratio, 0, 0, 0}, | |
{ 0, f, 0, 0}, | |
{ 0, 0, (z_far + z_near) / (z_near - z_far), (2.0f * z_far * z_near) / (z_near - z_far)}, | |
{ 0, 0, -1, 0}}; | |
return transpose(ret); | |
} | |
float3 nd_cord(float4 clip_cord) { | |
return { clip_cord.x / clip_cord.w, clip_cord.y / clip_cord.w, clip_cord.z / clip_cord.w}; | |
} | |
float3 window_cord(GC* gc, float3 nd_cord) { | |
float vp_x = 0; // glViewport | |
float vp_y = 0; // glViewport | |
float vp_w = gc->width; // glViewport | |
float vp_h = gc->height; // glViewport | |
float dr_n = gc->near; //glDepthRange | |
float dr_f = gc->far; //glDepthRange | |
return { | |
(vp_w / 2.0f) * nd_cord.x + (vp_x + vp_w/2.0f), | |
(vp_h / 2.0f) * nd_cord.y + (vp_y + vp_h/2.0f), | |
((dr_f - dr_n)/2.0f) * nd_cord.z + (dr_f + dr_n)/2.0f | |
}; | |
} | |
void clear(GC* gc, unsigned char r, unsigned char g, unsigned char b , unsigned char a) { | |
int pixel_i; | |
unsigned char *pb = gc->render_buffer; | |
FOR_RANGE(pixel_i, gc->width * gc->height) { | |
*pb++ = r; | |
*pb++ = g; | |
*pb++ = b; | |
*pb++ = a; | |
} | |
} | |
float2 project_point(GC* gc, float4x4 model_view, float3 point_coords) { | |
float4 _oc = {point_coords[0], point_coords[1], point_coords[2], 1}; | |
float4 ec = mul(model_view, _oc); | |
float4 cc = mul(gc->projection, ec); | |
float3 ndc = nd_cord(cc); | |
float3 wc = window_cord(gc, ndc); | |
return {wc[0], wc[1]}; | |
} | |
extern "C" void target(float current_time, void * _render_buffer, int width, int height) { | |
GC gc = {(unsigned char *)_render_buffer, width, height, 1, 10}; | |
unsigned char * render_buffer = (unsigned char *)_render_buffer; | |
gc.projection = IDENTITY; | |
// gc.projection = mul(gc.projection, ortho()); | |
gc.projection = mul(gc.projection, perspective()); | |
// Clear white | |
clear(&gc, 0xFF, 0xFF, 0xFF, 0xFF); | |
float4x4 mv = IDENTITY; | |
// mv = mul(mv, look_at()); | |
mv = mul(mv, translate({0.0, 0.0, 6})); | |
mv = mul(mv, rotate({1.0, 0.0, 0.0}, 15.0f)); | |
// mv = mul(mv, rotate({0.0, 0.0, 1.0}, -10.0f)); | |
mv = mul(mv, rotate({0.0, 1.0, 0.0}, -05.0f)); | |
float2 p0 = project_point(&gc, mv, { 1, 1, 1}); | |
float2 p1 = project_point(&gc, mv, { 1, 1, -1}); | |
float2 p2 = project_point(&gc, mv, { 1, -1, -1}); | |
float2 p3 = project_point(&gc, mv, { 1, -1, 1}); | |
float2 p4 = project_point(&gc, mv, {-1, 1, 1}); | |
float2 p5 = project_point(&gc, mv, {-1, 1, -1}); | |
float2 p6 = project_point(&gc, mv, {-1, -1, -1}); | |
float2 p7 = project_point(&gc, mv, {-1, -1, 1}); | |
draw_line(p0, p1, &gc); | |
draw_line(p1, p2, &gc); | |
draw_line(p2, p3, &gc); | |
draw_line(p3, p0, &gc); | |
draw_line(p4, p5, &gc); | |
draw_line(p5, p6, &gc); | |
draw_line(p6, p7, &gc); | |
draw_line(p7, p4, &gc); | |
draw_line(p0, p4, &gc); | |
draw_line(p1, p5, &gc); | |
draw_line(p2, p6, &gc); | |
draw_line(p3, p7, &gc); | |
} |
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
/* | |
clang++ -g tester.cpp -F /Library/Frameworks/ -framework SDL2 -framework OpenGL -o tester && ./tester | |
*/ | |
#include <dlfcn.h> | |
#if defined __APPLE__ | |
#include <SDL2/SDL.h> | |
#include <SDL2/SDL_opengl.h> | |
#else | |
#include <SDL.h> | |
#include <SDL_opengl.h> | |
#endif | |
#include <signal.h> | |
#define check_gl_error() _check_gl_error(__FILE__,__LINE__) | |
void _check_gl_error(const char *file, int line) { | |
GLenum err (glGetError()); | |
const char * error; | |
switch (err) { | |
case GL_INVALID_OPERATION: error = "INVALID_OPERATION"; break; | |
case GL_INVALID_ENUM: error = "INVALID_ENUM"; break; | |
case GL_INVALID_VALUE: error = "INVALID_VALUE"; break; | |
case GL_OUT_OF_MEMORY: error = "OUT_OF_MEMORY"; break; | |
case GL_INVALID_FRAMEBUFFER_OPERATION: error = "INVALID_FRAMEBUFFER_OPERATION"; break; | |
default: | |
error = "UNKNOWN"; | |
} | |
while(err!=GL_NO_ERROR) { | |
SDL_LogCritical(SDL_LOG_CATEGORY_SYSTEM, "%s:%d OPENGL_ERROR %s\n", file, line, error); | |
err=glGetError(); | |
} | |
} | |
typedef void target_t(float, void *, int, int); | |
//void sig_usr_handler(int signum) { game_code.needs_reload = true; } | |
int main(int argc, const char * argv[]) { | |
// signal(SIGUSR1, sig_usr_handler); | |
int render_width = 100; | |
int render_height = 100; | |
unsigned char render_buffer[render_width * render_height * 4]; | |
//Create window | |
SDL_Window * window = SDL_CreateWindow | |
( | |
"Untitled Window", | |
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, | |
render_width, render_height, | |
SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL | |
); | |
if(!window) | |
{ | |
SDL_LogCritical(SDL_LOG_CATEGORY_SYSTEM, "Window could not be created! SDL_Error: %s\n", SDL_GetError() ); | |
return -1; | |
} | |
SDL_GLContext context = SDL_GL_CreateContext( window ); | |
if( context == NULL ) | |
{ | |
SDL_LogCritical(SDL_LOG_CATEGORY_SYSTEM, "OpenGL context could not be created! SDL Error: %s\n", SDL_GetError() ); | |
return -3; | |
} | |
if( SDL_GL_SetSwapInterval( 1 ) < 0 ) | |
{ | |
SDL_LogCritical(SDL_LOG_CATEGORY_SYSTEM, "Warning: Unable to set VSync! SDL Error: %s\n", SDL_GetError() ); | |
} | |
bool has_focus = false; | |
bool running = true; | |
void * target_code = 0; | |
target_t * target_function = 0; | |
GLuint textureID; | |
glGenTextures(1, &textureID); | |
unsigned long long last_time = SDL_GetPerformanceCounter(); | |
while(running) { | |
SDL_Event e; | |
while(SDL_PollEvent( &e )) { | |
switch (e.type) { | |
case SDL_WINDOWEVENT: | |
if (e.window.event == SDL_WINDOWEVENT_FOCUS_GAINED) { | |
has_focus = true; | |
} else if (e.window.event == SDL_WINDOWEVENT_FOCUS_LOST) { | |
has_focus = false; | |
} | |
break; | |
case SDL_QUIT: | |
running = false; | |
break; | |
default: | |
break; | |
} | |
} | |
// if (!has_focus) { | |
// SDL_Delay(50); | |
// continue; | |
// } | |
if (target_code) { | |
if(dlclose(target_code)) { | |
printf("Target dll: Failed to unload target DLL: %s\n", dlerror()); | |
return -10; | |
} | |
} | |
target_code = dlopen("Target.so", RTLD_LAZY); | |
if (!target_code) { | |
printf("Target dll: could not be loaded: %s\n", dlerror()); | |
continue; | |
} | |
target_function = (target_t*)dlsym(target_code, "target"); | |
if(!target_function) { | |
printf("Target dll: update could not be loaded: %s\n", dlerror()); | |
return -11; | |
} | |
float freq = SDL_GetPerformanceFrequency(); | |
unsigned long long now = SDL_GetPerformanceCounter() % 1000; | |
float secs = (float)(now - last_time) / freq; | |
last_time = now; | |
target_function(secs / 1000.0f, &render_buffer, render_width, render_height); | |
glBindTexture(GL_TEXTURE_2D, textureID); | |
check_gl_error(); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |
check_gl_error(); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |
check_gl_error(); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); | |
check_gl_error(); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); | |
check_gl_error(); | |
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); | |
check_gl_error(); | |
glTexImage2D | |
(GL_TEXTURE_2D, 0, GL_RGBA8, render_width, render_height, 0, | |
GL_RGBA, GL_UNSIGNED_BYTE, &render_buffer); | |
check_gl_error(); | |
glMatrixMode(GL_PROJECTION); | |
check_gl_error(); | |
int window_width, window_height; | |
SDL_GetWindowSize(window, &window_width, &window_height); | |
glViewport(0, 0, window_width, window_height); | |
check_gl_error(); | |
glLoadIdentity(); | |
check_gl_error(); | |
glMatrixMode(GL_MODELVIEW); | |
check_gl_error(); | |
glLoadIdentity(); | |
check_gl_error(); | |
glOrtho(0, 1, 0, 1, -1.0f, 1.0f ); | |
check_gl_error(); | |
glEnable(GL_BLEND); | |
check_gl_error(); | |
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); | |
check_gl_error(); | |
glMatrixMode(GL_TEXTURE); | |
check_gl_error(); | |
glLoadIdentity(); | |
check_gl_error(); | |
glClearColor(.5f,.0f,.5f,.5f); | |
check_gl_error(); | |
glClear(GL_COLOR_BUFFER_BIT); | |
check_gl_error(); | |
glLoadIdentity(); | |
check_gl_error(); | |
glEnable(GL_TEXTURE_2D); | |
check_gl_error(); | |
glBindTexture(GL_TEXTURE_2D, textureID); | |
check_gl_error(); | |
glBegin( GL_QUADS ); | |
glColor3f(1.0f, 1.0f, 1.0f); | |
glTexCoord2f( 0.0f , 0.0f ); | |
glVertex2f( 0.0f , 0.0f ); | |
glTexCoord2f( 1.0f , 0.0f ); | |
glVertex2f( 1.0f , 0.0f ); | |
glTexCoord2f( 1.0f , 1.0f ); | |
glVertex2f( 1.0f , 1.0f ); | |
glTexCoord2f( 0.0f , 1.0f ); | |
glVertex2f( 0.0f , 1.0f ); | |
glEnd(); | |
SDL_GL_SwapWindow(window); | |
check_gl_error(); | |
} | |
return 0; | |
} |
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
/* | |
wget -c https://raw.githubusercontent.com/nothings/stb/master/stb_image_write.h && | |
wget -c https://raw.githubusercontent.com/sgorsten/linalg/master/linalg.h && | |
clang++ --std=c++11 -g tester_bmp_out.cpp -o tester_bmp_out && ./tester_bmp_out | |
*/ | |
#define STB_IMAGE_WRITE_IMPLEMENTATION | |
#include "stb_image_write.h" | |
#include "main.cpp" | |
int main () { | |
int width = 100; | |
int height = 100; | |
unsigned char _render_buffer[width * height * 4]; | |
GC gc = {(unsigned char *)&_render_buffer, width, height, 1, 10}; | |
unsigned char * render_buffer = (unsigned char *)_render_buffer; | |
gc.projection = IDENTITY; | |
// gc.projection = mul(gc.projection, ortho()); | |
gc.projection = mul(gc.projection, perspective()); | |
// Clear white | |
clear(&gc, 0xFF, 0xFF, 0xFF, 0xFF); | |
float4x4 mv = IDENTITY; | |
// mv = mul(mv, look_at()); | |
mv = mul(mv, translate({0.0, 0.0, 6})); | |
mv = mul(mv, rotate({1.0, 0.0, 0.0}, 15.0f)); | |
// mv = mul(mv, rotate({0.0, 0.0, 1.0}, -10.0f)); | |
mv = mul(mv, rotate({0.0, 1.0, 0.0}, -05.0f)); | |
float2 p0 = project_point(&gc, mv, { 1, 1, 1}); | |
float2 p1 = project_point(&gc, mv, { 1, 1, -1}); | |
float2 p2 = project_point(&gc, mv, { 1, -1, -1}); | |
float2 p3 = project_point(&gc, mv, { 1, -1, 1}); | |
float2 p4 = project_point(&gc, mv, {-1, 1, 1}); | |
float2 p5 = project_point(&gc, mv, {-1, 1, -1}); | |
float2 p6 = project_point(&gc, mv, {-1, -1, -1}); | |
float2 p7 = project_point(&gc, mv, {-1, -1, 1}); | |
draw_line(p0, p1, &gc); | |
draw_line(p1, p2, &gc); | |
draw_line(p2, p3, &gc); | |
draw_line(p3, p0, &gc); | |
draw_line(p4, p5, &gc); | |
draw_line(p5, p6, &gc); | |
draw_line(p6, p7, &gc); | |
draw_line(p7, p4, &gc); | |
draw_line(p0, p4, &gc); | |
draw_line(p1, p5, &gc); | |
draw_line(p2, p6, &gc); | |
draw_line(p3, p7, &gc); | |
return stbi_write_bmp("test_out.bmp", width, height, 4, &_render_buffer); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment