added new cmake setup, graphics, vendors, obj importer, etc.

This commit is contained in:
Daniel Ledda
2022-12-29 12:57:19 +01:00
parent 783f9ee055
commit e83185f011
30 changed files with 18711 additions and 116 deletions

254
src/SomaSolve.cpp Normal file
View File

@@ -0,0 +1,254 @@
#include <bitset>
#include <span>
#include <cstdint>
#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include "VoxelSpace.h"
auto get_dims_input(int dims[3]) -> void {
std::cout << "Enter dimensions separated by newlines. (x*y*z must not exceed 64)\n";
auto success = false;
while (!success) {
std::cout << "x: ";
std::cin >> dims[0];
std::cout << "y: ";
std::cin >> dims[1];
std::cout << "z: ";
std::cin >> dims[2];
auto size = dims[0]*dims[1]*dims[2];
if (size <= 64) {
success = true;
} else {
std::cout << "That resulted in " << size << " units. Try again.\n";
}
std::cin.ignore();
}
}
auto get_reprs_input(int units_required) -> std::vector<uint64_t> {
std::cout << "Enter bit-representations (big endian, max 64 bits, total 1s must add up to " << units_required << "). press ENTER twice to finish input.\n";
auto reprs = std::vector<uint64_t>();
auto total_units = 0;
while (true) {
auto input = std::string();
std::getline(std::cin, input);
if (input.size() == 0) {
if (total_units == units_required) {
break;
} else {
std::cout << "Bad number of units. You entered: " << total_units << ", but exactly " << units_required << " were required.\n";
total_units = 0;
continue;
}
}
auto bit_repr = 0ul;
auto i = 0;
auto good_repr = true;
for (auto it = input.rbegin(); it < input.rend(); it++, i++) {
if (*it == '1') {
bit_repr |= 1ul << i;
total_units++;
} else if (*it != '0' || i >= 64) {
std::cout << "Input invalid. Enter a binary string only with max 64 bits." << '\n';
good_repr = false;
break;
}
}
if (good_repr) {
reprs.push_back(bit_repr);
}
}
return reprs;
}
namespace SomaSolve {
using SomaSolution = std::vector<uint64_t>;
struct Solver {
std::vector<uint64_t>* input;
std::vector<int>* offsets;
std::vector<SomaSolution>* solutions;
};
auto STD_SOMA = std::vector<uint64_t>{ 23ul, 30ul, 15ul, 1043ul, 24594ul, 12306ul, 11ul };
auto backtrack_solve_iter(std::vector<uint64_t> *polycube_input, std::vector<int> *offsets)-> void {
auto num_inputs = offsets->size() - 1;
auto solns = std::vector<int>();
auto iter_stack = std::vector<int>();
auto curr_soln_stack = std::vector<int>();
auto soln_spaces_stack = std::vector<uint64_t>();
soln_spaces_stack.push_back(0ul);
auto depth = 0;
while (depth >= 0) {
if (depth >= iter_stack.size()) {
iter_stack.push_back(offsets->at(depth));
}
auto end = offsets->at(depth + 1);
auto broke = false;
for (; iter_stack[depth] < end; iter_stack[depth]++) {
auto next_space = polycube_input->at(iter_stack[depth]);
auto soln_space = soln_spaces_stack[depth];
std::cout << next_space << " " << soln_space << std::endl;
auto successful_fuse = (soln_space | next_space) == (soln_space ^ next_space);
if (successful_fuse) {
soln_spaces_stack.push_back(soln_space |= next_space);
curr_soln_stack.push_back(iter_stack[depth]);
depth++;
if (curr_soln_stack.size() == num_inputs) {
solns.push_back(1);
curr_soln_stack.pop_back();
soln_spaces_stack.pop_back();
depth--;
} else {
depth++;
auto broke = true;
break;
}
}
}
if (!broke) {
curr_soln_stack.pop_back();
soln_spaces_stack.pop_back();
depth--;
}
}
std::cout << "Done. Found " << solns.size() << " solutions." << std::endl;
}
auto backtrack_solve(Solver *solver, uint64_t working_solution = 0ul, int curr_piece = 0) -> void {
auto input = solver->input;
auto offsets = solver->offsets;
auto solutions = solver->solutions;
auto start = offsets->at(curr_piece);
auto end = offsets->at(curr_piece + 1);
auto num_pieces = offsets->size() - 1;
for (int i = start; i < end; i++) {
auto successful_fuse = !Voxel::collides(working_solution, input->at(i));
if (successful_fuse) {
auto new_working_solution = working_solution | input->at(i);
solutions->back().at(curr_piece) = input->at(i);
if (curr_piece == num_pieces - 1) {
auto last_soln = solutions->back();
solutions->push_back(SomaSolution(last_soln.begin(), last_soln.end()));
return;
} else {
backtrack_solve(solver, new_working_solution, curr_piece + 1);
}
}
}
if (curr_piece == 0) {
solutions->pop_back();
}
}
auto get_solution_rotations(SomaSolution *solution, int dims[3]) -> std::vector<SomaSolution> {
auto result = std::vector<SomaSolution>(Voxel::NUM_ROTS_3D);
for (int piece_i = 0; piece_i < solution->size(); piece_i++) {
auto space = Voxel::Space{
.space=solution->at(piece_i),
.dim_x=dims[0],
.dim_y=dims[1],
.dim_z=dims[2],
};
auto piece_rotations = Voxel::getAllRotations(&space);
for (int rot_i = 0; rot_i < piece_rotations.size(); rot_i++) {
result[rot_i].push_back(piece_rotations[rot_i].space);
}
}
return result;
}
auto filter_unique(std::vector<SomaSolution> *solutions, int dims[3]) -> std::vector<SomaSolution> {
if (solutions->size() == 0) {
return std::vector<SomaSolution>();
}
auto unique_solns = std::vector<SomaSolution>{};
for (auto &solution : *solutions) {
auto found_match = false;
for (auto &rotation : get_solution_rotations(&solution, dims)) {
for (auto &unique_soln : unique_solns) {
auto is_match = true;
for (int piece_i = 0; piece_i < unique_soln.size(); piece_i++) {
if (rotation[piece_i] != unique_soln[piece_i]) {
is_match = false;
break;
}
}
if (is_match) {
found_match = true;
break;
}
}
if (found_match) {
break;
}
}
if (!found_match) {
unique_solns.push_back(SomaSolution(solution));
}
}
return unique_solns;
}
auto solve(std::vector<uint64_t> *reprs_in, int dims[3]) -> std::vector<SomaSolution> {
auto reprs = *reprs_in;
auto offsets = std::vector<int>();
auto polycubes = std::vector<uint64_t>();
polycubes.reserve(reprs.size() * 10);
auto model_space = Voxel::Space{
.space={},
.dim_x=dims[0],
.dim_y=dims[1],
.dim_z=dims[2],
};
offsets.push_back(0);
auto space = model_space;
space.space = reprs[0];
Voxel::cullEmptySpace(&space);
auto positions = Voxel::getAllPositionsInPrism(&space, dims);
polycubes.insert(polycubes.end(), positions.begin(), positions.end());
for (int i = 1; i < reprs.size(); i++) {
offsets.push_back(polycubes.size());
auto space = model_space;
space.space = reprs[i];
Voxel::cullEmptySpace(&space);
auto perms = Voxel::getAllPermutationsInPrism(&space, dims);
polycubes.insert(polycubes.end(), perms.begin(), perms.end());
}
offsets.push_back(polycubes.size());
auto solutions = std::vector<SomaSolution>{std::vector<uint64_t>(reprs.size())};
auto solver = Solver{
.input=&polycubes,
.offsets=&offsets,
.solutions=&solutions,
};
backtrack_solve(&solver);
return filter_unique(solver.solutions, dims);
}
auto interactive_cmd_line_solve_soma() -> void {
int dims[3] = { 3, 3, 3 };
//get_dims_input(dims);
//std::cout << '\n';
//auto reprs = get_reprs_input(dims[0]*dims[1]*dims[2]);
std::cout << "Great. Calculating solutions...\n";
auto solutions = SomaSolve::solve(&SomaSolve::STD_SOMA, std::array<int, 3>{ 3, 3, 3 }.data());
std::cout << solutions.size() << " solutions found." << std::endl;
}
}

