Skip to content

Commit

Permalink
Merge pull request #91 from twhlynch/main
Browse files Browse the repository at this point in the history
Video and 3D model examples
  • Loading branch information
rozukke authored Sep 26, 2024
2 parents 38af0d3 + cde0c44 commit 73c6e9e
Show file tree
Hide file tree
Showing 7 changed files with 502 additions and 0 deletions.
19 changes: 19 additions & 0 deletions example/model-generation/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
## Models

`obj-mc.cpp`

### Usage

Using Blender or any other 3d modeling software, convert your model to a .obj file.

Additionally, the models cannot use quads, so in Blender, triangulate the model using ctrl/command + t

Run `./obj-mc filename.obj scale`

> Scale is the max size of any dimension
### Examples

`./obj-mc blender-monkey.obj 50`

<img src="blender-monkey.png" alt="Blender Monkey" width="200"/>
Binary file added example/model-generation/blender-monkey.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
226 changes: 226 additions & 0 deletions example/model-generation/obj-mc.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
#include <chrono>
#include <fstream>
#include <iostream>
#include <mcpp/mcpp.h>
#include <string>
#include <thread>
#include <vector>

struct Vec3 {
float x, y, z;
};

struct Face {
int first, second, third;
};

class Model {
public:
Model(const std::string& filename);
~Model() {
if (_file.is_open()) {
_file.close();
}
}

void BuildModel(mcpp::MinecraftConnection& mc);
void BuildPointCloud(mcpp::MinecraftConnection& mc);

void Scale(int scale);
void SetPosition(mcpp::Coordinate position);

private:
bool IsWithin(Vec3& position);

Vec3 sub(Vec3 a, Vec3 b);
float dot(Vec3 a, Vec3 b);
Vec3 cross(Vec3 a, Vec3 b);

std::ifstream _file;

std::vector<Vec3> _vertices;
std::vector<Face> _faces;

mcpp::Coordinate _position;
};

int main(int argc, char* argv[]) {
if (argc < 3) {
std::cerr << "Usage: " << argv[0] << " filename.obj scale" << std::endl;
return 1;
}

std::string filename = argv[1];
int scale = std::stoi(argv[2]);

mcpp::MinecraftConnection mc;

Model* model = new Model(filename);
model->SetPosition(mc.getPlayerPosition());
model->Scale(scale);
model->BuildModel(mc); // Fills the model
model->BuildPointCloud(mc); // Places blocks at vertices

return 0;
}

Model::Model(const std::string& filename) {
_file.open(filename, std::ios::binary);
if (!_file.is_open()) {
throw std::runtime_error("Error opening file");
}

std::string line;
while (std::getline(_file, line)) {
if (line.substr(0, 2) == "v ") {
Vec3 vertex;
std::sscanf(line.c_str(), "v %f %f %f", &vertex.x, &vertex.y,
&vertex.z);
_vertices.push_back(vertex);
} else if (line.substr(0, 2) == "f ") {
Face face;
if (line.find(std::string("//")) != std::string::npos) {
std::sscanf(line.c_str(), "f %d//%*d %d//%*d %d//%*d",
&face.first, &face.second, &face.third);
} else if (line.find(std::string("/")) != std::string::npos) {
std::sscanf(line.c_str(), "f %d/%*d/%*d %d/%*d/%*d %d/%*d/%*d",
&face.first, &face.second, &face.third);
} else {
std::sscanf(line.c_str(), "f %d %d %d", &face.first,
&face.second, &face.third);
}

_faces.push_back(face);
}
}
}

void Model::Scale(int scale) {
Vec3 min;
Vec3 max;
min.x = min.y = min.z = std::numeric_limits<float>::max();
max.x = max.y = max.z = std::numeric_limits<float>::lowest();

for (const Vec3& vertex : _vertices) {
min.x = std::min(min.x, vertex.x);
min.y = std::min(min.y, vertex.y);
min.z = std::min(min.z, vertex.z);

max.x = std::max(max.x, vertex.x);
max.y = std::max(max.y, vertex.y);
max.z = std::max(max.z, vertex.z);
}

float size =
std::max(max.x - min.x, std::max(max.y - min.y, max.z - min.z));
for (Vec3& vertex : _vertices) {
vertex.x = (vertex.x - min.x) / size * scale;
vertex.y = (vertex.y - min.y) / size * scale;
vertex.z = (vertex.z - min.z) / size * scale;
}
}

