// render.cpp : This file contains the 'main' function. Program execution begins and ends there. // #pragma comment(lib,"winmm.lib") #include #include #include #include #include //#include #include #include #define RES_WIDTH 160 #define RES_HEIGHT 120 #define RES_PIXELS (RES_WIDTH * RES_HEIGHT) static glm::vec3 s_colorbuffer[RES_PIXELS]; static float s_depthbuffer[RES_PIXELS]; static char s_outputbuffer[5000000]; static float s_fps = 0.0f; struct Vertex { glm::vec3 pos; glm::vec2 texcoord; glm::vec3 normal; }; struct Texture { std::vector> texels; size_t width; size_t height; glm::vec3 Sample(const glm::vec2& texcoord) const { auto idx = (int)(texcoord.x * width) + (int)((1.0f - texcoord.y) * height) * width; const auto& t = texels[idx]; return glm::vec3((float)t.r / 255.0f, (float)t.g / 255.0f, (float)t.b / 255.0f); } }; static void ClearBuffers(float depth, const glm::vec3& clear_color) { for (int i = 0; i < RES_PIXELS; ++i) { s_colorbuffer[i] = clear_color; s_depthbuffer[i] = depth; } } using Vec3ui8 = glm::vec<3, uint8_t>; static void OutputVT100() { //printf("\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m"); //printf("\x1b[1;1H"); //printf("\x1b[2J"); //std::cout << (char)27 << "[1;1H"; //std::string output; //output.reserve(1000000); int offset = 0; //std::vector oss; //oss.resize(RES_HEIGHT / 2); //output += "\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m\x1b[1;1H"; Vec3ui8 prevc1(0), prevc2(0); //#pragma omp parallel for for (int y = 0; y <= RES_HEIGHT - 2; y += 2) { //printf("\n"); //output += '\n'; offset += snprintf(s_outputbuffer + offset, sizeof(s_outputbuffer), "\n"); for (int x = 0; x < RES_WIDTH; ++x) { const auto& c1 = s_colorbuffer[x + y * RES_WIDTH]; const auto& c2 = s_colorbuffer[x + (y + 1) * RES_WIDTH]; Vec3ui8 c1i = c1 * 255.0f; Vec3ui8 c2i = c2 * 255.0f; if (prevc1 == c1i && prevc2 == c2i) { //offset += snprintf(s_outputbuffer + offset, sizeof(s_outputbuffer), "\xDF"); s_outputbuffer[offset++] = '\xDF'; //s_outputbuffer[offset++] = 'W'; s_outputbuffer[offset] = '\0'; } else { offset += snprintf(s_outputbuffer + offset, sizeof(s_outputbuffer), "\x1b[38;2;%d;%d;%dm\x1b[48;2;%d;%d;%dm\xDF", c1i.r, c1i.g, c1i.b, c2i.r, c2i.g, c2i.b); //offset += snprintf(s_outputbuffer + offset, sizeof(s_outputbuffer), "\x1b[38;2;%d;%d;%dmW", c1i.r, c1i.g, c1i.b, c2i.r, c2i.g, c2i.b); } prevc1 = c1i; prevc2 = c2i; //printf("\x1b[38;2;%d;%d;%dm\x1b[48;2;%d;%d;%dm\xDF", c1i.r, c1i.g, c1i.b, c2i.r, c2i.g, c2i.b); //std::ostringstream os; //auto& os = oss[y / 2]; //os << "\x1b[38;2;" << (int)c1i.r << ";" << (int)c1i.g << ";" << (int)c1i.b << "m" << "\x1b[48;2;" << (int)c2i.r << ";" << (int)c2i.g << ";" << (int)c2i.b << "m" << "\xDF"; //output += os.str(); } } printf("\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m"); printf("\x1b[1;1H"); printf("%s\n", s_outputbuffer); //for (int i = 0; i < oss.size(); ++i) // printf("%s\n", oss[i].str().c_str()); //output += "\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m"; //printf("%s\n", output.c_str()); printf("\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m\n"); printf("%6d %4.1f FPS ", offset, s_fps); } // //// Find the closest RGBx approximation of a 24-bit RGB color, for x = 0 or 1 //glm::u8vec3 rgbx_approx(unsigned char red, unsigned char green, unsigned char blue, int x) { // int threshold = (x + 1) * 255 / 3; // glm::u8vec3 result; // result.r = (red > threshold) ? 1 : 0; // result.g = (green > threshold) ? 1 : 0; // result.b = (blue > threshold) ? 1 : 0; // return result; //} // //// Convert a 4-bit RGBI color back to 24-bit RGB //glm::u8vec3 rgbi_to_rgb24(int r, int g, int b, int i) { // glm::u8vec3 result; // result.r = ((2 * r + i) * 255) / 3; // result.g = ((2 * g + i) * 255) / 3; // result.b = ((2 * b + i) * 255) / 3; // return result; //} // //float color_distance(const glm::u8vec3& colorA, const glm::u8vec3& colorB) { // int diffR = colorA.r - colorB.r; // int diffG = colorA.g - colorB.g; // int diffB = colorA.b - colorB.b; // return static_cast((diffR * diffR) + (diffG * diffG) + (diffB * diffB)); //} // //// Find the closest 4-bit RGBI approximation (by Euclidean distance) to a 24-bit RGB color //std::pair rgbi_approx(unsigned char red, unsigned char green, unsigned char blue) { // // Find best RGB0 and RGB1 approximations // glm::u8vec3 rgb0 = rgbx_approx(red, green, blue, 0); // glm::u8vec3 rgb1 = rgbx_approx(red, green, blue, 1); // // // Convert them back to 24-bit RGB // glm::u8vec3 rgb24_0 = rgbi_to_rgb24(rgb0.r, rgb0.g, rgb0.b, 0); // glm::u8vec3 rgb24_1 = rgbi_to_rgb24(rgb1.r, rgb1.g, rgb1.b, 1); // // // Calculate squared Euclidean distances // float d0 = color_distance(glm::u8vec3(red, green, blue), rgb24_0); // float d1 = color_distance(glm::u8vec3(red, green, blue), rgb24_1); // // if (d0 <= d1) { // return std::make_pair(rgb0, 0); // } // else { // return std::make_pair(rgb1, 1); // } //} // // //void OutputWindows() { // HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); // // COORD pos; // pos.X = 0; // pos.Y = 0; // SetConsoleCursorPosition(hConsole, pos); // // const char palette[] = ".,-~:;=!*#$@"; // int chars = sizeof(palette) - 1; // // for (int y = 0; y < RES_HEIGHT; ++y) { // for (int x = 0; x < RES_WIDTH; ++x) { // const auto& c1 = s_colorbuffer[x + y * RES_WIDTH]; // Vec3ui8 c1i = c1 * 255.0f; // // auto [c, i] = rgbi_approx(c1i.r, c1i.g, c1i.b); // // DWORD attr = 0; // if (c.r) attr |= FOREGROUND_RED; // if (c.g) attr |= FOREGROUND_GREEN; // if (c.b) attr |= FOREGROUND_BLUE; // if (i) attr |= FOREGROUND_INTENSITY; // SetConsoleTextAttribute(hConsole, attr); // printf("X"); // // //float brightness = (c1.r + c1.g + c1.b) / 3.0f; // //float b2 = glm::max(c1.r, glm::max(c1.g, c1.b)); // // //DWORD attr = 0; // //if (c1.r / b2 > 0.5) attr |= FOREGROUND_RED; // //if (c1.g / b2 > 0.5) attr |= FOREGROUND_GREEN; // //if (c1.b / b2 > 0.5) attr |= FOREGROUND_BLUE; // // //if (b2 > 0.5) attr |= FOREGROUND_INTENSITY; // // //printf("%c", palette[(int)(chars * b2)]); // // // } // printf("\n"); // } // // //} static bool ProcessVertex(const glm::vec3& pos, const glm::mat4& mvp, glm::vec2& sc, float& z) { auto cs = mvp * glm::vec4(pos.x, pos.y, pos.z, 1.0f); //if (cs.w < 0.0f) return true; cs /= cs.w; sc.x = (cs.x + 1.0f) * 0.5f * (RES_WIDTH - 1); sc.y = (1.0f - cs.y) * 0.5f * (RES_HEIGHT - 1); z = cs.z; return false; } static bool IsRight(glm::vec2 a, glm::vec2 b, glm::vec2 c) { return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x) <= 0; } glm::vec3 Barycentric(glm::vec2 p, glm::vec2 a, glm::vec2 b, glm::vec2 c) { glm::vec2 v0 = b - a, v1 = c - a, v2 = p - a; float d00 = glm::dot(v0, v0); float d01 = glm::dot(v0, v1); float d11 = glm::dot(v1, v1); float d20 = glm::dot(v2, v0); float d21 = glm::dot(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; return glm::vec3(u, v, w); } static void DrawTriangle(const Vertex& v0, const Vertex& v1, const Vertex& v2, const glm::mat4& mvp, const Texture& texture) { glm::vec2 p0, p1, p2; float z0, z1, z2; if (ProcessVertex(v0.pos, mvp, p0, z0) || ProcessVertex(v1.pos, mvp, p1, z1) || ProcessVertex(v2.pos, mvp, p2, z2)) return; auto aabbmin = glm::max(glm::min(glm::min(p0, p1), p2), glm::vec2(0, 0)); auto aabbmax = glm::min(glm::max(glm::max(p0, p1), p2), glm::vec2(RES_WIDTH - 1, RES_HEIGHT - 1)); int ymin = aabbmin.y; int ymax = aabbmax.y; //#pragma omp parallel for for (int y = ymin; y <= ymax; y++) { for (int x = aabbmin.x; x <= aabbmax.x; x++) { glm::vec2 p(x, y); if (IsRight(p0, p1, p) && IsRight(p1, p2, p) && IsRight(p2, p0, p)) { auto w = Barycentric(p, p0, p1, p2); auto depth = w[0] * z0 + w[1] * z1 + w[2] * z2; int idx = p.x + p.y * RES_WIDTH; if (depth < s_depthbuffer[idx] && depth > 0.0f) { s_depthbuffer[idx] = depth; auto texcoord = w[0] * v0.texcoord + w[1] * v1.texcoord + w[2] * v2.texcoord; auto normal = w[0] * v0.normal + w[1] * v1.normal + w[2] * v2.normal; glm::vec3 light_dir(-1, -2, -1); float diff = glm::max(glm::dot(glm::normalize(normal), -glm::normalize(light_dir)), 0.3f); s_colorbuffer[idx] = texture.Sample(texcoord) * diff; //glm::vec3(0, texcoord.x, texcoord.y); //s_colorbuffer[idx] = glm::vec3(0, texcoord.x, texcoord.y); } } } } } int main() { //omp_set_num_threads(4); std::ifstream data("data.txt"); size_t waste, num_vertices; std::vector vertices; data >> waste >> num_vertices; vertices.resize(num_vertices); for (int i = 0; i < num_vertices; ++i) { auto& v = vertices[i]; data >> v.pos.x >> v.pos.y >> v.pos.z >> v.texcoord.x >> v.texcoord.y >> v.normal.x >> v.normal.y >> v.normal.z; } Texture texture; data >> texture.width >> texture.height; texture.texels.resize(texture.width * texture.height); for (int i = 0; i < texture.texels.size(); ++i) { auto& t = texture.texels[i]; int r, g, b; data >> r >> g >> b; t.r = r; t.g = g; t.b = b; } //HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); //SetConsoleMode(hConsole, ENABLE_VIRTUAL_TERMINAL_PROCESSING); //system(" "); float rot = 0.0f; int lastfpstime = 0; int frames = 0; while (true) { frames++; auto time = timeGetTime(); if (lastfpstime + 1000 < time) { lastfpstime = time; s_fps = frames; frames = 0; } ClearBuffers(1.0f, glm::vec3(0.3f)); rot = (float)time * 0.001f; glm::vec3 campos; campos.x = glm::sin(rot) * 7.0f; campos.y = 3.0f; campos.z = glm::cos(rot) * 7.0f; campos *= (glm::sin(rot * 0.3f) + 1.0f) * 0.5f; //rot += glm::pi() / 500.0f; float aspect_ratio = (float)RES_WIDTH / (float)RES_HEIGHT; auto view = glm::lookAt(campos, glm::vec3(0), glm::vec3(0, 1, 0)); auto proj = glm::perspective(glm::radians(90.0f) / aspect_ratio, aspect_ratio, 0.1f, 100.0f); auto mvp = proj * view; for (int i = 0; i < vertices.size(); i += 3) { const auto& v0 = vertices[i]; const auto& v1 = vertices[i + 1]; const auto& v2 = vertices[i + 2]; DrawTriangle(v0, v1, v2, mvp, texture); } //for (int y = 0; y < RES_HEIGHT; ++y) { // for (int x = 0; x < RES_WIDTH; ++x) { // s_colorbuffer[x + RES_WIDTH * y] = glm::vec3((float)x / RES_WIDTH, (float)y / RES_HEIGHT, 0); // } //} OutputVT100(); //OutputWindows(); } } // Run program: Ctrl + F5 or Debug > Start Without Debugging menu // Debug program: F5 or Debug > Start Debugging menu // Tips for Getting Started: // 1. Use the Solution Explorer window to add/manage files // 2. Use the Team Explorer window to connect to source control // 3. Use the Output window to see build output and other messages // 4. Use the Error List window to view errors // 5. Go to Project > Add New Item to create new code files, or Project > Add Existing Item to add existing code files to the project // 6. In the future, to open this project again, go to File > Open > Project and select the .sln file