#include "detour-navmesh.h" #include #include #include #include #include #include static const int DEFAULT_TILE_SIZE = 64; static const float DEFAULT_CELL_SIZE = 0.3f; static const float DEFAULT_CELL_HEIGHT = 0.2f; static const float DEFAULT_AGENT_HEIGHT = 2.0f; static const float DEFAULT_AGENT_RADIUS = 0.6f; static const float DEFAULT_AGENT_MAX_CLIMB = 0.9f; static const float DEFAULT_AGENT_MAX_SLOPE = 45.0f; static const float DEFAULT_REGION_MIN_SIZE = 8.0f; static const float DEFAULT_REGION_MERGE_SIZE = 20.0f; static const float DEFAULT_EDGE_MAX_LENGTH = 12.0f; static const float DEFAULT_EDGE_MAX_ERROR = 1.3f; static const float DEFAULT_DETAIL_SAMPLE_DISTANCE = 6.0f; static const float DEFAULT_DETAIL_SAMPLE_MAX_ERROR = 1.0f; #ifdef TILE_CACHE static const int DEFAULT_MAX_OBSTACLES = 1024; static const int DEFAULT_MAX_LAYERS = 16; #endif #ifdef TILE_CACHE struct FastLZCompressor : public dtTileCacheCompressor { virtual int maxCompressedSize(const int bufferSize) { return (int)(bufferSize * 1.05f); } virtual dtStatus compress(const unsigned char *buffer, const int bufferSize, unsigned char *compressed, const int /*maxCompressedSize*/, int *compressedSize) { *compressedSize = fastlz_compress((const void *)buffer, bufferSize, compressed); return DT_SUCCESS; } virtual dtStatus decompress(const unsigned char *compressed, const int compressedSize, unsigned char *buffer, const int maxBufferSize, int *bufferSize) { *bufferSize = fastlz_decompress(compressed, compressedSize, buffer, maxBufferSize); return *bufferSize < 0 ? DT_FAILURE : DT_SUCCESS; } }; struct LinearAllocator : public dtTileCacheAlloc { unsigned char *buffer; size_t capacity; size_t top; size_t high; LinearAllocator(const size_t cap) : buffer(0), capacity(0), top(0), high(0) { resize(cap); } ~LinearAllocator() { dtFree(buffer); } void resize(const size_t cap) { if (buffer) dtFree(buffer); buffer = (unsigned char *)dtAlloc(cap, DT_ALLOC_PERM); capacity = cap; } virtual void reset() { high = MAX(high, top); top = 0; } virtual void *alloc(const size_t size) { if (!buffer) return 0; if (top + size > capacity) return 0; unsigned char *mem = &buffer[top]; top += size; return mem; } virtual void free(void * /*ptr*/) { // Empty } }; struct NavMeshProcess : public dtTileCacheMeshProcess { DetourNavigationMesh *nav; inline explicit NavMeshProcess(DetourNavigationMesh *mesh) : nav(mesh) {} virtual void process(struct dtNavMeshCreateParams *params, unsigned char *polyAreas, unsigned short *polyFlags) { /* Add proper flags and offmesh connections here */ for (int i = 0; i < params->polyCount; i++) { if (polyAreas[i] != RC_NULL_AREA) polyFlags[i] = RC_WALKABLE_AREA; } params->offMeshConCount = nav->offmesh_radii.size(); if (params->offMeshConCount > 0) { params->offMeshConVerts = reinterpret_cast(&nav->offmesh_vertices[0]); params->offMeshConRad = &nav->offmesh_radii[0]; params->offMeshConFlags = &nav->offmesh_flags[0]; params->offMeshConAreas = &nav->offmesh_areas[0]; params->offMeshConDir = &nav->offmesh_dir[0]; print_line("added offmesh connection"); } else { print_line("NO offmesh connection"); params->offMeshConVerts = NULL; params->offMeshConRad = NULL; params->offMeshConFlags = NULL; params->offMeshConAreas = NULL; params->offMeshConDir = NULL; } nav->clear_debug_mesh(); nav->get_debug_mesh(); } }; #endif DetourNavigationMesh::DetourNavigationMesh() : Resource(), navmesh(NULL), #ifdef TILE_CACHE tile_cache(0), tile_cache_alloc(new LinearAllocator(64000)), tile_cache_compressor(new FastLZCompressor), mesh_process(new NavMeshProcess(this)), #endif group(""), initialized(false), #ifdef TILE_CACHE max_obstacles(DEFAULT_MAX_OBSTACLES), max_layers(DEFAULT_MAX_LAYERS), #endif tile_size(DEFAULT_TILE_SIZE), cell_size(DEFAULT_CELL_SIZE), cell_height(DEFAULT_CELL_HEIGHT), agent_height(DEFAULT_AGENT_HEIGHT), agent_radius(DEFAULT_AGENT_RADIUS), agent_max_climb(DEFAULT_AGENT_MAX_CLIMB), agent_max_slope(DEFAULT_AGENT_MAX_SLOPE), region_min_size(DEFAULT_REGION_MIN_SIZE), region_merge_size(DEFAULT_REGION_MERGE_SIZE), edge_max_length(DEFAULT_EDGE_MAX_LENGTH), edge_max_error(DEFAULT_EDGE_MAX_ERROR), detail_sample_distance(DEFAULT_DETAIL_SAMPLE_DISTANCE), detail_sample_max_error(DEFAULT_DETAIL_SAMPLE_MAX_ERROR), bounding_box(AABB()), padding(Vector3(1.0f, 1.0f, 1.0f)) { } bool DetourNavigationMesh::alloc_tile_cache() { tile_cache = dtAllocTileCache(); if (!tile_cache) { ERR_PRINT("Could not allocate tile cache"); release_navmesh(); return false; } return true; } void DetourNavigationMesh::release_navmesh() { dtFreeNavMesh((dtNavMesh *)navmesh); navmesh = NULL; num_tiles_x = 0; num_tiles_z = 0; bounding_box = AABB(); } void DetourNavigationMesh::set_group(const String &group) { this->group = group; } unsigned int DetourNavigationMesh::add_obstacle(const Vector3 &pos, real_t radius, real_t height) { /* Need to test how this works and why this needed at all */ /* TODO implement navmesh changes queue */ // while (tile_cache->isObstacleQueueFull()) // tile_cache->update(1, navMesh_); dtObstacleRef ref = 0; if (dtStatusFailed( tile_cache->addObstacle(&pos.coord[0], radius, height, &ref))) { ERR_PRINT("can't add obstacle"); return 0; } return (unsigned int)ref; } void DetourNavigationMesh::remove_obstacle(unsigned int id) { /* Need to test how this works and why this needed at all */ /* TODO implement navmesh changes queue */ // while (tile_cache->isObstacleQueueFull()) // tile_cache->update(1, navMesh_); if (dtStatusFailed(tile_cache->removeObstacle(id))) ERR_PRINT("failed to remove obstacle"); } bool DetourNavigationMesh::alloc() { navmesh = dtAllocNavMesh(); return navmesh ? true : false; } bool DetourNavigationMesh::init(dtNavMeshParams *params) { if (dtStatusFailed((navmesh)->init(params))) { release_navmesh(); return false; } initialized = true; return true; } #ifdef TILE_CACHE bool DetourNavigationMesh::init_tile_cache(dtTileCacheParams *params) { if (dtStatusFailed(tile_cache->init(params, tile_cache_alloc, tile_cache_compressor, mesh_process))) { ERR_PRINT("Could not initialize tile cache"); release_navmesh(); return false; } return true; } #endif Ref DetourNavigationMesh::get_debug_mesh() { if (debug_mesh.is_valid()) return debug_mesh; if (!navmesh) return debug_mesh; print_line("building debug navmesh"); List lines; const dtNavMesh *navm = navmesh; for (int i = 0; i < navm->getMaxTiles(); i++) { const dtMeshTile *tile = navm->getTile(i); if (!tile || !tile->header) continue; for (int j = 0; j < tile->header->polyCount; j++) { dtPoly *poly = tile->polys + j; if (poly->getType() != DT_POLYTYPE_OFFMESH_CONNECTION) { for (int k = 0; k < poly->vertCount; k++) { lines.push_back(*reinterpret_cast( &tile->verts[poly->verts[k] * 3])); lines.push_back(*reinterpret_cast( &tile->verts[poly->verts[(k + 1) % poly->vertCount] * 3])); } } else if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION) { const dtOffMeshConnection *con = &tile->offMeshCons[j - tile->header->offMeshBase]; const float *va = &tile->verts[poly->verts[0] * 3]; const float *vb = &tile->verts[poly->verts[1] * 3]; #if 0 /* TODO: implement offmesh debug proper */ bool startSet = false; bool endSet = false; for (unsigned int k = poly->firstLink; k != DT_NULL_LINK; k = tile->links[k].next) { if (tile->links[k].edge == 0) startSet = true; if (tile->links[k].edge == 1) endSet = true; } #endif Vector3 p0 = *reinterpret_cast(va); Vector3 p1 = *reinterpret_cast(&con[0]); Vector3 p2 = *reinterpret_cast(&con[3]); Vector3 p3 = *reinterpret_cast(vb); lines.push_back(p0); lines.push_back(p1); lines.push_back(p1); lines.push_back(p2); lines.push_back(p2); lines.push_back(p3); print_line("debug offmesh connection"); } } } print_line("debug mesh lines: " + itos(lines.size())); PoolVector varr; varr.resize(lines.size()); PoolVector::Write w = varr.write(); int idx = 0; for (List::Element *E = lines.front(); E; E = E->next()) { w[idx++] = E->get(); } debug_mesh = Ref(memnew(ArrayMesh)); Array arr; arr.resize(Mesh::ARRAY_MAX); arr[Mesh::ARRAY_VERTEX] = varr; debug_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, arr); return debug_mesh; } void DetourNavigationMesh::set_data(const Dictionary &p_value) { dtNavMeshParams params; Vector3 orig = p_value["orig"]; rcVcopy(params.orig, &orig.coord[0]); params.tileWidth = p_value["tile_edge_length"]; params.tileHeight = p_value["tile_edge_length"]; params.maxTiles = p_value["max_tiles"]; params.maxPolys = p_value["max_polys"]; if (navmesh) { if (initialized) dtFreeNavMesh(navmesh); else dtFree(navmesh); navmesh = NULL; } if (!alloc()) return; if (!init(¶ms)) return; } Dictionary DetourNavigationMesh::get_data() { Dictionary t; #if 0 t["initialized"] = initialized; if (!initialized) { return t; } const dtNavMeshParams *params = navmesh->getParams(); Vector3 orig; rcVcopy(&orig.coord[0], params->orig); t["orig"] = orig; t["tile_edge_length"] = params->tileWidth; t["max_tiles"] = params->maxTiles; t["max_polys"] = params->maxPolys; PoolVector data; PoolVector::Write data_w = data.write(); const dtNavMesh *nm = navmesh; int pos = 0; for (int z = 0; z < num_tiles_z; z++) for (int x = 0; x < num_tiles_x; x++) { const dtMeshTile* tile = nm->getTileAt(x, z, 0); if (!tile) continue; if (pos >= data.size()) data.resize(data.size() + sizeof(int) * 2 + sizeof(unsigned int) * 2 + tile->dataSize); memcpy(&data_w[pos], &x, sizeof(x)); pos += sizeof(x); memcpy(&data_w[pos], &z, sizeof(x)); pos += sizeof(z); uint32_t tile_ref = (uint32_t)nm->getTileRef(tile); memcpy(&data_w[pos], &tile_ref, sizeof(tile_ref)); pos += sizeof(tile_ref); uint32_t data_size = (uint32_t)tile->dataSize; memcpy(&data_w[pos], &data_size, sizeof(data_size)); pos += sizeof(data_size); memcpy(&data_w[pos], tile->data, data_size); pos += data_size; } print_line("submitted: " + itos(data.size())); t["data"] = data; #endif return t; } #define SETGET(v, t) \ ClassDB::bind_method(D_METHOD("set_" #v, #v), \ &DetourNavigationMesh::set_##v); \ ClassDB::bind_method(D_METHOD("get_" #v), &DetourNavigationMesh::get_##v); \ ADD_PROPERTY(PropertyInfo(Variant::t, #v), "set_" #v, "get_" #v) void DetourNavigationMesh::_bind_methods() { SETGET(cell_size, REAL); SETGET(cell_height, REAL); SETGET(agent_height, REAL); SETGET(agent_radius, REAL); SETGET(agent_max_climb, REAL); SETGET(agent_max_slope, REAL); SETGET(region_min_size, REAL); SETGET(region_merge_size, REAL); SETGET(edge_max_length, REAL); SETGET(edge_max_error, REAL); SETGET(detail_sample_distance, REAL); SETGET(detail_sample_max_error, REAL); SETGET(group, STRING); SETGET(padding, VECTOR3); SETGET(tile_size, INT); BIND_ENUM_CONSTANT(PARTITION_WATERSHED); BIND_ENUM_CONSTANT(PARTITION_MONOTONE); ClassDB::bind_method(D_METHOD("set_partition_type", "type"), &DetourNavigationMesh::set_partition_type); ClassDB::bind_method(D_METHOD("get_partition_type"), &DetourNavigationMesh::get_partition_type); ClassDB::bind_method(D_METHOD("set_data", "data"), &DetourNavigationMesh::set_data); ClassDB::bind_method(D_METHOD("get_data"), &DetourNavigationMesh::get_data); ADD_PROPERTY(PropertyInfo(Variant::INT, "partition_type", PROPERTY_HINT_ENUM, "watershed,monotone"), "set_partition_type", "get_partition_type"); ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_data", "get_data"); } #undef SETGET void DetourNavigationMesh::get_tile_bounding_box(int x, int z, Vector3 &bmin, Vector3 &bmax) { const float tile_edge_length = (float)tile_size * cell_size; bmin = bounding_box.position + Vector3(tile_edge_length * (float)x, 0, tile_edge_length * (float)z); bmax = bmin + Vector3(tile_edge_length, bounding_box.size.y, tile_edge_length); // print_line("tile bounding box: " +itos(x) + " " + itos(z) + ": " + // String(bmin) + "/" + String(bmax)); // print_line("mesh bounding box: " + String(mesh->bounding_box)); } bool DetourNavigationMesh::build_tile(const Transform &xform, const Vector > &geometries, const Vector &xforms, int x, int z) { Vector3 bmin, bmax; get_tile_bounding_box(x, z, bmin, bmax); dtNavMesh *nav = get_navmesh(); #ifdef TILE_CACHE dtTileCache *tile_cache = get_tile_cache(); tile_cache->removeTile(nav->getTileRefAt(x, z, 0), NULL, NULL); #else nav->removeTile(nav->getTileRefAt(x, z, 0), NULL, NULL); #endif rcConfig cfg; cfg.cs = cell_size; cfg.ch = cell_height; cfg.walkableSlopeAngle = agent_max_slope; cfg.walkableHeight = (int)ceil(agent_height / cfg.ch); cfg.walkableClimb = (int)floor(agent_max_climb / cfg.ch); cfg.walkableRadius = (int)ceil(agent_radius / cfg.cs); cfg.maxEdgeLen = (int)(edge_max_length / cfg.cs); cfg.maxSimplificationError = edge_max_error; cfg.minRegionArea = (int)sqrtf(region_min_size); cfg.mergeRegionArea = (int)sqrtf(region_merge_size); cfg.maxVertsPerPoly = 6; cfg.tileSize = tile_size; cfg.borderSize = cfg.walkableRadius + 3; cfg.width = cfg.tileSize + cfg.borderSize * 2; cfg.height = cfg.tileSize + cfg.borderSize * 2; cfg.detailSampleDist = detail_sample_distance < 0.9f ? 0.0f : cell_size * detail_sample_distance; cfg.detailSampleMaxError = cell_height * detail_sample_max_error; rcVcopy(cfg.bmin, &bmin.coord[0]); rcVcopy(cfg.bmax, &bmax.coord[0]); cfg.bmin[0] -= cfg.borderSize * cfg.cs; cfg.bmin[2] -= cfg.borderSize * cfg.cs; cfg.bmax[0] += cfg.borderSize * cfg.cs; cfg.bmax[2] += cfg.borderSize * cfg.cs; AABB expbox(bmin, bmax - bmin); expbox.position.x -= cfg.borderSize * cfg.cs; expbox.position.z -= cfg.borderSize * cfg.cs; expbox.size.x += 2.0 * cfg.borderSize * cfg.cs; expbox.size.z += 2.0 * cfg.borderSize * cfg.cs; Vector points; Vector indices; Transform base = xform.inverse(); for (int idx = 0; idx < geometries.size(); idx++) { if (!geometries[idx].is_valid()) continue; AABB mesh_aabb = geometries[idx]->get_aabb(); Transform mxform = base * xforms[idx]; mesh_aabb = mxform.xform(mesh_aabb); if (!mesh_aabb.intersects_inclusive(expbox) && !expbox.encloses(mesh_aabb)) { continue; } // Add offmesh // Add NavArea // Add PhysicsBodies? Ref mdata = geometries[idx]; // FIXME add_meshdata(mdata, mxform, points, indices); } // print_line(String() + "points: " + itos(points.size()) + " indices: " + // itos(indices.size()) + " tile_size: " + itos(mesh->tile_size)); #if 0 print_line("mesh points:"); for (int k = 0; k < points.size(); k += 3) print_line("point: " + itos(k) + ": " + rtos(points[k]) + ", " + rtos(points[k + 1]) + ", " + rtos(points[k + 2])); #endif if (points.size() == 0 || indices.size() == 0) /* Nothing to do */ return true; rcHeightfield *heightfield = rcAllocHeightfield(); if (!heightfield) { ERR_PRINT("Failed to allocate height field"); return false; } rcContext *ctx = new rcContext(true); if (!rcCreateHeightfield(ctx, *heightfield, cfg.width, cfg.height, cfg.bmin, cfg.bmax, cfg.cs, cfg.ch)) { ERR_PRINT("Failed to create height field"); return false; } int ntris = indices.size() / 3; Vector tri_areas; tri_areas.resize(ntris); memset(&tri_areas.write[0], 0, ntris); rcMarkWalkableTriangles(ctx, cfg.walkableSlopeAngle, &points[0], points.size() / 3, &indices[0], ntris, &tri_areas.write[0]); rcRasterizeTriangles(ctx, &points[0], points.size() / 3, &indices[0], &tri_areas[0], ntris, *heightfield, cfg.walkableClimb); rcFilterLowHangingWalkableObstacles(ctx, cfg.walkableClimb, *heightfield); rcFilterLedgeSpans(ctx, cfg.walkableHeight, cfg.walkableClimb, *heightfield); rcFilterWalkableLowHeightSpans(ctx, cfg.walkableHeight, *heightfield); rcCompactHeightfield *compact_heightfield = rcAllocCompactHeightfield(); if (!compact_heightfield) { ERR_PRINT("Failed to allocate compact height field"); return false; } if (!rcBuildCompactHeightfield(ctx, cfg.walkableHeight, cfg.walkableClimb, *heightfield, *compact_heightfield)) { ERR_PRINT("Could not build compact height field"); return false; } if (!rcErodeWalkableArea(ctx, cfg.walkableRadius, *compact_heightfield)) { ERR_PRINT("Could not erode walkable area"); return false; } // TODO: Implement area storage in navmesh data and build from that data // populate at collect_geometry stage // areas should indicate if walkable #if 0 for (unsigned int i = 0; i < nav_areas.size(); i++) { Vector3 amin = nav_areas[i].bounds.position; Vector3 amax = amin + nav_areas[i].bounds.size; int id = nav_areas[i].id; rcMarkBoxArea(ctx, &amin.coord[0], &amax.coord[0], id, *compact_heightfield); } #endif if (partition_type == DetourNavigationMesh::PARTITION_WATERSHED) { if (!rcBuildDistanceField(ctx, *compact_heightfield)) return false; if (!rcBuildRegions(ctx, *compact_heightfield, cfg.borderSize, cfg.minRegionArea, cfg.mergeRegionArea)) return false; } else if (!rcBuildRegionsMonotone(ctx, *compact_heightfield, cfg.borderSize, cfg.minRegionArea, cfg.mergeRegionArea)) return false; #ifdef TILE_CACHE rcHeightfieldLayerSet *heightfield_layer_set = rcAllocHeightfieldLayerSet(); if (!heightfield_layer_set) { ERR_PRINT("Could not allocate height field layer set"); return false; } if (!rcBuildHeightfieldLayers(ctx, *compact_heightfield, cfg.borderSize, cfg.walkableHeight, *heightfield_layer_set)) { ERR_PRINT("Could not build heightfield layers"); return false; } for (int i = 0; i < heightfield_layer_set->nlayers; i++) { dtTileCacheLayerHeader header; header.magic = DT_TILECACHE_MAGIC; header.version = DT_TILECACHE_VERSION; header.tx = x; header.ty = z; header.tlayer = i; rcHeightfieldLayer *layer = &heightfield_layer_set->layers[i]; rcVcopy(header.bmin, layer->bmin); rcVcopy(header.bmax, layer->bmax); header.width = (unsigned char)layer->width; header.height = (unsigned char)layer->height; header.minx = (unsigned char)layer->minx; header.maxx = (unsigned char)layer->maxx; header.miny = (unsigned char)layer->miny; header.maxy = (unsigned char)layer->maxy; header.hmin = (unsigned short)layer->hmin; header.hmax = (unsigned short)layer->hmax; unsigned char *tile_data; int tile_data_size; if (dtStatusFailed(dtBuildTileCacheLayer( get_tile_cache_compressor(), &header, layer->heights, layer->areas, layer->cons, &tile_data, &tile_data_size))) { ERR_PRINT("Failed to build tile cache layers"); return false; } dtCompressedTileRef tileRef; int status = tile_cache->addTile(tile_data, tile_data_size, DT_COMPRESSEDTILE_FREE_DATA, &tileRef); if (dtStatusFailed((dtStatus)status)) { dtFree(tile_data); tile_data = NULL; } tile_cache->buildNavMeshTilesAt(x, z, nav); } #else rcContourSet *contour_set = rcAllocContourSet(); if (!contour_set) return false; print_line("allocated contour set"); if (!rcBuildContours(ctx, *compact_heightfield, cfg.maxSimplificationError, cfg.maxEdgeLen, *contour_set)) return false; print_line("created contour set"); rcPolyMesh *poly_mesh = rcAllocPolyMesh(); if (!poly_mesh) return false; print_line("allocated polymesh"); if (!rcBuildPolyMesh(ctx, *contour_set, cfg.maxVertsPerPoly, *poly_mesh)) return false; print_line("created polymesh"); rcPolyMeshDetail *poly_mesh_detail = rcAllocPolyMeshDetail(); if (!poly_mesh_detail) return false; print_line("allocated polymesh detail"); if (!rcBuildPolyMeshDetail(ctx, *poly_mesh, *compact_heightfield, cfg.detailSampleDist, cfg.detailSampleMaxError, *poly_mesh_detail)) return false; print_line("created polymesh detail"); /* Assign area flags TODO: use nav area assignment here */ for (int i = 0; i < poly_mesh->npolys; i++) { if (poly_mesh->areas[i] != RC_NULL_AREA) poly_mesh->flags[i] = 0x1; } print_line("created area flags"); unsigned char *nav_data = NULL; int nav_data_size = 0; dtNavMeshCreateParams params; memset(¶ms, 0, sizeof params); params.verts = poly_mesh->verts; params.vertCount = poly_mesh->nverts; params.polys = poly_mesh->polys; params.polyAreas = poly_mesh->areas; params.polyFlags = poly_mesh->flags; params.polyCount = poly_mesh->npolys; params.nvp = poly_mesh->nvp; params.detailMeshes = poly_mesh_detail->meshes; params.detailVerts = poly_mesh_detail->verts; params.detailVertsCount = poly_mesh_detail->nverts; params.detailTris = poly_mesh_detail->tris; params.detailTriCount = poly_mesh_detail->ntris; params.walkableHeight = mesh->agent_height; params.walkableRadius = mesh->agent_radius; params.walkableClimb = mesh->agent_max_climb; params.tileX = x; params.tileY = z; rcVcopy(params.bmin, poly_mesh->bmin); rcVcopy(params.bmax, poly_mesh->bmax); params.cs = cfg.cs; params.ch = cfg.ch; params.buildBvTree = true; #if 0 // building offmesh conections if (build.offMeshRadii_.Size()) { params.offMeshConCount = build.offMeshRadii_.Size(); params.offMeshConVerts = &build.offMeshVertices_[0].x_; params.offMeshConRad = &build.offMeshRadii_[0]; params.offMeshConFlags = &build.offMeshFlags_[0]; params.offMeshConAreas = &build.offMeshAreas_[0]; params.offMeshConDir = &build.offMeshDir_[0]; } #endif print_line("setup offmesh connections"); if (!dtCreateNavMeshData(¶ms, &nav_data, &nav_data_size)) return false; if (dtStatusFailed(mesh->get_navmesh()->addTile( nav_data, nav_data_size, DT_TILE_FREE_DATA, 0, NULL))) { dtFree(nav_data); return false; } print_line("created navmesh data"); #endif return true; } void DetourNavigationMesh::add_meshdata(const Ref &p_mesh, const Transform &p_xform, Vector &p_verticies, Vector &p_indices) { int current_vertex_count = 0; for (int i = 0; i < p_mesh->get_surface_count(); i++) { current_vertex_count = p_verticies.size() / 3; if (p_mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) continue; int index_count = 0; if (p_mesh->surface_get_format(i) & Mesh::ARRAY_FORMAT_INDEX) { index_count = p_mesh->surface_get_array_index_len(i); } else { index_count = p_mesh->surface_get_array_len(i); } ERR_CONTINUE((index_count == 0 || (index_count % 3) != 0)); int face_count = index_count / 3; Array a = p_mesh->surface_get_arrays(i); PoolVector mesh_vertices = a[Mesh::ARRAY_VERTEX]; PoolVector::Read vr = mesh_vertices.read(); if (p_mesh->surface_get_format(i) & Mesh::ARRAY_FORMAT_INDEX) { PoolVector mesh_indices = a[Mesh::ARRAY_INDEX]; PoolVector::Read ir = mesh_indices.read(); for (int i = 0; i < mesh_vertices.size(); i++) { Vector3 p_vec3 = p_xform.xform(vr[i]); p_verticies.push_back(p_vec3.x); p_verticies.push_back(p_vec3.y); p_verticies.push_back(p_vec3.z); } for (int i = 0; i < face_count; i++) { // CCW p_indices.push_back(current_vertex_count + (ir[i * 3 + 0])); p_indices.push_back(current_vertex_count + (ir[i * 3 + 2])); p_indices.push_back(current_vertex_count + (ir[i * 3 + 1])); } } else { face_count = mesh_vertices.size() / 3; for (int i = 0; i < face_count; i++) { Vector3 p_vec3 = p_xform.xform(vr[i * 3 + 0]); p_verticies.push_back(p_vec3.x); p_verticies.push_back(p_vec3.y); p_verticies.push_back(p_vec3.z); p_vec3 = p_xform.xform(vr[i * 3 + 2]); p_verticies.push_back(p_vec3.x); p_verticies.push_back(p_vec3.y); p_verticies.push_back(p_vec3.z); p_vec3 = p_xform.xform(vr[i * 3 + 1]); p_verticies.push_back(p_vec3.x); p_verticies.push_back(p_vec3.y); p_verticies.push_back(p_vec3.z); p_indices.push_back(current_vertex_count + (i * 3 + 0)); p_indices.push_back(current_vertex_count + (i * 3 + 1)); p_indices.push_back(current_vertex_count + (i * 3 + 2)); } } } } void DetourNavigationMesh::remove_tile(int x, int z) { #ifdef TILE_CACHE dtTileCache *tile_cache = get_tile_cache(); tile_cache->removeTile(navmesh->getTileRefAt(x, z, 0), NULL, NULL); #else nav->removeTile(navmesh->getTileRefAt(x, z, 0), NULL, NULL); #endif } unsigned int DetourNavigationMesh::build_tiles( const Transform &xform, const Vector > &geometries, const Vector &xforms, int x1, int z1, int x2, int z2) { unsigned ret = 0; for (int z = z1; z <= z2; z++) { for (int x = x1; x <= x2; x++) { if (build_tile(xform, geometries, xforms, x, z)) ret++; } } #ifdef TILE_CACHE get_tile_cache()->update(0, get_navmesh()); #endif return ret; }