void Model::BuildPointCloud(mcpp::MinecraftConnection& mc) {
for (const Vec3& vertex : _vertices) {
mcpp::Coordinate blockPosition =
mcpp::Coordinate(_position.x + vertex.x, _position.y + vertex.y,
_position.z + vertex.z);
mc.setBlock(blockPosition, mcpp::Blocks::GRAY_WOOL);
}
}

void Model::BuildModel(mcpp::MinecraftConnection& mc) {
Vec3 min;
Vec3 max;
min.x = min.y = min.z = std::numeric_limits<float>::max();
max.x = max.y = max.z = std::numeric_limits<float>::lowest();

for (const Vec3& vertex : _vertices) {
min.x = std::min(min.x, vertex.x);
min.y = std::min(min.y, vertex.y);
min.z = std::min(min.z, vertex.z);

max.x = std::max(max.x, vertex.x);
max.y = std::max(max.y, vertex.y);
max.z = std::max(max.z, vertex.z);
}

for (int x = min.x; x < max.x; x++) {
for (int y = min.y; y < max.y; y++) {
for (int z = min.z; z < max.z; z++) {
Vec3 position = {(float)x, (float)y, (float)z};
if (IsWithin(position)) {
mcpp::Coordinate blockPosition = mcpp::Coordinate(
_position.x + x, _position.y + y, _position.z + z);
mc.setBlock(blockPosition, mcpp::Blocks::GRAY_CONCRETE);
}
}
}
}
}

bool Model::IsWithin(Vec3& position) {
const float EpsilonFloat = 0.0000001;
const Vec3 directions[4] = {
{0.0, 1.0, 0.0}, {1.0, 0.0, 0.0}, {0.0, 0.0, 1.0}};

int crosses = 0;

for (Vec3 direction : directions) {
int intersects = 0;

for (const Face& face : _faces) {
// 1 based indexing -.-
Vec3 v1 = _vertices[face.first - 1];
Vec3 v2 = _vertices[face.second - 1];
Vec3 v3 = _vertices[face.third - 1];

Vec3 edge1 = sub(v2, v1);
Vec3 edge2 = sub(v3, v1);

Vec3 h = cross(direction, edge2);
float det = dot(edge1, h);

if (std::abs(det) < EpsilonFloat)
continue; // parallel

float f = 1.0 / det;
Vec3 s = sub(position, v1);
float u = f * dot(s, h);

if (u < 0.0 || u > 1.0)
continue; // outside face

Vec3 q = cross(s, edge1);
float v = f * dot(direction, q);

if (v < 0.0 || u + v > 1.0)
continue; // outside face

float t = f * dot(edge2, q);

if (t > EpsilonFloat)
intersects++;
}

if (intersects % 2 == 1)
crosses++;
}

return (crosses >= 2);
}

Vec3 Model::sub(Vec3 a, Vec3 b) {
return Vec3{a.x - b.x, a.y - b.y, a.z - b.z};
}
float Model::dot(Vec3 a, Vec3 b) { return a.x * b.x + a.y * b.y + a.z * b.z; }
Vec3 Model::cross(Vec3 a, Vec3 b) {
return Vec3{a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z,
a.x * b.y - a.y * b.x};
}

void Model::SetPosition(mcpp::Coordinate position) {
_position = position;
_position.x += 16;
_position.z += 16;
};
24 changes: 24 additions & 0 deletions example/video-generation/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
## Videos

`video-mc.cpp`
> Requires ffmpeg installed
### Usage

Using ffmpeg, convert an mp4 to a rgb file. To do so run `ffmpeg -i filename.mp4 -f rawvideo -pix_fmt rgb24 filename.rgb`

Run `./video-mc filename.rgb width height scale frame_rate`

> Scale is used to divide the image size
### Examples

`./video-mc rick-roll.rgb 640 480 16 24`

`./video-mc bad-apple.rgb 320 240 5 30`

<img src="rick-roll.png" alt="Rick Roll" width="200"/><img src="bad-apple.png" alt="Bad Apple" width="200"/>

YouTube video:

[![BAD APPLE!!](https://img.youtube.com/vi/FUt5WGvXZw0/0.jpg)](https://www.youtube.com/watch?v=FUt5WGvXZw0)
Binary file added example/video-generation/bad-apple.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added example/video-generation/rick-roll.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 73c6e9e

Please sign in to comment.