add horse

This commit is contained in:
Alexander Bass 2024-06-05 12:13:03 -04:00
parent 9809dfccd0
commit f47a2df75b
13 changed files with 100224 additions and 104 deletions

3
.gitignore vendored
View file

@ -1 +1,2 @@
build build
progress

100000
resources/horse.pts.txt Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,32 +1,34 @@
// Alexander Bass // Alexander Bass
// Created 4/16/24 // Created 4/16/24
// Modified 4/17/24
#include "donut.h" #include "donut.h"
#include "point.h" #include "point.h"
#include "types.h" #include "types.h"
#include <vector> #include <vector>
#include "rgb.h" #include "vec3.h"
rgb sample_rgb(u8 *texture, usize index) { vec3 sample_rgb(u8 *texture, usize index) {
u8 d_red = texture[index * 3]; u8 red = texture[index];
u8 d_green = texture[index * 3 + 1]; u8 green = texture[index + 1];
u8 d_blue = texture[index * 3 + 2]; u8 blue = texture[index + 2];
f64 d_r = (f64)d_red / 255.0; vec3 color = vec3((f64)red, (f64)green, (f64)blue);
f64 d_g = (f64)d_green / 255.0; color *= vec3(1.0 / 255.0, 1.0 / 255.0, 1.0 / 255.0);
f64 d_b = (f64)d_blue / 255.0; return color;
return rgb(d_r, d_g, d_b);
}; };
inline rgb mix_transparency(rgb transparent, rgb solid) { inline vec3 mix_transparency(vec3 transparent, vec3 solid) {
if (transparent.r == 0.0 && transparent.g == 0.0 && transparent.b == 0.0) { if (transparent.i == 0.0 && transparent.j == 0.0 && transparent.k == 0.0) {
return solid; return solid;
} else {
return transparent;
} }
return transparent;
} }
std::vector<Point> generate_cube(u8 *bottom_texture, u8 *top_texture, u8 *side_texture, i32 face_x, i32 face_y) { std::vector<Point> generate_cube(u8 *bottom_texture, u8 *top_texture, u8 *side_texture, i32 face_x, i32 face_y) {
std::vector<Point> out = {}; std::vector<Point> out = {};
const vec3 grass_color_scale = vec3(0.4, 1.0, 0.4);
const usize image_size = 16; const usize image_size = 16;
const usize point_count = 22; const usize point_count = 22;
const usize world_width = 16; const usize world_width = 16;
@ -35,46 +37,45 @@ std::vector<Point> generate_cube(u8 *bottom_texture, u8 *top_texture, u8 *side_t
f64 sx = (f64)l_x / (f64)point_count; f64 sx = (f64)l_x / (f64)point_count;
f64 sy = (f64)l_y / (f64)point_count; f64 sy = (f64)l_y / (f64)point_count;
usize index = (usize)(sx * (f64)(image_size)) + usize(sy * (f64)(image_size)) * image_size; usize index = 3 * ((usize)(sx * (f64)(image_size)) + usize(sy * (f64)(image_size)) * image_size);
rgb dirt = sample_rgb(bottom_texture, index); vec3 dirt = sample_rgb(bottom_texture, index);
rgb grass = sample_rgb(top_texture, index); vec3 grass = sample_rgb(top_texture, index);
rgb sides = sample_rgb(side_texture, index); vec3 side = sample_rgb(side_texture, index);
grass.mix(rgb(0.4, 1.0, 0.4)); grass *= grass_color_scale;
sides.mix(rgb(0.4, 1.0, 0.4)); side *= grass_color_scale;
sides = mix_transparency(sides, dirt); side = mix_transparency(side, dirt);
f64 x_w = sx * (f64)world_width; f64 x_w = sx * (f64)world_width;
f64 y_w = sy * (f64)world_width; f64 y_w = sy * (f64)world_width;
f64 w_w = (f64)world_width; f64 w_w = (f64)world_width;
if (face_y == -1 || (face_y == 1 && face_x == 0)) { if (face_y == -1 || (face_y == 1 && face_x == 0)) {
out.push_back(*Point(x_w, 0, y_w).set_rgb(sides)); out.push_back(*Point(x_w, 0, y_w).set_rgb(side));
} }
if (face_x == -1 || (face_x == 1 && face_y == 0)) { if (face_x == -1 || (face_x == 1 && face_y == 0)) {
out.push_back(*Point(0, x_w, y_w).set_rgb(sides)); out.push_back(*Point(0, x_w, y_w).set_rgb(side));
} }
if (face_x == 1 || (face_x == -1 && face_y == 0)) { if (face_x == 1 || (face_x == -1 && face_y == 0)) {
out.push_back(*Point(w_w, x_w, y_w).set_rgb(sides)); out.push_back(*Point(w_w, x_w, y_w).set_rgb(side));
} }
if (face_y == 1 || (face_y == -1 && face_x == 0)) { if (face_y == 1 || (face_y == -1 && face_x == 0)) {
out.push_back(*Point(x_w, w_w, y_w).set_rgb(sides)); out.push_back(*Point(x_w, w_w, y_w).set_rgb(side));
} }
out.push_back(*Point(x_w, y_w, w_w).set_rgb(dirt)); out.push_back(*Point(x_w, y_w, w_w).set_rgb(dirt));
out.push_back(*Point(x_w, y_w, 0.0).set_rgb(grass)); out.push_back(*Point(x_w, y_w, 0.0).set_rgb(grass));
} }
} }
for (usize i = 0; i < out.size(); i++) { for (auto &p : out) {
Point *p = &out[i]; p.translate_x((f64)face_x * (f64)world_width - (f64)world_width / 2.0);
p->translate_x((f64)face_x * (f64)world_width - (f64)world_width / 2.0); p.translate_y((f64)face_y * (f64)world_width - (f64)world_width / 2.0);
p->translate_y((f64)face_y * (f64)world_width - (f64)world_width / 2.0); p.translate_z(-(f64)world_width / 2.0);
p->translate_z(-(f64)world_width / 2.0);
} }
return out; return out;
} }
std::vector<Point> generate_donut(u8 *bottom_texture, u8 *top_texture, u8 *side_texture, f64 offset) { std::vector<Point> generate_donut(u8 *bottom_texture, u8 *top_texture, u8 *side_texture) {
std::vector<Point> points = {}; std::vector<Point> points = {};
for (i32 x = -1; x <= 1; x++) { for (i32 x = -1; x <= 1; x++) {
for (i32 y = -1; y <= 1; y++) { for (i32 y = -1; y <= 1; y++) {
@ -85,10 +86,6 @@ std::vector<Point> generate_donut(u8 *bottom_texture, u8 *top_texture, u8 *side_
points.insert(points.end(), pt.begin(), pt.end()); points.insert(points.end(), pt.begin(), pt.end());
} }
} }
for (usize i = 0; i < points.size(); i++) {
Point *point = &points[i];
point->translate_z(offset);
}
return points; return points;
} }

View file

@ -1,8 +1,9 @@
// Alexander Bass // Alexander Bass
// Created 4/16/24 // Created 4/16/24
// Modified 4/17/24
#pragma once #pragma once
#include "point.h" #include "point.h"
#include <vector> #include <vector>
std::vector<Point> generate_donut(u8 *bottom_texture, u8 *top_texture, u8 *side_texture, f64); std::vector<Point> generate_donut(u8 *bottom_texture, u8 *top_texture, u8 *side_texture);

View file

@ -1,18 +1,21 @@
// Alexander Bass // Alexander Bass
// Created 4/16/24 // Created 4/16/24
// Modified 4/17/24
#include "donut.h" #include "donut.h"
#include "point.h" #include "point.h"
#include "ppm/ppm.h" #include "ppm/ppm.h"
#include "pts.h"
#include "types.h" #include "types.h"
#include <cassert> #include <cassert>
#include <chrono> #include <chrono>
#include <cmath> #include <cmath>
#include <format> #include <format>
#include <fstream>
#include <iostream> #include <iostream>
#include <string> #include <string>
#include <thread> #include <thread>
#include <vector> #include <vector>
const i32 WIDTH = 120; const i32 WIDTH = 120;
const i32 HEIGHT = 100; const i32 HEIGHT = 100;
const i32 DEPTH = WIDTH; const i32 DEPTH = WIDTH;
@ -25,25 +28,24 @@ void update(vector<Point> &points);
int main() { int main() {
ifstream dirt_file("dirt.ppm"); u8 *dirt = ppm::from_file("dirt.ppm");
u8 *dirt = ppm::load_ppm(dirt_file); u8 *grass = ppm::from_file("grass_top.ppm");
dirt_file.close(); u8 *grass_side = ppm::from_file("grass_side.ppm");
ifstream grass_file("grass_top.ppm");
u8 *grass = ppm::load_ppm(grass_file);
grass_file.close();
ifstream grass_side_file("grass_side.ppm");
u8 *grass_side = ppm::load_ppm(grass_side_file);
grass_side_file.close();
std::vector<Point> points = {}; std::vector<Point> points = {};
points.push_back(*Point(0, 0, 0).set_rgb(1.0, 0, 0)); points.push_back(*Point().set_rgb(1.0, 0, 0));
std::vector<Point> horse = pts::load_pts_from_path("horse.pts.txt");
for (auto &p : horse) {
vec3 a = p.get_xyz();
a *= 300.0;
p.set_xyz(a);
}
points.insert(points.end(), horse.begin(), horse.end());
// std::vector<Point> donut = generate_donut(dirt, grass, grass_side);
// points.insert(points.end(), donut.begin(), donut.end());
std::vector<Point> donut = generate_donut(dirt, grass, grass_side, 0.0);
points.insert(points.end(), donut.begin(), donut.end());
donut = generate_donut(dirt, grass, grass_side, 32.0);
points.insert(points.end(), donut.begin(), donut.end());
donut = generate_donut(dirt, grass, grass_side, -32.0);
points.insert(points.end(), donut.begin(), donut.end());
delete[] dirt; delete[] dirt;
delete[] grass; delete[] grass;
delete[] grass_side; delete[] grass_side;
@ -51,20 +53,25 @@ int main() {
auto grid = new size_t[WIDTH * HEIGHT]; auto grid = new size_t[WIDTH * HEIGHT];
while (true) { while (true) {
std::cout << "\033c"; // Move cursor to top left
std::cout << "\033[0;0H";
// Draw points
draw(grid, points); draw(grid, points);
// Rotate points
update(points); update(points);
// Wait
std::this_thread::sleep_for(std::chrono::milliseconds(1000 / 60)); std::this_thread::sleep_for(std::chrono::milliseconds(1000 / 60));
// Repeat
} }
return 0; return 0;
} }
i32 w_x_to_screen(f64 w_x) { i32 w_x_to_screen(f64 w_x) {
return static_cast<i32>(std::round(w_x + static_cast<f64>(WIDTH) / 2.0)); return static_cast<i32>(std::round(w_x + static_cast<f64>(WIDTH) / 2.0));
} }
i32 w_y_to_screen(f64 w_y) { i32 w_y_to_screen(f64 w_y) {
return static_cast<i32>(std::round(w_y + static_cast<f64>(HEIGHT) / 2.0)); return static_cast<i32>(std::round(w_y + static_cast<f64>(HEIGHT) / 2.0 + 10.5));
} }
f64 w_z_to_screen(f64 w_z) { f64 w_z_to_screen(f64 w_z) {
@ -72,30 +79,37 @@ f64 w_z_to_screen(f64 w_z) {
} }
void draw(size_t *grid, vector<Point> &points) { void draw(size_t *grid, vector<Point> &points) {
// grid is a list of handles to points in the points vector.
// these handles can be 0 to indicate that there is no representative point
// and therefor no pixel for that position.
// the values of grid can also be the index to points to represent a point
// being mapped to a pixel. This system also handles depth buffering.
for (u32 i = 0; i < static_cast<u32>(WIDTH * HEIGHT); i++) { for (u32 i = 0; i < static_cast<u32>(WIDTH * HEIGHT); i++) {
grid[i] = 0; grid[i] = 0;
}; };
// Pseudo-rasterization
for (size_t i = 0; i < points.size(); i++) { for (size_t i = 0; i < points.size(); i++) {
Point p = points[i]; Point &p = points[i];
f64 p_x = p.get_x(); f64 p_x = p.get_x();
f64 p_y = p.get_y(); f64 p_y = p.get_y();
f64 p_z = p.get_z(); f64 p_z = p.get_z();
i32 s_x = w_x_to_screen(p_x); i32 s_x = w_x_to_screen(p_x);
i32 s_y = w_y_to_screen(p_y); i32 s_y = w_y_to_screen(p_y);
f64 s_z = w_z_to_screen(p_z);
if (0 <= s_x && s_x < HEIGHT && 0 <= s_y && s_y < HEIGHT) { if (0 <= s_x && s_x < HEIGHT && 0 <= s_y && s_y < HEIGHT) {
size_t index = static_cast<size_t>(s_x + s_y * HEIGHT); size_t index = static_cast<size_t>(s_x + s_y * HEIGHT);
size_t existing_pixel_handle = grid[index]; size_t existing_pixel_handle = grid[index];
if (existing_pixel_handle == 0) { if (existing_pixel_handle == 0) {
grid[index] = i + 1; grid[index] = i + 1;
} else if (points[existing_pixel_handle - 1].get_z() < p_z) { } else if (points[existing_pixel_handle - 1].get_z() < p_z) {
// Pixel already exists at point on screen. Check its depth.
grid[index] = i + 1; grid[index] = i + 1;
} }
} }
} }
// Printing
std::string line = ""; std::string line = "";
for (size_t y = 0; y < static_cast<size_t>(WIDTH); y++) { for (size_t y = 0; y < static_cast<size_t>(WIDTH); y++) {
for (size_t x = 0; x < static_cast<size_t>(HEIGHT); x++) { for (size_t x = 0; x < static_cast<size_t>(HEIGHT); x++) {
@ -109,12 +123,10 @@ void draw(size_t *grid, vector<Point> &points) {
f64 depth = w_z_to_screen(p.get_z()); f64 depth = w_z_to_screen(p.get_z());
assert(depth >= 0.0); assert(depth >= 0.0);
assert(depth <= 1.0); assert(depth <= 1.0);
f64 red = p.get_r() * depth; vec3 rgb = p.get_rgb() * depth * 255.0;
f64 green = p.get_g() * depth; u8 r8 = (u8)(round(rgb.i));
f64 blue = p.get_b() * depth; u8 g8 = (u8)(round(rgb.j));
u8 r8 = static_cast<u8>(round(red * 255.0)); u8 b8 = (u8)(round(rgb.k));
u8 g8 = static_cast<u8>(round(green * 255.0));
u8 b8 = static_cast<u8>(round(blue * 255.0));
line += format("\033[38;2;{};{};{}m██", r8, g8, b8); line += format("\033[38;2;{};{};{}m██", r8, g8, b8);
} }
line += '\n'; line += '\n';
@ -123,10 +135,9 @@ void draw(size_t *grid, vector<Point> &points) {
} }
void update(vector<Point> &points) { void update(vector<Point> &points) {
for (size_t i = 0; i < points.size(); i++) { for (auto &p : points) {
Point *p = &points[i]; p.rotate_x(0.02);
p->rotate_x(0.02); p.rotate_y(-0.04);
p->rotate_y(-0.04); p.rotate_z(0.03);
p->rotate_z(0.03);
} }
} }

View file

@ -1,8 +1,9 @@
// Alexander Bass // Alexander Bass
// Created 4/16/24 // Created 4/16/24
// Modified 4/17/24
#pragma once #pragma once
#include "rgb.h"
#include "types.h" #include "types.h"
#include "vec3.h"
class Point { class Point {
private: private:
f64 x; f64 x;
@ -21,11 +22,24 @@ class Point {
g = 1.0; g = 1.0;
b = 1.0; b = 1.0;
} }
Point() {
x = 0.0;
y = 0.0;
z = 0.0;
r = 0.0;
g = 0.0;
b = 0.0;
}
void set_xyz(f64 _x, f64 _y, f64 _z) { void set_xyz(f64 _x, f64 _y, f64 _z) {
x = _x; x = _x;
y = _y; y = _y;
z = _z; z = _z;
}; };
void set_xyz(vec3 pos) {
x = pos.i;
y = pos.j;
z = pos.k;
};
void rotate_x(f64 angle); void rotate_x(f64 angle);
void rotate_y(f64 angle); void rotate_y(f64 angle);
void rotate_z(f64 angle); void rotate_z(f64 angle);
@ -46,6 +60,10 @@ class Point {
return z; return z;
}; };
vec3 get_xyz() const {
return vec3(x, y, z);
}
f64 get_r() const { f64 get_r() const {
return r; return r;
} }
@ -56,6 +74,10 @@ class Point {
return b; return b;
} }
vec3 get_rgb() const {
return vec3(r, g, b);
}
void translate_x(f64 dx) { void translate_x(f64 dx) {
x += dx; x += dx;
} }
@ -72,10 +94,10 @@ class Point {
b = _b; b = _b;
return this; return this;
}; };
Point *set_rgb(rgb col) { Point *set_rgb(vec3 col) {
r = col.r; r = col.i;
g = col.g; g = col.j;
b = col.b; b = col.k;
return this; return this;
}; };
}; };

View file

@ -1,8 +1,10 @@
// Alexander Bass // Alexander Bass
// Created 4/16/24 // Created 4/16/24
// Modified 4/17/24
#include "ppm.h" #include "ppm.h"
using std::string; using std::string;
#include <cassert> #include <cassert>
#include <fstream>
#include <iostream> #include <iostream>
#include <string> #include <string>
u8 *ppm::load_ppm(std::istream &input) { u8 *ppm::load_ppm(std::istream &input) {
@ -19,11 +21,21 @@ u8 *ppm::load_ppm(std::istream &input) {
u8 *buffer = new u8[width * height * 3]; u8 *buffer = new u8[width * height * 3];
for (size_t i = 0; i < width * height * 3; i++) { for (size_t i = 0; i < width * height * 3; i++) {
u16 val = 0; // The extraction operator assumes a uint8_t type represents an ascii
// character and thus grabs the first ascii character of the color value
// and places it within the u8.
// A u32 is used as the extraction operator understands it to be an integer
u32 val = 0;
input >> val; input >> val;
buffer[i] = val; buffer[i] = (u8)(val);
} }
return buffer; return buffer;
}
u8 *ppm::from_file(string path) {
std::ifstream in(path);
u8 *ppm = ppm::load_ppm(in);
in.close();
return ppm;
} }

View file

@ -1,12 +1,14 @@
// Alexander Bass // Alexander Bass
// Created 4/16/24 // Created 4/16/24
// Modified 4/17/24
#pragma once #pragma once
#include "../types.h" #include "../types.h"
#include <istream> #include <istream>
#include <string>
namespace ppm { namespace ppm {
u8 *load_ppm(std::istream &input); u8 *load_ppm(std::istream &input);
u8 *from_file(std::string path);
} // namespace ppm } // namespace ppm

38
src/pts.cpp Normal file
View file

@ -0,0 +1,38 @@
// Alexander Bass
// Created 4/19/24
// Modified 4/19/24
#include "point.h"
#include <istream>
#include <string>
#include <vector>
using std::string;
#include <cassert>
#include <fstream>
#include <iostream>
#include <string>
namespace pts {
std::vector<Point> load_pts(std::istream &input) {
std::vector<Point> output = {};
while (true) {
f64 i = 0.0, j = 0.0, k = 0.0;
f64 ni = 0.0, nj = 0.0, nk = 0.0;
input >> i;
input >> j;
input >> k;
input >> ni;
input >> nj;
input >> nk;
if (input.eof() || input.fail()) {
return output;
}
output.push_back(Point(i, j, k));
}
return output;
}
std::vector<Point> load_pts_from_path(std::string path) {
std::ifstream input(path);
return load_pts(input);
input.close();
}
}

13
src/pts.h Normal file
View file

@ -0,0 +1,13 @@
#pragma once
// Alexander Bass
// Created 4/19/24
// Modified 4/19/24
#include "point.h"
#include <istream>
#include <string>
#include <vector>
namespace pts {
std::vector<Point> load_pts(std::istream &);
std::vector<Point> load_pts_from_path(std::string);
}

View file

@ -1,19 +0,0 @@
// Alexander Bass
// Created 4/16/24
#pragma once
#include "types.h"
struct rgb {
f64 r;
f64 g;
f64 b;
rgb(f64 _r, f64 _g, f64 _b) {
r = _r;
g = _g;
b = _b;
}
void mix(rgb rhs) {
r *= rhs.r;
g *= rhs.g;
b *= rhs.b;
}
};

View file

@ -1,18 +1,18 @@
// Alexander Bass // Alexander Bass
// Created 4/15/24 // Created 4/15/24
// Modified 4/14/24 // Modified 4/17/24
#pragma once #pragma once
#include <cstdint> #include <cstdint>
typedef uint8_t u8; typedef std::uint8_t u8;
typedef uint16_t u16; typedef std::uint16_t u16;
typedef uint32_t u32; typedef std::uint32_t u32;
typedef uint64_t u64; typedef std::uint64_t u64;
typedef int8_t i8; typedef std::int8_t i8;
typedef std::int16_t i16; typedef std::int16_t i16;
typedef int32_t i32; typedef std::int32_t i32;
typedef int64_t i64; typedef std::int64_t i64;
typedef float f32; typedef float f32;
typedef double f64; typedef double f64;

42
src/vec3.h Normal file
View file

@ -0,0 +1,42 @@
// Alexander Bass
// Created 4/16/24
// Modified 4/17/24
#pragma once
#include "types.h"
struct vec3 {
f64 i;
f64 j;
f64 k;
vec3(f64 _i, f64 _j, f64 _k) {
i = _i;
j = _j;
k = _k;
}
vec3 &operator*=(const vec3 &rhs) {
i *= rhs.i;
j *= rhs.j;
k *= rhs.k;
return *this;
}
vec3 operator*(const vec3 &rhs) {
vec3 copy = *this;
copy.i *= rhs.i;
copy.j *= rhs.j;
copy.k *= rhs.k;
return copy;
}
vec3 &operator*=(const f64 &rhs) {
i *= rhs;
j *= rhs;
k *= rhs;
return *this;
}
vec3 operator*(const f64 &rhs) {
vec3 copy = *this;
copy.i *= rhs;
copy.j *= rhs;
copy.k *= rhs;
return copy;
}
};