render/render.cpp
2026-03-20 22:24:10 +01:00

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