added new cmake setup, graphics, vendors, obj importer, etc.
This commit is contained in:
254
src/SomaSolve.cpp
Normal file
254
src/SomaSolve.cpp
Normal 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
10
src/SomaSolve.h
Normal 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
288
src/VoxelSpace.cpp
Normal 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
68
src/VoxelSpace.h
Normal 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
60
src/gfx/Mesh.cpp
Normal 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
17
src/gfx/Mesh.h
Normal 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
|
||||
0
src/gfx/OrbitControls.cpp
Normal file
0
src/gfx/OrbitControls.cpp
Normal file
81
src/gfx/OrbitControls.h
Normal file
81
src/gfx/OrbitControls.h
Normal 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
57
src/gfx/Shader.cpp
Normal 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
9
src/gfx/Shader.h
Normal 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
23
src/gfx/Texture.cpp
Normal 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
11
src/gfx/Texture.h
Normal 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
112
src/gfx/geometry.cpp
Normal 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
20
src/gfx/geometry.h
Normal 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
271
src/main.cpp
Normal 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
312
src/tests.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user