10
src/SomaSolve.h Normal file
View File

@@ -0,0 +1,10 @@
#include <cstdint>
#include <vector>
namespace SomaSolve {
extern std::vector<uint64_t> STD_SOMA;
using SomaSolution = std::vector<uint64_t>;
auto solve(std::vector<uint64_t> *reprs_in, int dims[3]) -> std::vector<SomaSolution>;
auto interactive_cmd_line_solve_soma() -> void;
}

288
src/VoxelSpace.cpp Normal file
View File

@@ -0,0 +1,288 @@
#include <vector>
#include <bitset>
#include <algorithm>
#include <iostream>
#include <cstdint>
#include "VoxelSpace.h"
namespace Voxel {
auto index(int dim_y, int dim_z, int x, int y, int z) -> int {
return dim_y * dim_z * x + dim_z * y + z;
}
// ┌ ┐ ┌ ┐ ┌ ┐
// │ 1, 0, 0 │ │ x │ │ x │
// │ 0, 0, -1 │ * │ y │ = │-z │
// │ 0, 1, 0 │ │ z │ │ y │
// └ ┘ └ ┘ └ ┘
auto newIndexRotX(Space *space, int x, int y, int z) -> int {
return space->dim_z * space->dim_y * x + space->dim_y * (space->dim_z - 1 - z) + y;
}
// ┌ ┐ ┌ ┐ ┌ ┐
// │ 0, 0, 1 │ │ x │ │ z │
// │ 0, 1, 0 │ * │ y │ = │-y │
// │ -1, 0, 0 │ │ z │ │ x │
// └ ┘ └ ┘ └ ┘
auto newIndexRotY(Space *space, int x, int y, int z) -> int {
return space->dim_y * space->dim_x * z + space->dim_x * y + (space->dim_x - 1 - x);
}
// ┌ ┐ ┌ ┐ ┌ ┐
// │ 0, -1, 0 │ │ x │ │-y │
// │ 1, 0, 0 │ * │ y │ = │ x │
// │ 0, 0, 1 │ │ z │ │ z │
// └ ┘ └ ┘ └ ┘
auto newIndexRotZ(Space *space, int x, int y, int z) -> int {
return space->dim_x * space->dim_z * (space->dim_y - 1 - y) + space->dim_z * x + z;
}
auto toggle(uint64_t space, int index) -> uint64_t {
space ^= 1ul << index;
return space;
}
auto set(uint64_t space, int index, bool val) -> uint64_t {
if (val) {
space |= 1ul << index;
} else {
space &= ~(1ul << index);
}
return space;
}
auto collides(uint64_t a, uint64_t b) -> bool {
return (a | b) != (a ^ b);
}
auto collides(Space *a, Space *b) -> bool {
return (a->space | b->space) != (a->space ^ b->space);
}
auto filledAt(Space *space, int x, int y, int z) -> bool {
auto mask = 1ul << (space->dim_y * space->dim_z * x + space->dim_z * y + z);
return (space->space & mask) != 0ul;
}
auto getExtrema(Space *space) -> Extrema {
auto extrema = Extrema{
.xMax=0,
.xMin=space->dim_x,
.yMax=0,
.yMin=space->dim_y,
.zMax=0,
.zMin=space->dim_z,
};
for (int x = 0; x < space->dim_x; x++) {
for (int y = 0; y < space->dim_y; y++) {
for (int z = 0; z < space->dim_z; z++) {
if (filledAt(space, x, y, z)) {
if (x > extrema.xMax) extrema.xMax = x;
if (x < extrema.xMin) extrema.xMin = x;
if (y > extrema.yMax) extrema.yMax = y;
if (y < extrema.yMin) extrema.yMin = y;
if (z > extrema.zMax) extrema.zMax = z;
if (z < extrema.zMin) extrema.zMin = z;
}
}
}
}
return extrema;
}
auto cullEmptySpace(Space *space) -> void {
auto extrema = getExtrema(space);
auto space_index = 0;
auto newSpace = 0ul;
for (int x = extrema.xMin; x <= extrema.xMax; x++) {
for (int y = extrema.yMin; y <= extrema.yMax; y++) {
for (int z = extrema.zMin; z <= extrema.zMax; z++) {
if (filledAt(space, x, y, z)) {
newSpace |= 1ul << space_index;
}
space_index++;
}
}
}
space->dim_x = extrema.xMax - extrema.xMin + 1;
space->dim_y = extrema.yMax - extrema.yMin + 1;
space->dim_z = extrema.zMax - extrema.zMin + 1;
space->space = newSpace;
}
auto rotate90X(Space *space) -> void {
auto new_space = 0ul;
for (int x = 0; x < space->dim_x; x++) {
for (int y = 0; y < space->dim_y; y++) {
for (int z = 0; z < space->dim_z; z++) {
if (filledAt(space, x, y, z)) {
new_space |= 1 << newIndexRotX(space, x, y, z);
}
}
}
}
auto temp = space->dim_y;
space->dim_y = space->dim_z;
space->dim_z = temp;
space->space = new_space;
}
auto rotate90Y(Space *space) -> void {
auto new_space = 0ul;
for (int x = 0; x < space->dim_x; x++) {
for (int y = 0; y < space->dim_y; y++) {
for (int z = 0; z < space->dim_z; z++) {
if (filledAt(space, x, y, z)) {
new_space |= 1 << newIndexRotY(space, x, y, z);
}
}
}
}
auto temp = space->dim_x;
space->dim_x = space->dim_z;
space->dim_z = temp;
space->space = new_space;
}
auto rotate90Z(Space *space) -> void {
auto new_space = 0ul;
for (int x = 0; x < space->dim_x; x++) {
for (int y = 0; y < space->dim_y; y++) {
for (int z = 0; z < space->dim_z; z++) {
if (filledAt(space, x, y, z)) {
new_space |= 1 << newIndexRotZ(space, x, y, z);
}
}
}
}
auto temp = space->dim_x;
space->dim_x = space->dim_y;
space->dim_y = temp;
space->space = new_space;
}
auto isMatch(Space *a, Space *b) -> bool {
return a->space == b->space
&& a->dim_x == b->dim_x
&& a->dim_y == b->dim_y
&& a->dim_z == b->dim_z;
}
auto pushNewUniqueSpins(std::vector<Space> *existingSpaces, Space* spaceToSpin) -> void {
Space spins[4] = {};
spins[0] = *spaceToSpin;
for (int i = 0; i < 3; i++) {
spins[i + 1] = spins[i];
rotate90X(&spins[i + 1]);
}
for (int i = 0; i < 4; i++) {
auto matchFound = false;
for (auto &existingSpace : *existingSpaces) {
if (isMatch(&existingSpace, &spins[i])) {
matchFound = true;
break;
}
}
if (!matchFound) {
existingSpaces->push_back(spins[i]);
}
}
}
auto pushXAxisSpins(std::vector<Space> *existingSpaces, Space* spaceToSpin) -> void {
auto refSpace = *spaceToSpin;
for (int i = 0; i < 4; i++) {
rotate90X(&refSpace);
existingSpaces->push_back(refSpace);
}
}
auto getUniqueRotations(Space *space) -> std::vector<Space> {
auto rotations = std::vector<Space>();
rotations.reserve(24);
auto refSpace = *space;
cullEmptySpace(&refSpace);
pushNewUniqueSpins(&rotations, &refSpace);
rotate90Y(&refSpace);
pushNewUniqueSpins(&rotations, &refSpace);
rotate90Y(&refSpace);
pushNewUniqueSpins(&rotations, &refSpace);
rotate90Y(&refSpace);
pushNewUniqueSpins(&rotations, &refSpace);
rotate90Z(&refSpace);
pushNewUniqueSpins(&rotations, &refSpace);
rotate90Z(&refSpace);
rotate90Z(&refSpace);
pushNewUniqueSpins(&rotations, &refSpace);
return rotations;
}
auto getAllRotations(Space *space) -> std::vector<Space> {
auto rotations = std::vector<Space>();
rotations.reserve(24);
auto refSpace = *space;
pushXAxisSpins(&rotations, &refSpace);
rotate90Y(&refSpace);
pushXAxisSpins(&rotations, &refSpace);
rotate90Y(&refSpace);
pushXAxisSpins(&rotations, &refSpace);
rotate90Y(&refSpace);
pushXAxisSpins(&rotations, &refSpace);
rotate90Z(&refSpace);
pushXAxisSpins(&rotations, &refSpace);
rotate90Z(&refSpace);
rotate90Z(&refSpace);
pushXAxisSpins(&rotations, &refSpace);
return rotations;
}
auto getAllPositionsInPrism(Space *space, int prism_dims[3]) -> std::vector<uint64_t> {
auto cubePositions = std::vector<uint64_t>();
if (space->dim_x > prism_dims[0] || space->dim_y > prism_dims[1] || space->dim_z > prism_dims[2]) {
return cubePositions;
}
auto xPositionCount = prism_dims[0] - space->dim_x + 1;
auto yPositionCount = prism_dims[1] - space->dim_y + 1;
auto zPositionCount = prism_dims[2] - space->dim_z + 1;
for (int x = 0; x < xPositionCount; x++) {
for (int y = 0; y < yPositionCount; y++) {
for (int z = 0; z < zPositionCount; z++) {
auto new_space = 0ul;
for (int posX = 0; posX < space->dim_x; posX++) {
for (int posY = 0; posY < space->dim_y; posY++) {
for (int posZ = 0; posZ < space->dim_z; posZ++) {
auto set_val = filledAt(space, posX, posY, posZ);
auto index_to_set = index(prism_dims[1], prism_dims[2], x + posX, y + posY, z + posZ);
new_space = set(new_space, index_to_set, set_val);
}
}
}
cubePositions.push_back(new_space);
}
}
}
return cubePositions;
}
auto getAllPermutationsInPrism(Space *space, int prism_dims[3]) -> std::vector<uint64_t> {
auto rotations = getUniqueRotations(space);
auto result = std::vector<uint64_t>();
for (auto &rotation : rotations) {
auto positions = getAllPositionsInPrism(&rotation, prism_dims);
result.insert(result.end(), positions.begin(), positions.end());
}
return result;
}
auto size(uint64_t space) -> int {
auto size = 0;
for (int i = 0; i < 64; i++) {
if ((space & (1ul << i)) != 0) {
size++;
}
}
return size;
}
}

