Skip to content

Commit

Permalink
Organized ray cast selection types.
Browse files Browse the repository at this point in the history
  • Loading branch information
afritz1 committed Aug 8, 2024
1 parent 67cb82a commit e2baf89
Show file tree
Hide file tree
Showing 10 changed files with 143 additions and 159 deletions.
2 changes: 2 additions & 0 deletions OpenTESArena/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ SET(TES_COLLISION
"${SRC_ROOT}/Collision/CollisionMeshDefinition.h"
"${SRC_ROOT}/Collision/Physics.cpp"
"${SRC_ROOT}/Collision/Physics.h"
"${SRC_ROOT}/Collision/RayCastTypes.cpp"
"${SRC_ROOT}/Collision/RayCastTypes.h"
"${SRC_ROOT}/Collision/SelectionUtils.h")

SET(TES_ENTITIES
Expand Down
91 changes: 15 additions & 76 deletions OpenTESArena/src/Collision/Physics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <cmath>

#include "Physics.h"
#include "RayCastTypes.h"
#include "../Assets/ArenaTypes.h"
#include "../Assets/MIFFile.h"
#include "../Collision/CollisionChunkManager.h"
Expand Down Expand Up @@ -236,7 +237,7 @@ namespace Physics
// Returns true if the ray hit something.
bool testInitialVoxelRay(const CoordDouble3 &rayCoord, const VoxelDouble3 &rayDirection, const VoxelInt3 &voxel,
VoxelFacing3D farFacing, double ceilingScale, const VoxelChunkManager &voxelChunkManager,
const CollisionChunkManager &collisionChunkManager, Physics::Hit &hit)
const CollisionChunkManager &collisionChunkManager, RayCastHit &hit)
{
const VoxelChunk *voxelChunk = voxelChunkManager.tryGetChunkAtPosition(rayCoord.chunk);
if (voxelChunk == nullptr)
Expand Down Expand Up @@ -325,7 +326,7 @@ namespace Physics
if (success)
{
const CoordDouble3 hitCoord(rayCoord.chunk, rayCoord.point + (rayDirection * t));
hit.initVoxel(t, hitCoord, voxel, &farFacing);
hit.initVoxel(t, hitCoord, voxel, farFacing);
break;
}
}
Expand All @@ -338,7 +339,7 @@ namespace Physics
bool testVoxelRay(const CoordDouble3 &rayCoord, const VoxelDouble3 &rayDirection, const CoordInt3 &voxelCoord,
VoxelFacing3D nearFacing, const CoordDouble3 &nearCoord, const CoordDouble3 &farCoord,
double ceilingScale, const VoxelChunkManager &voxelChunkManager, const CollisionChunkManager &collisionChunkManager,
Physics::Hit &hit)
RayCastHit &hit)
{
const VoxelChunk *voxelChunk = voxelChunkManager.tryGetChunkAtPosition(voxelCoord.chunk);
if (voxelChunk == nullptr)
Expand Down Expand Up @@ -431,7 +432,7 @@ namespace Physics
{
const CoordDouble3 hitCoord = ChunkUtils::recalculateCoord(rayCoord.chunk, rayCoord.point + (rayDirection * t));
const VoxelFacing3D facing = nearFacing; // @todo: probably needs to take hit normal into account
hit.initVoxel(t, hitCoord, voxel, &facing);
hit.initVoxel(t, hitCoord, voxel, facing);
break;
}
}
Expand All @@ -443,11 +444,11 @@ namespace Physics
bool testEntitiesInVoxel(const CoordDouble3 &rayCoord, const VoxelDouble3 &rayDirection,
const VoxelDouble3 &flatForward, const VoxelDouble3 &flatRight, const VoxelDouble3 &flatUp,
const VoxelInt3 &voxel, const ChunkEntityMap &chunkEntityMap, const EntityChunkManager &entityChunkManager,
const EntityDefinitionLibrary &entityDefLibrary, Physics::Hit &hit)
const EntityDefinitionLibrary &entityDefLibrary, RayCastHit &hit)
{
// Use a separate hit variable so we can determine whether an entity was closer.
Physics::Hit entityHit;
entityHit.setT(Hit::MAX_T);
RayCastHit entityHit;
entityHit.t = RayCastHit::NO_HIT_DISTANCE;

const auto &entityMappings = chunkEntityMap.mappings;
const auto iter = entityMappings.find(voxel);
Expand All @@ -474,15 +475,15 @@ namespace Physics
flatWidth, flatHeight, rayCoord, rayDirection, &hitCoord))
{
const double distance = (hitCoord - rayCoord).length();
if (distance < entityHit.getT())
if (distance < entityHit.t)
{
entityHit.initEntity(distance, hitCoord, entityInstID);
}
}
}
}

