Skip to content

Instantly share code, notes, and snippets.

@croepha
Last active December 7, 2016 16:38
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save croepha/2abaca90e03847c4f8d05a7e40685c5b to your computer and use it in GitHub Desktop.
Save croepha/2abaca90e03847c4f8d05a7e40685c5b to your computer and use it in GitHub Desktop.
work in progress software renderer for 3d
/*
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);
}
/*
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;
}
/*
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