game2006/server/gameserver/mapinstance.cc
aozhiwei 7d716e26ab 1
2024-08-24 07:10:49 +08:00

1447 lines
47 KiB
C++

#include "precompile.h"
#include <math.h>
#include <string.h>
#include <f8/udplog.h>
#include <glm/gtx/intersect.hpp>
#include "DetourCommon.h"
#include "mapinstance.h"
#include "mapservice.h"
#include "gridservice.h"
#include "obstacle.h"
#include "loot.h"
#include "mapmgr.h"
#include "room.h"
#include "entityfactory.h"
#include "creature.h"
#include "glmhelper.h"
#include "debugcmd.h"
#include "mt/MetaMgr.h"
#include "mt/Param.h"
#include "mt/Map.h"
#include "mt/MapArea.h"
#include "mt/MapCollider.h"
#include "roommgr.h"
#include "navmesh.pb.h"
static const int NAV_ERROR_NEARESTPOLY = -2;
static const int NAV_ERROR = -1;
static const long RCN_NAVMESH_VERSION = 1;
static const int INVALID_NAVMESH_POLYREF = 0;
const int MAP_GRID_WIDTH = 64;
static const int NAVMESHSET_MAGIC = 'M'<<24 | 'S'<<16 | 'E'<<8 | 'T'; //'MSET';
static const int NAVMESHSET_VERSION = 1;
struct NavMeshSetHeader
{
int magic;
int version;
int numTiles;
dtNavMeshParams params;
};
struct NavMeshTileHeader
{
dtTileRef tileRef;
int dataSize;
};
static float frand()
{
return (float)rand()/(float)RAND_MAX;
}
static void TraverseMapColliderNode(std::shared_ptr<mc::ColliderNode> node,
std::function<void (std::shared_ptr<mc::ColliderNode>)> cb)
{
for (auto& pair : node->childs) {
cb(pair.second);
if (!pair.second->childs.empty()) {
TraverseMapColliderNode(pair.second, cb);
}
}
}
class ConnectableQueryFilter : public dtQueryFilter
{
public:
MapInstance* map_instance = nullptr;
virtual bool passFilter(const dtPolyRef ref,
const dtMeshTile* tile,
const dtPoly* poly) const override
{
return map_instance->IsConnectablePoly(ref);
}
};
void MapInstance::Init()
{
map_meta_ = mt::Map::GetById(map_id);
if (!map_meta_) {
A8_ABORT();
}
if (map_meta_->map_width() < 1) {
A8_ABORT();
}
if (map_meta_->map_height() < 1) {
A8_ABORT();
}
map_service_ = std::make_shared<MapService>();
grid_service_ = std::make_shared<GridService>();
if (map_meta_->is_moba()) {
grid_service_->Init(map_meta_->map_width(),
map_meta_->map_height(),
std::max(map_meta_->map_width(), map_meta_->map_height()) + 0);
} else {
grid_service_->Init(map_meta_->map_width(),
map_meta_->map_height(),
mt::Param::s().map_cell_width);
}
map_service_->Init(map_meta_->map_width() / MAP_GRID_WIDTH,
map_meta_->map_height() / MAP_GRID_WIDTH,
MAP_GRID_WIDTH);
{
navmesh_ = dtAllocNavMesh();
std::string navmesh_file = map_meta_->navmesh_file();
FILE *fp = fopen((mt::MetaMgr::Instance()->GetResDir() + navmesh_file).c_str(), "rb");
if(fp){
//fseek(fp, 0, SEEK_END);
//int file_size = ftell(fp);
int file_size = 1;
if(file_size){
NavMeshSetHeader header;
size_t readLen = fread(&header, sizeof(NavMeshSetHeader), 1, fp);
if (readLen != 1) {
fclose(fp);
abort();
}
if (header.magic != NAVMESHSET_MAGIC) {
fclose(fp);
abort();
}
if (header.version != NAVMESHSET_VERSION) {
fclose(fp);
abort();
}
dtStatus status = navmesh_->init(&header.params);
if (dtStatusFailed(status)) {
fclose(fp);
abort();
}
#ifdef MYDEBUG
a8::XPrintf("orig:%f,%f,%f tileWidth:%f tileHeight:%f maxTiles:%d maxPolys:%d\n",
{
header.params.orig[0],
header.params.orig[1],
header.params.orig[2],
header.params.tileWidth,
header.params.tileHeight,
header.params.maxTiles,
header.params.maxPolys,
});
#endif
{
// Read tiles.
for (int i = 0; i < header.numTiles; ++i) {
NavMeshTileHeader tileHeader;
readLen = fread(&tileHeader, sizeof(tileHeader), 1, fp);
if (readLen != 1) {
fclose(fp);
abort();
}
if (!tileHeader.tileRef || !tileHeader.dataSize) {
abort();
break;
}
unsigned char* data = (unsigned char*)dtAlloc(tileHeader.dataSize,
DT_ALLOC_PERM);
if (!data) {
abort();
break;
}
memset(data, 0, tileHeader.dataSize);
readLen = fread(data, tileHeader.dataSize, 1, fp);
if (readLen != 1) {
dtFree(data);
fclose(fp);
abort();
}
navmesh_->addTile(data,
tileHeader.dataSize,
DT_TILE_FREE_DATA,
tileHeader.tileRef,
0);
}
}
}
fclose(fp);
}
}
navmesh_query_ = std::make_shared<dtNavMeshQuery>();
navmesh_query_->init(navmesh_, 1024);
MarkMapAreaPolys();
MarkConnectablePolys();
LoadHouse();
f8::UdpLog::Instance()->Info
("map_id:%d connectable_polys:%d",
{
map_id,
connectable_polys_.size()
});
}
void MapInstance::UnInit()
{
navmesh_query_ = nullptr;
dtFreeNavMesh(navmesh_);
navmesh_ = nullptr;
map_service_->UnInit();
grid_service_->UnInit();
map_service_ = nullptr;
grid_service_ = nullptr;
}
void MapInstance::AttachRoom(Room* room, RoomInitInfo& init_info)
{
init_info.map_meta = map_meta_;
init_info.grid_service = grid_service_;
init_info.map_service = map_service_;
init_info.map_instance = shared_from_this();
}
int MapInstance::FindStraightPath(
const glm::vec3& start,
const glm::vec3& end,
std::vector<MovePathPoint>& paths)
{
float spos[3];
spos[0] = start.x;
spos[1] = start.y;
spos[2] = start.z;
float epos[3];
epos[0] = end.x;
epos[1] = end.y;
epos[2] = end.z;
dtQueryFilter filter;
filter.setIncludeFlags(GetMoveIncludeFlags());
filter.setExcludeFlags(GetMoveExcludeFlags());
const float extents[3] = {2.f, 4.f, 2.f};
dtPolyRef startRef = INVALID_NAVMESH_POLYREF;
dtPolyRef endRef = INVALID_NAVMESH_POLYREF;
float startNearestPt[3];
float endNearestPt[3];
navmesh_query_->findNearestPoly(spos, extents, &filter, &startRef, startNearestPt);
navmesh_query_->findNearestPoly(epos, extents, &filter, &endRef, endNearestPt);
if (!startRef || !endRef) {
return NAV_ERROR_NEARESTPOLY;
}
int npolys;
float straightPath[MAX_POLYS * 3];
unsigned char straightPathFlags[MAX_POLYS];
dtPolyRef straightPathPolys[MAX_POLYS];
int nstraightPath;
navmesh_query_->findPath
(startRef,
endRef,
startNearestPt,
endNearestPt,
&filter,
polys_,
&npolys,
MAX_POLYS);
nstraightPath = 0;
if (npolys) {
float epos1[3];
dtVcopy(epos1, endNearestPt);
if (polys_[npolys-1] != endRef) {
navmesh_query_->closestPointOnPoly(polys_[npolys-1], endNearestPt, epos1, 0);
}
navmesh_query_->findStraightPath(startNearestPt,
endNearestPt,
polys_,
npolys,
straightPath,
straightPathFlags,
straightPathPolys,
&nstraightPath,
MAX_POLYS);
glm::vec3 last_pos = start;
UnScale(last_pos);
int last_poly_pos = 0;
int path_index = 0;
for(int i = 0; i < nstraightPath * 3; ) {
glm::vec3 pos;
pos.x = straightPath[i++];
pos.y = straightPath[i++];
pos.z = straightPath[i++];
UnScale(pos);
if (!GlmHelper::IsEqual2D(pos, last_pos)) {
glm::vec3 dir = pos - last_pos;
GlmHelper::Normalize(dir);
MovePathPoint point;
point.src_pos.FromGlmVec3(last_pos);
point.curr_pos.FromGlmVec3(last_pos);
point.dir.x = dir.x;
point.dir.y = dir.y;
point.dir.z = dir.z;
point.distance = GlmHelper::Norm2D(pos - last_pos);
point.tar_pos.FromGlmVec3(pos);
if (i > 0 && last_poly_pos < npolys && path_index > 0) {
dtPolyRef poly_ref = straightPathPolys[path_index];
int found_pos = -1;
bool is_same_flags = true;
unsigned short last_flags = 0;
for (int ii = last_poly_pos; ii < npolys; ++ii) {
dtPolyRef curr_poly_ref = polys_[ii];
if (curr_poly_ref != poly_ref) {
unsigned short flags = 0;
auto status = navmesh_->getPolyFlags(curr_poly_ref, &flags);
if (!dtStatusSucceed(status)) {
abort();
}
if (flags) {
point.spec_polys.push_back(curr_poly_ref);
}
if (last_flags && last_flags != flags) {
is_same_flags = false;
}
last_flags = flags;
} else {
found_pos = ii;
break;
}
}
if (found_pos > -1) {
last_poly_pos = found_pos;
} else {
#ifdef MYDEBUG1
abort();
#endif
}
if (is_same_flags) {
point.same_polys_flags = last_flags;
}
}
paths.push_back(point);
last_pos = pos;
}//end if
++path_index;
}
}
return paths.size();
}
bool MapInstance::RandPoint(const glm::vec3& center, float range, glm::vec3& out_point)
{
glm::vec3 hit_point;
bool hit_result = false;
glm::vec3 start = center;
glm::vec3 dir = GlmHelper::UP;
GlmHelper::RotateY(dir, glm::radians(1.0f + (float)(rand() % 360)));
GlmHelper::Normalize(dir);
glm::vec3 end = center + dir * (float)(rand() % (int)std::max(1.0f, range));
out_point = end;
#ifdef MYDEBUG
a8::XPrintf("MapInstance::RandPoint:%f %f %f\n",
{
out_point.x,
out_point.y,
out_point.z,
});
#endif
return true;
Scale(start);
Scale(end);
if (Raycast
(
start,
end,
hit_point,
hit_result,
GetMoveIncludeFlags(),
GetMoveExcludeFlags()
)) {
UnScale(hit_point);
out_point = hit_point;
return true;
} else {
out_point = center;
return false;
}
}
bool MapInstance::FindRandomPointAroundCircle(const glm::vec3& center_pos,
float max_radius,
glm::vec3& random_pt)
{
ConnectableQueryFilter filter;
filter.map_instance = this;
filter.setIncludeFlags(GetMoveIncludeFlags());
filter.setExcludeFlags(GetMoveExcludeFlags());
dtPolyRef startRef = INVALID_NAVMESH_POLYREF;
const float extents[3] = {2.f/10.0f, 4.f, 2.f/10.f};
float nearestPt[3];
float center[3];
center[0] = center_pos.x;
center[1] = center_pos.y;
center[2] = center_pos.z;
navmesh_query_->findNearestPoly(center, extents, &filter, &startRef, nearestPt);
if (!startRef) {
return false;
}
dtPolyRef randomRef = INVALID_NAVMESH_POLYREF;
float randomPt[3];
dtStatus status = navmesh_query_->findRandomPointAroundCircle
(startRef,
//center,
nearestPt,
max_radius,
&filter,
frand,
&randomRef,
randomPt);
if (dtStatusSucceed(status)) {
random_pt.x = randomPt[0];
random_pt.y = randomPt[1];
random_pt.z = randomPt[2];
return true;
} else {
return false;
}
}
bool MapInstance::Raycast(const glm::vec3& start, const glm::vec3& end,
glm::vec3& hit_point, bool& hit_result,
unsigned short includeFlags,
unsigned short excludeFlags)
{
float spos[3];
spos[0] = start.x;
spos[1] = start.y;
spos[2] = start.z;
float epos[3];
epos[0] = end.x;
epos[1] = end.y;
epos[2] = end.z;
dtStatus status = 0;
dtQueryFilter filter;
filter.setIncludeFlags(includeFlags);
filter.setExcludeFlags(excludeFlags);
dtPolyRef startRef = INVALID_NAVMESH_POLYREF;
{
const float extents[3] = {2.f/10, 4.f, 2.f/10};
float nearestPt[3];
status = navmesh_query_->findNearestPoly(spos, extents, &filter, &startRef, nearestPt);
if (startRef == INVALID_NAVMESH_POLYREF) {
return false;
}
}
float t = 0;
int npolys;
memset(hit_normal_, 0, sizeof(hit_normal_));
#if 1
status = navmesh_query_->newRaycast(666, startRef, spos, epos, &filter, &t, hit_normal_, polys_, &npolys, MAX_POLYS);
#else
status = navmesh_query_->raycast(startRef, spos, epos, &filter, &t, hit_normal_, polys_, &npolys, MAX_POLYS);
#endif
if (!dtStatusSucceed(status)) {
#ifdef MYDEBUG
abort();
#else
return false;
#endif
}
if (npolys <= 0) {
return false;
}
if (t > 1) {
// No Hit
hit_pos_[0] = epos[0];
hit_pos_[1] = epos[1];
hit_pos_[2] = epos[2];
hit_result = false;
} else {
if (t < 0.00001f) {
return false;
}
//需要处理spos == epos的情况!!!!
// Hit
dtVlerp(hit_pos_, spos, epos, t);
hit_result = true;
}
{
float closest[3];
bool pos_over_poly = false;
status = navmesh_query_->closestPointOnPoly(polys_[npolys - 1], hit_pos_, closest, &pos_over_poly);
if (!dtStatusSucceed(status)) {
abort();
}
hit_pos_[1] = closest[1];
}
hit_point.x = hit_pos_[0];
hit_point.y = hit_pos_[1];
hit_point.z = hit_pos_[2];
return true;
}
bool MapInstance::FindNearestPoint(const glm::vec3& center, float radius, glm::vec3& nearest_pt)
{
dtPolyRef startRef = INVALID_NAVMESH_POLYREF;
dtQueryFilter filter;
filter.setIncludeFlags(GetMoveIncludeFlags());
filter.setExcludeFlags(GetMoveExcludeFlags());
const float extents[3] = {radius, 10.0f, radius};
float nearestPt[3];
float pos[3];
pos[0] = center.x;
pos[1] = center.y;
pos[2] = center.z;
navmesh_query_->findNearestPoly(pos, extents, &filter, &startRef, nearestPt);
if (!startRef) {
return false;
}
nearest_pt.x = nearestPt[0];
nearest_pt.y = nearestPt[1];
nearest_pt.z = nearestPt[2];
return true;
}
bool MapInstance::FindConnectableNearestPoint(const glm::vec3& center, float radius, glm::vec3& nearest_pt)
{
dtPolyRef startRef = INVALID_NAVMESH_POLYREF;
ConnectableQueryFilter filter;
filter.map_instance = this;
filter.setIncludeFlags(GetMoveIncludeFlags());
filter.setExcludeFlags(GetMoveExcludeFlags());
const float extents[3] = {radius, 10.0f, radius};
float nearestPt[3];
float pos[3];
pos[0] = center.x;
pos[1] = center.y;
pos[2] = center.z;
navmesh_query_->findNearestPoly(pos, extents, &filter, &startRef, nearestPt);
if (!startRef) {
return false;
}
nearest_pt.x = nearestPt[0];
nearest_pt.y = nearestPt[1];
nearest_pt.z = nearestPt[2];
return true;
}
bool MapInstance::GetPosHeight(const Position& pos, float& out_height)
{
dtPolyRef startRef = INVALID_NAVMESH_POLYREF;
dtQueryFilter filter;
filter.setIncludeFlags(GetMoveIncludeFlags());
filter.setExcludeFlags(GetMoveExcludeFlags());
const float extents[3] = {2.f, 4.f, 2.f};
float nearestPt[3];
float center[3];
center[0] = pos.GetX() * GetMapMeta()->scale();
center[1] = pos.GetY();
center[2] = pos.GetZ() * GetMapMeta()->scale();
bool isOverPoly;
navmesh_query_->findNearestPoly(center, extents, &filter, &startRef, nearestPt, &isOverPoly);
if (!startRef) {
return false;
}
auto ret = navmesh_query_->getPolyHeight(startRef, nearestPt, &out_height);
#if 0
assert(ret == DT_SUCCESS);
#endif
if (ret != DT_SUCCESS) {
out_height = pos.GetY();
return false;
}
return true;
}
void MapInstance::Scale(glm::vec3& v)
{
float old_y = v.y;
v *= GetMapMeta()->scale();
v.y = old_y;
}
void MapInstance::UnScale(glm::vec3& v)
{
float old_y = v.y;
v /= GetMapMeta()->scale();
v.y = old_y;
}
glm::vec3 MapInstance::UnScaleEx(const glm::vec3& v)
{
glm::vec3 result = v;
UnScale(result);
return result;
}
dtPoly* MapInstance::GetPoly(glm::vec3 pos, int& poly_idx)
{
dtPolyRef startRef = INVALID_NAVMESH_POLYREF;
dtQueryFilter filter;
filter.setIncludeFlags(GetMoveIncludeFlags());
filter.setExcludeFlags(GetMoveExcludeFlags());
const float extents[3] = {2.f, 4.f, 2.f};
float nearestPt[3];
Scale(pos);
float pos1[3];
pos1[0] = pos.x;
pos1[1] = pos.y;
pos1[2] = pos.z;
const dtMeshTile* tile = navmesh_->getTileAt(0, 0, 0);
assert(tile);
navmesh_query_->findNearestPoly(pos1, extents, &filter, &startRef, nearestPt);
if (startRef) {
unsigned int slat = 0;
unsigned int it = 0;
unsigned int ip = 0;
navmesh_->decodePolyId(startRef, slat, it, ip);
poly_idx = ip;
return &tile->polys[ip];
}
return nullptr;
}
void MapInstance::MarkMapAreaPolys()
{
const dtMeshTile* tile = navmesh_->getTileAt(0, 0, 0);
if (!tile) {
abort();
}
poly_ext_datas_.reserve(tile->header->polyCount);
for (int i = 0; i < tile->header->polyCount; ++i) {
int ext_flag = 0;
dtPoly* poly = &tile->polys[i];
if ((poly->flags & SAMPLE_POLYFLAGS_SWIM) == SAMPLE_POLYFLAGS_SWIM) {
#if 0
const mt::MapArea* last_area_meta = nullptr;
for (int ii = 0; ii < poly->vertCount; ++ii) {
const float* vc = &tile->verts[poly->verts[ii]*3];
const mt::MapArea* area_meta = mt::MapArea::GetAreaByPoint
(map_meta_->map_id(),
vc[0] / GetMapMeta()->scale(),
vc[1],
vc[2] / GetMapMeta()->scale());
if (!area_meta) {
abort();
}
if (last_area_meta && last_area_meta != area_meta) {
abort();
}
last_area_meta = area_meta;
}
if (!last_area_meta) {
abort();
}
if (last_area_meta->area_type() != 1) {
abort();
}
switch (last_area_meta->area_subtype()) {
case 1:
{
a8::SetBitFlag(ext_flag, kWater1ExtFlag);
}
break;
case 2:
{
a8::SetBitFlag(ext_flag, kWater2ExtFlag);
}
break;
case 3:
{
a8::SetBitFlag(ext_flag, kWater3ExtFlag);
}
break;
default:
{
abort();
}
break;
}
#endif
} else if ((poly->flags & SAMPLE_POLYFLAGS_GLASS) == SAMPLE_POLYFLAGS_GLASS) {
for (int ii = 0; ii < poly->vertCount; ++ii) {
const float* vc = &tile->verts[poly->verts[ii]*3];
grass_pos_hash_.push_back
(glm::vec3(vc[0] / GetMapMeta()->scale(),
vc[1],
vc[2] / GetMapMeta()->scale()
));
}
}
poly_ext_datas_.push_back(ext_flag);
}
}
bool MapInstance::RaycastEx(const glm::vec3& start, const glm::vec3& end,
glm::vec3& hit_point, bool& hit_result,
int& same_polys_flags, std::vector<dtPolyRef>& spec_polys,
unsigned short exclude_flags)
{
same_polys_flags = 0;
float spos[3];
spos[0] = start.x;
spos[1] = start.y;
spos[2] = start.z;
float epos[3];
epos[0] = end.x;
epos[1] = end.y;
epos[2] = end.z;
dtStatus status = 0;
dtQueryFilter filter;
filter.setIncludeFlags(GetMoveIncludeFlags());
filter.setExcludeFlags(exclude_flags);
dtPolyRef startRef = INVALID_NAVMESH_POLYREF;
{
const float extents[3] = {2.f/10, 4.f, 2.f/10};
float nearestPt[3];
status = navmesh_query_->findNearestPoly(spos, extents, &filter, &startRef, nearestPt);
if (startRef == INVALID_NAVMESH_POLYREF) {
return false;
}
}
float t = 0;
int npolys;
memset(hit_normal_, 0, sizeof(hit_normal_));
#if 1
status = navmesh_query_->newRaycast(666, startRef, spos, epos, &filter, &t, hit_normal_, polys_, &npolys, MAX_POLYS);
#else
status = navmesh_query_->raycast(startRef, spos, epos, &filter, &t, hit_normal_, polys_, &npolys, MAX_POLYS);
#endif
if (!dtStatusSucceed(status)) {
#ifdef MYDEBUG
abort();
#else
return false;
#endif
}
if (npolys <= 0) {
return false;
}
if (t > 1) {
// No Hit
hit_pos_[0] = epos[0];
hit_pos_[1] = epos[1];
hit_pos_[2] = epos[2];
hit_result = false;
} else {
if (t < 0.00001f) {
return false;
}
//需要处理spos == epos的情况!!!!
// Hit
dtVlerp(hit_pos_, spos, epos, t);
hit_result = true;
}
{
float closest[3];
bool pos_over_poly = false;
status = navmesh_query_->closestPointOnPoly(polys_[npolys - 1], hit_pos_, closest, &pos_over_poly);
if (!dtStatusSucceed(status)) {
abort();
}
hit_pos_[1] = closest[1];
}
{
bool is_same_flags = true;
unsigned short last_flags = 0;
spec_polys.reserve(npolys);
for (int i = 0; i < npolys; ++i) {
unsigned short flags = 0;
status = navmesh_->getPolyFlags(polys_[i], &flags);
if (!dtStatusSucceed(status)) {
abort();
}
if (flags) {
spec_polys.push_back(polys_[i]);
}
if (last_flags && last_flags != flags) {
is_same_flags = false;
}
last_flags = flags;
}
if (is_same_flags) {
same_polys_flags = last_flags;
}
}
hit_point.x = hit_pos_[0];
hit_point.y = hit_pos_[1];
hit_point.z = hit_pos_[2];
return true;
}
void MapInstance::CheckTerrain(Creature* c, int same_poly_flags, const std::vector<dtPolyRef>& spec_polys)
{
if (same_poly_flags) {
c->CheckSpecObject(same_poly_flags);
} else {
if (spec_polys.empty()) {
c->CheckSpecObject(0);
} else {
float pos[3];
pos[0] = c->GetPos().GetX() * GetMapMeta()->scale();
pos[1] = c->GetPos().GetY();
pos[2] = c->GetPos().GetZ() * GetMapMeta()->scale();
float closest[3];
bool found = false;
for (auto& poly_ref : spec_polys) {
dtStatus status = navmesh_query_->closestPointOnPolyBoundary(poly_ref, pos, closest);
if (dtStatusSucceed(status) &&
std::fabs(closest[0] - c->GetPos().GetX() * GetMapMeta()->scale()) <= 0.1f &&
std::fabs(closest[2] - c->GetPos().GetZ() * GetMapMeta()->scale()) <= 0.1f) {
unsigned short flags = 0;
if (dtStatusSucceed(navmesh_->getPolyFlags(poly_ref, &flags))) {
c->CheckSpecObject(flags);
found = true;
break;
}
}
}
if (!found) {
c->CheckSpecObject(0);
}
}
}
}
void MapInstance::TraverseHouseList(std::function<void (HouseInfo*)> func)
{
}
bool MapInstance::PtInHouse(const glm::vec3& pt, glm::vec3& nearest_pt)
{
float fpt[3];
fpt[0] = pt.x;
fpt[1] = pt.y;
fpt[2] = pt.z;
float closest[3];
float edged[DT_VERTS_PER_POLYGON];
float edget[DT_VERTS_PER_POLYGON];
for (auto& house : houses_) {
if (house.verts.empty()) {
continue;
}
float* verts = &(house.verts[0]);
int nv = house.verts.size() / 3;
if (!dtPointInPolygon(fpt, verts, nv)) {
continue;
}
if (dtDistancePtPolyEdgesSqr(fpt, verts, nv, edged, edget)) {
float dmin = edged[0];
int imin = 0;
for (size_t i = 1; i < nv; ++i) {
if (edged[i] < dmin) {
dmin = edged[i];
imin = i;
}
}
const float* va = &verts[imin*3];
const float* vb = &verts[((imin+1)%nv)*3];
dtVlerp(closest, va, vb, edget[imin]);
nearest_pt.x = closest[0];
nearest_pt.y = closest[1];
nearest_pt.z = closest[2];
return true;
}
}
return false;
}
void MapInstance::LoadHouse()
{
if (GetMapMeta()->collider_info) {
std::shared_ptr<mc::ColliderNode> building_root = GetMapMeta()->collider_info->GetNode("building");
if (building_root) {
for (auto& pair : building_root->childs) {
auto node = pair.second;
std::vector<glm::vec3> old_points;
std::vector<glm::vec3> new_points;
{
mc::RotateBounds(node->bounds.center,
node->bounds.size + glm::vec3(10.0f, 0, 10.f),
glm::quat_identity<float, glm::qualifier::defaultp>(),
old_points
);
mc::RotateBounds(node->bounds.center,
node->bounds.size + glm::vec3(10.0f, 0, 10.f),
node->transform.local_rotation,
new_points
);
}
#ifdef MYDEBUG1
{
std::string data = "old: " + node->name + " ";
for (auto& point : old_points) {
data += a8::Format("%f,%f,%f ", {point.x, point.y, point.z});
}
data += "\nnew:";
for (auto& point : new_points) {
data += a8::Format("%f,%f,%f ", {point.x, point.y, point.z});
}
a8::XPrintf("%s\n", {data});
}
#endif
HouseInfo p;
p.node = node;
{
for (size_t i = 0; i < new_points.size(); ++i) {
const auto& point = new_points[i];
if (i >= 4) {
p.verts.push_back(point.x);
p.verts.push_back(point.y);
p.verts.push_back(point.z);
}
}
if (p.verts.size() != 3 * 4) {
abort();
}
}
houses_.push_back(p);
}
}
{
int box_tri_arr[12][3] = {
{0, 1, 2},
{0, 3, 2},
{0, 1, 5},
{0, 4, 5},
{0, 3, 7},
{0, 4, 7},
{6, 5, 4},
{6, 7, 4},
{6, 7, 3},
{6, 2, 3},
{6, 2, 1},
{6, 5, 1}
};
//求三角形min_y max_y
things_.reserve(1024 * 10);
auto cb =
[this, box_tri_arr] (std::shared_ptr<mc::ColliderNode> node)
{
if (!node->colliders.empty()) {
for (auto c : node->colliders) {
if (c->type == mc::kMeshCollider) {
mc::MeshCollider* mesh_collider = (mc::MeshCollider*)c.get();
mesh_collider->mesh.triangles.reserve(mesh_collider->mesh.raw_triangles.size() / 3);
for (int i = 0; i < mesh_collider->mesh.raw_triangles.size(); i += 3) {
auto& tri = a8::FastAppend(mesh_collider->mesh.triangles);
tri.node = node.get();
int v0 = mesh_collider->mesh.raw_triangles[i + 0];
int v1 = mesh_collider->mesh.raw_triangles[i + 1];
int v2 = mesh_collider->mesh.raw_triangles[i + 2];
tri.vert0 = mesh_collider->mesh.vertices[v0];
tri.vert1 = mesh_collider->mesh.vertices[v1];
tri.vert2 = mesh_collider->mesh.vertices[v2];
tri.min_y = tri.vert0.y;
tri.max_y = tri.vert0.y;
for (int ii = 1; ii < 3; ++ii) {
int vp = mesh_collider->mesh.raw_triangles[i + ii];
float y = mesh_collider->mesh.vertices.at(vp).y;
if (y < tri.min_y) {
tri.min_y = y;
}
if (y > tri.max_y) {
tri.max_y = y;
}
}//end for ii
tri.min_y -= 1.0f;
tri.max_y += 1.0f;
map_service_->AddTriangle(&tri);
}//end for i
} else if (c->type == mc::kBoxCollider) {
mc::BoxCollider* box_collider = (mc::BoxCollider*)c.get();
#if 0
if (node->name == "Wall2_1 (18)") {
printf("node->name %s %f,%f,%f %f,%f,%f\n",
node->name.c_str(),
box_collider->center.x,
box_collider->center.y,
box_collider->center.z,
box_collider->size.x,
box_collider->size.y,
box_collider->size.z
);
}
#endif
std::vector<glm::vec3> new_points;
mc::RotateBounds(box_collider->center,
box_collider->size,
node->transform.local_rotation,
new_points
);
box_collider->triangles.reserve(2 * 6);
for (int i = 0; i < 12; ++i) {
auto& tri = a8::FastAppend(box_collider->triangles);
tri.node = node.get();
tri.vert0 = new_points[box_tri_arr[i][0]];
tri.vert1 = new_points[box_tri_arr[i][1]];
tri.vert2 = new_points[box_tri_arr[i][2]];
tri.min_y = tri.vert0.y;
tri.max_y = tri.vert0.y;
for (int ii = 1; ii < 3; ++ii) {
float y = new_points[box_tri_arr[i][ii]].y;
if (y < tri.min_y) {
tri.min_y = y;
}
if (y > tri.max_y) {
tri.max_y = y;
}
} //endif ii
tri.min_y -= 1.0f;
tri.max_y += 1.0f;
map_service_->AddTriangle(&tri);
#if 0
if (node->name == "Wall2_1 (18)") {
printf("node->name %s %f,%f,%f %f,%f,%f min_y:%f max_y:%f %f,%f,%f %f,%f,%f %f,%f,%f %d:%d:%d\n",
node->name.c_str(),
box_collider->center.x,
box_collider->center.y,
box_collider->center.z,
box_collider->size.x,
box_collider->size.y,
box_collider->size.z,
tri.min_y,
tri.max_y,
#if 1
tri.vert0.x,
tri.vert0.y,
tri.vert0.z,
tri.vert1.x,
tri.vert1.y,
tri.vert1.z,
tri.vert2.x,
tri.vert2.y,
tri.vert2.z
#else
tri.vert0.x - box_collider->center.x,
tri.vert0.y - box_collider->center.y,
tri.vert0.z - box_collider->center.z,
tri.vert1.x - box_collider->center.x,
tri.vert1.y - box_collider->center.y,
tri.vert1.z - box_collider->center.z,
tri.vert2.x - box_collider->center.x,
tri.vert2.y - box_collider->center.y,
tri.vert2.z - box_collider->center.z
#endif
,box_tri_arr[i][0],
box_tri_arr[i][1],
box_tri_arr[i][2]
);
}
#endif
}//endif i
}//end forc
}
things_.push_back(node);
}
};
for (auto& pair : GetMapMeta()->collider_info->GetNodes()) {
#ifdef MYDEBUG1
printf("node->name %s childs:%d colliders:%d\n",
pair.second->name.c_str(),
(int)pair.second->childs.size(),
(int)pair.second->colliders.size());
#endif
cb(pair.second);
TraverseMapColliderNode(pair.second, cb);
}
}
}
}
bool MapInstance::SceneRaycast(const glm::vec3& orig,
const glm::vec3& dir,
float max_distance,
glm::vec3& hit_pos,
float& ray_length)
{
if (max_distance <= 0.001f) {
return false;
}
float nearest_distance = 0.0f;
CellNode* nearest_node = nullptr;
glm::vec3 nearest_pt(0.0f, 0.0f, 0.0f);
int raycast_index = MapMgr::Instance()->IncCurrRaycastIndex();
float len = 0.0f;
bool result = false;
bool end = false;
int tri_count = 0;
do {
glm::vec3 curr_pos = orig + dir * len;
if (len > max_distance) {
len = max_distance;
curr_pos = orig + dir * len;
end = true;
}
int grid_id = map_service_->GetGridId(curr_pos.x, curr_pos.z);
list_head* grid_list[9];
int grid_count = 0;
map_service_->GetGridList(grid_id, grid_list, grid_count);
for (int i = 0; i < grid_count; ++i){
list_head* head = grid_list[i];
if (list_empty(head)) {
continue;
}
CellNode *node, *tmp;
list_for_each_entry_safe(node, tmp, head, entry) {
if (node->tri->check_flag != raycast_index) {
glm::vec2 bary_position;
float distance;
bool hit = glm::intersectRayTriangle
(orig,
dir,
node->tri->vert0,
node->tri->vert1,
node->tri->vert2,
bary_position,
distance
);
#if 0
a8::XPrintf("rayname:%s hit:%d distance:%f orig:%f,%f,%f %f,%f,%f %f,%f,%f %f,%f,%fn\n",
{node->tri->node->name, hit, distance, orig.x, orig.y, orig.z,
node->tri->vert0.x,
node->tri->vert0.y,
node->tri->vert0.z,
node->tri->vert1.x,
node->tri->vert1.y,
node->tri->vert1.z,
node->tri->vert2.x,
node->tri->vert2.y,
node->tri->vert2.z,
});
#endif
node->tri->check_flag = raycast_index;
++tri_count;
if (hit && distance > 0.00001f) {
if (nearest_node) {
if (distance < nearest_distance) {
nearest_node = node;
nearest_distance = distance;
nearest_pt = orig + dir * distance;
}
} else if (distance <= max_distance) {
nearest_node = node;
nearest_distance = distance;
nearest_pt = orig + dir * distance;
}
}
}
}
}
if (nearest_node && nearest_distance <= len) {
result = true;
end = true;
} else {
len += 5.0f;
}
} while (!end);
if (result) {
#ifdef MYDEBUG1
if (DebugCmd::Enable()) {
a8::XPrintf("nearest_node:%s tri_count:%d nearest_distance:%f\n",
{nearest_node->tri->node->name,
tri_count,
nearest_distance});
}
#endif
hit_pos = nearest_pt;
ray_length = nearest_distance;
}
return result;
}
bool MapInstance::GetNearestGrass(const glm::vec3& center, glm::vec3& out_pt)
{
float nearest_distance = FLT_MAX;
glm::vec3* nearest_pt = nullptr;
for (auto& pt : grass_pos_hash_) {
float distance = std::fabs(pt.x - center.x) + std::fabs(pt.z - center.z);
if (distance < nearest_distance) {
nearest_distance = distance;
nearest_pt = & pt;
}
}
if (nearest_pt) {
out_pt = *nearest_pt;
return true;
}
return false;
}
void MapInstance::AdjustOnLandPoint(glm::vec3& point)
{
float center[3];
center[0] = point.x;
center[1] = point.y;
center[2] = point.z;
const float extents[3] = {4.0f/10.0f, 100.0f, 4.0f/10.0f};
ConnectableQueryFilter filter;
filter.map_instance = this;
filter.setIncludeFlags(GetMoveIncludeFlags());
filter.setExcludeFlags(GetMoveExcludeFlags());
int poly_count = 0;
dtStatus status = navmesh_query_->queryPolygons(center,
extents,
&filter,
polys_,
&poly_count,
MAX_POLYS);
if (dtStatusFailed(status) || poly_count <= 0) {
return;
}
glm::vec3 orig = glm::vec3(point.x, 100.0f, point.z);;
glm::vec3 dir = glm::vec3(0.0f, -1.0f, 0.0f);
const dtPoly* nearest_poly = nullptr;
float nearest_distance = 0.0f;
for (int i = 0; i < poly_count; ++i) {
unsigned int slat = 0;
unsigned int it = 0;
unsigned int ip = 0;
navmesh_->decodePolyId(polys_[i], slat, it, ip);
const dtMeshTile* tile = navmesh_->getTileByRef(polys_[i]);
if (!tile) {
abort();
}
if (ip >= tile->header->polyCount) {
abort();
}
const dtPoly* poly = &tile->polys[ip];
for (int ii = 2; ii < poly->vertCount; ++ii) {
const float* va = &tile->verts[poly->verts[0]*3];
const float* vb = &tile->verts[poly->verts[ii-1]*3];
const float* vc = &tile->verts[poly->verts[ii]*3];
glm::vec3 v0 = glm::vec3(va[0], va[1], va[2]);
glm::vec3 v1 = glm::vec3(vb[0], vb[1], vb[2]);
glm::vec3 v2 = glm::vec3(vc[0], vc[1], vc[2]);
glm::vec2 bary_position;
float distance;
bool hit = glm::intersectRayTriangle
(orig,
dir,
v0,
v1,
v2,
bary_position,
distance
);
if (hit && distance > 0.00001f) {
if (nearest_poly) {
if (distance < nearest_distance) {
nearest_poly = poly;
nearest_distance = distance;
}
} else {
nearest_poly = poly;
nearest_distance = distance;
}
}
}//end for ii
}//end for i;
if (!nearest_poly) {
return;
}
glm::vec3 nearest_pt = orig + dir * nearest_distance;
point.y = nearest_pt.y;
}
void MapInstance::MarkConnectablePolys()
{
dtPolyRef startRef = INVALID_NAVMESH_POLYREF;
{
glm::vec3 pos = map_meta_->GroundSamplingPos();
dtQueryFilter filter;
filter.setIncludeFlags(GetMoveIncludeFlags());
filter.setExcludeFlags(GetMoveExcludeFlags());
const float extents[3] = {2.f, 4.f, 2.f};
float nearestPt[3];
Scale(pos);
float pos1[3];
pos1[0] = pos.x;
pos1[1] = pos.y;
pos1[2] = pos.z;
navmesh_query_->findNearestPoly(pos1, extents, &filter, &startRef, nearestPt);
}
if (!startRef) {
abort();
}
std::list<dtPolyRef> queue;
queue.push_back(startRef);
connectable_polys_.insert(startRef);
while (!queue.empty()) {
dtPolyRef poly_ref = queue.front();
queue.pop_front();
const dtPoly* poly = nullptr;
const dtMeshTile* tile = 0;
navmesh_->getTileAndPolyByRefUnsafe(poly_ref, &tile, &poly);
for (unsigned int i = poly->firstLink; i != DT_NULL_LINK; i = tile->links[i].next) {
dtPolyRef neighbour_ref = tile->links[i].ref;
if (!neighbour_ref) {
continue;
}
if (connectable_polys_.find(neighbour_ref) !=
connectable_polys_.end()) {
continue;
}
queue.push_back(neighbour_ref);
connectable_polys_.insert(neighbour_ref);
}
}
}
bool MapInstance::IsConnectablePoly(dtPolyRef poly_ref)
{
return connectable_polys_.find(poly_ref) != connectable_polys_.end();
}
bool MapInstance::IsValidPos(const glm::vec3& point)
{
if (point.x >= GetMapMeta()->map_width()) {
return false;
}
if (point.x <= 0) {
return false;
}
if (point.z >= GetMapMeta()->map_height()) {
return false;
}
if (point.z <= 0) {
return false;
}
return true;
}
unsigned short MapInstance::GetMoveIncludeFlags()
{
return 0xffff;
}
unsigned short MapInstance::GetMoveExcludeFlags()
{
return SAMPLE_POLYFLAGS_HOP;
}
unsigned short MapInstance::GetBulletIncludeFlags()
{
return 0xffff;
}
unsigned short MapInstance::GetBulletExcludeFlags()
{
return 0;
}