#include "precompile.h" #include #include #include #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 "mt/MetaMgr.h" #include "mt/Param.h" #include "mt/Map.h" #include "mt/MapArea.h" #include "roommgr.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; } 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_ = new MapService(); grid_service_ = new GridService(); 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); CreateThings(); f8::UdpLog::Instance()->Info ("map_id:%d current_uniid:%d ", { map_id, current_uniid_, }); if (current_uniid_ >= FIXED_OBJECT_MAXID) { A8_ABORT(); } { navmesh_ = dtAllocNavMesh(); FILE *fp = fopen((mt::MetaMgr::Instance()->GetResDir() + "map4.bin").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 DEBUG 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_ = new dtNavMeshQuery(); navmesh_query_->init(navmesh_, 1024); MarkMapAreaPolys(); } void MapInstance::UnInit() { delete navmesh_query_; navmesh_query_ = nullptr; dtFreeNavMesh(navmesh_); navmesh_ = nullptr; map_service_->UnInit(); grid_service_->UnInit(); A8_SAFE_DELETE(map_service_); A8_SAFE_DELETE(grid_service_); } void MapInstance::AttachRoom(Room* room, RoomInitInfo& init_info) { init_info.map_tpl_name = map_tpl_name_; 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(); } void MapInstance::CreateThings() { map_tpl_name_ = map_meta_->RandTemplate(); } Entity* MapInstance::GetEntityByUniId(int uniid) { auto itr = uniid_hash_.find(uniid); return itr != uniid_hash_.end() ? itr->second : nullptr; } int MapInstance::AllocUniid() { while (GetEntityByUniId(++current_uniid_) || current_uniid_ == 0) { } return current_uniid_; } int MapInstance::FindStraightPath(int layer, const glm::vec3& start, const glm::vec3& end, std::vector& 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(0xffff); filter.setExcludeFlags(0); 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; int pos = 0; 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); for(int i = 0; i < nstraightPath * 3; ) { glm::vec3 currpos; currpos.x = straightPath[i++]; currpos.y = straightPath[i++]; currpos.z = straightPath[i++]; paths.push_back(currpos); pos++; } } return pos; } int MapInstance::FindRandomPointAroundCircle(int layer, const glm::vec3& center_pos, float max_radius, glm::vec3& random_pt) { dtQueryFilter filter; filter.setIncludeFlags(0xffff); filter.setExcludeFlags(0); dtPolyRef startRef = INVALID_NAVMESH_POLYREF; const float extents[3] = {2.f, 4.f, 2.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 NAV_ERROR_NEARESTPOLY; } dtPolyRef randomRef = INVALID_NAVMESH_POLYREF; float randomPt[3]; dtStatus status = navmesh_query_->findRandomPointAroundCircle (startRef, center, 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 1; } else { return 0; } } bool MapInstance::Raycast(int layer, const glm::vec3& start, const glm::vec3& end, glm::vec3& hit_point, bool& hit_result) { 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; const float extents[3] = {2.f, 4.f, 2.f}; float nearestPt[3]; dtPolyRef startRef = INVALID_NAVMESH_POLYREF; class MyDtQueryFtiler : public dtQueryFilter { public: std::function cb; bool passFilter(const dtPolyRef ref, const dtMeshTile* tile, const dtPoly* poly) const override { bool ret = dtQueryFilter::passFilter(ref, tile, poly); if (cb) { ret = cb(ret, ref, tile, poly); } return ret; } }; MyDtQueryFtiler filter; filter.setIncludeFlags(0xffff); filter.setExcludeFlags(0); std::map> poly_hash; { filter.cb = [&poly_hash] (bool ret, const dtPolyRef ref, const dtMeshTile* tile, const dtPoly* poly) -> bool { poly_hash[ref] = std::make_tuple(tile, poly); return ret; }; } navmesh_query_->findNearestPoly(spos, extents, &filter, &startRef, nearestPt); if (!startRef) { return false; } float t = 0; int npolys; memset(hit_normal_, 0, sizeof(hit_normal_)); navmesh_query_->raycast(startRef, spos, epos, &filter, &t, hit_normal_, polys_, &npolys, MAX_POLYS); #ifdef DEBUG1 { std::string dbg_data = a8::Format("npolys:%d t:%f ", {npolys, t}); for (int i = 0; i < npolys; ++i) { auto itr = poly_hash.find(polys_[i]); if (itr != poly_hash.end()) { auto tile = std::get<0>(itr->second); auto poly = std::get<1>(itr->second); assert(poly->vertCount > 2); dbg_data += a8::Format("poly%d: vert_count:%d ", {i + 1, poly->vertCount}); for (int ii = 0; ii < poly->vertCount; ++ii) { const float* va = &tile->verts[poly->verts[ii] * 3]; dbg_data += a8::Format(" v:%d %f,%f,%f ", {ii, va[0], va[1], va[2]}); } } else { abort(); } } a8::XPrintf("raycast hit dbg_data:%s\n", {dbg_data}); } #endif 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); if (npolys > 0) { glm::vec3 dir(epos[0] - spos[0], epos[1] - spos[1], epos[2] - spos[2]); GlmHelper::Normalize(dir); float hit_pos_copy[3]; dtVcopy(hit_pos_copy, hit_pos_); float h = 0; bool ok = false; Scale(dir); for (int ii = npolys - 1; ii >= 0; --ii) { auto ret = navmesh_query_->getPolyHeight(polys_[ii], hit_pos_copy, &h); if (ret == DT_SUCCESS){ ok = true; break; } } if (!ok){ for (int i = 0; i < 30; ++i) { hit_pos_copy[0] -= dir.x; hit_pos_copy[2] -= dir.z; for (int ii = npolys - 1; ii >= 0; --ii) { auto ret = navmesh_query_->getPolyHeight(polys_[ii], hit_pos_copy, &h); if (ret == DT_SUCCESS){ ok = true; break; } } if (ok) { break; } } dtVcopy(hit_pos_copy, hit_pos_); for (int i = 0; i < 30; ++i) { hit_pos_copy[0] += dir.x; hit_pos_copy[2] += dir.z; for (int ii = npolys - 1; ii >= 0; --ii) { auto ret = navmesh_query_->getPolyHeight(polys_[ii], hit_pos_copy, &h); if (ret == DT_SUCCESS){ ok = true; break; } } if (ok) { break; } } } assert(ok); hit_pos_[0] = hit_pos_copy[0]; hit_pos_[1] = h; hit_pos_[2] = hit_pos_copy[2]; } else { //abort(); } hit_result = true; } 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(0xffff); filter.setExcludeFlags(0); const float extents[3] = {radius, radius, 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(0xffff); filter.setExcludeFlags(0); const float extents[3] = {2.f, 4.f, 2.f}; float nearestPt[3]; float center[3]; center[0] = pos.x * GetMapMeta()->scale(); center[1] = pos.y; center[2] = pos.z * 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.y; 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(0xffff); filter.setExcludeFlags(0); 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) { 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; } } poly_ext_datas_.push_back(ext_flag); } }