68
src/VoxelSpace.h Normal file
View File

@@ -0,0 +1,68 @@
#ifndef VOXELSPACE_H
#define VOXELSPACE_H
#include <vector>
#include <cstdint>
namespace Voxel {
constexpr int NUM_ROTS_3D = 24;
struct Extrema {
int xMax;
int xMin;
int yMax;
int yMin;
int zMax;
int zMin;
};
struct Space {
uint64_t space;
int dim_x;
int dim_y;
int dim_z;
};
auto newIndexRotX(Space *space, int x, int y, int z) -> int;
auto newIndexRotY(Space *space, int x, int y, int z) -> int;
auto newIndexRotZ(Space *space, int x, int y, int z) -> int;
auto toggle(uint64_t space, int index) -> uint64_t;
auto set(uint64_t space, int index, bool val) -> uint64_t;
auto collides(Space *a, Space *b) -> bool;
auto collides(uint64_t a, uint64_t b) -> bool;
auto add(Space *a, Space *b) -> Space;
auto filledAt(Space *space, int x, int y, int z) -> bool;
auto getExtrema(Space *space) -> Extrema;
auto cullEmptySpace(Space *space) -> void;
auto isMatch(Space *a, Space *b) -> bool;
auto rotate90X(Space *space) -> void;
auto rotate90Y(Space *space) -> void;
auto rotate90Z(Space *space) -> void;
auto pushNewUniqueSpins(std::vector<Space> *existingSpaces, Space* spaceToSpin) -> void;
auto getUniqueRotations(Space *space) -> std::vector<Space>;
auto getAllRotations(Space *space) -> std::vector<Space>;
auto getAllPositionsInPrism(Space *space, int prism_dims[3]) -> std::vector<uint64_t>;
auto getAllPermutationsInPrism(Space *space, int prism_dims[3]) -> std::vector<uint64_t>;
auto size(uint64_t space) -> int;
}
#endif

