New paste Repaste Download
/*
Main TODO:
* fix clipping
* (including finishing interpolating vertex attributes for vertices moved or created by clipping)
Then the typical stuff like:
* mesh loading
* Gouraud shading, phong shading...
*/
/*
If I really want the core pixel loop to be fast I should rewrite it to use only integer arithmetic or something...
*/
/*
The coordinate system in use is:
x = horizontal (positive is right),
y = vertical (positive is up),
z = depth (positive is into the screen)
*/
#include "SDL.h"
#include "SDL_image.h"
#include "math.h"
#include "3dmath.h"
#include "shapes.h"
#include "time.h"
#include "load_wavefront_obj.h"
#define WIDTH 1280
#define HEIGHT 960
// #define SHADOWS_ENABLED
/*
To make shadow mapping work, two more significant things would have to be done:
* front face culling render flag to get rid of artifacts
* separating sun and point light components (more interpolation cost...)
*/
// static SDL_Surface *test_texture;
/* TODO - maybe colors should also be stored as floats and only converted at the very end to save some conversions when interpolating */
/* Rendering */
#define MAX_VERTICES 16384
#define MAX_TRIS 16384
typedef struct {
Vector2 projected_position;
Vector3 view_space_position;
#ifdef SHADOWS_ENABLED
Vector3 world_space_position; // used for shadow mapping - maybe there's a more elegant solution ???
#endif
VertexAttributes attributes;
} ProjectedVertex;
Matrix3x3 projection_matrix;
Vector3 camera_point;
static ProjectedVertex projected_vertices[MAX_VERTICES];
static Tri clipped_tris[MAX_TRIS];
static float clip_start = 0.01;
static float clip_end = 1000000;
static float fov = 90;
#define INTERPOLATION_NEAREST 0
#define INTERPOLATION_BILINEAR 1
static char texture_interpolation_mode;
static long total_microseconds = 0;
static long total_frames = 0;
static Vector3 sun_vector = {-1, -1, -0.5};
typedef struct {
Vector3 position;
float strength;
RGB24 color;
} PointLight;
static PointLight point_lights[8] = {
{{-160, 40, 80}, 100, {255, 192, 0}},
{{-80, -160, 0}, 150, {128, 128, 255}}
};
static int num_point_lights = 2;
static SDL_Surface *virtual_screen;
static float depth_buffer[WIDTH][HEIGHT];
#ifdef SHADOWS_ENABLED
#define GLOBAL_SHADOWMAP_SIZE 256
#define SHADOW_BIAS_MIN 0.002
#define SHADOW_BIAS_MAX 0.02
static float global_shadowmap_depth_buffer[GLOBAL_SHADOWMAP_SIZE][GLOBAL_SHADOWMAP_SIZE];
static Matrix3x3 sun_matrix; // this really should not be a global
static Vector3 sun_position = {0, 0, 0}; // meaning the world space coordinate corresponding to the center of the global shadowmap
#endif
#define RENDERER_FLAG_ORTHOGRAPHIC 1
#define RENDERER_FLAG_SHADOW 2 // are we rendering to a shadow map? if so, don't compute color information
#define RENDERER_FLAG_NO_CLIP 4 // disables clipping
#define RENDERER_FLAG_RECEIVE_GLOBAL_SHADOW 8
void draw_mesh(Mesh *mesh, Matrix3x3 *model_matrix, Vector3 model_position, Matrix3x3 *projection_matrix, Vector3 camera_position, float fov, unsigned int renderer_flags, int target_width, int target_height, SDL_Surface *target, float *depth_buffer) {
/* This is the giant monolithic rendering function.
It is useful to note that target may be NULL if RENDERER_FLAG_SHADOW is set.
*/
// transformation
float zoom_factor;
if (renderer_flags & RENDERER_FLAG_ORTHOGRAPHIC) zoom_factor = target_width;
else zoom_factor = (target_width / 2.0) / tan(fov / 2.0 * M_PI / 180.0); // at z=1, how many pixels does 1 unit in the XY-plane correspond to?
int v;
int num_projected_vertices = 0;
for (v=0; v<mesh->num_vertices; v++) {
Vector3 world_space_position = vector3_add(matrix3x3_apply(*model_matrix, mesh->vertices[v].position), model_position);
#ifdef SHADOWS_ENABLED
projected_vertices[num_projected_vertices].world_space_position = world_space_position;
#endif
projected_vertices[num_projected_vertices].view_space_position = matrix3x3_apply(*projection_matrix, vector3_sub(world_space_position, camera_position));
projected_vertices[num_projected_vertices].view_space_position.y = -projected_vertices[num_projected_vertices].view_space_position.y; // screen coordinates are positive y = down, scene coordinates are positive y = up
Vector3 world_space_normal;
// Any further vertex shader code goes here.
if ((renderer_flags & RENDERER_FLAG_SHADOW) == 0) {
// TODO: proper normal matrix
world_space_normal = vector3_normalize(matrix3x3_apply(*model_matrix, mesh->vertices[v].attributes.normal));
Vector3 view_vector;
if (renderer_flags & RENDERER_FLAG_ORTHOGRAPHIC) {
// z row of the projection matrix gives the direction the camera is pointing
view_vector.x = -projection_matrix->c13;
view_vector.y = -projection_matrix->c23;
view_vector.z = -projection_matrix->c33;
}
else view_vector = vector3_sub(camera_position, world_space_position);
float diffuse_dot = vector3_dot(world_space_normal, vector3_scalef(vector3_normalize(sun_vector), -1));
float light = diffuse_dot;
if (light < 0) light = 0;
light += materials[mesh->material_id].ambient_amount; // ambient
if (light > 1) light = 1;
Vector3 halfway = vector3_normalize(vector3_add(vector3_normalize(view_vector), vector3_scalef(vector3_normalize(sun_vector), -1)));
float specular_dot = vector3_dot(halfway, world_space_normal);
if (specular_dot < 0) specular_dot = 0;
float specular = pow(specular_dot, materials[mesh->material_id].specular_exponent);
if (specular > 1) specular = 1;
int diffuse_r = 0;
int diffuse_g = 0;
int diffuse_b = 0;
int specular_r = 0;
int specular_g = 0;
int specular_b = 0;
int component = (int)(255*light);
diffuse_r = component;
diffuse_g = component;
diffuse_b = component;
// the world's most fake subsurface scattering effect
float add = diffuse_dot;
if (add < 0) add *= -1;
float sss = pow(pow(1.0-(add*add), materials[mesh->material_id].subsurface_exponent), 3);
diffuse_r += materials[mesh->material_id].subsurface_color.r * sss;
diffuse_g += materials[mesh->material_id].subsurface_color.g * sss;
diffuse_b += materials[mesh->material_id].subsurface_color.b * sss;
int specular_component = (int)(255*specular);
specular_r = (int)(specular * (float)materials[mesh->material_id].specular_color.r);
specular_g = (int)(specular * (float)materials[mesh->material_id].specular_color.g);
specular_b = (int)(specular * (float)materials[mesh->material_id].specular_color.b);
for (int p=0; p<num_point_lights; p++) {
Vector3 delta_position = vector3_sub(point_lights[p].position, world_space_position);
float light_strength = point_lights[p].strength;
float m = vector3_magnitude(delta_position);
if (m < 1) m = 1; // fake light cap
if (m > point_lights[p].strength * 10) continue; // fake light cutoff
light_strength /= m; // fake linear falloff
float diffuse_dot = vector3_dot(world_space_normal, vector3_normalize(delta_position));
if (diffuse_dot < 0) diffuse_dot = 0;
diffuse_r += (int)((float)point_lights[p].color.r * diffuse_dot * light_strength);
diffuse_g += (int)((float)point_lights[p].color.g * diffuse_dot * light_strength);
diffuse_b += (int)((float)point_lights[p].color.b * diffuse_dot * light_strength);
Vector3 halfway = vector3_normalize(vector3_add(vector3_normalize(view_vector), vector3_normalize(delta_position)));
float specular_dot = vector3_dot(halfway, world_space_normal);
if (specular_dot < 0) specular_dot = 0;
float specular = pow(specular_dot, materials[mesh->material_id].specular_exponent) * light_strength;
specular_r += (int)(specular * (float)point_lights[p].color.r * (float)materials[mesh->material_id].specular_color.r / 255.0);
specular_g += (int)(specular * (float)point_lights[p].color.g * (float)materials[mesh->material_id].specular_color.g / 255.0);
specular_b += (int)(specular * (float)point_lights[p].color.b * (float)materials[mesh->material_id].specular_color.b / 255.0);
}
if (diffuse_r > 255) diffuse_r = 255;
if (diffuse_g > 255) diffuse_g = 255;
if (diffuse_b > 255) diffuse_b = 255;
if (specular_r > 255) specular_r = 255;
if (specular_g > 255) specular_g = 255;
if (specular_b > 255) specular_b = 255;
projected_vertices[num_projected_vertices].attributes.color.r = (unsigned char)diffuse_r;
projected_vertices[num_projected_vertices].attributes.color.g = (unsigned char)diffuse_g;
projected_vertices[num_projected_vertices].attributes.color.b = (unsigned char)diffuse_b;
projected_vertices[num_projected_vertices].attributes.specular_color.r = (unsigned char)specular_r;
projected_vertices[num_projected_vertices].attributes.specular_color.g = (unsigned char)specular_g;
projected_vertices[num_projected_vertices].attributes.specular_color.b = (unsigned char)specular_b;
}
projected_vertices[num_projected_vertices].attributes.texture_coordinate = mesh->vertices[v].attributes.texture_coordinate;
#ifdef NORMAL_POST_SHADER_VERTEX_ATTRIBUTE
projected_vertices[num_projected_vertices].attributes.world_space_normal = world_space_normal;
#endif
num_projected_vertices++;
}
// clipping
// tessellation would probably have to get merged in with this step
int num_clipped_tris = 0;
for (int t=0; t<mesh->num_tris; t++) {
if (renderer_flags & RENDERER_FLAG_NO_CLIP) {
clipped_tris[num_clipped_tris++] = mesh->tris[t];
continue;
}
float z1 = projected_vertices[mesh->tris[t].vertices[0]].view_space_position.z;
float z2 = projected_vertices[mesh->tris[t].vertices[1]].view_space_position.z;
float z3 = projected_vertices[mesh->tris[t].vertices[2]].view_space_position.z;
if (z1 < clip_start && z2 < clip_start && z3 < clip_start) continue;
if (z1 > clip_end && z2 > clip_end && z3 > clip_end) continue;
// end clipping
// start clipping
char clipped_vertex_indices[2];
char num_clipped_vertex_indices = 0;
if (z1 < clip_start) {
clipped_vertex_indices[num_clipped_vertex_indices++] = 0;
}
if (z2 < clip_start) {
clipped_vertex_indices[num_clipped_vertex_indices++] = 1;
}
if (z3 < clip_start) {
clipped_vertex_indices[num_clipped_vertex_indices++] = 2;
}
if (num_clipped_vertex_indices == 2) {
char unclipped_vertex_index = 0;
while (clipped_vertex_indices[0] == unclipped_vertex_index || clipped_vertex_indices[1] == unclipped_vertex_index) unclipped_vertex_index++;
int clipped_vertex_1 = mesh->tris[t].vertices[clipped_vertex_indices[0]];
int clipped_vertex_2 = mesh->tris[t].vertices[clipped_vertex_indices[1]];
int unclipped_vertex = mesh->tris[t].vertices[unclipped_vertex_index];
int new_clipped_vertex_1 = num_projected_vertices++;
int new_clipped_vertex_2 = num_projected_vertices++;
float ucx = projected_vertices[unclipped_vertex].view_space_position.x;
float ucy = projected_vertices[unclipped_vertex].view_space_position.y;
float ucz = projected_vertices[unclipped_vertex].view_space_position.z;
float xd1 = projected_vertices[clipped_vertex_1].view_space_position.x - ucx;
float xd2 = projected_vertices[clipped_vertex_2].view_space_position.x - ucx;
float yd1 = projected_vertices[clipped_vertex_1].view_space_position.y - ucy;
float yd2 = projected_vertices[clipped_vertex_2].view_space_position.y - ucy;
float zd1 = projected_vertices[clipped_vertex_1].view_space_position.z - ucz;
float zd2 = projected_vertices[clipped_vertex_2].view_space_position.z - ucz;
float zds = clip_start - ucz;
float iu = zds / zd1;
float iv = zds / zd2;
projected_vertices[new_clipped_vertex_1].view_space_position.x = ucx + xd1 * iu;
projected_vertices[new_clipped_vertex_2].view_space_position.x = ucx + xd2 * iv;
projected_vertices[new_clipped_vertex_1].view_space_position.y = ucy + yd1 * iu;
projected_vertices[new_clipped_vertex_2].view_space_position.y = ucy + yd2 * iv;
projected_vertices[new_clipped_vertex_1].view_space_position.z = clip_start;
projected_vertices[new_clipped_vertex_2].view_space_position.z = clip_start;
#ifdef SHADOWS_ENABLED
projected_vertices[new_clipped_vertex_1].world_space_position.x = projected_vertices[unclipped_vertex].world_space_position.x*(1-iu) + projected_vertices[clipped_vertex_1].world_space_position.x * iu;
projected_vertices[new_clipped_vertex_1].world_space_position.y = projected_vertices[unclipped_vertex].world_space_position.y*(1-iu) + projected_vertices[clipped_vertex_1].world_space_position.y * iu;
projected_vertices[new_clipped_vertex_1].world_space_position.z = projected_vertices[unclipped_vertex].world_space_position.z*(1-iu) + projected_vertices[clipped_vertex_1].world_space_position.z * iu;
projected_vertices[new_clipped_vertex_2].world_space_position.x = projected_vertices[unclipped_vertex].world_space_position.x*(1-iv) + projected_vertices[clipped_vertex_2].world_space_position.x * iv;
projected_vertices[new_clipped_vertex_2].world_space_position.y = projected_vertices[unclipped_vertex].world_space_position.y*(1-iv) + projected_vertices[clipped_vertex_2].world_space_position.y * iv;
projected_vertices[new_clipped_vertex_2].world_space_position.z = projected_vertices[unclipped_vertex].world_space_position.z*(1-iv) + projected_vertices[clipped_vertex_2].world_space_position.z * iv;
#endif
interpolate_2_vertex_attributes_into(
&projected_vertices[unclipped_vertex].attributes,
&projected_vertices[clipped_vertex_1].attributes,
iu,
&projected_vertices[new_clipped_vertex_1].attributes
);
interpolate_2_vertex_attributes_into(
&projected_vertices[unclipped_vertex].attributes,
&projected_vertices[clipped_vertex_2].attributes,
iv,
&projected_vertices[new_clipped_vertex_2].attributes
);
// Now the triangle must be built with the vertices in the right order so back face culling behavior will not change
clipped_tris[num_clipped_tris].vertices[unclipped_vertex_index] = unclipped_vertex;
clipped_tris[num_clipped_tris].vertices[clipped_vertex_indices[0]] = new_clipped_vertex_1;
clipped_tris[num_clipped_tris++].vertices[clipped_vertex_indices[1]] = new_clipped_vertex_2;
// clipped_tris[num_clipped_tris++].material_index = mesh->tris[t].material_index;
} else if (num_clipped_vertex_indices == 1) {
char unclipped_vertex_1_index = 0;
if (unclipped_vertex_1_index == clipped_vertex_indices[0]) unclipped_vertex_1_index += 1;
char unclipped_vertex_2_index = unclipped_vertex_1_index + 1;
if (unclipped_vertex_2_index == clipped_vertex_indices[0]) unclipped_vertex_2_index += 1;
int unclipped_vertex_1 = mesh->tris[t].vertices[unclipped_vertex_1_index];
int unclipped_vertex_2 = mesh->tris[t].vertices[unclipped_vertex_2_index];
int clipped_vertex = mesh->tris[t].vertices[clipped_vertex_indices[0]];
// add a new vertex and triangle
int new_clipped_vertex_1 = num_projected_vertices++;
int new_clipped_vertex_2 = num_projected_vertices++;
// memset(&projected_vertices[num_projected_vertices++], 0, sizeof(ProjectedVertex));
float cx = projected_vertices[clipped_vertex].view_space_position.x;
float cy = projected_vertices[clipped_vertex].view_space_position.y;
float cz = projected_vertices[clipped_vertex].view_space_position.z;
float xd1 = projected_vertices[unclipped_vertex_1].view_space_position.x - cx;
float xd2 = projected_vertices[unclipped_vertex_2].view_space_position.x - cx;
float yd1 = projected_vertices[unclipped_vertex_1].view_space_position.y - cy;
float yd2 = projected_vertices[unclipped_vertex_2].view_space_position.y - cy;
float zd1 = projected_vertices[unclipped_vertex_1].view_space_position.z - cz;
float zd2 = projected_vertices[unclipped_vertex_2].view_space_position.z - cz;
float zds = clip_start - cz;
float u = zds / zd1;
float v = zds / zd2;
projected_vertices[new_clipped_vertex_1].view_space_position.x = cx + xd1 * u;
projected_vertices[new_clipped_vertex_2].view_space_position.x = cx + xd2 * v;
projected_vertices[new_clipped_vertex_1].view_space_position.y = cy + yd1 * u;
projected_vertices[new_clipped_vertex_2].view_space_position.y = cy + yd2 * v;
projected_vertices[new_clipped_vertex_1].view_space_position.z = clip_start;
projected_vertices[new_clipped_vertex_2].view_space_position.z = clip_start;
#ifdef SHADOWS_ENABLED
projected_vertices[new_clipped_vertex_1].world_space_position.x = projected_vertices[clipped_vertex].world_space_position.x*(1-u) + projected_vertices[unclipped_vertex_1].world_space_position.x * u;
projected_vertices[new_clipped_vertex_1].world_space_position.y = projected_vertices[clipped_vertex].world_space_position.y*(1-u) + projected_vertices[unclipped_vertex_1].world_space_position.y * u;
projected_vertices[new_clipped_vertex_1].world_space_position.z = projected_vertices[clipped_vertex].world_space_position.z*(1-u) + projected_vertices[unclipped_vertex_1].world_space_position.z * u;
projected_vertices[new_clipped_vertex_2].world_space_position.x = projected_vertices[clipped_vertex].world_space_position.x*(1-v) + projected_vertices[unclipped_vertex_2].world_space_position.x * v;
projected_vertices[new_clipped_vertex_2].world_space_position.y = projected_vertices[clipped_vertex].world_space_position.y*(1-v) + projected_vertices[unclipped_vertex_2].world_space_position.y * v;
projected_vertices[new_clipped_vertex_2].world_space_position.z = projected_vertices[clipped_vertex].world_space_position.z*(1-v) + projected_vertices[unclipped_vertex_2].world_space_position.z * v;
#endif
// add two new triangles, again using original vertex indices to preserve back face culling behavior
clipped_tris[num_clipped_tris].vertices[unclipped_vertex_1_index] = unclipped_vertex_1;
clipped_tris[num_clipped_tris].vertices[unclipped_vertex_2_index] = unclipped_vertex_2;
clipped_tris[num_clipped_tris++].vertices[clipped_vertex_indices[0]] = new_clipped_vertex_1;
// clipped_tris[num_clipped_tris++].material_index = mesh->tris[t].material_index;
clipped_tris[num_clipped_tris].vertices[unclipped_vertex_1_index] = new_clipped_vertex_2;
clipped_tris[num_clipped_tris].vertices[unclipped_vertex_2_index] = new_clipped_vertex_1;
clipped_tris[num_clipped_tris++].vertices[clipped_vertex_indices[0]] = unclipped_vertex_2;
// clipped_tris[num_clipped_tris++].material_index = mesh->tris[t].material_index;
interpolate_2_vertex_attributes_into(
&projected_vertices[clipped_vertex].attributes,
&projected_vertices[unclipped_vertex_1].attributes,
u,
&projected_vertices[new_clipped_vertex_1].attributes
);
interpolate_2_vertex_attributes_into(
&projected_vertices[clipped_vertex].attributes,
&projected_vertices[unclipped_vertex_2].attributes,
v,
&projected_vertices[new_clipped_vertex_2].attributes
);
} else {
clipped_tris[num_clipped_tris++] = mesh->tris[t];
}
}
// projection
for (int i=0; i<num_projected_vertices; i++) {
float iz;
if (renderer_flags & RENDERER_FLAG_ORTHOGRAPHIC) iz = 1;
else iz = 1/projected_vertices[i].view_space_position.z;
projected_vertices[i].projected_position.x = projected_vertices[i].view_space_position.x*iz*zoom_factor;
projected_vertices[i].projected_position.y = projected_vertices[i].view_space_position.y*iz*zoom_factor;
}
// rasterization
// struct timespec timedata;
// clock_gettime(CLOCK_REALTIME, &timedata);
// int t1 = timedata.tv_sec*1000000 + timedata.tv_nsec/1000;
int textured = 0;
unsigned int *texture_pixels;
int texture_width;
int texture_height;
RGB24 solid_color;
int texture_id = materials[mesh->material_id].texture_id;
if (texture_id == -1) {
solid_color = materials[mesh->material_id].color;
} else {
SDL_Surface *texture = textures[texture_id];
texture_pixels = ((unsigned int*)texture->pixels);
texture_width = texture->w;
texture_height = texture->h;
textured = 1;
}
float i_zoom_factor = 1.0 / zoom_factor;
for (int t=0; t<num_clipped_tris; t++) {
/* TODO - load material properties + texture pointer(s) here */
/* ^^^ actually don't do that ^^^ */
/* Order the vertices - top, split (where one side changes slope) and bottom */
int top_vertex, left_vertex, right_vertex;
float p0yi = projected_vertices[clipped_tris[t].vertices[0]].projected_position.y;
float p1yi = projected_vertices[clipped_tris[t].vertices[1]].projected_position.y;
float p2yi = projected_vertices[clipped_tris[t].vertices[2]].projected_position.y;
float top_y, split_y, bottom_y;
if (p1yi < p0yi) {
top_y = p1yi;
split_y = p0yi;
top_vertex = clipped_tris[t].vertices[1];
left_vertex = clipped_tris[t].vertices[0];
right_vertex = clipped_tris[t].vertices[2];
} else {
top_y = p0yi;
split_y = p1yi;
top_vertex = clipped_tris[t].vertices[0];
left_vertex = clipped_tris[t].vertices[2];
right_vertex = clipped_tris[t].vertices[1];
}
if (p2yi < top_y) {
bottom_y = split_y;
split_y = top_y;
top_y = p2yi;
top_vertex = clipped_tris[t].vertices[2];
left_vertex = clipped_tris[t].vertices[1];
right_vertex = clipped_tris[t].vertices[0];
} else if (p2yi < split_y) {
bottom_y = split_y;
split_y = p2yi;
} else {
bottom_y = p2yi;
}
/* Y bound checking */
int upper_bound = ceil(top_y);
int lower_bound = ceil(bottom_y);
if (upper_bound >= target_height/2) continue;
if (lower_bound < -target_height/2) continue;
/* Unpacking */
// char left_vertex = top_vertex - 1;
// if (left_vertex == -1) left_vertex = 2;
// char right_vertex = (top_vertex + 1) % 3;
float tpx, tpy, lpx, lpy, rpx, rpy;
tpx = projected_vertices[top_vertex].projected_position.x;
tpy = projected_vertices[top_vertex].projected_position.y;
lpx = projected_vertices[left_vertex].projected_position.x;
lpy = projected_vertices[left_vertex].projected_position.y;
rpx = projected_vertices[right_vertex].projected_position.x;
rpy = projected_vertices[right_vertex].projected_position.y;
float x1 = projected_vertices[top_vertex].view_space_position.x;
float x2 = projected_vertices[left_vertex].view_space_position.x;
float x3 = projected_vertices[right_vertex].view_space_position.x;
float y1 = projected_vertices[top_vertex].view_space_position.y;
float y2 = projected_vertices[left_vertex].view_space_position.y;
float y3 = projected_vertices[right_vertex].view_space_position.y;
float z1 = projected_vertices[top_vertex].view_space_position.z;
float z2 = projected_vertices[left_vertex].view_space_position.z;
float z3 = projected_vertices[right_vertex].view_space_position.z;
if ((renderer_flags & RENDERER_FLAG_ORTHOGRAPHIC) == 0) {
// we divide by zoom factor so the simple relationship x/z = screen x, etc. holds
// this makes the barycentric coordinate solving per pixel easier, thus faster
z1 *= i_zoom_factor;
z2 *= i_zoom_factor;
z3 *= i_zoom_factor;
}
#ifdef SHADOWS_ENABLED
float wsx1 = projected_vertices[top_vertex].world_space_position.x;
float wsx2 = projected_vertices[left_vertex].world_space_position.x;
float wsx3 = projected_vertices[right_vertex].world_space_position.x;
float wsy1 = projected_vertices[top_vertex].world_space_position.y;
float wsy2 = projected_vertices[left_vertex].world_space_position.y;
float wsy3 = projected_vertices[right_vertex].world_space_position.y;
float wsz1 = projected_vertices[top_vertex].world_space_position.z;
float wsz2 = projected_vertices[left_vertex].world_space_position.z;
float wsz3 = projected_vertices[right_vertex].world_space_position.z;
#endif
float dx_left, dx_right, dy_left, dy_right;
dx_left = lpx-tpx;
dx_right = rpx-tpx;
dy_left = lpy-tpy;
dy_right = rpy-tpy;
/* Back face culling */
if (dx_left*dy_right > dx_right*dy_left) continue;
/* Scanline rendering */
int x_left, x_right;
if (upper_bound < -target_height/2) upper_bound = -target_height/2;
if (lower_bound > target_height/2) lower_bound = target_height/2;
for (int y=upper_bound; y<lower_bound; y++) {
float u_left, u_right, v_left, v_right;
float x_left_frac, x_right_frac;
if (y < split_y || lpy > rpy) {
x_left_frac = tpx+dx_left*((float)(y-top_y)/(float)dy_left);
if (renderer_flags & RENDERER_FLAG_ORTHOGRAPHIC) u_left = (y - tpy)/(lpy - tpy);
else u_left = (y*z1 - y1)/(y2 - y1 + y * (z1 - z2));
v_left = 0;
} else {
x_left_frac = lpx+(rpx-lpx)*((float)(y-split_y)/(float)(rpy-lpy));
if (renderer_flags & RENDERER_FLAG_ORTHOGRAPHIC) v_left = (y - lpy)/(rpy - lpy);
else v_left = (y*z2 - y2)/(y3 - y2 + y * (z2 - z3));
u_left = 1-v_left;
}
if (y < split_y || rpy > lpy) {
x_right_frac = tpx+dx_right*((float)(y-top_y)/(float)dy_right);
if (renderer_flags & RENDERER_FLAG_ORTHOGRAPHIC) v_right = (y - tpy)/(rpy - tpy);
else v_right = (y*z1 - y1)/(y3 - y1 + y * (z1 - z3));
u_right = 0;
} else {
x_right_frac = rpx+(lpx-rpx)*((float)(y-split_y)/(float)(lpy-rpy));
if (renderer_flags & RENDERER_FLAG_ORTHOGRAPHIC) u_right = (y - rpy)/(lpy - rpy);
else u_right = (y*z3 - y3)/(y2 - y3 + y * (z3 - z2));
v_right = 1-u_right;
}
x_left = ceil(x_left_frac);
x_right = ceil(x_right_frac);
// bounds checking
int left_bound = x_left;
int right_bound = x_right;
if (left_bound < -target_width/2) left_bound = -target_width/2;
if (left_bound >= target_width/2) continue;
if (right_bound > target_width/2) right_bound = target_width/2;
if (right_bound <= -target_width/2) continue;
if (left_bound >= right_bound) continue;
float u_diff = u_right - u_left;
float v_diff = v_right - v_left;
float z_left = z1 + (z2-z1)*u_left + (z3-z1)*v_left;
float z_diff = (z2-z1)*u_diff + (z3-z1)*v_diff;
float view_space_x_left = x1 + (x2-x1)*u_left + (x3-x1)*v_left;
float view_space_x_diff = (x2-x1)*u_diff + (x3-x1)*v_diff;
VertexAttributes interpolated_attributes_left;
VertexAttributes interpolated_attributes_right;
interpolate_3_vertex_attributes_into(
&projected_vertices[top_vertex].attributes,
&projected_vertices[left_vertex].attributes,
&projected_vertices[right_vertex].attributes,
u_left, v_left,
&interpolated_attributes_left);
interpolate_3_vertex_attributes_into(
&projected_vertices[top_vertex].attributes,
&projected_vertices[left_vertex].attributes,
&projected_vertices[right_vertex].attributes,
u_right, v_right,
&interpolated_attributes_right);
// draw scanline
for (int x=left_bound; x<right_bound; x++) {
float t;
if (renderer_flags & RENDERER_FLAG_ORTHOGRAPHIC) t = (x - x_left_frac)/(x_right_frac - x_left_frac);
else t = (x*z_left - view_space_x_left)/(view_space_x_diff - x*z_diff);
// Fun fact, there was actually a really pernicious bug in here where the sign of z_diff was wrong, leading to some depth testing errors
float z = z_left + z_diff * t;
int depth_buffer_offset = x+target_width/2 + target_width*(y+target_height/2);
if (z >= depth_buffer[depth_buffer_offset]) continue;
depth_buffer[depth_buffer_offset] = z;
if (renderer_flags & RENDERER_FLAG_SHADOW) {
continue;
// TODO: check texture for threshold shadow mode surfaces
}
float u = u_left + u_diff * t;
float v = v_left + v_diff * t;
VertexAttributes interpolated_attributes;
// interpolate_3_vertex_attributes_into(
// &projected_vertices[top_vertex].attributes,
// &projected_vertices[left_vertex].attributes,
// &projected_vertices[right_vertex].attributes,
// u, v,
// &interpolated_attributes);
interpolate_2_vertex_attributes_into(
&interpolated_attributes_left,
&interpolated_attributes_right,
t,
&interpolated_attributes);
#ifdef SHADOWS_ENABLED
int in_shadow = 0;
if (renderer_flags & RENDERER_FLAG_RECEIVE_GLOBAL_SHADOW) {
Vector3 world_space_position = {
wsx1*(1-u-v) + wsx2*u + wsx3*v,
wsy1*(1-u-v) + wsy2*u + wsy3*v,
wsz1*(1-u-v) + wsz2*u + wsz3*v
}; // TODO: interpolate this the same way as z, i.e. by calculating world space positions for points on the edges of the tri and using t here
Vector3 sun_view_space_position = matrix3x3_apply(sun_matrix, vector3_sub(world_space_position, sun_position));
int shadowmap_x = GLOBAL_SHADOWMAP_SIZE/2 + ((float)sun_view_space_position.x*(float)GLOBAL_SHADOWMAP_SIZE);
int shadowmap_y, shadowmap_offset;
if (shadowmap_x < 0 || shadowmap_x >= GLOBAL_SHADOWMAP_SIZE) {
goto shadow_calculation_done;
}
shadowmap_y = GLOBAL_SHADOWMAP_SIZE/2 - ((float)sun_view_space_position.y*(float)GLOBAL_SHADOWMAP_SIZE);
if (shadowmap_y < 0 || shadowmap_y >= GLOBAL_SHADOWMAP_SIZE) {
goto shadow_calculation_done;
}
// printf("%f vs %f\n", global_shadowmap_depth_buffer[shadowmap_y][shadowmap_x], sun_view_space_position.z - 0.01);
float dot = -vector3_dot(vector3_normalize(sun_vector), interpolated_attributes.world_space_normal); // TODO: get rid of the normalize call
if (dot < 0.001) dot = 0.001;
float bias = SHADOW_BIAS_MIN * dot + SHADOW_BIAS_MAX * (1-dot);
if (global_shadowmap_depth_buffer[shadowmap_y][shadowmap_x] < sun_view_space_position.z - bias) in_shadow = 1;
shadow_calculation_done:
}
#endif
/* unsigned char r = (unsigned char)round(projected_vertices[top_vertex].attributes.color.r*(1-u-v)+projected_vertices[left_vertex].attributes.color.r*(u)+projected_vertices[right_vertex].attributes.color.r*(v));
unsigned char g = (unsigned char)round(projected_vertices[top_vertex].attributes.color.g*(1-u-v)+projected_vertices[left_vertex].attributes.color.g*(u)+projected_vertices[right_vertex].attributes.color.g*(v));
unsigned char b = (unsigned char)round(projected_vertices[top_vertex].attributes.color.b*(1-u-v)+projected_vertices[left_vertex].attributes.color.b*(u)+projected_vertices[right_vertex].attributes.color.b*(v)); */
// Shader code goes here...
int tr, tg, tb; // see the "Software Optimization Guide for the AMD Family 15h Processors", apparently 32-bit ints are faster
// and experimental results verify this on an AMD processor at least
if (textured == 0) {
tr = solid_color.r;
tg = solid_color.g;
tb = solid_color.b;
} else if (texture_interpolation_mode == INTERPOLATION_NEAREST) {
float tcx = texture_width * interpolated_attributes.texture_coordinate.x;
float tcy = texture_height * (1 - interpolated_attributes.texture_coordinate.y); // for images +y = down, for texture coords +y = up
// if texture width/heights are always a power of 2, this is much faster than normal % modulo:
int int_tcx = ((int)tcx) & (texture_width-1);
int int_tcy = ((int)tcy) & (texture_height-1);
int texture_offset = int_tcy*texture_width + int_tcx;
unsigned int texture_color_packed = texture_pixels[texture_offset];
tb = (texture_color_packed & 0xFF0000) >> 16;
tg = (texture_color_packed & 0x00FF00) >> 8;
tr = (texture_color_packed & 0x0000FF);
} else if (texture_interpolation_mode == INTERPOLATION_BILINEAR) {
float tcx = texture_width * interpolated_attributes.texture_coordinate.x - 0.5;
float tcy = texture_height * (1 - interpolated_attributes.texture_coordinate.y) - 0.5; // for images +y = down, for texture coords +y = up
// re: the -0.5 bias: in NN interpolation mode a texture coordinate with fractional parts 0.5 actually corresponds to the center of the pixel,
// so in bilinear interpolation that texture coordinate should correspond to the unmixed single pixel.
// if texture width/heights are always a power of 2, this is much faster than normal % modulo:
int int_tcx = ((int)tcx) & (texture_width - 1);
int int_tcy = ((int)tcy) & (texture_height - 1);
int int_tcx_p1 = (int_tcx + 1)&(texture_width - 1);
int int_tcy_p1 = (int_tcy + 1)&(texture_height - 1);
int int_tcy_offset = int_tcy*texture_width;
int int_tcy_p1_offset = int_tcy_p1*texture_width;
int texture_offset = int_tcy_offset + int_tcx;
unsigned int texture_color_packed = texture_pixels[texture_offset];
float tb_tl = (float)((texture_color_packed & 0xFF0000) >> 16);
float tg_tl = (float)((texture_color_packed & 0x00FF00) >> 8);
float tr_tl = (float)((texture_color_packed & 0x0000FF));
texture_offset = int_tcy_offset + int_tcx_p1;
texture_color_packed = texture_pixels[texture_offset];
float tb_tr = (float)((texture_color_packed & 0xFF0000) >> 16);
float tg_tr = (float)((texture_color_packed & 0x00FF00) >> 8);
float tr_tr = (float)((texture_color_packed & 0x0000FF));
texture_offset = int_tcy_p1_offset + int_tcx;
texture_color_packed = texture_pixels[texture_offset];
float tb_bl = (float)((texture_color_packed & 0xFF0000) >> 16);
float tg_bl = (float)((texture_color_packed & 0x00FF00) >> 8);
float tr_bl = (float)((texture_color_packed & 0x0000FF));
texture_offset = int_tcy_p1_offset + int_tcx_p1;
texture_color_packed = texture_pixels[texture_offset];
float tb_br = (float)((texture_color_packed & 0xFF0000) >> 16);
float tg_br = (float)((texture_color_packed & 0x00FF00) >> 8);
float tr_br = (float)((texture_color_packed & 0x0000FF));
float frac_x = tcx - (int)tcx; // these casts should be floor() calls but... performance
float frac_y = tcy - (int)tcy;
float i_frac_x = 1 - frac_x;
float i_frac_y = 1 - frac_y;
float ifxify = i_frac_x*i_frac_y;
float fxfy = frac_x*frac_y;
float ifxfy = i_frac_x*frac_y;
float fxify = frac_x*i_frac_y;
tr = (int)(((tr_tl*ifxify)+(tr_tr*fxify)+(tr_bl*ifxfy)+(tr_br*fxfy)));
tg = (int)(((tg_tl*ifxify)+(tg_tr*fxify)+(tg_bl*ifxfy)+(tg_br*fxfy)));
tb = (int)(((tb_tl*ifxify)+(tb_tr*fxify)+(tb_bl*ifxfy)+(tb_br*fxfy)));
}
#ifdef SHADOWS_ENABLED
if (in_shadow) {
interpolated_attributes.color.r = interpolated_attributes.color.g = interpolated_attributes.color.b = (unsigned char)(255.0*materials[mesh->material_id].ambient_amount);
interpolated_attributes.specular_color.r = interpolated_attributes.specular_color.g = interpolated_attributes.specular_color.b = 0;
}
#endif
int r = ((int)(((int)interpolated_attributes.color.r)*tr)>>8) + (int)interpolated_attributes.specular_color.r;
int g = ((int)(((int)interpolated_attributes.color.g)*tg)>>8) + (int)interpolated_attributes.specular_color.g;
int b = ((int)(((int)interpolated_attributes.color.b)*tb)>>8) + (int)interpolated_attributes.specular_color.b;
// float fog = 1.0 - ((z * zoom_factor) - 100.0) / 1000.0;
// if (fog < 0.0) fog = 0.0;
// if (fog > 1.0) fog = 1.0;
// unsigned char fog_i = (unsigned char)(255.0*fog);
// r = (r * fog_i) >> 8;
// g = (g * fog_i) >> 8;
// b = (b * fog_i) >> 8;
if (r > 255) r = 255;
if (g > 255) g = 255;
if (b > 255) b = 255;
// end of shader code
int offset = (target_width*(y+target_height/2)+(x+target_width/2));
((unsigned int*)target->pixels)[offset] = r | (g << 8) | (b << 16);
}
}
}
// clock_gettime(CLOCK_REALTIME, &timedata);
// int t2 = timedata.tv_sec*1000000 + timedata.tv_nsec/1000;
// printf("Took %d microseconds.\n", t2-t1);
}
void draw_meshgroup(MeshGroup *group, Matrix3x3 *model_matrix, Vector3 model_position, Matrix3x3 *projection_matrix, Vector3 camera_position, float fov, unsigned int renderer_flags, int target_width, int target_height, SDL_Surface *target, float *depth_buffer) {
for (int i=0; i<group->num_meshes; i++) draw_mesh(group->meshes[i], model_matrix, model_position, projection_matrix, camera_position, fov, renderer_flags, target_width, target_height, target, depth_buffer);
}
void render_reset() { // prepare for a new render pass - clears the depth buffer, etc.
// if (virtual_screen != NULL) SDL_FreeSurface(virtual_screen);
if (virtual_screen == NULL) virtual_screen = SDL_CreateRGBSurface(0, WIDTH, HEIGHT, 32, 0x000000FF, 0x0000FF00, 0x00FF0000, 0);
memset(virtual_screen->pixels, 0, 4*WIDTH*HEIGHT);
// unsigned int c = 0x00FF0000;
// int i = 0;
// for (int y=0; y<HEIGHT; y++) {
// for (int x=0; x<WIDTH; x++, i++) {
// ((unsigned int*)virtual_screen->pixels)[i] = c;
// }
// if (y & 1) c += 0x00000101;
// }
// 0x7f just because it has to be some really large positive number and the first bit of every four bytes is a sign bit
memset(depth_buffer, 0x7f, sizeof(float)*WIDTH*HEIGHT);
#ifdef SHADOWS_ENABLED
memset(global_shadowmap_depth_buffer, 0x7f, sizeof(float)*GLOBAL_SHADOWMAP_SIZE*GLOBAL_SHADOWMAP_SIZE);
#endif
}
static SDL_Window *window;
static SDL_Renderer *renderer;
static MeshGroup *test_mesh, *test_mesh_2;
static SDL_PixelFormat *rgba32;
#define SCALE 1
#define NUM_FILES 13
static char *files[NUM_FILES] = {
"hills.obj",
"bigsphere.obj",
"crate.obj",
"finaldonut.obj",
"hovercycle.obj",
"cross_thing.obj",
"hallway_practice.obj",
"asteroidship.obj",
"knob.obj",
"asteroidsaucer.obj",
"asset.obj",
"untitled.obj",
"elite_ship_2.obj"
};
static int file_index = 11;
void setup() {
window = SDL_CreateWindow("3D Rendering Take 2", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, WIDTH*SCALE, HEIGHT*SCALE, 0);
renderer = SDL_CreateRenderer(window, -1, 0);
rgba32 = SDL_AllocFormat(SDL_PIXELFORMAT_RGBA32);
load_wavefront_init();
num_materials = 0;
num_textures = 0;
test_mesh = load_wavefront_obj(files[file_index]);
// MeshGroup *engines = load_wavefront_obj("engines.obj");
// test_mesh = (MeshGroup*)malloc(sizeof(MeshGroup));
// test_mesh->num_meshes = 2;
// test_mesh->meshes = (Mesh**)calloc(2, sizeof(Mesh*));
// test_mesh->meshes[0] = body->meshes[0];
// test_mesh->meshes[1] = engines->meshes[0];
if (test_mesh == NULL) {
printf("Something went wrong during mesh loading\n");
exit(0);
} else {
printf("Mesh loaded successfully.\n");
}
printf("%d\n%d, %d, %d\n%d, %d, %d\n%f\n%f\n",
materials[0].texture_id,
materials[0].color.r,
materials[0].color.g,
materials[0].color.b,
materials[0].specular_color.r,
materials[0].specular_color.g,
materials[0].specular_color.b,
materials[0].specular_exponent,
materials[0].ambient_amount
);
// SDL_Surface *original_texture = IMG_Load("big_texture_256.tga"); // IMG_Load("test_texture.png");
// test_texture = SDL_ConvertSurface(original_texture, rgba32, 0);
// SDL_FreeSurface(original_texture);
virtual_screen = NULL;
texture_interpolation_mode = INTERPOLATION_NEAREST;
// textures[num_textures++] = test_texture;
// Material material = {
// 0,
// {128, 128, 128},
// {224, 224, 224},
// 32,
// 0.1
// };
// materials[num_materials++] = material;
// test_mesh->material_id = 0;
// test_mesh = (Mesh*) malloc(sizeof(Mesh));
// test_mesh->num_vertices = 4;
// test_mesh->num_tris = 4;
// test_mesh->vertices = (Vertex*) malloc(sizeof(Vertex)*4);
// test_mesh->tris = (Tri*) malloc(sizeof(Tri)*4);
// // test_mesh->vertices[0].position.x = -75;
// // test_mesh->vertices[0].position.y = -75;
// // test_mesh->vertices[0].position.z = 1;
// // test_mesh->vertices[1].position.x = 75;
// // test_mesh->vertices[1].position.y = -75;
// // test_mesh->vertices[1].position.z = 1;
// // test_mesh->vertices[2].position.x = -75;
// // test_mesh->vertices[2].position.y = 75;
// // test_mesh->vertices[2].position.z = 1;
// // test_mesh->vertices[3].position.x = 75;
// // test_mesh->vertices[3].position.y = 75;
// // test_mesh->vertices[3].position.z = 1;
// test_mesh->vertices[0].position.x = -75;
// test_mesh->vertices[0].position.z = -75;
// test_mesh->vertices[0].position.y = 1;
// test_mesh->vertices[1].position.x = 75;
// test_mesh->vertices[1].position.z = -75;
// test_mesh->vertices[1].position.y = 1;
// test_mesh->vertices[2].position.x = -75;
// test_mesh->vertices[2].position.z = 75;
// test_mesh->vertices[2].position.y = 1;
// test_mesh->vertices[3].position.x = 75;
// test_mesh->vertices[3].position.z = 75;
// test_mesh->vertices[3].position.y = 1;
// test_mesh->tris[0].vertices[0] = 0;
// test_mesh->tris[0].vertices[1] = 1;
// test_mesh->tris[0].vertices[2] = 2;
// test_mesh->tris[1].vertices[0] = 3;
// test_mesh->tris[1].vertices[1] = 2;
// test_mesh->tris[1].vertices[2] = 1;
// test_mesh->tris[2].vertices[0] = 2;
// test_mesh->tris[2].vertices[1] = 1;
// test_mesh->tris[2].vertices[2] = 0;
// test_mesh->tris[3].vertices[0] = 1;
// test_mesh->tris[3].vertices[1] = 2;
// test_mesh->tris[3].vertices[2] = 3;
// test_mesh->vertices[0].attributes.color.r = 64;
// test_mesh->vertices[0].attributes.color.g = 64;
// test_mesh->vertices[0].attributes.color.b = 64;
// test_mesh->vertices[1].attributes.color.r = 128;
// test_mesh->vertices[1].attributes.color.g = 128;
// test_mesh->vertices[1].attributes.color.b = 128;
// test_mesh->vertices[2].attributes.color.r = 192;
// test_mesh->vertices[2].attributes.color.g = 192;
// test_mesh->vertices[2].attributes.color.b = 192;
// test_mesh->vertices[3].attributes.color.r = 255;
// test_mesh->vertices[3].attributes.color.g = 255;
// test_mesh->vertices[3].attributes.color.b = 255;
// test_mesh->vertices[0].attributes.texture_coordinate.x = 0;
// test_mesh->vertices[0].attributes.texture_coordinate.y = 0;
// test_mesh->vertices[1].attributes.texture_coordinate.x = 1;
// test_mesh->vertices[1].attributes.texture_coordinate.y = 0;
// test_mesh->vertices[2].attributes.texture_coordinate.x = 0;
// test_mesh->vertices[2].attributes.texture_coordinate.y = 1;
// test_mesh->vertices[3].attributes.texture_coordinate.x = 1;
// test_mesh->vertices[3].attributes.texture_coordinate.y = 1;
// projection_matrix.c11 = 1;
// projection_matrix.c21 = 0;
// projection_matrix.c31 = 0;
// projection_matrix.c12 = 0;
// projection_matrix.c22 = 1;
// projection_matrix.c32 = 0;
// projection_matrix.c13 = 0;
// projection_matrix.c23 = 0;
// projection_matrix.c33 = 1;
// camera_point.x = 0;
// camera_point.y = 0;
// camera_point.z = 0;
// test_mesh = mesh;
}
void cleanup() {
SDL_FreeSurface(virtual_screen);
destroy_meshgroup(test_mesh);
}
#include <stdlib.h>
void mainloop() {
struct timespec timedata;
clock_gettime(CLOCK_REALTIME, &timedata);
int ts = timedata.tv_sec*1000000 + timedata.tv_nsec/1000;
srand(ts);
int us_ringbuffer[16];
memset(&us_ringbuffer[0], 0, sizeof(us_ringbuffer));
int us_ringbuffer_pointer = 0;
float elevation = 30;
Vector3 camera_forward_vector = {0, -sin(elevation*M_PI/180.0), cos(elevation*M_PI/180.0)};
Vector3 camera_right_vector = {1, 0, 0};
Vector3 camera_up_vector = {0, cos(elevation*M_PI/180.0), sin(elevation*M_PI/180.0)};
float rotation_speed = 1;
Matrix3x3 camera_right_rotation_matrix = create_rotation_matrix_y(rotation_speed);
Matrix3x3 camera_left_rotation_matrix = create_rotation_matrix_y(-rotation_speed);
Matrix3x3 right_left_matrix = {1, 0, 0, 0, 1, 0, 0, 0, 1};
Matrix3x3 camera_up_rotation_matrix = create_rotation_matrix_x(rotation_speed);
Matrix3x3 camera_down_rotation_matrix = create_rotation_matrix_x(-rotation_speed);
Vector3 camera_forward_effective_vector = camera_forward_vector;
Vector3 camera_right_effective_vector = camera_right_vector;
Vector3 camera_up_effective_vector = camera_up_vector;
int running = 1;
Matrix3x3 model_matrix = {4, 0, 0, 0, 4, 0, 0, 0, 4};
float degrees = -0.05;
Matrix3x3 rotation_matrix_1 = create_rotation_matrix_z(degrees);
Matrix3x3 rotation_matrix_2 = create_rotation_matrix_y(degrees);
Matrix3x3 scale_matrix = {1, 0, 0, 0, 1, 0, 0, 0, 1};
// rotation_matrix = matrix3x3_multiply(rotation_matrix, scale_matrix);
Matrix3x3 rotation_matrix = matrix3x3_multiply(rotation_matrix_1, rotation_matrix_2);
rotation_matrix = matrix3x3_multiply(rotation_matrix, scale_matrix);
camera_point.z = -30000;
Vector3 model_position = {0, 0, 0}; // {0, 0, 100};
char right_held = 0;
char left_held = 0;
char up_held = 0;
char down_held = 0;
char q_held = 0;
char a_held = 0;
float camera_distance = 500;
unsigned int renderer_flags = 0;
while (running) {
SDL_Event e;
while (SDL_PollEvent(&e) != 0) {
switch (e.type) {
case SDL_QUIT:
running = 0;
break;
case SDL_KEYDOWN:
switch (e.key.keysym.sym) {
case SDLK_RIGHT:
right_held = 1;
break;
case SDLK_LEFT:
left_held = 1;
break;
case SDLK_UP:
up_held = 1;
break;
case SDLK_DOWN:
down_held = 1;
break;
case SDLK_q:
q_held = 1;
break;
case SDLK_a:
a_held = 1;
break;
case SDLK_i:
texture_interpolation_mode = 1 - texture_interpolation_mode;
break;
case SDLK_k:
file_index = (file_index - 1);
if (file_index < 0) file_index = NUM_FILES - 1;
destroy_meshgroup(test_mesh);
test_mesh = load_wavefront_obj(files[file_index]);
break;
case SDLK_l:
file_index = (file_index + 1);
if (file_index >= NUM_FILES) file_index = 0;
destroy_meshgroup(test_mesh);
test_mesh = load_wavefront_obj(files[file_index]);
break;
case SDLK_v:
sun_vector.x -= 1;
break;
case SDLK_b:
sun_vector.x += 1;
break;
case SDLK_o:
renderer_flags ^= RENDERER_FLAG_ORTHOGRAPHIC;
break;
case SDLK_y:
fov += 5;
break;
case SDLK_h:
fov -= 5;
break;
}
break;
case SDL_KEYUP:
switch (e.key.keysym.sym) {
case SDLK_RIGHT:
right_held = 0;
break;
case SDLK_LEFT:
left_held = 0;
break;
case SDLK_UP:
up_held = 0;
break;
case SDLK_DOWN:
down_held = 0;
break;
case SDLK_q:
q_held = 0;
break;
case SDLK_a:
a_held = 0;
break;
}
break;
}
}
clock_gettime(CLOCK_REALTIME, &timedata);
int t1 = timedata.tv_sec*1000000 + timedata.tv_nsec/1000;
// camera_point.z += 96*4;
// camera_point.x = 800*sin(camera_point.z/20000);
// camera_point.y = 800*sin(camera_point.z/40000);
// model_matrix = matrix3x3_multiply(model_matrix, rotation_matrix);
if (q_held) camera_distance -= 3;
if (a_held) camera_distance += 3;
if (right_held) {
right_left_matrix = matrix3x3_multiply(right_left_matrix, camera_right_rotation_matrix);
camera_forward_effective_vector = matrix3x3_apply(right_left_matrix, camera_forward_vector);
camera_right_effective_vector = matrix3x3_apply(right_left_matrix, camera_right_vector);
camera_up_effective_vector = matrix3x3_apply(right_left_matrix, camera_up_vector);
}
if (left_held) {
right_left_matrix = matrix3x3_multiply(right_left_matrix, camera_left_rotation_matrix);
camera_forward_effective_vector = matrix3x3_apply(right_left_matrix, camera_forward_vector);
camera_right_effective_vector = matrix3x3_apply(right_left_matrix, camera_right_vector);
camera_up_effective_vector = matrix3x3_apply(right_left_matrix, camera_up_vector);
}
if (up_held) {
camera_forward_vector = matrix3x3_apply(camera_up_rotation_matrix, camera_forward_vector);
camera_right_vector = matrix3x3_apply(camera_up_rotation_matrix, camera_right_vector);
camera_up_vector = matrix3x3_apply(camera_up_rotation_matrix, camera_up_vector);
camera_forward_effective_vector = matrix3x3_apply(right_left_matrix, camera_forward_vector);
camera_right_effective_vector = matrix3x3_apply(right_left_matrix, camera_right_vector);
camera_up_effective_vector = matrix3x3_apply(right_left_matrix, camera_up_vector);
}
if (down_held) {
camera_forward_vector = matrix3x3_apply(camera_down_rotation_matrix, camera_forward_vector);
camera_right_vector = matrix3x3_apply(camera_down_rotation_matrix, camera_right_vector);
camera_up_vector = matrix3x3_apply(camera_down_rotation_matrix, camera_up_vector);
camera_forward_effective_vector = matrix3x3_apply(right_left_matrix, camera_forward_vector);
camera_right_effective_vector = matrix3x3_apply(right_left_matrix, camera_right_vector);
camera_up_effective_vector = matrix3x3_apply(right_left_matrix, camera_up_vector);
}
Vector3 vector_to_point = {-camera_distance, -camera_distance, -camera_distance};
camera_point = vector3_scale(camera_forward_effective_vector, vector_to_point);
Vector3 unit_x = {1, 0, 0};
Vector3 unit_y = {0, 1, 0};
Vector3 unit_z = {0, 0, 1};
projection_matrix.c11 = camera_right_effective_vector.x;
projection_matrix.c21 = camera_right_effective_vector.y;
projection_matrix.c31 = camera_right_effective_vector.z;
projection_matrix.c12 = camera_up_effective_vector.x;
projection_matrix.c22 = camera_up_effective_vector.y;
projection_matrix.c32 = camera_up_effective_vector.z;
projection_matrix.c13 = camera_forward_effective_vector.x;
projection_matrix.c23 = camera_forward_effective_vector.y;
projection_matrix.c33 = camera_forward_effective_vector.z;
model_matrix = matrix3x3_multiply(model_matrix, rotation_matrix_2);
render_reset();
srand(ts);
for (int i=0; i<1; i++) {
// model_position.x = ((rand()%10000)-5000)/0.1;
// model_position.y = ((rand()%10000)-5000)/0.1;
// model_position.z = ((rand()%10000)-5000)/0.1;
// model_position.z = 800+(rand()%10000)/0.01;
float scale = 5.0*((float)(rand()%20))/5.0;
scale = 10;
Matrix3x3 custom_scale_matrix = {scale, 0, 0, 0, scale, 0, 0, 0, scale};
Matrix3x3 custom_matrix = matrix3x3_multiply(model_matrix, custom_scale_matrix);
Matrix3x3 scaled_projection_matrix = projection_matrix;
if (renderer_flags & RENDERER_FLAG_ORTHOGRAPHIC) scaled_projection_matrix = matrix3x3_multiply(create_scale_matrix(.005), projection_matrix);
#ifdef SHADOWS_ENABLED
Vector3 up = {0, 1, 0};
Vector3 sun_right = vector3_normalize(vector3_cross(vector3_normalize(sun_vector), up));
Vector3 sun_up = vector3_cross(vector3_normalize(sun_vector), sun_right);
sun_matrix = matrix3x3_from_rows(sun_right, sun_up, vector3_normalize(sun_vector));
sun_matrix = matrix3x3_multiply(create_scale_matrix(.002), sun_matrix);
draw_meshgroup(test_mesh, &custom_matrix, model_position, &sun_matrix, sun_position, 0, RENDERER_FLAG_ORTHOGRAPHIC | RENDERER_FLAG_SHADOW | RENDERER_FLAG_NO_CLIP, GLOBAL_SHADOWMAP_SIZE, GLOBAL_SHADOWMAP_SIZE, NULL, &global_shadowmap_depth_buffer[0][0]);
#endif
draw_meshgroup(test_mesh, &custom_matrix, model_position, &scaled_projection_matrix, camera_point, fov, renderer_flags | RENDERER_FLAG_RECEIVE_GLOBAL_SHADOW, WIDTH, HEIGHT, virtual_screen, &depth_buffer[0][0]);
}
// for (int x=0; x<GLOBAL_SHADOWMAP_SIZE; x++) {
// for (int y=0; y<GLOBAL_SHADOWMAP_SIZE; y++) {
// float component_float = (global_shadowmap_depth_buffer[x][y])*300;
// if (component_float < 0) component_float = 0;
// if (component_float > 255) component_float = 255;
// unsigned char component = (unsigned char)component_float;
// ((unsigned int*)virtual_screen->pixels)[x+WIDTH*y] = component | (component<<8) | (component<<16);
// }
// }
clock_gettime(CLOCK_REALTIME, &timedata);
int t2 = timedata.tv_sec*1000000 + timedata.tv_nsec/1000;
// total_microseconds += t2 - t1;
// total_frames += 1;
us_ringbuffer[us_ringbuffer_pointer] = t2 - t1;
us_ringbuffer_pointer = (us_ringbuffer_pointer+1)%16;
float sum = 0;
for (int i=0; i<16; i++) sum += us_ringbuffer[i];
printf("Average %f FPS.\n", 1.6e7 / sum);//total_frames*1000000/(float)total_microseconds);
SDL_Texture *screen = SDL_CreateTextureFromSurface(renderer, virtual_screen);
SDL_Rect source = {0, 0, WIDTH, HEIGHT};
SDL_Rect dest = {0, 0, WIDTH*SCALE, HEIGHT*SCALE};
SDL_RenderCopy(renderer, screen, &source, &dest);
SDL_DestroyTexture(screen);
SDL_RenderPresent(renderer);
}
}
int main() {
setup();
mainloop();
cleanup();
}
Filename: main.c. Size: 52kb. View raw, , hex, or download this file.

This paste expires on 2026-03-30 05:04:04.743457+00:00. Pasted through web.