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
// Created 4/16/24
// Modified 4/17/24
#include "donut.h"
#include "point.h"
#include "types.h"
#include <vector>
#include "rgb.h"
#include "vec3.h"
rgb sample_rgb(u8 *texture, usize index) {
u8 d_red = texture[index * 3];
u8 d_green = texture[index * 3 + 1];
u8 d_blue = texture[index * 3 + 2];
f64 d_r = (f64)d_red / 255.0;
f64 d_g = (f64)d_green / 255.0;
f64 d_b = (f64)d_blue / 255.0;
return rgb(d_r, d_g, d_b);
vec3 sample_rgb(u8 *texture, usize index) {
u8 red = texture[index];
u8 green = texture[index + 1];
u8 blue = texture[index + 2];
vec3 color = vec3((f64)red, (f64)green, (f64)blue);
color *= vec3(1.0 / 255.0, 1.0 / 255.0, 1.0 / 255.0);
return color;
};
inline rgb mix_transparency(rgb transparent, rgb solid) {
if (transparent.r == 0.0 && transparent.g == 0.0 && transparent.b == 0.0) {
inline vec3 mix_transparency(vec3 transparent, vec3 solid) {
if (transparent.i == 0.0 && transparent.j == 0.0 && transparent.k == 0.0) {
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> out = {};
const vec3 grass_color_scale = vec3(0.4, 1.0, 0.4);
const usize image_size = 16;
const usize point_count = 22;
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 sy = (f64)l_y / (f64)point_count;
usize index = (usize)(sx * (f64)(image_size)) + usize(sy * (f64)(image_size)) * image_size;
rgb dirt = sample_rgb(bottom_texture, index);
rgb grass = sample_rgb(top_texture, index);
rgb sides = sample_rgb(side_texture, index);
grass.mix(rgb(0.4, 1.0, 0.4));
sides.mix(rgb(0.4, 1.0, 0.4));
usize index = 3 * ((usize)(sx * (f64)(image_size)) + usize(sy * (f64)(image_size)) * image_size);
vec3 dirt = sample_rgb(bottom_texture, index);
vec3 grass = sample_rgb(top_texture, index);
vec3 side = sample_rgb(side_texture, index);
grass *= grass_color_scale;
side *= grass_color_scale;
sides = mix_transparency(sides, dirt);
side = mix_transparency(side, dirt);
f64 x_w = sx * (f64)world_width;
f64 y_w = sy * (f64)world_width;
f64 w_w = (f64)world_width;
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)) {
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)) {
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)) {
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, 0.0).set_rgb(grass));
}
}
for (usize i = 0; i < out.size(); i++) {
Point *p = &out[i];
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_z(-(f64)world_width / 2.0);
for (auto &p : out) {
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_z(-(f64)world_width / 2.0);
}
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 = {};
for (i32 x = -1; x <= 1; x++) {
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());
}
}
for (usize i = 0; i < points.size(); i++) {
Point *point = &points[i];
point->translate_z(offset);
}
return points;
}

View file

@ -1,8 +1,9 @@
// Alexander Bass
// Created 4/16/24
// Modified 4/17/24
#pragma once
#include "point.h"
#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
// Created 4/16/24
// Modified 4/17/24
#include "donut.h"
#include "point.h"
#include "ppm/ppm.h"
#include "pts.h"
#include "types.h"
#include <cassert>
#include <chrono>
#include <cmath>
#include <format>
#include <fstream>
#include <iostream>
#include <string>
#include <thread>
#include <vector>
const i32 WIDTH = 120;
const i32 HEIGHT = 100;
const i32 DEPTH = WIDTH;
@ -25,25 +28,24 @@ void update(vector<Point> &points);
int main() {
ifstream dirt_file("dirt.ppm");
u8 *dirt = ppm::load_ppm(dirt_file);
dirt_file.close();
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();
u8 *dirt = ppm::from_file("dirt.ppm");
u8 *grass = ppm::from_file("grass_top.ppm");
u8 *grass_side = ppm::from_file("grass_side.ppm");
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[] grass;
delete[] grass_side;
@ -51,20 +53,25 @@ int main() {
auto grid = new size_t[WIDTH * HEIGHT];
while (true) {
std::cout << "\033c";
// Move cursor to top left
std::cout << "\033[0;0H";
// Draw points
draw(grid, points);
// Rotate points
update(points);
// Wait
std::this_thread::sleep_for(std::chrono::milliseconds(1000 / 60));
// Repeat
}
return 0;
}
i32 w_x_to_screen(f64 w_x) {
return static_cast<i32>(std::round(w_x + static_cast<f64>(WIDTH) / 2.0));
}
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) {
@ -72,30 +79,37 @@ f64 w_z_to_screen(f64 w_z) {
}
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++) {
grid[i] = 0;
};
// Pseudo-rasterization
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_y = p.get_y();
f64 p_z = p.get_z();
i32 s_x = w_x_to_screen(p_x);
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) {
size_t index = static_cast<size_t>(s_x + s_y * HEIGHT);
size_t existing_pixel_handle = grid[index];
if (existing_pixel_handle == 0) {
grid[index] = i + 1;
} 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;
}
}
}
// Printing
std::string line = "";
for (size_t y = 0; y < static_cast<size_t>(WIDTH); y++) {
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());
assert(depth >= 0.0);
assert(depth <= 1.0);
f64 red = p.get_r() * depth;
f64 green = p.get_g() * depth;
f64 blue = p.get_b() * depth;
u8 r8 = static_cast<u8>(round(red * 255.0));
u8 g8 = static_cast<u8>(round(green * 255.0));
u8 b8 = static_cast<u8>(round(blue * 255.0));
vec3 rgb = p.get_rgb() * depth * 255.0;
u8 r8 = (u8)(round(rgb.i));
u8 g8 = (u8)(round(rgb.j));
u8 b8 = (u8)(round(rgb.k));
line += format("\033[38;2;{};{};{}m██", r8, g8, b8);
}
line += '\n';
@ -123,10 +135,9 @@ void draw(size_t *grid, vector<Point> &points) {
}
void update(vector<Point> &points) {
for (size_t i = 0; i < points.size(); i++) {
Point *p = &points[i];
p->rotate_x(0.02);
p->rotate_y(-0.04);
p->rotate_z(0.03);
for (auto &p : points) {
p.rotate_x(0.02);
p.rotate_y(-0.04);
p.rotate_z(0.03);
}
}

View file

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

View file

@ -1,8 +1,10 @@
// Alexander Bass
// Created 4/16/24
// Modified 4/17/24
#include "ppm.h"
using std::string;
#include <cassert>
#include <fstream>
#include <iostream>
#include <string>
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];
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;
buffer[i] = val;
buffer[i] = (u8)(val);
}
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
// Created 4/16/24
// Modified 4/17/24
#pragma once
#include "../types.h"
#include <istream>
#include <string>
namespace ppm {
u8 *load_ppm(std::istream &input);
u8 *from_file(std::string path);
} // 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
// Created 4/15/24
// Modified 4/14/24
// Modified 4/17/24
#pragma once
#include <cstdint>
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
typedef std::uint8_t u8;
typedef std::uint16_t u16;
typedef std::uint32_t u32;
typedef std::uint64_t u64;
typedef int8_t i8;
typedef std::int8_t i8;
typedef std::int16_t i16;
typedef int32_t i32;
typedef int64_t i64;
typedef std::int32_t i32;
typedef std::int64_t i64;
typedef float f32;
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;
}
};