60
src/gfx/Mesh.cpp Normal file
View File

@@ -0,0 +1,60 @@
#include <iostream>
#include "Mesh.h"
#include "loaders/tinyobj.h"
auto Mesh::init(const char* obj_file) -> void {
auto reader = tinyobj::ObjReader();
auto success = reader.ParseFromFile(obj_file);
std::cout << reader.Error() << std::endl;
auto attrib = reader.GetAttrib();
auto indices_t = reader.GetShapes().at(0).mesh.indices;
auto indices = std::vector<unsigned int>(indices_t.size());
for (int i = 0; i < indices_t.size(); i++) {
indices[i] = indices_t[i].vertex_index;
}
num_indices = indices.size();
glGenVertexArrays(1, &vao);
glGenBuffers(1, &vbo_xyz);
glGenBuffers(1, &vbo_uv);
glGenBuffers(1, &ebo);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo_xyz);
glBufferData(GL_ARRAY_BUFFER, attrib.vertices.size() * sizeof(float), attrib.vertices.data(), GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vbo_uv);
glBufferData(GL_ARRAY_BUFFER, attrib.texcoords.size() * sizeof(float), attrib.texcoords.data(), GL_STATIC_DRAW);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), indices.data(), GL_STATIC_DRAW);
}
auto Mesh::init(const LeddaGeometry::Shape* shape) -> void {
num_indices = shape->indices_size;
glGenVertexArrays(1, &vao);
glGenBuffers(1, &vbo_xyz);
glGenBuffers(1, &vbo_uv);
glGenBuffers(1, &ebo);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo_xyz);
glBufferData(GL_ARRAY_BUFFER, shape->xyz_size * sizeof(float), shape->xyz, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vbo_uv);
glBufferData(GL_ARRAY_BUFFER, shape->uv_size * sizeof(float), shape->uv, GL_STATIC_DRAW);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, shape->indices_size * sizeof(unsigned int), shape->indices, GL_STATIC_DRAW);
}

17
src/gfx/Mesh.h Normal file
View File

@@ -0,0 +1,17 @@
#ifndef LEDDA_MESH_H
#define LEDDA_MESH_H
#include "glad/glad.h"
#include "geometry.h"
struct Mesh {
unsigned int vao;
unsigned int vbo_xyz;
unsigned int vbo_uv;
unsigned int ebo;
unsigned int num_indices;
auto init(const char* obj_file) -> void;
auto init(const LeddaGeometry::Shape* shape) -> void;
};
#endif

View File

81
src/gfx/OrbitControls.h Normal file
View File

@@ -0,0 +1,81 @@
#ifndef ORBIT_CONTROLS_H
#define ORBIT_CONTROLS_H
#include "glad/glad.h"
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include "loaders/stb_image.h"
constexpr auto ROTATION_FACTOR = 1.0f / 200.0f;
struct Point {
float x;
float y;
};
class OrbitControls {
private:
bool dragging;
bool hovered;
bool scrolling;
bool flyingEnabled;
float lastX;
float lastY;
Point lastScroll1;
Point lastScroll2;
glm::vec3 y_axis;
glm::vec3 x_axis;
glm::vec3 start;
Entity* orbited_object;
OrbitControls(Entity* orbited, Camera* camera) {
camera = camera;
orbited_object = orbited;
y_axis = orbited_object.worldToLocal(camera.up);
x_axis = orbited_object.position.sub(camera.position);
x_axis /= sqrt(pow(x_axis.x) + pow(x_axis.y, 2) + pow(x_axis.z, 2));
x_axis = glm::cross(x_axis, y_axis);
start = orbited_object.rotation;
this.element.addEventListener('wheel', (ev) => this.handleScroll(ev));
this.element.addEventListener('mouseover', () => this.hovered = true);
this.element.addEventListener('mouseout', () => this.hovered = false);
this.element.addEventListener('mousedown', (ev) => this.handleMouseDown(ev));
window.addEventListener('mousemove', (ev) => this.handleMove(ev));
window.addEventListener('mouseup', () => this.dragging = false);
}
on_mouse_down(event) {
if (event.button === 1) {
this.object.setRotationFromEuler(this.start);
}
if (!this.dragging) {
this.lastX = event.x;
this.lastY = event.y;
this.dragging = true;
}
}
on_mouse_move(event) {
if (dragging) {
auto x_diff = event.movementX * ROTATION_FACTOR;
auto y_diff = event.movementY * ROTATION_FACTOR;
glm::rotate(&orbited_object, x_diff, &y_axis);
//rotate on world axis ???
glm::rotate(&orbited_object, y_diff &x_axis);
}
}
on_scroll(event) {
if (this.flyingEnabled && this.hovered) {
for (const fliable of this.fliables) {
const direction = event.deltaY / Math.abs(event.deltaY);
fliable.flyBy(direction / 10);
}
}
}
}
#endif