const bool entityIsCloser = entityHit.getT() < hit.getT();
const bool entityIsCloser = entityHit.t < hit.t;
if (entityIsCloser)
{
hit = entityHit;
Expand All @@ -500,7 +501,7 @@ namespace Physics
void rayCastInternal(const CoordDouble3 &rayCoord, const VoxelDouble3 &rayDirection, const VoxelDouble3 &cameraForward,
double ceilingScale, const VoxelChunkManager &voxelChunkManager, const EntityChunkManager &entityChunkManager,
const CollisionChunkManager &collisionChunkManager, bool includeEntities, const EntityDefinitionLibrary &entityDefLibrary,
std::vector<ChunkEntityMap> &chunkEntityMaps, Physics::Hit &hit)
std::vector<ChunkEntityMap> &chunkEntityMaps, RayCastHit &hit)
{
// Each flat shares the same axes. Their forward direction always faces opposite to the camera direction.
const VoxelDouble3 flatForward = VoxelDouble3(-cameraForward.x, 0.0, -cameraForward.z).normalized();
Expand Down Expand Up @@ -770,76 +771,14 @@ namespace Physics
}
}

void Physics::Hit::initVoxel(double t, const CoordDouble3 &coord, const VoxelInt3 &voxel, const VoxelFacing3D *facing)
{
this->t = t;
this->coord = coord;
this->type = HitType::Voxel;
this->voxelHit.voxel = voxel;

if (facing != nullptr)
{
this->voxelHit.facing = *facing;
}
else
{
this->voxelHit.facing = std::nullopt;
}
}

void Physics::Hit::initEntity(double t, const CoordDouble3 &coord, EntityInstanceID id)
{
this->t = t;
this->coord = coord;
this->type = HitType::Entity;
this->entityHit.id = id;
}

double Physics::Hit::getT() const
{
return this->t;
}

double Physics::Hit::getTSqr() const
{
return this->t * this->t;
}

const CoordDouble3 &Physics::Hit::getCoord() const
{
return this->coord;
}

Physics::HitType Physics::Hit::getType() const
{
return this->type;
}

const Physics::Hit::VoxelHit &Physics::Hit::getVoxelHit() const
{
DebugAssert(this->getType() == HitType::Voxel);
return this->voxelHit;
}

const Physics::Hit::EntityHit &Physics::Hit::getEntityHit() const
{
DebugAssert(this->getType() == HitType::Entity);
return this->entityHit;
}

void Physics::Hit::setT(double t)
{
this->t = t;
}

bool Physics::rayCast(const CoordDouble3 &rayStart, const VoxelDouble3 &rayDirection, double ceilingScale,
const VoxelDouble3 &cameraForward, bool includeEntities, const VoxelChunkManager &voxelChunkManager,
const EntityChunkManager &entityChunkManager, const CollisionChunkManager &collisionChunkManager,
const EntityDefinitionLibrary &entityDefLibrary, Physics::Hit &hit)
const EntityDefinitionLibrary &entityDefLibrary, RayCastHit &hit)
{
// Set the hit distance to max. This will ensure that if we don't hit a voxel but do hit an
// entity, the distance can still be used.
hit.setT(Hit::MAX_T);
hit.t = RayCastHit::NO_HIT_DISTANCE;

// Voxel->entity mappings for each chunk touched by the ray casting loop.
std::vector<ChunkEntityMap> chunkEntityMaps;
Expand Down Expand Up @@ -918,13 +857,13 @@ bool Physics::rayCast(const CoordDouble3 &rayStart, const VoxelDouble3 &rayDirec
}

// Return whether the ray hit something.
return hit.getT() < Hit::MAX_T;
return hit.t < RayCastHit::NO_HIT_DISTANCE;
}

