Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions include/reone/game/debug.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@ namespace game {
bool isShowAABBEnabled();
bool isShowWalkmeshEnabled();
bool isShowTriggersEnabled();
bool isShowPathEnabled();

void setShowAABB(bool show);
void setShowWalkmesh(bool show);
void setShowTriggers(bool show);
void setShowPath(bool show);

} // namespace game

Expand Down
1 change: 1 addition & 0 deletions include/reone/game/game.h
Original file line number Diff line number Diff line change
Expand Up @@ -795,6 +795,7 @@ class Game : boost::noncopyable {
void consoleTurretState(const ConsoleArgs &tokens);
void consoleStartTurretGame(const ConsoleArgs &tokens);
void consoleShowImGui(const ConsoleArgs &tokens);
void consoleShowPath(const ConsoleArgs &tokens);

// END Console commands
};
Expand Down
8 changes: 4 additions & 4 deletions include/reone/game/object/area.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ class Area : public Object {
bool landObject(Object &object);
void add(const std::shared_ptr<Object> &object);

bool moveCreature(const std::shared_ptr<Creature> &creature, const glm::vec2 &dir, bool run, float dt);
bool moveCreatureTowards(const std::shared_ptr<Creature> &creature, const glm::vec2 &dest, bool run, float dt);
bool moveCreature(const std::shared_ptr<Creature> &creature, const glm::vec2 &dir, bool run, float dt,
float maxDistance = FLT_MAX);
void determineObjectRoom(Object &object);

bool isUnescapable() const { return _unescapable; }
Expand All @@ -113,12 +113,13 @@ class Area : public Object {
const CameraStyle &camStyleDefault() const { return _camStyleDefault; }
const std::string &music() const { return _music; }
const ObjectList &objects() const { return _objects; }
const Pathfinder &pathfinder() const { return _pathfinder; }
const std::string &localizedName() const { return _localizedName; }
const RoomMap &rooms() const { return _rooms; }
const Grass &grass() const { return _grass; }
const glm::vec3 &ambientColor() const { return _ambientColor; }

Pathfinder &pathfinder() { return _pathfinder; }

void setUnescapable(bool value);

// Objects
Expand Down Expand Up @@ -313,7 +314,6 @@ class Area : public Object {

void loadLYT();
void loadVIS();
void loadPTH();
void applySceneProperties();
void attachRoomToSceneGraph(Room &room);
void attachObjectToSceneGraph(const std::shared_ptr<Object> &object);
Expand Down
31 changes: 13 additions & 18 deletions include/reone/game/object/creature.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include "../d20/attributes.h"
#include "../d20/itemattributes.h"
#include "../object.h"
#include "../pathfinder.h"

#include "item.h"

Expand Down Expand Up @@ -63,15 +64,6 @@ class Creature : public Object, public scene::IAnimationEventListener {
Run
};

struct Path {
glm::vec3 destination {0.0f};
std::vector<glm::vec3> points;
uint32_t timeFound {0};
int pointIdx {0};

void selectNextPoint();
};

struct BodyBag {
std::string name;
int appearance {0}; /**< index into placeables.2da */
Expand Down Expand Up @@ -202,16 +194,10 @@ class Creature : public Object, public scene::IAnimationEventListener {
// END Equipment

// Pathfinding

bool navigateTo(const glm::vec3 &dest, bool run, float distance, float dt);
void advanceOnPath(bool run, float dt);
void updatePath(const glm::vec3 &dest);

void clearPath();
void setPath(const glm::vec3 &dest, std::vector<glm::vec3> &&points, uint32_t timeFound);

std::shared_ptr<Path> &path() { return _path; }

void advanceOnPath(const glm::vec3 &dest, const glm::vec3 &dir, bool run, float distance, float dt);
glm::vec3 computeSteeringForce(const Uniwalk &uni, const glm::vec3 &next, float dt);
// END Pathfinding

// Blocking doors
Expand Down Expand Up @@ -421,7 +407,16 @@ class Creature : public Object, public scene::IAnimationEventListener {
ModelType _modelType {ModelType::Creature};
std::shared_ptr<graphics::Texture> _portrait;

std::shared_ptr<Path> _path;
// Current path that the creature is following, its velocity and position at
// the previous frame.
std::optional<Path> _path;
glm::vec3 _pathVelocity;
glm::vec3 _previousPosition;
// When there is no progress on the path, apply _stuckForce to steer the
// creature in a random direction until the timer runs out.
Timer _stuckTimer;
glm::vec3 _stuckForce;

float _walkSpeed {0.0f};
float _runSpeed {0.0f};
float _creaturePersonalSpace {0.6f};
Expand Down
108 changes: 81 additions & 27 deletions include/reone/game/pathfinder.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,45 +17,99 @@

#pragma once

#include "reone/resource/path.h"
#include "reone/graphics/aabb.h"
#include "reone/graphics/walkmesh.h"

namespace reone {

namespace game {

/**
* A* pathfinding.
*/
class Pathfinder : boost::noncopyable {
public:
void load(const std::vector<resource::Path::Point> &points, const std::unordered_map<int, float> &pointZ);

const std::vector<glm::vec3> findPath(const glm::vec3 &from, const glm::vec3 &to) const;

private:
struct ContextVertex {
uint16_t index {0};
uint16_t parentIndex {0xffff};
float distance {0.0f};
float heuristic {0.0f};
float totalCost {0.0f};
};
/// Walkable face that is used for pathfinding.
struct Uniface {
/// Indices into Uniwalk::vertices array.
uint32_t vertices[3];

/// Indices of adjecent faces. Edges [0, 1], [1, 2], [2, 0] are
// adjecent when they are common with any other face.
uint32_t adjecent[3];

struct Context {
std::unordered_map<uint16_t, ContextVertex> vertices;
std::set<uint16_t> open;
std::set<uint16_t> closed;
/// Center of a face. Used to estimate distance between two faces.
glm::vec3 centroid;
};

/// Subdivision of a walkmesh. Uniroom associates a range of faces [begin, end)
/// to AABB.
struct Uniroom {
uint32_t begin;
uint32_t end;
glm::vec3 min;
glm::vec3 max;
};

const ContextVertex &getVertexWithLeastTotalCostFromOpen() const;
/// Unified walkmesh, assembled from walkmeshes of all rooms in the area.
struct Uniwalk {
std::vector<glm::vec3> vertices;
std::vector<Uniface> faces;
std::vector<Uniroom> rooms;
};

/// State of a face for A* algorithm.
struct AStarFace {
enum Flag {
Open = 1,
Closed = 2,
};

std::vector<glm::vec3> _vertices;
std::unordered_map<uint16_t, std::vector<uint16_t>> _adjacentVertices;
Flag flag;
uint32_t parent;
};

/// Element of a list of faces to consider next for A* algorithm.
struct AStarOpenFace {
uint32_t index;
float cost;
};

struct AStarContext {
std::vector<AStarFace> state;
std::vector<AStarOpenFace> open;
};

/// Fully calculated path.
struct AStarPath {
std::vector<uint32_t> faces;
glm::vec3 from;
glm::vec3 to;
uint32_t next;
glm::vec3 nextPoint;

uint16_t getNearestVertex(const glm::vec3 &point) const;
uint16_t getNearestVertexBetweenPoints(const glm::vec3 &point, const glm::vec3 &ref) const;
int32_t index;
bool active;
};

/// Handle to a calculated path.
struct Path {
int32_t index;
};

struct Pathfinder : public boost::noncopyable {
Uniwalk uni;
AStarContext astar;
std::vector<AStarPath> paths;
};

void uniwalkLoadRoom(struct Uniwalk &wm, graphics::Walkmesh &data, std::set<uint32_t> &walkableMaterial);
void uniwalkFinalize(struct Uniwalk &uni);

std::optional<Path> createPath(Pathfinder &pf, const glm::vec3 &from, const glm::vec3 &to);
bool updatePath(Pathfinder &pf, Path p, const glm::vec3 &current);
void releasePath(Pathfinder &pf, Path path);

glm::vec3 getNextPathPoint(Pathfinder &pf, Path path);
glm::vec3 getLastPathPoint(Pathfinder &pf, Path path);

glm::vec3 computeKeepoutForce(const Uniwalk &uni, const glm::vec3 &position);

} // namespace game

} // namespace reone
9 changes: 2 additions & 7 deletions include/reone/graphics/format/bwmreader.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class BwmReader : boost::noncopyable {
uint32_t _numVertices {0};
uint32_t _offVertices {0};
uint32_t _numFaces {0};
uint32_t _offIndices {0};
uint32_t _offFaces {0};
uint32_t _offMaterials {0};
uint32_t _offNormals {0};
uint32_t _offPlanarDistances {0};
Expand All @@ -63,15 +63,10 @@ class BwmReader : boost::noncopyable {
uint32_t _numPerimeters {0};
uint32_t _offPerimeters {0};

std::vector<float> _vertices;
std::vector<uint32_t> _indices;
std::vector<uint32_t> _materials;
std::vector<float> _normals;

std::shared_ptr<Walkmesh> _walkmesh;

void loadVertices();
void loadIndices();
void loadFaces();
void loadMaterials();
void loadNormals();
void loadAABB();
Expand Down
65 changes: 47 additions & 18 deletions include/reone/graphics/walkmesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,32 @@ namespace reone {

namespace graphics {

enum RaycastFail {
RAYCAST_OK = 0,
RAYCAST_NO_INTERSECTION,
RAYCAST_NO_MATERIAL,
RAYCAST_FLIPPED_NORMAL,
};

struct Raycast {
uint32_t face;
float distance;
RaycastFail fail;
};

class Walkmesh : boost::noncopyable {
public:
struct Face {
int index {0};
uint32_t index {0};
uint32_t material {0};
std::vector<glm::vec3> vertices;
glm::vec3 vertices[3];
glm::vec3 normal {0.0f};
};

struct FaceVertices {
uint32_t indices[3];
};

struct AABB {
graphics::AABB value;
int faceIdx {-1};
Expand All @@ -41,52 +58,64 @@ class Walkmesh : boost::noncopyable {
};

/**
* @return pointer to intersected face or nullptr when no intersection
* @return index of the face that the ray intersects, distance from the
* origin point to the intersection, and an error code if there is no
* intersection.
*/
const Walkmesh::Face *raycast(
Raycast raycast(
std::set<uint32_t> walkcheckSurfaces,
const glm::vec3 &origin,
const glm::vec3 &dir,
float maxDistance,
bool ignoreBackface,
float &outDistance) const;
bool ignoreBackface) const;

bool contains(const glm::vec2 &point) const;

bool isAreaWalkmesh() const { return _area; }

const std::vector<Face> &faces() const { return _faces; }

void add(Face &&face) {
_faces.push_back(face);
Face getFace(uint32_t index) const {
FaceVertices face = faces[index];
return Face {
index,
materials[index],
{
vertices[face.indices[0]],
vertices[face.indices[1]],
vertices[face.indices[2]],
},
normals[index],
};
}

void setRootAABB(std::shared_ptr<AABB> aabb) {
_rootAabb = std::move(aabb);
}

void verify() const;

std::vector<glm::vec3> vertices;
std::vector<FaceVertices> faces;
std::vector<glm::vec3> normals;
std::vector<uint32_t> materials;

private:
std::vector<Face> _faces;
std::shared_ptr<AABB> _rootAabb;

bool _area {false};

const Walkmesh::Face *raycastAABB(
Raycast raycastAABB(
std::set<uint32_t> surfaces,
const glm::vec3 &origin,
const glm::vec3 &dir,
float maxDistance,
bool ignoreBackface,
float &outDistance) const;
bool ignoreBackface) const;

bool raycastFace(
Raycast raycastFace(
std::set<uint32_t> surfaces,
const Walkmesh::Face &face,
const glm::vec3 &origin,
const glm::vec3 &dir,
float maxDistance,
bool ignoreBackface,
float &outDistance) const;
bool ignoreBackface) const;

friend class BwmReader;
};
Expand Down
Loading
Loading