| /*
|
| 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();
|
| }
|