SWR_FN void swr_render_model(swr_render_target *target, u32 render_mode, model *model, vec3 cam_pos, mat4 model_mat, mat4 viewproj_mat, mat4 screen_mat, vec3 sun_direction, col4 sun_col, float ambient_intencity, mem_pool *pool) { tex2d target_tex = *target->texture; float *z_buffer = target->z_buffer; u8 *old_hi_ptr = pool->hi; vec4 *vertices = (vec4 *)mem_push_back(pool, model->nvertices * sizeof(*vertices)); vec3 *cam_directions = (vec3 *)mem_push_back(pool, model->nvertices * sizeof(*cam_directions)); for (u32 i = 0, e = model->nvertices; i < e; ++i) { vec3 v = mul_m4v4(model_mat, v3_to_v4(model->vertices[i], 1.0f)).xyz; cam_directions[i] = norm_v3(sub_v3(v, cam_pos)); vertices[i] = mul_m4v4(viewproj_mat, v3_to_v4(v, 1.0f)); } face **culled_faces = (face **)mem_push_back(pool, model->nface_groups * sizeof(*culled_faces)); u32 *nculled_faces = (u32 *)mem_push_back(pool, model->nface_groups * sizeof(*nculled_faces)); for (u32 face_group = 0; face_group < model->nface_groups; ++face_group) { face *src_faces = model->face_groups[face_group].faces; u32 nsrc_faces = model->face_groups[face_group].nfaces; face *faces = culled_faces[face_group] = (face *)mem_push_back(pool, nsrc_faces * sizeof(*faces)); u32 nfaces = 0; for (u32 i = 0; i < nsrc_faces; ++i) { face face = src_faces[i]; b32 inside_frustrum = true; for (u32 j = 0; j < 3; ++j) { vec4 vertex = vertices[face.v[j]]; if (vertex.x > vertex.w || vertex.x < -vertex.w || vertex.y > vertex.w || vertex.y < -vertex.w || vertex.z > vertex.w || vertex.z < -vertex.w || vertex.w == 0) { inside_frustrum = false; break; } } if (inside_frustrum) faces[nfaces++] = face; } nculled_faces[face_group] = nfaces; } // TODO: avoid computing irrelevant data (?) for (u32 i = 0; i < model->nvertices; ++i) { vec4 vertex = vertices[i]; vertex = div_v4f(vertex, vertex.w); vertex = mul_m4v4(screen_mat, vertex); vertices[i] = vertex; } for (u32 face_group = 0; face_group < model->nface_groups; ++face_group) { face *faces = culled_faces[face_group]; u32 nfaces = nculled_faces[face_group]; material *material = model->face_groups[face_group].material; for (u32 i = 0; i < nfaces; ++i) { face face = faces[i]; vec4 verts[] = {vertices[face.v[0]], vertices[face.v[1]], vertices[face.v[2]]}; u32 x1 = (u32)verts[0].x; u32 y1 = (u32)verts[0].y; u32 x2 = (u32)verts[1].x; u32 y2 = (u32)verts[1].y; u32 x3 = (u32)verts[2].x; u32 y3 = (u32)verts[2].y; if (render_mode & (SRM_SHADED | SRM_TEXTURED)) { u32 minX = minu(x1, minu(x2, x3)); u32 minY = minu(y1, minu(y2, y3)); u32 maxX = maxu(x1, maxu(x2, x3)) + 1; u32 maxY = maxu(y1, maxu(y2, y3)) + 1; vec3 norms[3]; float lum[3]; if (render_mode & SRM_SHADED) { // TODO: apply reverse transformations to normales norms[0] = model->normales[face.n[0]]; norms[1] = model->normales[face.n[1]]; norms[2] = model->normales[face.n[2]]; float diffuse[3]; for (u32 j = 0; j < 3; ++j) diffuse[j] = clamp(dot_v3(norms[j], sun_direction), 0, 1.0f); vec3 L = neg_v3(sun_direction); float specular[3] = {0}; for (u32 j = 0; j < 3; ++j) { if (diffuse[j]) { vec3 V = cam_directions[face.v[j]]; vec3 H = norm_v3(add_v3(V, L)); specular[j] = (float)pow(dot_v3(H, norms[j]), 32); } } for (u32 j = 0; j < 3; ++j) lum[j] = ambient_intencity + diffuse[j] + specular[j]; } vec2 face_uvs[3]; face_uvs[0] = model->uvs[face.uv[0]]; face_uvs[1] = model->uvs[face.uv[1]]; face_uvs[2] = model->uvs[face.uv[2]]; vec2 a = {(float)x1, (float)y1}; vec2 b = {(float)x2, (float)y2}; vec2 c = {(float)x3, (float)y3}; vec2 v0 = sub_v2(b, a); vec2 v1 = sub_v2(c, a); for (u32 x = minX; x < maxX; ++x) { for (u32 y = minY; y < maxY; ++y) { // calculate barycentric coords... vec2 p = {(float)x, (float)y}; vec2 v2 = sub_v2(p, a); float d00 = dot_v2(v0, v0); float d01 = dot_v2(v0, v1); float d11 = dot_v2(v1, v1); float d20 = dot_v2(v2, v0); float d21 = dot_v2(v2, v1); float denom = d00 * d11 - d01 * d01; float v = (d11 * d20 - d01 * d21) / denom; float w = (d00 * d21 - d01 * d20) / denom; float u = 1.0f - v - w; if (!(v >= -0.001 && w >= -0.001 && u >= -0.001)) continue; u32 z_buff_idx = y * target_tex.width + x; float z = verts[1].z * v + verts[2].z * w + verts[0].z * u; if (z_buffer[z_buff_idx] > z) { float l = 1.0f; if (render_mode & SRM_SHADED) { l = lum[1] * v + lum[2] * w + lum[0] * u; } col4 texel = {255, 255, 255, 255}; if ((render_mode & SRM_TEXTURED) && material->diffuse) { float tu = face_uvs[1].x * v + face_uvs[2].x * w + face_uvs[0].x * u; float tv = face_uvs[1].y * v + face_uvs[2].y * w + face_uvs[0].y * u; tu *= material->diffuse->width; tv *= material->diffuse->height; texel = *sample_t2d(*material->diffuse, (u32)tu, (u32)tv); } col4 fragment_col = { .e[3] = 255 }; for (u32 j = 0; j < 3; ++j) { float cl = sun_col.e[j] * l / 255.0f; fragment_col.e[j] = (u8)clamp(texel.e[j] * cl, 0, 255.0f); } *sample_t2d(target_tex, x, y) = fragment_col; z_buffer[z_buff_idx] = z; } } } } } if (render_mode & SRM_WIREFRAME) { for (u32 i = 0; i < nfaces; ++i) { const col4 model_col = {255, 255, 255, 255}; vec4 v1 = vertices[faces[i].v[0]]; vec4 v2 = vertices[faces[i].v[1]]; vec4 v3 = vertices[faces[i].v[2]]; s32 x1 = (s32)v1.x; s32 y1 = (s32)v1.y; s32 x2 = (s32)v2.x; s32 y2 = (s32)v2.y; s32 x3 = (s32)v3.x; s32 y3 = (s32)v3.y; swr_line(x1, y1, x2, y2, model_col, target_tex); swr_line(x2, y2, x3, y3, model_col, target_tex); swr_line(x3, y3, x1, y1, model_col, target_tex); } } }
int main(void) { if(!glfwInit()) { printf("Could not init GLFW"); getchar(); return -1; } glfwWindowHint(GLFW_SAMPLES, 4); //Opengl 2.1 //glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); //glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); //Opengl 3.3 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // MacOS fix glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); if(window_init(&window, 640, 480, "Test")) { fprintf( stderr, "Failed to open window.\n" ); getchar(); glfwTerminate(); }; window_set_size_callback(&window, window_size_callback); glewExperimental = GL_TRUE; // Needed for core profile if (glewInit() != GLEW_OK) { fprintf(stderr, "Failed to initialize GLEW\n"); getchar(); glfwTerminate(); return -1; } //#clear errors GLEW may trigger printError(); glClearColor(0.0f, 0.0f, 0.4f, 0.0f); GLuint VertexArrayID; glGenVertexArrays(1, &VertexArrayID); glBindVertexArray(VertexArrayID); //Load model //---------- Model cube; model_init(&cube); model_set_data_length(&cube, sizeof(cube_vertex_data)); model_set_vertices(&cube, cube_vertex_data); model_set_colors(&cube, cube_color_data); model_set_uv_map(&cube, cube_uv_data); model_set_texture(&cube, "./textures/bricks.dds"); model_bind(&cube); //Create shaders //-------------- GLuint vertexShader, fragmentShader; loadShader(&vertexShader, GL_VERTEX_SHADER, mvpVertexShaderSource); loadShader(&fragmentShader, GL_FRAGMENT_SHADER, fragmentShaderSource); //Create program //-------------- GLuint program; createProgram(&program, vertexShader, fragmentShader); // Enable z-buffer // --------------- glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); // Enable culling // -------------- glEnable(GL_CULL_FACE); // Get MVP uniform // --------------- GLuint mvp_ul = glGetUniformLocation(program, "MVP"); // Model Matrix // ------------ Mat4 s; scale_m4(&s, 0.1f, 0.1f, 0.1f); printf("Scale:\n"); print_m4(&s); Mat4 r; rotate_m4(&r, 0.0f, 1.0f, 0.0f, 0.0f); printf("Rotate:\n"); print_m4(&r); Mat4 t; translate_m4(&t, 0.0f, 0.0f, 0.0f); printf("Translate:\n"); print_m4(&t); Mat4 rs; printf("Rotated*Scaled:\n"); mul_m4(&rs, &r, &s); print_m4(&rs); Mat4 model; printf("Model:\n"); mul_m4(&model, &t, &rs); print_m4(&model); // Camera // ------ Vec3 pos; Vec3 center; Vec3 up; Vec3 direction; Vec3 right; camera_init(&camera); camera_set_fov(&camera, 1.0f); camera_set_aspect(&camera, (float)window.width/(float)window.height); Input input; input_init(&input, &window, 0.5f, 0.5f, 0.8f, -5.7f, -2.7f); do { glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); glUseProgram(program); input_get_data(&input, &pos, &direction, &right); add_v3(¢er, &pos, &direction); cross_v3(&up, &direction, &right); camera_set_position(&camera, &pos); camera_set_center(&camera, ¢er); camera_set_up(&camera, &up); // Mvp Matrix // ---------- Mat4 vp; camera_get_matrix(&camera, &vp); Mat4 mvp; mul_m4(&mvp, &vp, &model); printf("Perspective:\n"); print_m4(&mvp); // Set MVP transform glUniformMatrix4fv(mvp_ul, 1, GL_TRUE, &mvp[0][0]); model_render(&cube); window_swap_buffers(&window); glfwPollEvents(); } while( glfwGetKey(window.handle, GLFW_KEY_ESCAPE ) != GLFW_PRESS && glfwWindowShouldClose(window.handle) == 0 ); // Dispose // ------- model_dispose(&cube); glDeleteVertexArrays(1, &VertexArrayID); glDetachShader(program, fragmentShader); glDetachShader(program, vertexShader); glDeleteShader(fragmentShader); glDeleteShader(vertexShader); glDeleteProgram(program); glfwTerminate(); return 0; }