diff --git a/README.md b/README.md new file mode 100644 index 0000000..3bcbe58 --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +# render +box, tree and teapot dumb software renderer + +![image](res/screen-dosbox.png) + +| name | lang | notes | +|---------------------|------------------------|---------------------------------------| +| `render.fcode` | Flowgorithm pseudocode | original, VT100/PPM output | +| `render.lua` | Lua | Lua port, VT100 output | +| `render_origo.lua` | Lua | unknown | +| `render_cc.lua` | Lua | ComputerCraft version, monitor output, requires processing with `convert_cc.py` and `reduce_cc.py` if too big to store on CC drive | +| `render.cpp` | C++ | C++ implementation, different structure, unknown state | +| `render_dos.c` | C | DOS, requires 32bit extender, output to VGA mode 13h (256 colors), much faster triangle rasterization alg. | + + diff --git a/build_dos.bat b/build_dos.bat new file mode 100644 index 0000000..f574b47 --- /dev/null +++ b/build_dos.bat @@ -0,0 +1,7 @@ +set WATCOM=c:\dev\render\wc +set EDPATH=%WATCOM%\eddat +set INCLUDE=%WATCOM%\h +set PATH=%PATH%;%WATCOM%\binnt;%WATCOM%\binw; + +wcl386 -3 -fpi87 -fp3 -os -d0 -mf -bt=dos -l=stub32x -fe=render.exe render_dos.c +pause diff --git a/convert_cc.py b/convert_cc.py new file mode 100644 index 0000000..87a2b3f --- /dev/null +++ b/convert_cc.py @@ -0,0 +1,51 @@ +def convert(input_filename): + with open(input_filename, "r") as f: + lines = [line.strip() for line in f if line.strip()] + + # --- Step 1: Parse header --- + idx = 0 + _ignored = int(lines[idx]) # First line, ignored + idx += 1 + + num_vertices = int(lines[idx]) + idx += 1 + + # --- Step 2: Extract vertex data --- + vertices = [] + for _ in range(num_vertices): + # Each vertex: x y z u v nx ny nz (8 numbers) + vertex = [lines[idx + i] for i in range(8)] + vertices.append(vertex) + idx += 8 + + # --- Step 3: Write mesh.txt --- + with open("mesh.txt", "w") as mesh_file: + mesh_file.write(str(num_vertices) + "\n") + for v in vertices: + for comp in v: + mesh_file.write(comp + "\n") + + # --- Step 4: Parse texture --- + width = int(lines[idx]); idx += 1 + height = int(lines[idx]); idx += 1 + + num_pixels = width * height + pixels = [] + for _ in range(num_pixels): + r = int(lines[idx]); idx += 1 + g = int(lines[idx]); idx += 1 + b = int(lines[idx]); idx += 1 + pixels.append((r, g, b)) + + # --- Step 5: Write texture.bin --- + with open("texture.bin", "wb") as tex_file: + for r, g, b in pixels: + tex_file.write(bytes([r, g, b])) + + +if __name__ == "__main__": + import sys + if len(sys.argv) != 2: + print(f"Usage: {sys.argv[0]} ") + else: + convert(sys.argv[1]) diff --git a/reduce_cc.py b/reduce_cc.py new file mode 100644 index 0000000..70e7bce --- /dev/null +++ b/reduce_cc.py @@ -0,0 +1,20 @@ +def reduce_precision(input_file, output_file): + with open(input_file, "r") as infile, open(output_file, "w") as outfile: + for line in infile: + line = line.strip() + if line: # skip empty lines + try: + num = float(line) + if num.is_integer(): + outfile.write(f"{int(num)}\n") + else: + outfile.write(f"{num:.3f}\n") + except ValueError: + # If not a number, write unchanged + outfile.write(line + "\n") + +if __name__ == "__main__": + input_path = "data.txt" # original file + output_path = "data2.txt" # new file + reduce_precision(input_path, output_path) + \ No newline at end of file diff --git a/render.cpp b/render.cpp new file mode 100644 index 0000000..1895c1c --- /dev/null +++ b/render.cpp @@ -0,0 +1,400 @@ +// 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 diff --git a/render.lua b/render.lua new file mode 100644 index 0000000..f0bb113 --- /dev/null +++ b/render.lua @@ -0,0 +1,529 @@ +local fps = 0 +local math_floor = math.floor +local time = 0 + +local function ClearBuffers(meta, colorbuffer, depthbuffer, color, depth) + local i + for i = 0, meta[3] - 1 do + colorbuffer[1 + i * 3] = color[1] + colorbuffer[1 + i * 3 + 1] = color[2] + colorbuffer[1 + i * 3 + 2] = color[3] + + depthbuffer[1 + i] = depth + end +end + +local function DegToRad(deg) + local rad + rad = deg * 0.0174532925 + return rad +end + +local function Min(a, b) + local r + if a < b then + r = a + else + r = b + end + return r +end + +local function Min3(a, b, c) + local r + r = Min(Min(a, b), c) + return r +end + +local function Max(a, b) + local r + if a > b then + r = a + else + r = b + end + return r +end + +local function Max3(a, b, c) + local r + r = Max(Max(a, b), c) + return r +end + +local function IsOnRight(a, b, c) + local r + if (b[1] - a[1]) * (c[2] - a[2]) - (b[2] - a[2]) * (c[1] - a[1]) <= 0 then + r = true + else + r = false + end + return r +end + +local function Distance2(a, b) + local x, y, d + x = b[1] - a[1] + y = b[2] - a[2] + d = math.sqrt(x * x + y * y) + return d +end + +local function Normalize(a) + local l + l = math.sqrt(a[1] * a[1] + a[2] * a[2] + a[3] * a[3]) + a[1] = a[1] / l + a[2] = a[2] / l + a[3] = a[3] / l +end + +local function MapToRes(meta, a) + a[1] = a[1] / a[4] + a[2] = a[2] / a[4] + a[3] = a[3] / a[4] + + a[1] = (a[1] + 1.0) * 0.5 * (meta[1] - 1) + a[2] = (1.0 - a[2]) * 0.5 * (meta[2] - 1) +end + +local function Cross(result, a, b) + result[1] = a[2] * b[3] - a[3] * b[2] + result[2] = a[3] * b[1] - a[1] * b[3] + result[3] = a[1] * b[2] - a[2] * b[1] +end + +local function Dot(a, b) + local result + result = a[1] * b[1] + a[2] * b[2] + a[3] * b[3] + return result +end + +local function Dot2(a, b) + local result + result = a[1] * b[1] + a[2] * b[2] + return result +end + +local function VecCopy3(result, a) + result[1] = a[1] + result[2] = a[2] + result[3] = a[3] +end + +local function VecSubVec3(result, a, b) + result[1] = a[1] - b[1] + result[2] = a[2] - b[2] + result[3] = a[3] - b[3] +end + +local function MatMultMat(result, a, b) + result[1] = a[1] * b[1] + a[2] * b[5] + a[3] * b[9] + a[4] * b[13] + result[2] = a[1] * b[2] + a[2] * b[6] + a[3] * b[10] + a[4] * b[14] + result[3] = a[1] * b[3] + a[2] * b[7] + a[3] * b[11] + a[4] * b[15] + result[4] = a[1] * b[4] + a[2] * b[8] + a[3] * b[12] + a[4] * b[16] + result[5] = a[5] * b[1] + a[6] * b[5] + a[7] * b[9] + a[8] * b[13] + result[6] = a[5] * b[2] + a[6] * b[6] + a[7] * b[10] + a[8] * b[14] + result[7] = a[5] * b[3] + a[6] * b[7] + a[7] * b[11] + a[8] * b[15] + result[8] = a[5] * b[4] + a[6] * b[8] + a[7] * b[12] + a[8] * b[16] + result[9] = a[9] * b[1] + a[10] * b[5] + a[11] * b[9] + a[12] * b[13] + result[10] = a[9] * b[2] + a[10] * b[6] + a[11] * b[10] + a[12] * b[14] + result[11] = a[9] * b[3] + a[10] * b[7] + a[11] * b[11] + a[12] * b[15] + result[12] = a[9] * b[4] + a[10] * b[8] + a[11] * b[12] + a[12] * b[16] + result[13] = a[13] * b[1] + a[14] * b[5] + a[15] * b[9] + a[16] * b[13] + result[14] = a[13] * b[2] + a[14] * b[6] + a[15] * b[10] + a[16] * b[14] + result[15] = a[13] * b[3] + a[14] * b[7] + a[15] * b[11] + a[16] * b[15] + result[16] = a[13] * b[4] + a[14] * b[8] + a[15] * b[12] + a[16] * b[16] +end + +local function MatMultVec(result, a, v) + result[1] = a[1] * v[1] + a[2] * v[2] + a[3] * v[3] + a[4] * v[4] + result[2] = a[5] * v[1] + a[6] * v[2] + a[7] * v[3] + a[8] * v[4] + result[3] = a[9] * v[1] + a[10] * v[2] + a[11] * v[3] + a[12] * v[4] + result[4] = a[13] * v[1] + a[14] * v[2] + a[15] * v[3] + a[16] * v[4] +end + +local function MatMultVec3(result, a, v) + result[1] = a[1] * v[1] + a[2] * v[2] + a[3] * v[3] + a[4] + result[2] = a[5] * v[1] + a[6] * v[2] + a[7] * v[3] + a[8] + result[3] = a[9] * v[1] + a[10] * v[2] + a[11] * v[3] + a[12] + result[4] = a[13] * v[1] + a[14] * v[2] + a[15] * v[3] + a[16] +end + +local function LookAt(mat, campos, targetpos, upvec) + local forward = {} + local right = {} + local up = {} + + VecSubVec3(forward, targetpos, campos) + Normalize(forward) + + Cross(right, forward, upvec) + Normalize(right) + + Cross(up, right, forward) + + mat[1] = right[1] + mat[2] = right[2] + mat[3] = right[3] + mat[4] = -Dot(right, campos) + mat[5] = up[1] + mat[6] = up[2] + mat[7] = up[3] + mat[8] = -Dot(up, campos) + mat[9] = -forward[1] + mat[10] = -forward[2] + mat[11] = -forward[3] + mat[12] = Dot(forward, campos) + mat[13] = 0.0 + mat[14] = 0.0 + mat[15] = 0.0 + mat[16] = 1.0 +end + +local function Perspective(mat, fov, aspectratio, near, far) + local tanhalffovy + tanhalffovy = math.tan(fov * 0.5) + + mat[1] = 1 / (aspectratio * tanhalffovy) + mat[2] = 0.0 + mat[3] = 0.0 + mat[4] = 0.0 + mat[5] = 0.0 + mat[6] = 1 / tanhalffovy + mat[7] = 0.0 + mat[8] = 0.0 + mat[9] = 0.0 + mat[10] = 0.0 + mat[11] = -(far + near) / (far - near) + mat[12] = -(2 * far * near) / (far - near) + mat[13] = 0.0 + mat[14] = 0.0 + mat[15] = -1.0 + mat[16] = 0.0 +end + +local function OutputVT100(meta, colorbuffer) + print("\x1b[1;1H") + + local r1, g1, b1, r2, b2, g2, width, height, idx, rowoffset + width = meta[1] + height = meta[2] + + local out = {} + + local pr1, pg1, pb1, pr2, pg2, pb2 = 0, 0, 0, 0, 0, 0 + + for y = 0, height - 2, 2 do + table.insert(out, "\n") + for x = 0, width - 1 do + idx = (x + y * width) * 3 + rowoffset = width * 3 + + r1 = math_floor(colorbuffer[1 + idx] * 255) + g1 = math_floor(colorbuffer[1 + idx + 1] * 255) + b1 = math_floor(colorbuffer[1 + idx + 2] * 255) + + r2 = math_floor(colorbuffer[1 + idx + rowoffset] * 255) + g2 = math_floor(colorbuffer[1 + idx + 1 + rowoffset] * 255) + b2 = math_floor(colorbuffer[1 + idx + 2 + rowoffset] * 255) + + if r1 == pr1 and g1 == pg1 and b1 == pb1 and r2 == pr2 and g2 == pg2 and b2 == pb2 then + table.insert(out, "\xDF") + else + pr1, pg1, pb1, pr2, pg2, pb2 = r1, g1, b1, r2, g2, b2 + table.insert(out, string.format("\x1b[38;2;%d;%d;%dm\x1b[48;2;%d;%d;%dm\xDF", r1, g1, b1, r2, g2, b2)) + end + + + -- io.write("\x1b[38;2;" .. + -- r1 .. ";" .. g1 .. ";" .. b1 .. "m" .. "\x1b[48;2;" .. r2 .. ";" .. g2 .. ";" .. b2 .. "m" .. "\xDF") + end + end + + io.write(table.concat(out)) + + io.write(string.format("\n\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m%d FPS", fps)) +end + +local function DrawTriangle(meta, colorbuffer, depthbuffer, wp0, wp1, wp2, texture, c0, c1, c2, mvp, lightdir, n0, n1, n2) + local p0 = {} + local p1 = {} + local p2 = {} + + MatMultVec3(p0, mvp, wp0) + MatMultVec3(p1, mvp, wp1) + MatMultVec3(p2, mvp, wp2) + + if p0[4] ~= 0.0 and p1[4] ~= 0.0 and p2[4] ~= 0.0 then + MapToRes(meta, p0) + MapToRes(meta, p1) + MapToRes(meta, p2) + + local aabbmin = {} + local aabbmax = {} + + aabbmin[1] = Max(Min3(p0[1], p1[1], p2[1]), 0) + aabbmin[2] = Max(Min3(p0[2], p1[2], p2[2]), 0) + aabbmax[1] = Min(Max3(p0[1], p1[1], p2[1]), meta[1] - 1) + aabbmax[2] = Min(Max3(p0[2], p1[2], p2[2]), meta[2] - 1) + + local idx, tx, ty, ti + local p = {} + local depth + local w0, w1, w2, dot00, dot01, dot11, dot20, dot21, denom, d0, d1, d2 + + local v0 = {} + local v1 = {} + local v2 = {} + + local texcoord = {} + + local normal = {} + local diff + + for y = math_floor(aabbmin[2]), math_floor(aabbmax[2]) do + for x = math_floor(aabbmin[1]), math_floor(aabbmax[1]) do + p[1] = x + p[2] = y + + if IsOnRight(p0, p1, p) and IsOnRight(p1, p2, p) and IsOnRight(p2, p0, p) then + v0[1] = p1[1] - p0[1] + v0[2] = p1[2] - p0[2] + v1[1] = p2[1] - p0[1] + v1[2] = p2[2] - p0[2] + v2[1] = p[1] - p0[1] + v2[2] = p[2] - p0[2] + + dot00 = Dot2(v0, v0) + dot01 = Dot2(v0, v1) + dot11 = Dot2(v1, v1) + dot20 = Dot2(v2, v0) + dot21 = Dot2(v2, v1) + + denom = dot00 * dot11 - dot01 * dot01 + w1 = (dot11 * dot20 - dot01 * dot21) / denom + w2 = (dot00 * dot21 - dot01 * dot20) / denom + w0 = 1.0 - w1 - w2 + + depth = p0[3] * w0 + p1[3] * w1 + p2[3] * w2 + + idx = math_floor(x) + meta[1] * math_floor(y) + + if depth > 0.0 and depthbuffer[1 + idx] > depth then + depthbuffer[1 + idx] = depth + idx = idx * 3 + + texcoord[1] = w0 * c0[1] + w1 * c1[1] + w2 * c2[1] + texcoord[2] = w0 * c0[2] + w1 * c1[2] + w2 * c2[2] + + normal[1] = w0 * n0[1] + w1 * n1[1] + w2 * n2[1] + normal[2] = w0 * n0[2] + w1 * n1[2] + w2 * n2[2] + normal[3] = w0 * n0[3] + w1 * n1[3] + w2 * n2[3] + + Normalize(normal) + diff = Max(0.5, Dot(normal, lightdir)) + + tx = math_floor(texcoord[1] * meta[4]) + ty = math_floor((1 - texcoord[2]) * meta[5]) + ti = (tx + ty * meta[4]) * 3 + + + -- uz ne kokote + + + + colorbuffer[1 + idx] = texture[1 + ti] / 255 * diff + colorbuffer[1 + idx + 1] = texture[1 + ti + 1] / 255 * diff + colorbuffer[1 + idx + 2] = texture[1 + ti + 2] / 255 * diff + + OutputVT100(meta, colorbuffer) + + end + end + end + end + end +end + +local file = io.open("data.txt", "r") + +local function Input() + return file:read("*n") +end + +-- file:close() -- Close the file + +-- return number + +local function Main() + os.execute(" ") + + local meta = {} + + -- meta[1] = 175 + -- meta[2] = 120 + + meta[1] = 175 * 2 + meta[2] = 120 * 2 + + meta[3] = meta[1] * meta[2] + + local depthbuffer = {} + local colorbuffer = {} + + local background = {} + background[1] = 0.3 + background[2] = 0.3 + background[3] = 0.3 + + ClearBuffers(meta, colorbuffer, depthbuffer, background, 100.0) + + local lightdir = {} + lightdir[1] = .3 + lightdir[2] = 1 + lightdir[3] = .4 + Normalize(lightdir) + + local view = {} + local proj = {} + local mvp = {} + + local campos = {} + local targetpos = {} + local upvector = {} + + targetpos[1] = 0 + targetpos[2] = 0 + targetpos[3] = 0 + + upvector[1] = 0 + upvector[2] = 1 + upvector[3] = 0 + local aspectratio + aspectratio = 1.46 + + + Perspective(proj, DegToRad(90.0) / aspectratio, aspectratio, 0.1, 50.0) + + local p0 = {} + local p1 = {} + local p2 = {} + + local c0 = {} + local c1 = {} + local c2 = {} + + local n0 = {} + local n1 = {} + local n2 = {} + + local datasize, i, vertices + datasize = Input() + vertices = Input() + + local data = {} + for i = 1, datasize do + data[i] = Input() + end + + print("model rdy") + + meta[4] = Input() + meta[5] = Input() + + local tp + tp = meta[4] * meta[5] * 3 + + local texture = {} + + for i = 0, tp - 1 do + texture[1 + i] = Input() + end + + print("textura rdy") + + local temp + + local i0, i1, i2 + + local camangle + camangle = 0 + + local frames = 0 + local lastframereset = 0 + + while true do + frames = frames + 1 + + time = os.clock() * 1000 + + if time > lastframereset + 1000 then + fps = frames + frames = 0 + lastframereset = time + end + + + ClearBuffers(meta, colorbuffer, depthbuffer, background, 100.0) + + local camdis + camdis = 7.0 + + campos[1] = math.sin(camangle) * camdis + campos[2] = 0.8 * camdis + campos[3] = math.cos(camangle) * camdis + + + camangle = time * 0.0003 + + LookAt(view, campos, targetpos, upvector) + MatMultMat(mvp, proj, view) + + for i = 0, vertices - 3, 3 do + i0 = i * 8 + i1 = (i + 1) * 8 + i2 = (i + 2) * 8 + + p0[1] = data[1 + i0] + p0[2] = data[1 + i0 + 1] + p0[3] = data[1 + i0 + 2] + + p1[1] = data[1 + i1] + p1[2] = data[1 + i1 + 1] + p1[3] = data[1 + i1 + 2] + + p2[1] = data[1 + i2] + p2[2] = data[1 + i2 + 1] + p2[3] = data[1 + i2 + 2] + + + c0[1] = data[1 + i0 + 3] + c0[2] = data[1 + i0 + 4] + + c1[1] = data[1 + i1 + 3] + c1[2] = data[1 + i1 + 4] + + c2[1] = data[1 + i2 + 3] + c2[2] = data[1 + i2 + 4] + + n0[1] = data[1 + i0 + 5] + n0[2] = data[1 + i0 + 6] + n0[3] = data[1 + i0 + 7] + + n1[1] = data[1 + i1 + 5] + n1[2] = data[1 + i1 + 6] + n1[3] = data[1 + i1 + 7] + + n2[1] = data[1 + i2 + 5] + n2[2] = data[1 + i2 + 6] + n2[3] = data[1 + i2 + 7] + + DrawTriangle(meta, colorbuffer, depthbuffer, p0, p1, p2, texture, c0, c1, c2, mvp, lightdir, n0, n1, n2) + end + + + OutputVT100(meta, colorbuffer) + end +end + +Main() diff --git a/render_cc.lua b/render_cc.lua new file mode 100644 index 0000000..3003f57 --- /dev/null +++ b/render_cc.lua @@ -0,0 +1,616 @@ +local fps = 0 +local math_floor = math.floor +local time = 0 + +local function ClearBuffers(meta, colorbuffer, depthbuffer, color, depth) + local i + for i = 0, meta[3] - 1 do + colorbuffer[1 + i * 3] = color[1] + colorbuffer[1 + i * 3 + 1] = color[2] + colorbuffer[1 + i * 3 + 2] = color[3] + + depthbuffer[1 + i] = depth + end +end + +local function DegToRad(deg) + local rad + rad = deg * 0.0174532925 + return rad +end + +local function Min(a, b) + local r + if a < b then + r = a + else + r = b + end + return r +end + +local function Min3(a, b, c) + local r + r = Min(Min(a, b), c) + return r +end + +local function Max(a, b) + local r + if a > b then + r = a + else + r = b + end + return r +end + +local function Max3(a, b, c) + local r + r = Max(Max(a, b), c) + return r +end + +local function IsOnRight(a, b, c) + local r + if (b[1] - a[1]) * (c[2] - a[2]) - (b[2] - a[2]) * (c[1] - a[1]) <= 0 then + r = true + else + r = false + end + return r +end + +local function Distance2(a, b) + local x, y, d + x = b[1] - a[1] + y = b[2] - a[2] + d = math.sqrt(x * x + y * y) + return d +end + +local function Normalize(a) + local l + l = math.sqrt(a[1] * a[1] + a[2] * a[2] + a[3] * a[3]) + a[1] = a[1] / l + a[2] = a[2] / l + a[3] = a[3] / l +end + +local function MapToRes(meta, a) + a[1] = a[1] / a[4] + a[2] = a[2] / a[4] + a[3] = a[3] / a[4] + + a[1] = (a[1] + 1.0) * 0.5 * (meta[1] - 1) + a[2] = (1.0 - a[2]) * 0.5 * (meta[2] - 1) +end + +local function Cross(result, a, b) + result[1] = a[2] * b[3] - a[3] * b[2] + result[2] = a[3] * b[1] - a[1] * b[3] + result[3] = a[1] * b[2] - a[2] * b[1] +end + +local function Dot(a, b) + local result + result = a[1] * b[1] + a[2] * b[2] + a[3] * b[3] + return result +end + +local function Dot2(a, b) + local result + result = a[1] * b[1] + a[2] * b[2] + return result +end + +local function VecCopy3(result, a) + result[1] = a[1] + result[2] = a[2] + result[3] = a[3] +end + +local function VecSubVec3(result, a, b) + result[1] = a[1] - b[1] + result[2] = a[2] - b[2] + result[3] = a[3] - b[3] +end + +local function MatMultMat(result, a, b) + result[1] = a[1] * b[1] + a[2] * b[5] + a[3] * b[9] + a[4] * b[13] + result[2] = a[1] * b[2] + a[2] * b[6] + a[3] * b[10] + a[4] * b[14] + result[3] = a[1] * b[3] + a[2] * b[7] + a[3] * b[11] + a[4] * b[15] + result[4] = a[1] * b[4] + a[2] * b[8] + a[3] * b[12] + a[4] * b[16] + result[5] = a[5] * b[1] + a[6] * b[5] + a[7] * b[9] + a[8] * b[13] + result[6] = a[5] * b[2] + a[6] * b[6] + a[7] * b[10] + a[8] * b[14] + result[7] = a[5] * b[3] + a[6] * b[7] + a[7] * b[11] + a[8] * b[15] + result[8] = a[5] * b[4] + a[6] * b[8] + a[7] * b[12] + a[8] * b[16] + result[9] = a[9] * b[1] + a[10] * b[5] + a[11] * b[9] + a[12] * b[13] + result[10] = a[9] * b[2] + a[10] * b[6] + a[11] * b[10] + a[12] * b[14] + result[11] = a[9] * b[3] + a[10] * b[7] + a[11] * b[11] + a[12] * b[15] + result[12] = a[9] * b[4] + a[10] * b[8] + a[11] * b[12] + a[12] * b[16] + result[13] = a[13] * b[1] + a[14] * b[5] + a[15] * b[9] + a[16] * b[13] + result[14] = a[13] * b[2] + a[14] * b[6] + a[15] * b[10] + a[16] * b[14] + result[15] = a[13] * b[3] + a[14] * b[7] + a[15] * b[11] + a[16] * b[15] + result[16] = a[13] * b[4] + a[14] * b[8] + a[15] * b[12] + a[16] * b[16] +end + +local function MatMultVec(result, a, v) + result[1] = a[1] * v[1] + a[2] * v[2] + a[3] * v[3] + a[4] * v[4] + result[2] = a[5] * v[1] + a[6] * v[2] + a[7] * v[3] + a[8] * v[4] + result[3] = a[9] * v[1] + a[10] * v[2] + a[11] * v[3] + a[12] * v[4] + result[4] = a[13] * v[1] + a[14] * v[2] + a[15] * v[3] + a[16] * v[4] +end + +local function MatMultVec3(result, a, v) + result[1] = a[1] * v[1] + a[2] * v[2] + a[3] * v[3] + a[4] + result[2] = a[5] * v[1] + a[6] * v[2] + a[7] * v[3] + a[8] + result[3] = a[9] * v[1] + a[10] * v[2] + a[11] * v[3] + a[12] + result[4] = a[13] * v[1] + a[14] * v[2] + a[15] * v[3] + a[16] +end + +local function LookAt(mat, campos, targetpos, upvec) + local forward = {} + local right = {} + local up = {} + + VecSubVec3(forward, targetpos, campos) + Normalize(forward) + + Cross(right, forward, upvec) + Normalize(right) + + Cross(up, right, forward) + + mat[1] = right[1] + mat[2] = right[2] + mat[3] = right[3] + mat[4] = -Dot(right, campos) + mat[5] = up[1] + mat[6] = up[2] + mat[7] = up[3] + mat[8] = -Dot(up, campos) + mat[9] = -forward[1] + mat[10] = -forward[2] + mat[11] = -forward[3] + mat[12] = Dot(forward, campos) + mat[13] = 0.0 + mat[14] = 0.0 + mat[15] = 0.0 + mat[16] = 1.0 +end + +local function Perspective(mat, fov, aspectratio, near, far) + local tanhalffovy + tanhalffovy = math.tan(fov * 0.5) + + mat[1] = 1 / (aspectratio * tanhalffovy) + mat[2] = 0.0 + mat[3] = 0.0 + mat[4] = 0.0 + mat[5] = 0.0 + mat[6] = 1 / tanhalffovy + mat[7] = 0.0 + mat[8] = 0.0 + mat[9] = 0.0 + mat[10] = 0.0 + mat[11] = -(far + near) / (far - near) + mat[12] = -(2 * far * near) / (far - near) + mat[13] = 0.0 + mat[14] = 0.0 + mat[15] = -1.0 + mat[16] = 0.0 +end + +-- local function OutputVT100(meta, colorbuffer) +-- print("\x1b[1;1H") + +-- local r1, g1, b1, r2, b2, g2, width, height, idx, rowoffset +-- width = meta[1] +-- height = meta[2] + +-- local out = {} + +-- local pr1, pg1, pb1, pr2, pg2, pb2 = 0, 0, 0, 0, 0, 0 + +-- for y = 0, height - 2, 2 do +-- table.insert(out, "\n") +-- for x = 0, width - 1 do +-- idx = (x + y * width) * 3 +-- rowoffset = width * 3 + +-- r1 = math_floor(colorbuffer[1 + idx] * 255) +-- g1 = math_floor(colorbuffer[1 + idx + 1] * 255) +-- b1 = math_floor(colorbuffer[1 + idx + 2] * 255) + +-- r2 = math_floor(colorbuffer[1 + idx + rowoffset] * 255) +-- g2 = math_floor(colorbuffer[1 + idx + 1 + rowoffset] * 255) +-- b2 = math_floor(colorbuffer[1 + idx + 2 + rowoffset] * 255) + +-- if r1 == pr1 and g1 == pg1 and b1 == pb1 and r2 == pr2 and g2 == pg2 and b2 == pb2 then +-- table.insert(out, "\xDF") +-- else +-- pr1, pg1, pb1, pr2, pg2, pb2 = r1, g1, b1, r2, g2, b2 +-- table.insert(out, string.format("\x1b[38;2;%d;%d;%dm\x1b[48;2;%d;%d;%dm\xDF", r1, g1, b1, r2, g2, b2)) +-- end + + +-- -- io.write("\x1b[38;2;" .. +-- -- r1 .. ";" .. g1 .. ";" .. b1 .. "m" .. "\x1b[48;2;" .. r2 .. ";" .. g2 .. ";" .. b2 .. "m" .. "\xDF") +-- end +-- end + +-- io.write(table.concat(out)) + +-- io.write(string.format("\n\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m%d FPS", fps)) +-- end + +local monitor = peripheral.find("monitor") + +local function SetupMonitor() + monitor.setTextScale(0.5) + monitor.clear() + + monitor.setPaletteColor(colors.white, 0, 0, 0 ) -- 0 + monitor.setPaletteColor(colors.orange, 0, 0, 127 ) -- 1 + monitor.setPaletteColor(colors.magenta, 0, 127, 0 ) -- 2 + monitor.setPaletteColor(colors.lightBlue, 0, 127, 127 ) -- 3 + monitor.setPaletteColor(colors.yellow, 127, 0, 0 ) -- 4 + monitor.setPaletteColor(colors.lime, 127, 0, 127 ) -- 5 + monitor.setPaletteColor(colors.pink, 127, 127, 0 ) -- 6 + monitor.setPaletteColor(colors.gray, 127, 127, 127 ) -- 7 + + monitor.setPaletteColor(colors.lightGray, 127, 127, 127 ) -- 8 + monitor.setPaletteColor(colors.cyan, 127, 127, 255 ) -- 9 + monitor.setPaletteColor(colors.purple, 127, 255, 127 ) -- a + monitor.setPaletteColor(colors.blue, 127, 255, 255 ) -- b + monitor.setPaletteColor(colors.brown, 255, 127, 127 ) -- c + monitor.setPaletteColor(colors.green, 255, 127, 255 ) -- d + monitor.setPaletteColor(colors.red, 255, 255, 127 ) -- e + monitor.setPaletteColor(colors.black, 255, 255, 255 ) -- f +end + +local function OutputCCMonitor(meta, colorbuffer) + + local width = meta[1] + local height = meta[2] + + local chars = {} + local fcs = {} + local bcs = {} + local hex_digits = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" } + + for y = 0, height - 1 do + for x = 0, width - 1 do + idx = (x + y * width) * 3 + rowoffset = width * 3 + + local r = colorbuffer[1 + idx] + local g = colorbuffer[1 + idx + 1] + local b = colorbuffer[1 + idx + 2] + + local avg = (r + g + b) / 3 + + local c = 0 + local threshold = 1.2 + if b > avg * threshold then c = c + 1 end + if g > avg * threshold then c = c + 2 end + if r > avg * threshold then c = c + 4 end + + local bright_threshold = 0.5 + if r > bright_threshold or g > bright_threshold or b > bright_threshold then c = c + 8 end + + local xx = x + 1 + local cc = hex_digits[c + 1] + chars[xx] = " " + fcs[xx] = cc + bcs[xx] = cc + + end + + monitor.setCursorPos(1, y + 1) + monitor.blit(table.concat(chars), table.concat(fcs), table.concat(bcs)) + end + +end + +local function DrawTriangle(meta, colorbuffer, depthbuffer, wp0, wp1, wp2, texture, c0, c1, c2, mvp, lightdir, n0, n1, n2) + local p0 = {} + local p1 = {} + local p2 = {} + + MatMultVec3(p0, mvp, wp0) + MatMultVec3(p1, mvp, wp1) + MatMultVec3(p2, mvp, wp2) + + if p0[4] ~= 0.0 and p1[4] ~= 0.0 and p2[4] ~= 0.0 then + MapToRes(meta, p0) + MapToRes(meta, p1) + MapToRes(meta, p2) + + local aabbmin = {} + local aabbmax = {} + + aabbmin[1] = Max(Min3(p0[1], p1[1], p2[1]), 0) + aabbmin[2] = Max(Min3(p0[2], p1[2], p2[2]), 0) + aabbmax[1] = Min(Max3(p0[1], p1[1], p2[1]), meta[1] - 1) + aabbmax[2] = Min(Max3(p0[2], p1[2], p2[2]), meta[2] - 1) + + local idx, tx, ty, ti + local p = {} + local depth + local w0, w1, w2, dot00, dot01, dot11, dot20, dot21, denom, d0, d1, d2 + + local v0 = {} + local v1 = {} + local v2 = {} + + local texcoord = {} + + local normal = {} + local diff + + for y = math_floor(aabbmin[2]), math_floor(aabbmax[2]) do + for x = math_floor(aabbmin[1]), math_floor(aabbmax[1]) do + p[1] = x + p[2] = y + + if IsOnRight(p0, p1, p) and IsOnRight(p1, p2, p) and IsOnRight(p2, p0, p) then + v0[1] = p1[1] - p0[1] + v0[2] = p1[2] - p0[2] + v1[1] = p2[1] - p0[1] + v1[2] = p2[2] - p0[2] + v2[1] = p[1] - p0[1] + v2[2] = p[2] - p0[2] + + dot00 = Dot2(v0, v0) + dot01 = Dot2(v0, v1) + dot11 = Dot2(v1, v1) + dot20 = Dot2(v2, v0) + dot21 = Dot2(v2, v1) + + denom = dot00 * dot11 - dot01 * dot01 + w1 = (dot11 * dot20 - dot01 * dot21) / denom + w2 = (dot00 * dot21 - dot01 * dot20) / denom + w0 = 1.0 - w1 - w2 + + depth = p0[3] * w0 + p1[3] * w1 + p2[3] * w2 + + idx = math_floor(x) + meta[1] * math_floor(y) + + if depth > 0.0 and depthbuffer[1 + idx] > depth then + depthbuffer[1 + idx] = depth + idx = idx * 3 + + texcoord[1] = w0 * c0[1] + w1 * c1[1] + w2 * c2[1] + texcoord[2] = w0 * c0[2] + w1 * c1[2] + w2 * c2[2] + + normal[1] = w0 * n0[1] + w1 * n1[1] + w2 * n2[1] + normal[2] = w0 * n0[2] + w1 * n1[2] + w2 * n2[2] + normal[3] = w0 * n0[3] + w1 * n1[3] + w2 * n2[3] + + Normalize(normal) + diff = Max(0.5, Dot(normal, lightdir)) + + tx = math_floor(texcoord[1] * meta[4]) + ty = math_floor((1 - texcoord[2]) * meta[5]) + ti = (tx + ty * meta[4]) * 3 + + + -- uz ne kokote + + + + colorbuffer[1 + idx] = texture[1 + ti] / 255 * diff + colorbuffer[1 + idx + 1] = texture[1 + ti + 1] / 255 * diff + colorbuffer[1 + idx + 2] = texture[1 + ti + 2] / 255 * diff + + -- OutputVT100(meta, colorbuffer) + + end + end + end + end + end +end + +local file = assert(io.open("mesh.txt", "r")) + +local function Input() + return tonumber(file:read()) +end + +-- file:close() -- Close the file + +-- return number + +function load_texture(filename) + local file = assert(io.open(filename, "rb")) + local data = file:read("*all") + file:close() + + local tex = {} + for i = 1, #data do + tex[i] = string.byte(data, i) + end + + return tex +end + +local function Main() + SetupMonitor() + + local meta = {} + + -- meta[1] = 175 + -- meta[2] = 120 + + meta[1] = 79 + meta[2] = 38 + local charaspect = 1.5 + + meta[3] = meta[1] * meta[2] + + local depthbuffer = {} + local colorbuffer = {} + + local background = {} + background[1] = 0.3 + background[2] = 0.3 + background[3] = 0.3 + + ClearBuffers(meta, colorbuffer, depthbuffer, background, 100.0) + + local lightdir = {} + lightdir[1] = .3 + lightdir[2] = 1 + lightdir[3] = .4 + Normalize(lightdir) + + local view = {} + local proj = {} + local mvp = {} + + local campos = {} + local targetpos = {} + local upvector = {} + + targetpos[1] = 0 + targetpos[2] = 0 + targetpos[3] = 0 + + upvector[1] = 0 + upvector[2] = 1 + upvector[3] = 0 + local aspectratio + aspectratio = (meta[1] / meta[2]) / charaspect + + + Perspective(proj, DegToRad(90.0) / aspectratio, aspectratio, 0.1, 50.0) + + local p0 = {} + local p1 = {} + local p2 = {} + + local c0 = {} + local c1 = {} + local c2 = {} + + local n0 = {} + local n1 = {} + local n2 = {} + + local i, vertices + vertices = Input() + local datasize = vertices * 8 + + local data = {} + for i = 1, datasize do + data[i] = Input() + end + + print("model rdy") + + meta[4] = 256 -- tex width + meta[5] = 256 -- tex height + + -- local tp + -- tp = meta[4] * meta[5] * 3 + + -- local texture = {} + + -- for i = 0, tp - 1 do + -- texture[1 + i] = Input() + -- end + + local texture = load_texture("texture.bin") + + print("textura rdy") + + local temp + + local i0, i1, i2 + + local camangle + camangle = 0 + + local frames = 0 + local lastframereset = 0 + + while true do + frames = frames + 1 + + time = os.clock() * 1000 + + if time > lastframereset + 1000 then + fps = frames + frames = 0 + lastframereset = time + end + + + ClearBuffers(meta, colorbuffer, depthbuffer, background, 100.0) + + local camdis + camdis = 7.0 + + campos[1] = math.sin(camangle) * camdis + campos[2] = 0.8 * camdis + campos[3] = math.cos(camangle) * camdis + + + camangle = time * 0.0005 + + LookAt(view, campos, targetpos, upvector) + MatMultMat(mvp, proj, view) + + for i = 0, vertices - 3, 3 do + i0 = i * 8 + i1 = (i + 1) * 8 + i2 = (i + 2) * 8 + + p0[1] = data[1 + i0] + p0[2] = data[1 + i0 + 1] + p0[3] = data[1 + i0 + 2] + + p1[1] = data[1 + i1] + p1[2] = data[1 + i1 + 1] + p1[3] = data[1 + i1 + 2] + + p2[1] = data[1 + i2] + p2[2] = data[1 + i2 + 1] + p2[3] = data[1 + i2 + 2] + + + c0[1] = data[1 + i0 + 3] + c0[2] = data[1 + i0 + 4] + + c1[1] = data[1 + i1 + 3] + c1[2] = data[1 + i1 + 4] + + c2[1] = data[1 + i2 + 3] + c2[2] = data[1 + i2 + 4] + + n0[1] = data[1 + i0 + 5] + n0[2] = data[1 + i0 + 6] + n0[3] = data[1 + i0 + 7] + + n1[1] = data[1 + i1 + 5] + n1[2] = data[1 + i1 + 6] + n1[3] = data[1 + i1 + 7] + + n2[1] = data[1 + i2 + 5] + n2[2] = data[1 + i2 + 6] + n2[3] = data[1 + i2 + 7] + + DrawTriangle(meta, colorbuffer, depthbuffer, p0, p1, p2, texture, c0, c1, c2, mvp, lightdir, n0, n1, n2) + end + + + OutputCCMonitor(meta, colorbuffer) + + os.sleep(0.01) + end +end + +Main() diff --git a/render_origo.lua b/render_origo.lua new file mode 100644 index 0000000..6fc0db9 --- /dev/null +++ b/render_origo.lua @@ -0,0 +1,522 @@ +local fps = 0 + +local function ClearBuffers(meta, colorbuffer, depthbuffer, color, depth) + local i + for i = 0, meta[3] - 1 do + colorbuffer[1 + i * 3] = color[1] + colorbuffer[1 + i * 3 + 1] = color[2] + colorbuffer[1 + i * 3 + 2] = color[3] + + depthbuffer[1 + i] = depth + end +end + +local function DegToRad(deg) + local rad + rad = deg * 0.0174532925 + return rad +end + +local function Min(a, b) + local r + if a < b then + r = a + else + r = b + end + return r +end + +local function Min3(a, b, c) + local r + r = Min(Min(a, b), c) + return r +end + +local function Max(a, b) + local r + if a > b then + r = a + else + r = b + end + return r +end + +local function Max3(a, b, c) + local r + r = Max(Max(a, b), c) + return r +end + +local function IsOnRight(a, b, c) + local r + if (b[1] - a[1]) * (c[2] - a[2]) - (b[2] - a[2]) * (c[1] - a[1]) <= 0 then + r = true + else + r = false + end + return r +end + +local function Distance2(a, b) + local x, y, d + x = b[1] - a[1] + y = b[2] - a[2] + d = math.sqrt(x * x + y * y) + return d +end + +local function Normalize(a) + local l + l = math.sqrt(a[1] * a[1] + a[2] * a[2] + a[3] * a[3]) + a[1] = a[1] / l + a[2] = a[2] / l + a[3] = a[3] / l +end + +local function MapToRes(meta, a) + a[1] = a[1] / a[4] + a[2] = a[2] / a[4] + a[3] = a[3] / a[4] + + a[1] = (a[1] + 1.0) * 0.5 * (meta[1] - 1) + a[2] = (1.0 - a[2]) * 0.5 * (meta[2] - 1) +end + +local function Cross(result, a, b) + result[1] = a[2] * b[3] - a[3] * b[2] + result[2] = a[3] * b[1] - a[1] * b[3] + result[3] = a[1] * b[2] - a[2] * b[1] +end + +local function Dot(a, b) + local result + result = a[1] * b[1] + a[2] * b[2] + a[3] * b[3] + return result +end + +local function Dot2(a, b) + local result + result = a[1] * b[1] + a[2] * b[2] + return result +end + +local function VecCopy3(result, a) + result[1] = a[1] + result[2] = a[2] + result[3] = a[3] +end + +local function VecSubVec3(result, a, b) + result[1] = a[1] - b[1] + result[2] = a[2] - b[2] + result[3] = a[3] - b[3] +end + +local function MatMultMat(result, a, b) + result[1] = a[1] * b[1] + a[2] * b[5] + a[3] * b[9] + a[4] * b[13] + result[2] = a[1] * b[2] + a[2] * b[6] + a[3] * b[10] + a[4] * b[14] + result[3] = a[1] * b[3] + a[2] * b[7] + a[3] * b[11] + a[4] * b[15] + result[4] = a[1] * b[4] + a[2] * b[8] + a[3] * b[12] + a[4] * b[16] + result[5] = a[5] * b[1] + a[6] * b[5] + a[7] * b[9] + a[8] * b[13] + result[6] = a[5] * b[2] + a[6] * b[6] + a[7] * b[10] + a[8] * b[14] + result[7] = a[5] * b[3] + a[6] * b[7] + a[7] * b[11] + a[8] * b[15] + result[8] = a[5] * b[4] + a[6] * b[8] + a[7] * b[12] + a[8] * b[16] + result[9] = a[9] * b[1] + a[10] * b[5] + a[11] * b[9] + a[12] * b[13] + result[10] = a[9] * b[2] + a[10] * b[6] + a[11] * b[10] + a[12] * b[14] + result[11] = a[9] * b[3] + a[10] * b[7] + a[11] * b[11] + a[12] * b[15] + result[12] = a[9] * b[4] + a[10] * b[8] + a[11] * b[12] + a[12] * b[16] + result[13] = a[13] * b[1] + a[14] * b[5] + a[15] * b[9] + a[16] * b[13] + result[14] = a[13] * b[2] + a[14] * b[6] + a[15] * b[10] + a[16] * b[14] + result[15] = a[13] * b[3] + a[14] * b[7] + a[15] * b[11] + a[16] * b[15] + result[16] = a[13] * b[4] + a[14] * b[8] + a[15] * b[12] + a[16] * b[16] +end + +local function MatMultVec(result, a, v) + result[1] = a[1] * v[1] + a[2] * v[2] + a[3] * v[3] + a[4] * v[4] + result[2] = a[5] * v[1] + a[6] * v[2] + a[7] * v[3] + a[8] * v[4] + result[3] = a[9] * v[1] + a[10] * v[2] + a[11] * v[3] + a[12] * v[4] + result[4] = a[13] * v[1] + a[14] * v[2] + a[15] * v[3] + a[16] * v[4] +end + +local function MatMultVec3(result, a, v) + result[1] = a[1] * v[1] + a[2] * v[2] + a[3] * v[3] + a[4] + result[2] = a[5] * v[1] + a[6] * v[2] + a[7] * v[3] + a[8] + result[3] = a[9] * v[1] + a[10] * v[2] + a[11] * v[3] + a[12] + result[4] = a[13] * v[1] + a[14] * v[2] + a[15] * v[3] + a[16] +end + +local function LookAt(mat, campos, targetpos, upvec) + local forward = {} + local right = {} + local up = {} + + VecSubVec3(forward, targetpos, campos) + Normalize(forward) + + Cross(right, forward, upvec) + Normalize(right) + + Cross(up, right, forward) + + mat[1] = right[1] + mat[2] = right[2] + mat[3] = right[3] + mat[4] = -Dot(right, campos) + mat[5] = up[1] + mat[6] = up[2] + mat[7] = up[3] + mat[8] = -Dot(up, campos) + mat[9] = -forward[1] + mat[10] = -forward[2] + mat[11] = -forward[3] + mat[12] = Dot(forward, campos) + mat[13] = 0.0 + mat[14] = 0.0 + mat[15] = 0.0 + mat[16] = 1.0 +end + +local function Perspective(mat, fov, aspectratio, near, far) + local tanhalffovy + tanhalffovy = math.tan(fov * 0.5) + + mat[1] = 1 / (aspectratio * tanhalffovy) + mat[2] = 0.0 + mat[3] = 0.0 + mat[4] = 0.0 + mat[5] = 0.0 + mat[6] = 1 / tanhalffovy + mat[7] = 0.0 + mat[8] = 0.0 + mat[9] = 0.0 + mat[10] = 0.0 + mat[11] = -(far + near) / (far - near) + mat[12] = -(2 * far * near) / (far - near) + mat[13] = 0.0 + mat[14] = 0.0 + mat[15] = -1.0 + mat[16] = 0.0 +end + +local function DrawTriangle(meta, colorbuffer, depthbuffer, wp0, wp1, wp2, texture, c0, c1, c2, mvp, lightdir, n0, n1, n2) + local p0 = {} + local p1 = {} + local p2 = {} + + MatMultVec3(p0, mvp, wp0) + MatMultVec3(p1, mvp, wp1) + MatMultVec3(p2, mvp, wp2) + + if p0[4] ~= 0.0 and p1[4] ~= 0.0 and p2[4] ~= 0.0 then + MapToRes(meta, p0) + MapToRes(meta, p1) + MapToRes(meta, p2) + + local aabbmin = {} + local aabbmax = {} + + aabbmin[1] = Max(Min3(p0[1], p1[1], p2[1]), 0) + aabbmin[2] = Max(Min3(p0[2], p1[2], p2[2]), 0) + aabbmax[1] = Min(Max3(p0[1], p1[1], p2[1]), meta[1] - 1) + aabbmax[2] = Min(Max3(p0[2], p1[2], p2[2]), meta[2] - 1) + + local idx, tx, ty, ti + local p = {} + local depth + local w0, w1, w2, dot00, dot01, dot11, dot20, dot21, denom, d0, d1, d2 + + local v0 = {} + local v1 = {} + local v2 = {} + + local texcoord = {} + + local normal = {} + local diff + + for x = math.floor(aabbmin[1]), math.floor(aabbmax[1]) do + for y = math.floor(aabbmin[2]), math.floor(aabbmax[2]) do + p[1] = x + p[2] = y + + if IsOnRight(p0, p1, p) and IsOnRight(p1, p2, p) and IsOnRight(p2, p0, p) then + v0[1] = p1[1] - p0[1] + v0[2] = p1[2] - p0[2] + v1[1] = p2[1] - p0[1] + v1[2] = p2[2] - p0[2] + v2[1] = p[1] - p0[1] + v2[2] = p[2] - p0[2] + + dot00 = Dot2(v0, v0) + dot01 = Dot2(v0, v1) + dot11 = Dot2(v1, v1) + dot20 = Dot2(v2, v0) + dot21 = Dot2(v2, v1) + + denom = dot00 * dot11 - dot01 * dot01 + w1 = (dot11 * dot20 - dot01 * dot21) / denom + w2 = (dot00 * dot21 - dot01 * dot20) / denom + w0 = 1.0 - w1 - w2 + + depth = p0[3] * w0 + p1[3] * w1 + p2[3] * w2 + + idx = math.floor(x) + meta[1] * math.floor(y) + + if depth > 0.0 and depthbuffer[1 + idx] > depth then + depthbuffer[1 + idx] = depth + idx = idx * 3 + + texcoord[1] = w0 * c0[1] + w1 * c1[1] + w2 * c2[1] + texcoord[2] = w0 * c0[2] + w1 * c1[2] + w2 * c2[2] + + normal[1] = w0 * n0[1] + w1 * n1[1] + w2 * n2[1] + normal[2] = w0 * n0[2] + w1 * n1[2] + w2 * n2[2] + normal[3] = w0 * n0[3] + w1 * n1[3] + w2 * n2[3] + + Normalize(normal) + diff = Max(0.5, Dot(normal, lightdir)) + + tx = math.floor(texcoord[1] * meta[4]) + ty = math.floor((1 - texcoord[2]) * meta[5]) + ti = (tx + ty * meta[4]) * 3 + + + -- uz ne kokote + + + + colorbuffer[1 + idx] = texture[1 + ti] / 255 * diff + colorbuffer[1 + idx + 1] = texture[1 + ti + 1] / 255 * diff + colorbuffer[1 + idx + 2] = texture[1 + ti + 2] / 255 * diff + end + end + end + end + end +end + +local function OutputVT100(meta, colorbuffer) + print("\x1b[1;1H") + + local r1, g1, b1, r2, b2, g2, width, height, idx, rowoffset + width = meta[1] + height = meta[2] + + local out = {} + + local pr1, pg1, pb1, pr2, pg2, pb2 = 0, 0, 0, 0, 0, 0 + + for y = 0, height - 2, 2 do + table.insert(out, "\n") + for x = 0, width - 1 do + idx = (x + y * width) * 3 + rowoffset = width * 3 + + r1 = math.floor(colorbuffer[1 + idx] * 255) + g1 = math.floor(colorbuffer[1 + idx + 1] * 255) + b1 = math.floor(colorbuffer[1 + idx + 2] * 255) + + r2 = math.floor(colorbuffer[1 + idx + rowoffset] * 255) + g2 = math.floor(colorbuffer[1 + idx + 1 + rowoffset] * 255) + b2 = math.floor(colorbuffer[1 + idx + 2 + rowoffset] * 255) + + if r1 == pr1 and g1 == pg1 and b1 == pb1 and r2 == pr2 and g2 == pg2 and b2 == pb2 then + table.insert(out, "\xDF") + else + pr1, pg1, pb1, pr2, pg2, pb2 = r1, g1, b1, r2, g2, b2 + table.insert(out, string.format("\x1b[38;2;%d;%d;%dm\x1b[48;2;%d;%d;%dm\xDF", r1, g1, b1, r2, g2, b2)) + end + + + -- io.write("\x1b[38;2;" .. + -- r1 .. ";" .. g1 .. ";" .. b1 .. "m" .. "\x1b[48;2;" .. r2 .. ";" .. g2 .. ";" .. b2 .. "m" .. "\xDF") + end + end + + io.write(table.concat(out)) + + io.write(string.format("\n\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m%d FPS", fps)) +end + +local file = io.open("data.txt", "r") + +local function Input() + return file:read("*n") +end + +-- file:close() -- Close the file + +-- return number + +local function Main() + os.execute(" ") + + local meta = {} + + meta[1] = 175 + meta[2] = 120 + + + meta[3] = meta[1] * meta[2] + + local depthbuffer = {} + local colorbuffer = {} + + local background = {} + background[1] = 0.3 + background[2] = 0.3 + background[3] = 0.3 + + ClearBuffers(meta, colorbuffer, depthbuffer, background, 100.0) + + local lightdir = {} + lightdir[1] = .3 + lightdir[2] = 1 + lightdir[3] = .4 + Normalize(lightdir) + + local view = {} + local proj = {} + local mvp = {} + + local campos = {} + local targetpos = {} + local upvector = {} + + targetpos[1] = 0 + targetpos[2] = 0 + targetpos[3] = 0 + + upvector[1] = 0 + upvector[2] = 1 + upvector[3] = 0 + local aspectratio + aspectratio = 1.46 + + + Perspective(proj, DegToRad(90.0) / aspectratio, aspectratio, 0.1, 50.0) + + local p0 = {} + local p1 = {} + local p2 = {} + + local c0 = {} + local c1 = {} + local c2 = {} + + local n0 = {} + local n1 = {} + local n2 = {} + + local datasize, i, vertices + datasize = Input() + vertices = Input() + + local data = {} + for i = 1, datasize do + data[i] = Input() + end + + print("model rdy") + + meta[4] = Input() + meta[5] = Input() + + local tp + tp = meta[4] * meta[5] * 3 + + local texture = {} + + for i = 0, tp - 1 do + texture[1 + i] = Input() + end + + print("textura rdy") + + local temp + + local i0, i1, i2 + + local camangle + camangle = 0 + + local frames = 0 + local lastframereset = 0 + + while true do + frames = frames + 1 + + local time = os.clock() * 1000 + + if time > lastframereset + 1000 then + fps = frames + frames = 0 + lastframereset = time + end + + + ClearBuffers(meta, colorbuffer, depthbuffer, background, 100.0) + + local camdis + camdis = 7.0 + + campos[1] = math.sin(camangle) * camdis + campos[2] = 0.8 * camdis + campos[3] = math.cos(camangle) * camdis + + + camangle = camangle + math.pi / 50 + + LookAt(view, campos, targetpos, upvector) + MatMultMat(mvp, proj, view) + + for i = 0, vertices - 3, 3 do + i0 = i * 8 + i1 = (i + 1) * 8 + i2 = (i + 2) * 8 + + p0[1] = data[1 + i0] + p0[2] = data[1 + i0 + 1] + p0[3] = data[1 + i0 + 2] + + p1[1] = data[1 + i1] + p1[2] = data[1 + i1 + 1] + p1[3] = data[1 + i1 + 2] + + p2[1] = data[1 + i2] + p2[2] = data[1 + i2 + 1] + p2[3] = data[1 + i2 + 2] + + + c0[1] = data[1 + i0 + 3] + c0[2] = data[1 + i0 + 4] + + c1[1] = data[1 + i1 + 3] + c1[2] = data[1 + i1 + 4] + + c2[1] = data[1 + i2 + 3] + c2[2] = data[1 + i2 + 4] + + n0[1] = data[1 + i0 + 5] + n0[2] = data[1 + i0 + 6] + n0[3] = data[1 + i0 + 7] + + n1[1] = data[1 + i1 + 5] + n1[2] = data[1 + i1 + 6] + n1[3] = data[1 + i1 + 7] + + n2[1] = data[1 + i2 + 5] + n2[2] = data[1 + i2 + 6] + n2[3] = data[1 + i2 + 7] + + DrawTriangle(meta, colorbuffer, depthbuffer, p0, p1, p2, texture, c0, c1, c2, mvp, lightdir, n0, n1, n2) + end + + + OutputVT100(meta, colorbuffer) + end +end + +Main() diff --git a/res/screen-dosbox.png b/res/screen-dosbox.png new file mode 100644 index 0000000..4a7ddc6 Binary files /dev/null and b/res/screen-dosbox.png differ