401 lines
12 KiB
C++
401 lines
12 KiB
C++
// render.cpp : This file contains the 'main' function. Program execution begins and ends there.
|
|
//
|
|
#pragma comment(lib,"winmm.lib")
|
|
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <sstream>
|
|
#include <vector>
|
|
#include <Windows.h>
|
|
|
|
//#include <omp.h>
|
|
|
|
#include <glm/glm.hpp>
|
|
#include <glm/gtc/matrix_transform.hpp>
|
|
|
|
#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<glm::vec<3, uint8_t>> 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<std::ostringstream> 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<float>((diffR * diffR) + (diffG * diffG) + (diffB * diffB));
|
|
//}
|
|
//
|
|
//// Find the closest 4-bit RGBI approximation (by Euclidean distance) to a 24-bit RGB color
|
|
//std::pair<glm::u8vec3, int> 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<Vertex> 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<float>() / 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
|