#include "recastmeshbuilder.hpp" #include "debug.hpp" #include "exceptions.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace DetourNavigator { using BulletHelpers::makeProcessTriangleCallback; namespace { RecastMeshTriangle makeRecastMeshTriangle(const btVector3* vertices, const AreaType areaType) { RecastMeshTriangle result; result.mAreaType = areaType; for (std::size_t i = 0; i < 3; ++i) result.mVertices[i] = Misc::Convert::makeOsgVec3f(vertices[i]); return result; } } Mesh makeMesh(std::vector&& triangles) { std::vector uniqueVertices; uniqueVertices.reserve(3 * triangles.size()); for (const RecastMeshTriangle& v : triangles) for (const osg::Vec3f& v : v.mVertices) uniqueVertices.push_back(v); std::sort(uniqueVertices.begin(), uniqueVertices.end()); uniqueVertices.erase(std::unique(uniqueVertices.begin(), uniqueVertices.end()), uniqueVertices.end()); std::vector indices; indices.reserve(3 * triangles.size()); std::vector areaTypes; areaTypes.reserve(triangles.size()); for (const RecastMeshTriangle& v : triangles) { areaTypes.push_back(v.mAreaType); for (const osg::Vec3f& v : v.mVertices) { const auto it = std::lower_bound(uniqueVertices.begin(), uniqueVertices.end(), v); assert(it != uniqueVertices.end()); assert(*it == v); indices.push_back(static_cast(it - uniqueVertices.begin())); } } triangles.clear(); std::vector vertices; vertices.reserve(3 * uniqueVertices.size()); for (const osg::Vec3f& v : uniqueVertices) { vertices.push_back(v.x()); vertices.push_back(v.y()); vertices.push_back(v.z()); } return Mesh(std::move(indices), std::move(vertices), std::move(areaTypes)); } RecastMeshBuilder::RecastMeshBuilder(const TileBounds& bounds) noexcept : mBounds(bounds) { } void RecastMeshBuilder::addObject(const btCollisionShape& shape, const btTransform& transform, const AreaType areaType) { if (shape.isCompound()) return addObject(static_cast(shape), transform, areaType); else if (shape.getShapeType() == TERRAIN_SHAPE_PROXYTYPE) return addObject(static_cast(shape), transform, areaType); else if (shape.isConcave()) return addObject(static_cast(shape), transform, areaType); else if (shape.getShapeType() == BOX_SHAPE_PROXYTYPE) return addObject(static_cast(shape), transform, areaType); std::ostringstream message; message << "Unsupported shape type: " << BroadphaseNativeTypes(shape.getShapeType()); throw InvalidArgument(message.str()); } void RecastMeshBuilder::addObject(const btCompoundShape& shape, const btTransform& transform, const AreaType areaType) { for (int i = 0, num = shape.getNumChildShapes(); i < num; ++i) addObject(*shape.getChildShape(i), transform * shape.getChildTransform(i), areaType); } void RecastMeshBuilder::addObject(const btConcaveShape& shape, const btTransform& transform, const AreaType areaType) { return addObject(shape, transform, makeProcessTriangleCallback([&] (btVector3* vertices, int, int) { RecastMeshTriangle triangle = makeRecastMeshTriangle(vertices, areaType); std::reverse(triangle.mVertices.begin(), triangle.mVertices.end()); mTriangles.emplace_back(triangle); })); } void RecastMeshBuilder::addObject(const btHeightfieldTerrainShape& shape, const btTransform& transform, const AreaType areaType) { return addObject(shape, transform, makeProcessTriangleCallback([&] (btVector3* vertices, int, int) { mTriangles.emplace_back(makeRecastMeshTriangle(vertices, areaType)); })); } void RecastMeshBuilder::addObject(const btBoxShape& shape, const btTransform& transform, const AreaType areaType) { constexpr std::array indices {{ 0, 2, 3, 3, 1, 0, 0, 4, 6, 6, 2, 0, 0, 1, 5, 5, 4, 0, 7, 5, 1, 1, 3, 7, 7, 3, 2, 2, 6, 7, 7, 6, 4, 4, 5, 7, }}; for (std::size_t i = 0; i < indices.size(); i += 3) { std::array vertices; for (std::size_t j = 0; j < 3; ++j) { btVector3 position; shape.getVertex(indices[i + j], position); vertices[j] = transform(position); } mTriangles.emplace_back(makeRecastMeshTriangle(vertices.data(), areaType)); } } void RecastMeshBuilder::addWater(const int cellSize, const osg::Vec3f& shift) { mWater.push_back(Cell {cellSize, shift}); } std::shared_ptr RecastMeshBuilder::create(std::size_t generation, std::size_t revision) && { std::sort(mTriangles.begin(), mTriangles.end()); std::sort(mWater.begin(), mWater.end()); Mesh mesh = makeMesh(std::move(mTriangles)); return std::make_shared(generation, revision, std::move(mesh), std::move(mWater)); } void RecastMeshBuilder::addObject(const btConcaveShape& shape, const btTransform& transform, btTriangleCallback&& callback) { btVector3 aabbMin; btVector3 aabbMax; shape.getAabb(btTransform::getIdentity(), aabbMin, aabbMax); const btVector3 boundsMin(mBounds.mMin.x(), mBounds.mMin.y(), -std::numeric_limits::max() * std::numeric_limits::epsilon()); const btVector3 boundsMax(mBounds.mMax.x(), mBounds.mMax.y(), std::numeric_limits::max() * std::numeric_limits::epsilon()); auto wrapper = makeProcessTriangleCallback([&] (btVector3* triangle, int partId, int triangleIndex) { std::array transformed; for (std::size_t i = 0; i < 3; ++i) transformed[i] = transform(triangle[i]); if (TestTriangleAgainstAabb2(transformed.data(), boundsMin, boundsMax)) callback.processTriangle(transformed.data(), partId, triangleIndex); }); shape.processAllTriangles(&wrapper, aabbMin, aabbMax); } void RecastMeshBuilder::addObject(const btHeightfieldTerrainShape& shape, const btTransform& transform, btTriangleCallback&& callback) { using BulletHelpers::transformBoundingBox; btVector3 aabbMin; btVector3 aabbMax; shape.getAabb(btTransform::getIdentity(), aabbMin, aabbMax); transformBoundingBox(transform, aabbMin, aabbMax); aabbMin.setX(std::max(static_cast(mBounds.mMin.x()), aabbMin.x())); aabbMin.setX(std::min(static_cast(mBounds.mMax.x()), aabbMin.x())); aabbMin.setY(std::max(static_cast(mBounds.mMin.y()), aabbMin.y())); aabbMin.setY(std::min(static_cast(mBounds.mMax.y()), aabbMin.y())); aabbMax.setX(std::max(static_cast(mBounds.mMin.x()), aabbMax.x())); aabbMax.setX(std::min(static_cast(mBounds.mMax.x()), aabbMax.x())); aabbMax.setY(std::max(static_cast(mBounds.mMin.y()), aabbMax.y())); aabbMax.setY(std::min(static_cast(mBounds.mMax.y()), aabbMax.y())); transformBoundingBox(transform.inverse(), aabbMin, aabbMax); auto wrapper = makeProcessTriangleCallback([&] (btVector3* triangle, int partId, int triangleIndex) { std::array transformed; for (std::size_t i = 0; i < 3; ++i) transformed[i] = transform(triangle[i]); callback.processTriangle(transformed.data(), partId, triangleIndex); }); shape.processAllTriangles(&wrapper, aabbMin, aabbMax); } }