bool Physics::rayCast(const CoordDouble3 &rayStart, const VoxelDouble3 &rayDirection,
const VoxelDouble3 &cameraForward, bool includeEntities, const VoxelChunkManager &voxelChunkManager,
const EntityChunkManager &entityChunkManager, const CollisionChunkManager &collisionChunkManager,
const EntityDefinitionLibrary &entityDefLibrary, Physics::Hit &hit)
const EntityDefinitionLibrary &entityDefLibrary, RayCastHit &hit)
{
constexpr double ceilingScale = 1.0;
return Physics::rayCast(rayStart, rayDirection, ceilingScale, cameraForward, includeEntities, voxelChunkManager,
Expand Down
53 changes: 4 additions & 49 deletions OpenTESArena/src/Collision/Physics.h
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
#ifndef PHYSICS_H
#define PHYSICS_H

#include <limits>
#include <optional>
#include <unordered_map>
#include <vector>

#include "../Entities/EntityInstance.h"
#include "../Math/Vector2.h"
#include "../Math/Vector3.h"
Expand All @@ -15,62 +10,22 @@ class CollisionChunkManager;
class EntityChunkManager;
class VoxelChunkManager;

// Namespace for physics-related calculations like ray casting.
struct RayCastHit;

namespace Physics
{
enum class HitType { Voxel, Entity };

// Intersection data for ray casts.
class Hit
{
public:
struct VoxelHit
{
VoxelInt3 voxel;
std::optional<VoxelFacing3D> facing;
};

struct EntityHit
{
EntityInstanceID id;
};
private:
double t;
CoordDouble3 coord; // Hit point in 3D space.
HitType type;

// Not in a union so VoxelHit can use std::optional.
VoxelHit voxelHit;
EntityHit entityHit;
public:
static constexpr double MAX_T = std::numeric_limits<double>::infinity();

void initVoxel(double t, const CoordDouble3 &coord, const VoxelInt3 &voxel, const VoxelFacing3D *facing);
void initEntity(double t, const CoordDouble3 &coord, EntityInstanceID id);

double getT() const;
double getTSqr() const;
const CoordDouble3 &getCoord() const;
HitType getType() const;
const VoxelHit &getVoxelHit() const;
const EntityHit &getEntityHit() const;

void setT(double t);
};

// @todo: bit mask elements for each voxel data type.
// @todo: bit mask elements for each voxel type.

// Casts a ray through the world and writes any intersection data into the output parameter. Returns true
// if the ray hit something.
bool rayCast(const CoordDouble3 &rayStart, const VoxelDouble3 &rayDirection, double ceilingScale,
const VoxelDouble3 &cameraForward, bool includeEntities, const VoxelChunkManager &voxelChunkManager,
const EntityChunkManager &entityChunkManager, const CollisionChunkManager &collisionChunkManager,
const EntityDefinitionLibrary &entityDefLibrary, Physics::Hit &hit);
const EntityDefinitionLibrary &entityDefLibrary, RayCastHit &hit);
bool rayCast(const CoordDouble3 &rayStart, const VoxelDouble3 &rayDirection, const VoxelDouble3 &cameraForward,
bool includeEntities, const VoxelChunkManager &voxelChunkManager, const EntityChunkManager &entityChunkManager,
const CollisionChunkManager &collisionChunkManager, const EntityDefinitionLibrary &entityDefLibrary,
Physics::Hit &hit);
RayCastHit &hit);
};

#endif
36 changes: 36 additions & 0 deletions OpenTESArena/src/Collision/RayCastTypes.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#include "RayCastTypes.h"

RayCastVoxelHit::RayCastVoxelHit()
{
this->facing = static_cast<VoxelFacing3D>(-1);
}

RayCastHit::RayCastHit()
{
this->t = 0.0;
this->type = static_cast<RayCastHitType>(-1);
}

RayCastEntityHit::RayCastEntityHit()
{
this->id = -1;
}

void RayCastHit::initVoxel(double t, const CoordDouble3 &coord, const VoxelInt3 &voxel, VoxelFacing3D facing)
{
this->t = t;
this->coord = coord;

this->type = RayCastHitType::Voxel;
this->voxelHit.voxel = voxel;
this->voxelHit.facing = facing;
}

void RayCastHit::initEntity(double t, const CoordDouble3 &coord, EntityInstanceID id)
{
this->t = t;
this->coord = coord;

this->type = RayCastHitType::Entity;
this->entityHit.id = id;
}
49 changes: 49 additions & 0 deletions OpenTESArena/src/Collision/RayCastTypes.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#ifndef RAY_CAST_TYPES_H
#define RAY_CAST_TYPES_H

#include <limits>

#include "../Entities/EntityInstance.h"
#include "../Math/Vector3.h"
#include "../Voxels/VoxelUtils.h"

enum class RayCastHitType
{
Voxel,
Entity
};

struct RayCastVoxelHit
{
VoxelInt3 voxel;
VoxelFacing3D facing;

RayCastVoxelHit();
};

struct RayCastEntityHit
{
EntityInstanceID id;

RayCastEntityHit();
};

// Intersection data for ray casts.
struct RayCastHit
{
static constexpr double NO_HIT_DISTANCE = std::numeric_limits<double>::infinity();

double t; // Distance from ray start.
CoordDouble3 coord; // Hit point in the scene.

RayCastHitType type;
RayCastVoxelHit voxelHit;
RayCastEntityHit entityHit;

RayCastHit();

void initVoxel(double t, const CoordDouble3 &coord, const VoxelInt3 &voxel, VoxelFacing3D facing);
void initEntity(double t, const CoordDouble3 &coord, EntityInstanceID id);
};

#endif
4 changes: 3 additions & 1 deletion OpenTESArena/src/Collision/SelectionUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@

namespace SelectionUtils
{
constexpr double MAX_DISTANCE = 1.75;
// Max distance the player can click on regular objects.
// @todo: this should be a function of something constexpr in ArenaSelectionUtils
constexpr double MAX_PRIMARY_INTERACTION_DISTANCE = 1.75;
}

#endif
Loading

0 comments on commit e2baf89

Please sign in to comment.