57
src/gfx/Shader.cpp Normal file
View File

@@ -0,0 +1,57 @@
#include "glad/glad.h"
#include <array>
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
#include "Shader.h"
enum ShaderType {
fragment=GL_FRAGMENT_SHADER,
vertex=GL_VERTEX_SHADER,
};
auto create_shader(const char* file_path, ShaderType shader_type, char* info_log) -> unsigned int {
std::stringstream shader_stream;
std::ifstream shader_file;
shader_file.open(file_path);
shader_stream << shader_file.rdbuf();
shader_file.close();
auto shader_string = shader_stream.str();
const auto shader_code = shader_string.c_str();
auto vertex_shader = glCreateShader(shader_type);
glShaderSource(vertex_shader, 1, &shader_code, NULL);
glCompileShader(vertex_shader);
int success;
glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(vertex_shader, 512, NULL, info_log);
auto shader_type_name = shader_type == ShaderType::fragment ? "FRAGMENT" : "VERTEX";
std::cout << "ERROR::SHADER::" << shader_type_name << "::COMPILATION_FAILED\n" << info_log << std::endl;
}
return vertex_shader;
}
auto Shader::init(const char* vertex_path, const char* fragment_path) -> void {
auto info_log = std::array<char, 512>();
auto vertex_shader = create_shader(vertex_path, ShaderType::vertex, info_log.data());
auto fragment_shader = create_shader(fragment_path, ShaderType::fragment, info_log.data());
prog_id = glCreateProgram();
glAttachShader(prog_id, vertex_shader);
glAttachShader(prog_id, fragment_shader);
glLinkProgram(prog_id);
int success;
glGetProgramiv(prog_id, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(prog_id, 512, NULL, info_log.data());
std::cout << "ERROR::SHADER::PROGRAM::LINK_FAILED\n" << info_log.data() << std::endl;
}
glDeleteShader(vertex_shader);
glDeleteShader(fragment_shader);
}

9
src/gfx/Shader.h Normal file
View File

@@ -0,0 +1,9 @@
#ifndef LEDDA_SHADER_H
#define LEDDA_SHADER_H
struct Shader {
unsigned int prog_id;
auto init(const char* vertex_path, const char* fragment_path) -> void;
};
#endif

23
src/gfx/Texture.cpp Normal file
View File

@@ -0,0 +1,23 @@
#include "Texture.h"
#include <iostream>
#include "loaders/stb_image.h"
#include "glad/glad.h"
auto Texture::init(const char* source_path) -> void {
glGenTextures(1, &tex_id);
glBindTexture(GL_TEXTURE_2D, tex_id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
int nr_channels;
auto data = stbi_load(source_path, &width, &height, &nr_channels, 0);
if (data) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
} else {
std::cout << "Failed to load texture." << std::endl;
}
stbi_image_free(data);
}

11
src/gfx/Texture.h Normal file
View File

@@ -0,0 +1,11 @@
#ifndef LEDDA_TEXTURE_H
#define LEDDA_TEXTURE_H
struct Texture {
unsigned int tex_id;
int width;
int height;
auto init(const char* source_path) -> void;
};
#endif

112
src/gfx/geometry.cpp Normal file
View File

@@ -0,0 +1,112 @@
#include <array>
#include "geometry.h"
// Buffer layout:
// X, Y, Z, U, V
auto triangle_vertices = std::to_array<float>({
-0.5f, -0.5f, 0.0f, 1.0f, 1.0f,
0.5f, -0.5f, 0.0f, 0.5f, 0.5f,
0.0f, 0.5f, 0.0f, 0.0f, 0.0f,
});
auto triangle_indices = std::to_array<unsigned int>({
0, 1, 2
});
auto cube_vertices = std::to_array<float>({
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 1.0f, 1.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f
});
auto cube_indices = std::to_array<unsigned int>({
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31
});
auto square_xyz = std::to_array<float>({
0.5f, 0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
-0.5f, 0.5f, 0.0f,
});
auto square_uv = std::to_array<float>({
1.0f, 1.0f,
1.0f, 0.0f,
0.0f, 0.0f,
0.0f, 1.0f,
});
auto square_indices = std::to_array<unsigned int>({
0, 1, 3,
1, 2, 3,
});
namespace LeddaGeometry {
const Shape TRIANGLE = {
.indices = triangle_indices.data(),
.indices_size = sizeof(triangle_indices),
.uv = triangle_vertices.data(),
.uv_size = sizeof(triangle_vertices),
.xyz = triangle_vertices.data(),
.xyz_size = sizeof(triangle_vertices),
};
const Shape SQUARE = {
.indices = square_indices.data(),
.indices_size = square_indices.size(),
.uv = square_uv.data(),
.uv_size = square_uv.size(),
.xyz = square_xyz.data(),
.xyz_size = square_xyz.size(),
};
const Shape CUBE = {
.indices = cube_indices.data(),
.indices_size = cube_indices.size(),
.uv = triangle_vertices.data(),
.uv_size = triangle_vertices.size(),
.xyz = triangle_vertices.data(),
.xyz_size = triangle_vertices.size(),
};
}

20
src/gfx/geometry.h Normal file
View File

@@ -0,0 +1,20 @@
#ifndef LEDDA_GEOMETRY_H
#define LEDDA_GEOMETRY_H
#include <cstddef>
namespace LeddaGeometry {
struct Shape {
unsigned int* indices;
size_t indices_size;
float* uv;
size_t uv_size;
float* xyz;
size_t xyz_size;
};
extern const Shape TRIANGLE;
extern const Shape SQUARE;
extern const Shape CUBE;
}
#endif

