From e2d307da522ef4d7ccaf762aa3c1e5465593c52e Mon Sep 17 00:00:00 2001 From: King_DuckZ Date: Wed, 8 Feb 2017 16:39:46 +0000 Subject: [PATCH] Initial WiP implementation of 2D grid raytracing. Not working. --- CMakeLists.txt | 3 +- lib/vectorwrapper | 2 +- src/character.cpp | 30 +++++++-- src/grid_raytrace.cpp | 124 ++++++++++++++++++++++++++++++++++++ src/grid_raytrace.hpp | 39 ++++++++++++ test/unit/CMakeLists.txt | 4 ++ test/unit/grid_raytrace.cpp | 76 ++++++++++++++++++++++ 7 files changed, 271 insertions(+), 7 deletions(-) create mode 100644 src/grid_raytrace.cpp create mode 100644 src/grid_raytrace.hpp create mode 100644 test/unit/grid_raytrace.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index afe2770..f98758e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/lib/vectorwrapper b/lib/vectorwrapper index fc7b666..aaa8e75 160000 --- a/lib/vectorwrapper +++ b/lib/vectorwrapper @@ -1 +1 @@ -Subproject commit fc7b66642959e7de19aade010b6ebc1e59ba4b7a +Subproject commit aaa8e75dc125e4116bed76ed14cbfc028bf6e9c7 diff --git a/src/character.cpp b/src/character.cpp index 971e4f9..7af063c 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -21,13 +21,12 @@ #include "inputbag.hpp" #include "gameactions.hpp" #include "worldgrid.hpp" +#include "grid_raytrace.hpp" #include #include +#include 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(last_valid_pos)); + // } + // else { + // last_valid_pos = tile_vec; + // } + // } + //} } void Character::set_speed (float parSpeed) { diff --git a/src/grid_raytrace.cpp b/src/grid_raytrace.cpp new file mode 100644 index 0000000..5fd8519 --- /dev/null +++ b/src/grid_raytrace.cpp @@ -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 . +*/ + +#include "grid_raytrace.hpp" +#include "worldgrid.hpp" +#include "vectorwrapper/vectorops.hpp" +#include "fsgn.hpp" +#include "tileproperty.hpp" +#include +#include +#include +#include +#include +#include + +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 parFunc) { + const vec2f tile_size = vector_cast(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(parWorld.world_size())); + assert(parTo / tile_size <= vector_cast(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(start.x() + next_tile_boundary.x()), 0.0f), + vec2f(0.0f, static_cast(parWorld.world_size().y()) * tile_size.y()) + ), + segment_intersection( + u, + v, + vec2f(0.0f, static_cast(start.y()) + next_tile_boundary.y()), + vec2f(static_cast(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 diff --git a/src/grid_raytrace.hpp b/src/grid_raytrace.hpp new file mode 100644 index 0000000..f3ab590 --- /dev/null +++ b/src/grid_raytrace.hpp @@ -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 . +*/ + +#pragma once + +#include "vector.hpp" +#include + +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 parFunc ); +} //namespace curry diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 548fe84..78f141b 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -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} diff --git a/test/unit/grid_raytrace.cpp b/test/unit/grid_raytrace.cpp new file mode 100644 index 0000000..43970f4 --- /dev/null +++ b/test/unit/grid_raytrace.cpp @@ -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 . +*/ + +#include "catch.hpp" +#include "grid_raytrace.hpp" +#include "worldgrid.hpp" +#include "tileindextype.hpp" +#include "tileproperty.hpp" +#include + +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>( {{ + 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() })); + + REQUIRE(world.world_size() == vec2us(10)); + REQUIRE(world.layer_count() == 1); + + { + std::vector 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 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); + } +}