diff --git a/src/maths/rays.cpp b/src/maths/rays.cpp new file mode 100644 index 00000000..711c660f --- /dev/null +++ b/src/maths/rays.cpp @@ -0,0 +1,333 @@ + +#include "rays.h" +#include "aabb.h" + +#include "glm/glm.hpp" + +constexpr std::array AABBFACES_KINDS_ORDER = { + AAFaceKind::Xperp, AAFaceKind::Xperp, + AAFaceKind::Yperp, AAFaceKind::Yperp, + AAFaceKind::Zperp, AAFaceKind::Zperp}; +std::unordered_map Rays::raysBoxCache_ = {}; +const rayvec3 X_AXIS = rayvec3(1,0,0), Y_AXIS = rayvec3(0,1,0), Z_AXIS = rayvec3(0,0,1); + +//make edges from AABB +AABBFaces::AABBFaces(const rayvec3& parentBoxPos, const AABB& parentBox){ + rayvec3 pbMin = parentBox.min(), + pbMax = parentBox.max(), + pbRealPos = parentBoxPos + pbMin; + rayvec2 yzMax = rayvec2(parentBoxPos.y + pbMax.y, parentBoxPos.z + pbMax.z ), + xzMax = rayvec2(parentBoxPos.x + pbMax.x, parentBoxPos.z + pbMax.z ), + xyMax = rayvec2(parentBoxPos.x + pbMax.x, parentBoxPos.y + pbMax.y ); + faces[0] = {pbRealPos, yzMax}; + + faces[1] = {parentBoxPos + rayvec3(pbMax.x, pbMin.y, pbMin.z), yzMax}; + + faces[2] = {pbRealPos, xzMax}; + + faces[3] = {parentBoxPos + rayvec3(pbMin.x, pbMax.y, pbMin.z), xzMax}; + + faces[4] = {pbRealPos, xyMax}; + + faces[5] = {parentBoxPos + rayvec3(pbMin.x, pbMin.y, pbMax.z), xyMax}; +} + +template <> +RayRelation Rays::rayIntersectAAFace( + const rayvec3& rayOrigin, + const rayvec3& rayDir, + const rayvec3& faceMin, + const rayvec2& faceOppositeCorner, //y and z global coords of opposite corner + rayvec3& intersectPoint_ret + ){ + if (fabs(glm::dot(rayDir, X_AXIS)) < 1.0E-8){ //precision + if (rayOrigin.x == faceMin.x) { + return RayRelation::Embed; // there can be check of hit, but not necessarily + } + return RayRelation::Parallel; + } + + glm::float64 rayCoef = (faceMin.x - rayOrigin.x) / (rayDir.x); + intersectPoint_ret = {faceMin.x, + rayCoef*rayDir.y + rayOrigin.y, + rayCoef*rayDir.z + rayOrigin.z}; + + if (rayDir.x > 0){ + if (intersectPoint_ret.y >= faceMin.y + && intersectPoint_ret.y <= faceOppositeCorner[0] + && intersectPoint_ret.z >= faceMin.z + && intersectPoint_ret.z <= faceOppositeCorner[1]){ + return RayRelation::Intersect; + } + } + else{ + if (intersectPoint_ret.y <= faceMin.y + && intersectPoint_ret.y >= faceOppositeCorner[0] + && intersectPoint_ret.z <= faceMin.z + && intersectPoint_ret.z >= faceOppositeCorner[1]){ + return RayRelation::Intersect; + } + } + return RayRelation::None; +} + +template <> +RayRelation Rays::rayIntersectAAFace( + const rayvec3& rayOrigin, + const rayvec3& rayDir, + const rayvec3& faceMin, + const rayvec2& faceOppositeCorner, //x and z global coords of opposite corner + rayvec3& intersectPoint_ret + ){ + if (fabs(glm::dot(rayDir, Y_AXIS)) < 1.0E-8){ //precision + if (rayOrigin.y == faceMin.y) { + return RayRelation::Embed; // there can be check of hit, but not necessarily + } + return RayRelation::Parallel; + } + + glm::float64 rayCoef = (faceMin.y - rayOrigin.y) / (rayDir.y); + intersectPoint_ret = {rayCoef*rayDir.x + rayOrigin.x, + faceMin.y, + rayCoef*rayDir.z + rayOrigin.z}; + + if (rayDir.y > 0){ + if (intersectPoint_ret.x >= faceMin.x //Face-hit check + && intersectPoint_ret.x <= faceOppositeCorner[0] + && intersectPoint_ret.z >= faceMin.z + && intersectPoint_ret.z <= faceOppositeCorner[1] ){ + return RayRelation::Intersect; + } + } + else{ + if (intersectPoint_ret.x <= faceMin.x //Face-hit check for negative dir. + && intersectPoint_ret.x >= faceOppositeCorner[0] + && intersectPoint_ret.z <= faceMin.z + && intersectPoint_ret.z >= faceOppositeCorner[1]){ + return RayRelation::Intersect; + } + } + return RayRelation::None; +} + +template <> +RayRelation Rays::rayIntersectAAFace( + const rayvec3& rayOrigin, + const rayvec3& rayDir, + const rayvec3& faceMin, + const rayvec2& faceOppositeCorner, //x and y global coords of opposite corner + rayvec3& intersectPoint_ret + ){ + if (fabs(glm::dot(rayDir, Z_AXIS)) < 1.0E-8){ //precision + if (rayOrigin.z == faceMin.z) { + return RayRelation::Embed; // there can be check of hit, but not necessarily + } + return RayRelation::Parallel; + } + + glm::float64 rayCoef = (faceMin.z - rayOrigin.z) / (rayDir.z); + intersectPoint_ret = {rayCoef*rayDir.x + rayOrigin.x, + rayCoef*rayDir.y + rayOrigin.y, + faceMin.z}; + + if (rayDir.y > 0){ + if (intersectPoint_ret.x >= faceMin.x //Face-hit check + && intersectPoint_ret.x <= faceOppositeCorner[0] + && intersectPoint_ret.y >= faceMin.y + && intersectPoint_ret.y <= faceOppositeCorner[1] ){ + return RayRelation::Intersect; + } + } + else{ + if (intersectPoint_ret.x <= faceMin.x //Face-hit check + && intersectPoint_ret.x >= faceOppositeCorner[0] + && intersectPoint_ret.y <= faceMin.y + && intersectPoint_ret.y >= faceOppositeCorner[1] ){ + return RayRelation::Intersect; + } + } + return RayRelation::None; +} + +template <> +RayRelation Rays::isRayIntersectsAAFace( + const rayvec3& rayOrigin, + const rayvec3& rayDir, + const rayvec3& faceMin, + const rayvec2& faceOppositeCorner + ){ + if (fabs(glm::dot(rayDir, X_AXIS)) < 1.0E-8){ //precision of "parallelity" + if (rayOrigin.x == faceMin.x) { + return RayRelation::Embed; // there can be check of hit, but not necessarily + } + return RayRelation::Parallel; + } + + glm::float64 rayCoef = (faceMin.x - rayOrigin.x); + rayvec3 intersectPointMult = {faceMin.x * rayDir.x, + rayCoef*rayDir.y + rayOrigin.y * rayDir.x, + rayCoef*rayDir.z + rayOrigin.z * rayDir.x}; + + if (rayDir.x > 0){ + if (intersectPointMult.y >= faceMin.y * rayDir.x //Face-hit check + && intersectPointMult.y <= faceOppositeCorner[0] * rayDir.x + && intersectPointMult.z >= faceMin.z * rayDir.x + && intersectPointMult.z <= faceOppositeCorner[1] * rayDir.x){ + return RayRelation::Intersect; + } + } + else{ + if (intersectPointMult.y <= faceMin.y * rayDir.x //Face-hit check for negative dir. + && intersectPointMult.y >= faceOppositeCorner[0] * rayDir.x + && intersectPointMult.z <= faceMin.z * rayDir.x + && intersectPointMult.z >= faceOppositeCorner[1] * rayDir.x){ + return RayRelation::Intersect; + } + } + return RayRelation::None; +} + +template <> +RayRelation Rays::isRayIntersectsAAFace( + const rayvec3& rayOrigin, + const rayvec3& rayDir, + const rayvec3& faceMin, + const rayvec2& faceOppositeCorner + ){ + if (fabs(glm::dot(rayDir, Y_AXIS)) < 1.0E-8){ //precision + if (rayOrigin.y == faceMin.y) { + return RayRelation::Embed; // there can be check of hit, but not necessarily + } + return RayRelation::Parallel; + } + + glm::float64 rayCoef = (faceMin.y - rayOrigin.y); + rayvec3 intersectPointMult = {rayCoef*rayDir.x + rayOrigin.x * rayDir.y, + faceMin.y * rayDir.y, + rayCoef*rayDir.z + rayOrigin.z * rayDir.y}; + + if (rayDir.y > 0){ + if (intersectPointMult.x >= faceMin.x * rayDir.y //Face-hit check + && intersectPointMult.x <= faceOppositeCorner[0] * rayDir.y + && intersectPointMult.z >= faceMin.z * rayDir.y + && intersectPointMult.z <= faceOppositeCorner[1] * rayDir.y){ + return RayRelation::Intersect; + } + } + else{ + if (intersectPointMult.x <= faceMin.x * rayDir.y //Face-hit check for negative dir. + && intersectPointMult.x >= faceOppositeCorner[0] * rayDir.y + && intersectPointMult.z <= faceMin.z * rayDir.y + && intersectPointMult.z >= faceOppositeCorner[1] * rayDir.y){ + return RayRelation::Intersect; + } + } + return RayRelation::None; +} + +template <> +RayRelation Rays::isRayIntersectsAAFace( + const rayvec3& rayOrigin, + const rayvec3& rayDir, + const rayvec3& faceMin, + const rayvec2& faceOppositeCorner + ){ + if (fabs(glm::dot(rayDir, Z_AXIS)) < 1.0E-8){ //precision + if (rayOrigin.z == faceMin.z) { + return RayRelation::Embed; // there can be check of hit, but not necessarily + } + return RayRelation::Parallel; + } + + glm::float64 rayCoef = (faceMin.z - rayOrigin.z); + rayvec3 intersectPointMult = {rayCoef*rayDir.x + rayOrigin.x * rayDir.z, + rayCoef*rayDir.y + rayOrigin.y * rayDir.z, + faceMin.z * rayDir.z}; + + if (rayDir.y > 0){ + if (intersectPointMult.x >= faceMin.x * rayDir.z //Face-hit check + && intersectPointMult.x <= faceOppositeCorner[0] * rayDir.z + && intersectPointMult.y >= faceMin.y * rayDir.z + && intersectPointMult.y <= faceOppositeCorner[1] * rayDir.z){ + return RayRelation::Intersect; + } + } + else{ + if (intersectPointMult.x <= faceMin.x * rayDir.z //Face-hit check + && intersectPointMult.x >= faceOppositeCorner[0] * rayDir.z + && intersectPointMult.y <= faceMin.y * rayDir.z + && intersectPointMult.y >= faceOppositeCorner[1] * rayDir.z){ + return RayRelation::Intersect; + } + } + return RayRelation::None; +} + +RayRelation Rays::rayIntersectAABB( + const rayvec3& rayOrigin, + const rayvec3& rayDir, + const rayvec3& boxPos, + const AABB& box, + rayvec3& pointIn_ret, + rayvec3& pointOut_ret, + glm::ivec3& normal_ret){ + if constexpr (IS_RAYS_BOX_CACHE_ON){ + + if (raysBoxCache_.find(boxPos) != raysBoxCache_.end()){ + const AABBFaces& boxFaces = raysBoxCache_[boxPos]; + return rayIntersectAABBFaces(rayOrigin, rayDir, boxFaces, pointIn_ret, pointOut_ret, normal_ret); + } else { + const AABBFaces& boxFaces = AABBFaces(boxPos, box); + raysBoxCache_[boxPos] = boxFaces; + return rayIntersectAABBFaces(rayOrigin, rayDir, boxFaces, pointIn_ret, pointOut_ret, normal_ret); + } + + } else { + const AABBFaces& boxFaces = AABBFaces(boxPos, box); + return rayIntersectAABBFaces(rayOrigin, rayDir, boxFaces, pointIn_ret, pointOut_ret, normal_ret); + } + +} + +RayRelation Rays::rayIntersectAABBFaces( + const rayvec3& rayOrigin, + const rayvec3& rayDir, + const AABBFaces& boxFaces, + rayvec3& pointIn_ret, + rayvec3& pointOut_ret, + glm::ivec3& normal_ret){//TODO: refs update + RayRelation rel; + unsigned char intersectedCount = 0; + rel = isRayIntersectsAAFace( + rayOrigin, rayDir, boxFaces.faces[0].first, boxFaces.faces[0].second + ); + intersectedCount+= (bool)rel; + + rel = isRayIntersectsAAFace( + rayOrigin, rayDir, boxFaces.faces[1].first, boxFaces.faces[1].second + ); + intersectedCount+= (bool)rel; + + rel = isRayIntersectsAAFace( + rayOrigin, rayDir, boxFaces.faces[2].first, boxFaces.faces[2].second + ); + intersectedCount+= (bool)rel; + + rel = isRayIntersectsAAFace( + rayOrigin, rayDir, boxFaces.faces[3].first, boxFaces.faces[3].second + ); + intersectedCount+= (bool)rel; + + rel = isRayIntersectsAAFace( + rayOrigin, rayDir, boxFaces.faces[4].first, boxFaces.faces[4].second + ); + intersectedCount+= (bool)rel; + + rel = isRayIntersectsAAFace( + rayOrigin, rayDir, boxFaces.faces[5].first, boxFaces.faces[5].second + ); + intersectedCount+= (bool)rel; + + if (intersectedCount > 0) return RayRelation::Intersect; + return RayRelation::None; +} \ No newline at end of file diff --git a/src/maths/rays.h b/src/maths/rays.h new file mode 100644 index 00000000..94293bb6 --- /dev/null +++ b/src/maths/rays.h @@ -0,0 +1,81 @@ +#ifndef MATHS_RAYS_H_ +#define MATHS_RAYS_H_ + +// #include "../typedefs.h" +#include "aabb.h" +#include "glm/glm.hpp" + +#include + +typedef glm::highp_dvec3 rayvec3; +typedef glm::highp_dvec2 rayvec2; + +enum class RayRelation{ + Embed=2, Intersect=1, Parallel=0, None=0 +}; +enum class AAFaceKind : unsigned char{ + Xperp=0, Yperp=1, Zperp=2 //perpendicular faces to corresponding axis +}; + +const unsigned char AABBFACES_COUNT = 6; + +class AABBFaces{ +public: + std::array, AABBFACES_COUNT> faces; // every face is min-point and opposite corner point + + AABBFaces(){}; + AABBFaces(const rayvec3& parentBoxPos, const AABB& parentBox); + +}; + +template<> +struct std::hash{ + std::size_t operator()(const rayvec3& r) const noexcept{ + return std::hash{}(r.x) ^ (std::hash{}(r.y) << 1) ^ (std::hash{}(r.z) << 2); + } +}; + +class Rays{ +protected: + static const bool IS_RAYS_BOX_CACHE_ON = false; + static std::unordered_map raysBoxCache_; //[boxPos]: faces array + +public: + +template +static RayRelation rayIntersectAAFace( + const rayvec3& rayOrigin, + const rayvec3& rayDir, + const rayvec3& faceMin, + const rayvec2& faceOppositeCorner, + rayvec3& intersectPoint_ret + ); + +//optimized, not returns intersectPoint coordinates +template +static RayRelation isRayIntersectsAAFace( + const rayvec3& rayOrigin, + const rayvec3& rayDir, + const rayvec3& faceMin, + const rayvec2& faceOppositeCorner + ); + +static RayRelation rayIntersectAABB( + const rayvec3& rayOrigin, + const rayvec3& rayDir, + const rayvec3& boxPos, + const AABB& box, + rayvec3& pointIn_ret, + rayvec3& pointOut_ret, + glm::ivec3& normal_ret); + +static RayRelation rayIntersectAABBFaces( + const rayvec3& rayOrigin, + const rayvec3& rayDir, + const AABBFaces& boxFaces, + rayvec3& pointIn_ret, + rayvec3& pointOut_ret, + glm::ivec3& normal_ret); +}; + +#endif // SRC_VOXNATHS_H_ diff --git a/src/voxels/Chunks.cpp b/src/voxels/Chunks.cpp index 7d46bc44..60172e6e 100644 --- a/src/voxels/Chunks.cpp +++ b/src/voxels/Chunks.cpp @@ -10,6 +10,8 @@ #include "../graphics/Mesh.h" #include "../maths/voxmaths.h" +#include "../maths/aabb.h" +#include "../maths/rays.h" #include #include @@ -55,7 +57,7 @@ voxel* Chunks::get(int x, int y, int z){ if (z < 0) cz--; if (cx < 0 || cy < 0 || cz < 0 || cx >= w || cy >= 1 || cz >= d) return nullptr; - shared_ptr chunk = chunks[(cy * d + cz) * w + cx]; + shared_ptr chunk = chunks[cz * w + cx]; // chunks is 2D-array if (chunk == nullptr) return nullptr; int lx = x - cx * CHUNK_W; @@ -175,6 +177,7 @@ void Chunks::set(int x, int y, int z, int id, uint8_t states){ chunk->setModified(true); } #include "../util/timeutil.h" +#include voxel* Chunks::rayCast(vec3 start, vec3 dir, float maxDist, @@ -212,33 +215,37 @@ voxel* Chunks::rayCast(vec3 start, float tyMax = (tyDelta < infinity) ? tyDelta * ydist : infinity; float tzMax = (tzDelta < infinity) ? tzDelta * zdist : infinity; - int steppedIndex = -1; + int steppedIndex = -1; + + while (t <= maxDist){ + voxel* voxel = get(ix, iy, iz); + if (!voxel){ return nullptr; } - while (t <= maxDist){ - voxel* voxel = get(ix, iy, iz); - const Block* def = nullptr; - if (voxel == nullptr || (def = contentIds->getBlockDef(voxel->id))->selectable){ + const Block* def = contentIds->getBlockDef(voxel->id); + if (def->selectable){ timeutil::ScopeLogTimer lg((long long)def); end.x = px + t * dx; end.y = py + t * dy; end.z = pz + t * dz; + iend.x = ix; + iend.y = iy; + iend.z = iz; - // TODO: replace this dumb solution with something better if (def && !def->rt.solid) { const int gridSize = BLOCK_AABB_GRID * 2; const AABB& box = def->rotatable ? def->rt.hitboxes[voxel->rotation()] : def->hitbox; - const int subs = gridSize; + /*const int subs = gridSize; iend = vec3(ix, iy, iz); - end -= iend; + end -= iend; // in-block coordinates int six = end.x * gridSize; int siy = end.y * gridSize; int siz = end.z * gridSize; float stxMax = (txDelta < infinity) ? txDelta * xdist : infinity; float styMax = (tyDelta < infinity) ? tyDelta * ydist : infinity; float stzMax = (tzDelta < infinity) ? tzDelta * zdist : infinity; - for (int i = 0; i < subs*2; i++) { + for (int i = 0; i < subs*3; i++) { end.x = six / float(gridSize); end.y = siy / float(gridSize); end.z = siz / float(gridSize); @@ -271,7 +278,17 @@ voxel* Chunks::rayCast(vec3 start, steppedIndex = 2; } } + }*/ + rayvec3 in, out; + glm::ivec3 normal; + if ((bool)Rays::rayIntersectAABB(start, dir, iend, box, in, out, normal)){ + norm.x = norm.y = norm.z = 0.0f; + if (steppedIndex == 0) norm.x = -stepx; + if (steppedIndex == 1) norm.y = -stepy; + if (steppedIndex == 2) norm.z = -stepz; + return voxel; } + } else { iend.x = ix; iend.y = iy;