/* 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; vnum_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 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; tnum_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; imaterial_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= 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 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= 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; inum_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; ypixels)[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 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 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(); }