Initial WiP implementation of 2D grid raytracing. Not working.

This commit is contained in:
King_DuckZ 2017-02-08 16:39:46 +00:00
parent f91ff6625f
commit e2d307da52
7 changed files with 271 additions and 7 deletions

View file

@ -7,7 +7,7 @@ include(TargetArch)
include(FindPkgConfig)
include(CTest)
set(common_gcc_flags "-Wall -Wextra -pedantic -Wconversion")
set(common_gcc_flags "-Wall -Wextra -pedantic -Wconversion -ffast-math")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${common_gcc_flags} -O0")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${common_gcc_flags}")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${common_gcc_flags} -O0")
@ -72,6 +72,7 @@ add_executable(${PROJECT_NAME}
src/worlditems.cpp
src/moveable.cpp
src/fsgn.cpp
src/grid_raytrace.cpp
)
target_include_directories(${PROJECT_NAME} SYSTEM

@ -1 +1 @@
Subproject commit fc7b66642959e7de19aade010b6ebc1e59ba4b7a
Subproject commit aaa8e75dc125e4116bed76ed14cbfc028bf6e9c7

View file

@ -21,13 +21,12 @@
#include "inputbag.hpp"
#include "gameactions.hpp"
#include "worldgrid.hpp"
#include "grid_raytrace.hpp"
#include <ciso646>
#include <cassert>
#include <cstdint>
namespace curry {
namespace {
} //unnamed namespace
Character::Character (cloonel::InputBag* parInputBag, const WorldSizeNotifiable::DeferredRegister& parDeferredRegister) :
WorldSizeNotifiable(parDeferredRegister),
m_position(vec2f(0.0f), vec2f(0.0f), vec2f(0.0f)),
@ -102,9 +101,30 @@ namespace curry {
const auto old_pos = this->position();
const auto new_pos = old_pos + offset;
//if (m_world
this->set_position(new_pos);
if (position() == old_pos)
return;
vec2us last_valid_pos(0xFFFF);
auto is_walkable = [](const WorldTileProperty& wtp) {
return wtp.property->walkable;
};
for_each_voxel_under_segment(old_pos, this->position(), m_texture.width_height(), *m_world, is_walkable);
//for (auto tile_vec : crossed_tiles(old_pos, this->position(), m_world->tile_size())) {
// const TileIndex* const tile = m_world->tile(tile_vec);
// for (uint16_t z = 0; z < m_world->layer_count(); ++z) {
// const auto& tile_prop = m_world->tile_property(tile + z);
// if (not tile_prop.walkable) {
// assert(last_valid_pos != vec2us(0xFFFF));
// this->set_position(vector_cast<vec2f>(last_valid_pos));
// }
// else {
// last_valid_pos = tile_vec;
// }
// }
//}
}
void Character::set_speed (float parSpeed) {

124
src/grid_raytrace.cpp Normal file
View file

@ -0,0 +1,124 @@
/*
Copyright 2016, 2017 Michele "King_DuckZ" Santullo
This file is part of MyCurry.
MyCurry is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
MyCurry is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with MyCurry. If not, see <http://www.gnu.org/licenses/>.
*/
#include "grid_raytrace.hpp"
#include "worldgrid.hpp"
#include "vectorwrapper/vectorops.hpp"
#include "fsgn.hpp"
#include "tileproperty.hpp"
#include <cassert>
#include <ciso646>
#include <cstdint>
#include <cmath>
#include <cfloat>
#include <algorithm>
namespace curry {
namespace {
float inv_length (const vec2f& parVec) {
return 1.0f / std::sqrt(parVec.x() * parVec.x() + parVec.y() * parVec.y());
}
vec2f abs (const vec2f& parVec) {
return vec2f(std::abs(parVec.x()), std::abs(parVec.y()));
}
float segment_intersection (const vec2f& parA, const vec2f& parDirA, const vec2f& parB, const vec2f& parDirB) {
const auto& p = parA;
const auto& r = parDirA;
const auto& q = parB;
const auto& s = parDirB;
const auto r_cross_s = cross(r, s);
if (std::abs(r_cross_s) <= FLT_EPSILON) {
if (std::abs(cross(q - p, r)) <= FLT_EPSILON) {
assert(std::abs(dot(r, r)) > FLT_EPSILON);
auto t0 = dot(q - p, r) / dot(r, r);
auto t1 = dot(q - p + s, r) / dot(r, r);
if (dot(s, r) < 0.0f)
std::swap(t0, t1);
assert(t0 <= t1);
if (0.0f <= t0)
return t0;
else if (t1 <= 1.0f)
return t1;
else
return INFINITY;
}
else {
return INFINITY;
}
}
assert(not (std::abs(r_cross_s) <= FLT_EPSILON));
const auto inv_r_cross_s = 1.0f / r_cross_s;
const auto t = cross(q - p, s) * inv_r_cross_s;
const auto u = cross(q - p, r) * inv_r_cross_s;
return t;
//if (0.0f <= t and t <= 1.0f and 0.0f <= u and u <= 1.0f)
//return t;
//else
//return INFINITY;
}
} //unnamed namespace
//see:
//http://stackoverflow.com/questions/24679963/precise-subpixel-line-drawing-algorithm-rasterization-algorithm
void for_each_voxel_under_segment (const vec2f& parFrom, const vec2f& parTo, vec2us parObjSize, const WorldGrid& parWorld, std::function<bool(const WorldTileProperty&)> parFunc) {
const vec2f tile_size = vector_cast<vec2f>(parWorld.tile_size());
//in this simplified case everything should be part of the world grid
assert(parFrom >= vec2f(0.0f));
assert(parTo >= vec2f(0.0f));
assert(parFrom / tile_size <= vector_cast<vec2f>(parWorld.world_size()));
assert(parTo / tile_size <= vector_cast<vec2f>(parWorld.world_size()));
float t = 0.0f;
const vec2f& u = parFrom;
const vec2f v = parTo - parFrom;
vec2us start = pixel_to_world_tile(parWorld, u);
vec2f step(fsgn(v.x()), fsgn(v.y()));
const auto next_tile_boundary = (step + abs(step)) / 2.0f;
const auto intersection = vec2f(
segment_intersection(
u,
v,
vec2f(static_cast<float>(start.x() + next_tile_boundary.x()), 0.0f),
vec2f(0.0f, static_cast<float>(parWorld.world_size().y()) * tile_size.y())
),
segment_intersection(
u,
v,
vec2f(0.0f, static_cast<float>(start.y()) + next_tile_boundary.y()),
vec2f(static_cast<float>(parWorld.world_size().x()) * tile_size.x(), 0.0f)
)
);
const auto delta = vec2f(
std::isinf(intersection.x()) ? 0.0f : tile_size.x() / dot(vec2f(1.0f, 0.0f), v * inv_length(v)),
0.0f //std::isinf(intersection.y()) ? 0.0f : tile_size.y() /
);
//if (not std::isinf(intersection_vert)) {
//todo: continuare da qui
//}
}
} //namespace curry

39
src/grid_raytrace.hpp Normal file
View file

@ -0,0 +1,39 @@
/*
Copyright 2016, 2017 Michele "King_DuckZ" Santullo
This file is part of MyCurry.
MyCurry is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
MyCurry is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with MyCurry. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "vector.hpp"
#include <functional>
namespace curry {
class WorldGrid;
class TileProperty;
struct WorldTileProperty {
vec2us index;
const TileProperty* property;
bool operator== (const WorldTileProperty& parOther) const {
return parOther.index == index && parOther.property == property;
}
};
void for_each_voxel_under_segment ( const vec2f& parFrom, const vec2f& parTo, vec2us parObjSize, const WorldGrid& parWorld, std::function<bool(const WorldTileProperty&)> parFunc );
} //namespace curry

View file

@ -5,6 +5,10 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_executable(${PROJECT_NAME}
main.cpp
${MYCURRY_SOURCE_DIR}/grid_raytrace.cpp
grid_raytrace.cpp
${MYCURRY_SOURCE_DIR}/worldgrid.cpp
${MYCURRY_SOURCE_DIR}/fsgn.cpp
)
target_compile_definitions(${PROJECT_NAME}

View file

@ -0,0 +1,76 @@
/*
Copyright 2016, 2017 Michele "King_DuckZ" Santullo
This file is part of MyCurry.
MyCurry is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
MyCurry is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with MyCurry. If not, see <http://www.gnu.org/licenses/>.
*/
#include "catch.hpp"
#include "grid_raytrace.hpp"
#include "worldgrid.hpp"
#include "tileindextype.hpp"
#include "tileproperty.hpp"
#include <vector>
TEST_CASE ("Check that 2D raytracing works", "[raytracing, geometry]") {
using curry::for_each_voxel_under_segment;
using curry::vec2us;
using curry::vec2f;
using curry::WorldTileProperty;
curry::WorldGrid world(vec2us(64));
world.set_layers(vec2us(10, 10), std::vector<std::vector<curry::TileIndex>>( {{
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1
}}));
world.set_tile_properties(std::vector<curry::TileProperty>({ curry::TileProperty() }));
REQUIRE(world.world_size() == vec2us(10));
REQUIRE(world.layer_count() == 1);
{
std::vector<WorldTileProperty> diagonal;
for_each_voxel_under_segment (
vec2f(0.0f, 0.0f), //from
vec2f(640.0f, 640.0f), //to
vec2us(1, 1), //objsize
world, //world
[diagonal](const WorldTileProperty& wtp) mutable {diagonal.push_back(wtp); return true;}
);
const auto* tile_prop = &world.tile_property(world.tile(vec2us(0)));
std::vector<WorldTileProperty> expected {
WorldTileProperty{vec2us(0, 0), tile_prop},
WorldTileProperty{vec2us(1, 1), tile_prop},
WorldTileProperty{vec2us(2, 2), tile_prop},
WorldTileProperty{vec2us(3, 3), tile_prop},
WorldTileProperty{vec2us(4, 4), tile_prop},
WorldTileProperty{vec2us(5, 5), tile_prop},
WorldTileProperty{vec2us(6, 6), tile_prop},
WorldTileProperty{vec2us(7, 7), tile_prop},
WorldTileProperty{vec2us(8, 8), tile_prop},
WorldTileProperty{vec2us(9, 9), tile_prop}
};
CHECK(expected == diagonal);
}
}