271
src/main.cpp Normal file
View File

@@ -0,0 +1,271 @@
#include <bitset>
#include <array>
#include <glm/ext/matrix_transform.hpp>
#include <span>
#include <cstdint>
#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include "glad/glad.h"
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtx/quaternion.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include "loaders/stb_image.h"
#include "gfx/geometry.h"
#include "gfx/Texture.h"
#include "gfx/Mesh.h"
#include "gfx/Shader.h"
#include "VoxelSpace.h"
#include "SomaSolve.h"
struct Camera {
glm::mat4 view;
glm::mat4 proj;
glm::vec3 pos;
glm::vec3 up;
glm::vec3 target;
auto init(float aspect_ratio = 800.0f / 600.0f) -> void {
view = glm::mat4();
proj = glm::perspective(glm::radians(45.0f), aspect_ratio, 0.1f, 100.0f);
pos = glm::vec3(0.0f, 5.0f, 0.0f);
up = glm::vec3(0.0f, 1.0f, 0.0f);
}
auto look_at(float x, float y, float z) -> void {
target = glm::vec3(x, y, z);
view = glm::lookAt(pos, target, up);
}
auto set_up(float up_x, float up_y, float up_z) -> void {
up = glm::vec3(up_x, up_y, up_z);
}
};
struct WindowDims {
unsigned int width;
unsigned int height;
};
struct TrsSpecs {
glm::vec3 translation;
glm::quat rotation;
glm::vec3 scale;
auto reset() -> void {
scale = glm::vec3(1.0f, 1.0f, 1.0f);
translation = glm::vec3(0.0f, 0.0f, 0.0f);
rotation = glm::quat(0.0f, 0.0f, 0.0f, 0.0f);
}
};
struct Entity {
glm::mat4 local;
glm::mat4 world;
TrsSpecs trs;
Mesh* mesh;
Texture* tex;
auto init() -> void {
trs.reset();
local = glm::mat4(1.0f);
}
auto update_model_mat() -> void {
local = glm::mat4(1.0f);
local = glm::scale(
glm::translate(
local,
trs.translation
) * glm::toMat4(trs.rotation),
trs.scale
);
world = local;
}
};
struct Polycube {
std::vector<Entity*>* entities;
uint8_t color;
};
struct Frame {
unsigned int width;
unsigned int height;
int x;
int y;
Camera* cam;
auto init(Camera* camera) -> void {
camera->init((float)width / (float)height);
cam = camera;
}
};
auto framebuffer_size_callback(GLFWwindow* window, int width, int height) -> void {
glViewport(0, 0, width, height);
}
auto process_input(GLFWwindow *window) -> void {
static auto wireframe = false;
static auto last_frame_state_press = false;
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
glfwSetWindowShouldClose(window, true);
} else if (glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS && !last_frame_state_press) {
glPolygonMode(GL_FRONT_AND_BACK, !wireframe ? GL_LINE : GL_FILL);
wireframe = !wireframe;
last_frame_state_press = true;
} else if (glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_RELEASE) {
last_frame_state_press = false;
}
}
auto init_window_and_gl(WindowDims* window_dims) -> GLFWwindow* {
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
auto window = glfwCreateWindow(window_dims->width, window_dims->height, "Somaesque", NULL, NULL);
if (window == NULL) {
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return nullptr;
}
glfwMakeContextCurrent(window);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
std::cout << "Failed to initilaize GLAD" << std::endl;
return nullptr;
}
glViewport(0, 0, 800, 600);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
glEnable(GL_DEPTH_TEST);
return window;
}
auto gl_update_viewport(WindowDims* window_dims, Frame* frame) -> void {
glViewport(frame->x, window_dims->height - frame->y - frame->height, frame->width, frame->height);
}
auto rotate_cam_around_centre(Camera* cam, int radius) -> void {
cam->pos.x = sin(glfwGetTime()) * radius;
cam->pos.z = cos(glfwGetTime()) * radius;
cam->look_at(0.0f, 0.0f, 0.0f);
}
auto active_shader = Shader{};
auto cube_mesh = Mesh{};
auto wall_tex = Texture{};
auto entities = std::vector<Entity>();
auto draw_entity(Entity* entity) -> void {
auto modelUniformLoc = glGetUniformLocation(active_shader.prog_id, "model");
glUniformMatrix4fv(modelUniformLoc, 1, GL_FALSE, glm::value_ptr(entity->world));
glBindTexture(GL_TEXTURE_2D, entity->tex->tex_id);
glBindVertexArray(entity->mesh->vao);
glDrawElements(GL_TRIANGLES, entity->mesh->num_indices, GL_UNSIGNED_INT, 0);
}
auto use_default_shader(Camera* cam) -> void {
glUseProgram(active_shader.prog_id);
auto viewUniformLoc = glGetUniformLocation(active_shader.prog_id, "view");
auto projectionUniformLoc = glGetUniformLocation(active_shader.prog_id, "projection");
glUniformMatrix4fv(projectionUniformLoc, 1, GL_FALSE, glm::value_ptr(cam->proj));
glUniformMatrix4fv(viewUniformLoc, 1, GL_FALSE, glm::value_ptr(cam->view));
}
auto print_mat(glm::mat4* matrix) -> void {
auto mat = *matrix;
std::cout << mat[0][0] << mat[0][1] << mat[0][2] << mat[0][3] << std::endl;
std::cout << mat[1][0] << mat[1][1] << mat[1][2] << mat[1][3] << std::endl;
std::cout << mat[2][0] << mat[2][1] << mat[2][2] << mat[2][3] << std::endl;
std::cout << mat[3][0] << mat[3][1] << mat[3][2] << mat[3][3] << std::endl;
std::cout << std::endl;
}
auto create_polycube_from_repr(Voxel::Space* repr) -> std::vector<Entity*> {
auto result = std::vector<Entity*>(Voxel::size(repr->space));
for (int x = 0; x < repr->dim_x; x++) {
for (int y = 0; y < repr->dim_y; y++) {
for (int z = 0; z < repr->dim_z; z++) {
if (Voxel::filledAt(repr, x, y, z)) {
entities.push_back({
.mesh=&cube_mesh,
.tex=&wall_tex,
});
auto polycube_segment = &entities.back();
polycube_segment->init();
polycube_segment->trs.translation = glm::vec3(
-((repr->dim_z - 1)/2) + z,
((repr->dim_x - 1)/2) - x,
-((repr->dim_y - 1)/2) + y
);
result.push_back(polycube_segment);
}
}
}
}
return result;
}
auto main() -> int {
auto window_dims = WindowDims{ 800, 600 };
auto window = init_window_and_gl(&window_dims);
if (window == nullptr) {
return -1;
}
auto little_frame = Frame{ .width=80, .height=60, .x=20, .y=20 };
auto big_frame = Frame{ .width=800, .height=600, .x=0, .y=0 };
auto main_cam = Camera{};
auto other_cam = Camera{};
little_frame.init(&other_cam);
big_frame.init(&main_cam);
auto frames = std::vector{ &big_frame, &little_frame };
active_shader.init("../assets/shaders/basic.vertex.glsl", "../assets/shaders/basic.fragment.glsl");
cube_mesh.init("../assets/models/c000000.obj");
wall_tex.init("../assets/textures/brick-wall.jpg");
auto voxel_space = Voxel::Space{
.space=SomaSolve::STD_SOMA[0],
.dim_x=3,
.dim_y=3,
.dim_z=3
};
Voxel::cullEmptySpace(&voxel_space);
auto polycube1 = create_polycube_from_repr(&voxel_space);
for (auto &entity : entities) {
entity.update_model_mat();
}
while (!glfwWindowShouldClose(window)) {
process_input(window);
rotate_cam_around_centre(big_frame.cam, 10.0f);
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
use_default_shader(big_frame.cam);
gl_update_viewport(&window_dims, &big_frame);
for (auto &entity : entities) {
entity.update_model_mat();
auto scale = glm::mat4(1.0f);
entity.world = glm::scale(glm::mat4(1.0f), glm::vec3(1.0f) * abs((float)sin(glfwGetTime()))) * entity.world;
draw_entity(&entity);
}
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate();
return 0;
}

312
src/tests.cpp Normal file
View File

@@ -0,0 +1,312 @@
#include <gtest/gtest.h>
#include <istream>
#include <tuple>
#include "VoxelSpace.h"
TEST(VoxelSpaces, BasicPositions) {
auto space = Voxel::Space{
.space=23ul,
.dim_x=3,
.dim_y=3,
.dim_z=3,
};
EXPECT_EQ(Voxel::filledAt(&space, 0, 0, 1), true);
EXPECT_EQ(Voxel::filledAt(&space, 1, 0, 0), false);
EXPECT_EQ(Voxel::filledAt(&space, 2, 1, 2), false);
EXPECT_EQ(Voxel::filledAt(&space, 1, 2, 1), false);
EXPECT_EQ(Voxel::filledAt(&space, 0, 0, 0), true);
EXPECT_EQ(Voxel::filledAt(&space, 2, 2, 1), false);
space.space = 30ul;
EXPECT_EQ(Voxel::filledAt(&space, 0, 0, 1), true);
EXPECT_EQ(Voxel::filledAt(&space, 1, 0, 0), false);
EXPECT_EQ(Voxel::filledAt(&space, 2, 1, 2), false);
EXPECT_EQ(Voxel::filledAt(&space, 1, 2, 1), false);
EXPECT_EQ(Voxel::filledAt(&space, 0, 0, 0), false);
EXPECT_EQ(Voxel::filledAt(&space, 2, 2, 1), false);
space.space = 15ul;
EXPECT_EQ(Voxel::filledAt(&space, 0, 0, 1), true);
EXPECT_EQ(Voxel::filledAt(&space, 1, 0, 0), false);
EXPECT_EQ(Voxel::filledAt(&space, 2, 1, 2), false);
EXPECT_EQ(Voxel::filledAt(&space, 1, 2, 1), false);
EXPECT_EQ(Voxel::filledAt(&space, 0, 0, 0), true);
EXPECT_EQ(Voxel::filledAt(&space, 2, 2, 1), false);
space.space = 23ul;
EXPECT_EQ(Voxel::filledAt(&space, 0, 0, 1), true);
EXPECT_EQ(Voxel::filledAt(&space, 1, 0, 0), false);
EXPECT_EQ(Voxel::filledAt(&space, 2, 1, 2), false);
EXPECT_EQ(Voxel::filledAt(&space, 1, 2, 1), false);
EXPECT_EQ(Voxel::filledAt(&space, 0, 0, 0), true);
EXPECT_EQ(Voxel::filledAt(&space, 2, 2, 1), false);
}
TEST(VoxelSpaces, RotatedIndices) {
auto space1 = Voxel::Space{
.space=172ul,
.dim_x=3,
.dim_y=3,
.dim_z=3,
};
EXPECT_EQ(Voxel::newIndexRotX(&space1, 0, 0, 0), 6);
EXPECT_EQ(Voxel::newIndexRotX(&space1, 1, 0, 1), 12);
EXPECT_EQ(Voxel::newIndexRotY(&space1, 0, 1, 0), 5);
EXPECT_EQ(Voxel::newIndexRotY(&space1, 1, 2, 0), 7);
EXPECT_EQ(Voxel::newIndexRotZ(&space1, 1, 0, 2), 23);
EXPECT_EQ(Voxel::newIndexRotZ(&space1, 0, 0, 0), 18);
}
TEST(VoxelSpaces, UniqueRotations) {
auto space1 = Voxel::Space{
.space=30ul,
.dim_x=3,
.dim_y=3,
.dim_z=3,
};
auto rotations = Voxel::getUniqueRotations(&space1);
auto expected_rots = std::vector<Voxel::Space>{
{ 30ul, 1, 2, 3 },
{ 45ul, 1, 3, 2 },
{ 30ul, 3, 2, 1 },
{ 30ul, 3, 1, 2 },
{ 45ul, 3, 2, 1 },
{ 45ul, 3, 1, 2 },
{ 51ul, 1, 2, 3 },
{ 30ul, 1, 3, 2 },
{ 30ul, 2, 3, 1 },
{ 30ul, 2, 1, 3 },
{ 51ul, 2, 3, 1 },
{ 51ul, 2, 1, 3 },
};
ASSERT_EQ(expected_rots.size(), rotations.size());
for (int i = 0; i < rotations.size(); i++) {
if (i <= expected_rots.size()) {
EXPECT_EQ(Voxel::isMatch(&expected_rots.at(i), &rotations.at(i)), true);
}
}
}
TEST(VoxelSpaces, AllPermutationsInPrism) {
auto space1 = Voxel::Space{
.space=30ul,
.dim_x=3,
.dim_y=3,
.dim_z=3,
};
auto prism_dims = std::array<int, 3>{ 3, 3, 3 };
auto perms = Voxel::getAllPermutationsInPrism(&space1, prism_dims.begin());
auto expected_perms = std::vector<uint64_t>{
30ul,
240ul,
15360ul,
122880ul,
7864320ul,
62914560ul,
153ul,
306ul,
78336ul,
156672ul,
40108032ul,
80216064ul,
266760ul,
533520ul,
1067040ul,
2134080ul,
4268160ul,
8536320ul,
263682ul,
527364ul,
2109456ul,
4218912ul,
16875648ul,
33751296ul,
2101761ul,
4203522ul,
8407044ul,
16814088ul,
33628176ul,
67256352ul,
525825ul,
1051650ul,
4206600ul,
8413200ul,
33652800ul,
67305600ul,
51ul,
408ul,
26112ul,
208896ul,
13369344ul,
106954752ul,
90ul,
180ul,
46080ul,
92160ul,
23592960ul,
47185920ul,
4680ul,
9360ul,
18720ul,
2396160ul,
4792320ul,
9584640ul,
1542ul,
12336ul,
98688ul,
789504ul,
6316032ul,
50528256ul,
36873ul,
73746ul,
147492ul,
18878976ul,
37757952ul,
75515904ul,
3075ul,
24600ul,
196800ul,
1574400ul,
12595200ul,
100761600ul,
};
ASSERT_EQ(expected_perms.size(), perms.size());
for (int i = 0; i < perms.size(); i++) {
if (i <= expected_perms.size()) {
EXPECT_EQ(expected_perms.at(i), perms.at(i));
}
}
}
TEST(VoxelSpaces, RotateXYZ) {
auto space1 = Voxel::Space{
.space=30ul,
.dim_x=3,
.dim_y=3,
.dim_z=3,
};
auto space2 = Voxel::Space{
.space=30ul,
.dim_x=3,
.dim_y=3,
.dim_z=3,
};
auto space3 = Voxel::Space{
.space=30ul,
.dim_x=3,
.dim_y=3,
.dim_z=3,
};
Voxel::rotate90X(&space1);
Voxel::rotate90Y(&space2);
Voxel::rotate90Z(&space3);
EXPECT_EQ(space1.space, 153ul);
EXPECT_EQ(space2.space, 1067040ul);
EXPECT_EQ(space3.space, 1574400ul);
}
TEST(VoxelSpaces, GetAllPositionsInPrism) {
int dims[] = {3, 3, 3};
auto space1 = Voxel::Space{
.space=30ul,
.dim_x=3,
.dim_y=3,
.dim_z=3,
};
Voxel::cullEmptySpace(&space1);
uint64_t expected_results1[] = {
30ul,
240ul,
15360ul,
122880ul,
7864320ul,
62914560ul,
};
auto space2 = Voxel::Space{
.space=23ul,
.dim_x=3,
.dim_y=3,
.dim_z=3,
};
Voxel::cullEmptySpace(&space2);
uint64_t expected_results2[] = {
23ul,
184ul,
11776ul,
94208ul,
6029312ul,
48234496ul,
};
auto space3 = Voxel::Space{
.space=15ul,
.dim_x=3,
.dim_y=3,
.dim_z=3,
};
Voxel::cullEmptySpace(&space3);
uint64_t expected_results3[] = {
15ul,
120ul,
7680ul,
61440ul,
3932160ul,
31457280ul,
};
auto positions1 = Voxel::getAllPositionsInPrism(&space1, dims);
auto positions2 = Voxel::getAllPositionsInPrism(&space2, dims);
auto positions3 = Voxel::getAllPositionsInPrism(&space3, dims);
auto mismatches1 = std::vector<std::tuple<int, uint64_t, uint64_t>>();
auto mismatches2 = std::vector<std::tuple<int, uint64_t, uint64_t>>();
auto mismatches3 = std::vector<std::tuple<int, uint64_t, uint64_t>>();
for (int i = 0; i < 6; i++) {
if (positions1[i] != expected_results1[i]) {
mismatches1.push_back({ i, positions1[i], expected_results1[i] });
}
if (positions2[i] != expected_results2[i]) {
mismatches2.push_back({ i, positions2[i], expected_results2[i] });
}
if (positions3[i] != expected_results3[i]) {
mismatches3.push_back({ i, positions3[i], expected_results3[i] });
}
}
EXPECT_EQ(mismatches1.size(), 0);
if (mismatches1.size() > 0) {
std::cout << "Index - Actual - Expected" << std::endl;
for (auto &mismatch : mismatches1) {
std::cout << std::get<0>(mismatch) << " - " << std::get<1>(mismatch) << " - " << std::get<2>(mismatch) << std::endl;
}
}
EXPECT_EQ(mismatches2.size(), 0);
if (mismatches2.size() > 0) {
std::cout << "Index - Actual - Expected" << std::endl;
for (auto &mismatch : mismatches2) {
std::cout << std::get<0>(mismatch) << " - " << std::get<1>(mismatch) << " - " << std::get<2>(mismatch) << std::endl;
}
}
EXPECT_EQ(mismatches3.size(), 0);
if (mismatches3.size() > 0) {
std::cout << "Index - Actual - Expected" << std::endl;
for (auto &mismatch : mismatches3) {
std::cout << "At " << std::get<0>(mismatch) << ": " << std::get<1>(mismatch) << " != " << std::get<2>(mismatch) << std::endl;
}
}
}