#include "stdafx.h" #include "v8world/KDTree.h" #include "BulletCollision/CollisionShapes/btTriangleCallback.h" DYNAMIC_FASTINTVARIABLE(SmoothTerrainPhysicsRayAabbSlop, 0) namespace RBX { struct RayNode { unsigned int index; float tmin; float tmax; RayNode() { } RayNode(unsigned int index, float tmin, float tmax) : index(index) , tmin(tmin) , tmax(tmax) { } }; static int getOutcode(const Vector3& p, const Vector3& aabbMin, const Vector3& aabbMax) { return (p.x < aabbMin.x ? 0x01 : 0x0) | (p.x > aabbMax.x ? 0x08 : 0x0) | (p.y < aabbMin.y ? 0x02 : 0x0) | (p.y > aabbMax.y ? 0x10 : 0x0) | (p.z < aabbMin.z ? 0x4 : 0x0) | (p.z > aabbMax.z ? 0x20 : 0x0); } static bool rayAabb(const Vector3& rayFrom, const Vector3& rayTo, const Vector3& aabbMin, const Vector3& aabbMax, float& paramMin, float& paramMax) { int sourceOutcode = getOutcode(rayFrom, aabbMin, aabbMax); int targetOutcode = getOutcode(rayTo, aabbMin, aabbMax); if ((sourceOutcode & targetOutcode) == 0x0) { Vector3 rayDir = rayTo - rayFrom; float lambdaMin = paramMin; float lambdaMax = paramMax; int bit = 1; for (int j = 0; j < 2; ++j) { const Vector3& aabbBound = j ? aabbMax : aabbMin; for (int i = 0; i != 3; ++i) { if (sourceOutcode & bit) { float lambda = (aabbBound[i] - rayFrom[i]) / rayDir[i]; if (lambdaMin > lambda) lambdaMin = lambda; } else if (targetOutcode & bit) { float lambda = (aabbBound[i] - rayFrom[i]) / rayDir[i]; if (lambdaMax < lambda) lambdaMax = lambda; } bit <<= 1; } } if (lambdaMin <= lambdaMax) { paramMin = lambdaMin; paramMax = lambdaMax; return true; } } return false; } template static T* getBuffer(T (&stackBuffer)[32], boost::scoped_array& heapBuffer, size_t bufferSize) { if (bufferSize <= sizeof(stackBuffer) / sizeof(stackBuffer[0])) return stackBuffer; heapBuffer.reset(new T[bufferSize]); return heapBuffer.get(); } static void queryAABBStackless(const KDTree* tree, btTriangleCallback* callback, const Vector3& aabbMin, const Vector3& aabbMax) { unsigned int stackBuffer[32]; boost::scoped_array heapBuffer; unsigned int* buffer = getBuffer(stackBuffer, heapBuffer, tree->depth); size_t bufferOffset = 0; buffer[bufferOffset++] = 0; while (bufferOffset > 0) { size_t index = buffer[--bufferOffset]; const KDNode& node = tree->nodes[index]; if (node.isLeaf()) { size_t triangleCount = node.leaf.triangleCount; for (size_t i = 0; i < triangleCount; ++i) { unsigned int tri = node.leaf.triangles[i]; unsigned int i0 = tree->indices[3 * tri + 0]; unsigned int i1 = tree->indices[3 * tri + 1]; unsigned int i2 = tree->indices[3 * tri + 2]; btVector3 data[3]; data[0] = btVector3(tree->vertexPositions[i0].x, tree->vertexPositions[i0].y, tree->vertexPositions[i0].z); data[1] = btVector3(tree->vertexPositions[i1].x, tree->vertexPositions[i1].y, tree->vertexPositions[i1].z); data[2] = btVector3(tree->vertexPositions[i2].x, tree->vertexPositions[i2].y, tree->vertexPositions[i2].z); callback->processTriangle(data, 0, tri); } } else { int axis = node.branch.axis; unsigned int childIndex = node.branch.childIndex; RBXASSERT(bufferOffset + 2 <= tree->depth); if (node.branch.splits[1] <= aabbMax[axis]) buffer[bufferOffset++] = childIndex + 1; if (node.branch.splits[0] >= aabbMin[axis]) buffer[bufferOffset++] = childIndex + 0; } } } static void rayTriangle(KDTree::RayResult& result, const Vector3& raySource, const Vector3& rayDir, const Vector3& v0, const Vector3& v1, const Vector3& v2, const KDTree* tree, unsigned int tri) { Vector3 edge1 = v1 - v0; Vector3 edge2 = v2 - v0; Vector3 P = rayDir.cross(edge2); float det = edge1.dot(P); if (det <= 0) return; Vector3 T = raySource - v0; float u = T.dot(P); if (u < 0 || u > det) return; Vector3 Q = T.cross(edge1); float v = rayDir.dot(Q); if (v < 0 || u + v > det) return; float t = edge2.dot(Q) / det; if (t < 0 || t >= result.fraction) return; result = KDTree::RayResult(t, tree, tri); } static void queryRayStackless(const KDTree* tree, KDTree::RayResult& result, const Vector3& raySource, const Vector3& rayTarget, float tmin, float tmax) { RayNode stackBuffer[32]; boost::scoped_array heapBuffer; RayNode* buffer = getBuffer(stackBuffer, heapBuffer, tree->depth); Vector3 rayDir = rayTarget - raySource; size_t bufferOffset = 0; buffer[bufferOffset++] = RayNode(0, tmin, tmax); while (bufferOffset > 0) { RayNode rn = buffer[--bufferOffset]; if (rn.tmin >= result.fraction) continue; const KDNode& node = tree->nodes[rn.index]; if (node.isLeaf()) { size_t triangleCount = node.leaf.triangleCount; for (size_t i = 0; i < triangleCount; ++i) { unsigned int tri = node.leaf.triangles[i]; unsigned int i0 = tree->indices[3 * tri + 0]; unsigned int i1 = tree->indices[3 * tri + 1]; unsigned int i2 = tree->indices[3 * tri + 2]; const Vector3& v0 = tree->vertexPositions[i0]; const Vector3& v1 = tree->vertexPositions[i1]; const Vector3& v2 = tree->vertexPositions[i2]; rayTriangle(result, raySource, rayDir, v0, v1, v2, tree, tri); } } else { int axis = node.branch.axis; unsigned int childIndex = node.branch.childIndex; RBXASSERT(bufferOffset + 2 <= tree->depth); float sa = raySource[axis]; float da = rayDir[axis]; if (da == 0) { if (node.branch.splits[0] >= sa) buffer[bufferOffset++] = RayNode(childIndex + 0, rn.tmin, rn.tmax); if (node.branch.splits[1] <= sa) buffer[bufferOffset++] = RayNode(childIndex + 1, rn.tmin, rn.tmax); } else { // start with the node that's closer to the ray origin int i0 = (da > 0) ? 0 : 1; int i1 = 1 - i0; float t0 = (node.branch.splits[i0] - sa) / da; float t1 = (node.branch.splits[i1] - sa) / da; if (t1 <= rn.tmax) buffer[bufferOffset++] = RayNode(childIndex + i1, std::max(t1, rn.tmin), rn.tmax); if (t0 >= rn.tmin) buffer[bufferOffset++] = RayNode(childIndex + i0, rn.tmin, std::min(t0, rn.tmax)); } } } } KDTree::KDTree() : vertexPositions(0) , vertexMaterials(0) , indices(0) , depth(0) { } void KDTree::queryAABB(btTriangleCallback* callback, const Vector3& aabbMin, const Vector3& aabbMax) const { if (nodes.empty()) return; Vector3 clampedMin = aabbMin.max(extentsMin); Vector3 clampedMax = aabbMax.min(extentsMax); queryAABBStackless(this, callback, clampedMin, clampedMax); } void KDTree::queryRay(RayResult& result, const Vector3& raySource, const Vector3& rayTarget) const { if (nodes.empty()) return; float tmin = 0, tmax = 1; if (!rayAabb(raySource, rayTarget, extentsMin, extentsMax, tmin, tmax)) return; // btRayAabbExact is not too exact - floating point imprecision means we could lose the ray hit unless we extend the min/max bounds a bit if (DFInt::SmoothTerrainPhysicsRayAabbSlop) { tmin = std::max(tmin - DFInt::SmoothTerrainPhysicsRayAabbSlop / 1000.f, 0.f); tmax = std::min(tmax + DFInt::SmoothTerrainPhysicsRayAabbSlop / 1000.f, 1.f); } queryRayStackless(this, result, raySource, rayTarget, tmin, tmax); } Vector3 KDTree::getTriangleNormal(unsigned int triangle) const { unsigned int i0 = indices[3 * triangle + 0]; unsigned int i1 = indices[3 * triangle + 1]; unsigned int i2 = indices[3 * triangle + 2]; const Vector3& v0 = vertexPositions[i0]; const Vector3& v1 = vertexPositions[i1]; const Vector3& v2 = vertexPositions[i2]; return (v1 - v0).unitCross(v2 - v0); } unsigned char KDTree::getMaterial(unsigned int triangle, const Vector3& position) const { unsigned int i0 = indices[3 * triangle + 0]; unsigned int i1 = indices[3 * triangle + 1]; unsigned int i2 = indices[3 * triangle + 2]; const Vector3& v0 = vertexPositions[i0]; const Vector3& v1 = vertexPositions[i1]; const Vector3& v2 = vertexPositions[i2]; unsigned char m0 = vertexMaterials[i0]; unsigned char m1 = vertexMaterials[i1]; unsigned char m2 = vertexMaterials[i2]; float d0 = (v0 - position).squaredLength(); float d1 = (v1 - position).squaredLength(); float d2 = (v2 - position).squaredLength(); return (d0 < d1 && d0 < d2) ? m0 : (d1 < d2) ? m1 : m2; } struct KDTreeBuilder { struct Triangle { unsigned int index; Vector3 midpoint; }; template struct TriangleAxisSplitter { float divider; TriangleAxisSplitter(float divider) : divider(divider) { } bool operator()(const Triangle& lhs) const { return lhs.midpoint[Axis] < divider; } }; std::pair build(size_t vertexCount, size_t triangleCount) { if (triangleCount == 0) return std::make_pair(0, Extents()); triangles.resize(triangleCount); for (size_t i = 0; i < triangleCount; ++i) { unsigned int i0 = indices[i * 3 + 0]; unsigned int i1 = indices[i * 3 + 1]; unsigned int i2 = indices[i * 3 + 2]; Triangle& t = triangles[i]; t.index = i; t.midpoint = (vertices[i0] + vertices[i1] + vertices[i2]) * (1.f / 3); } nodes->clear(); nodes->push_back(KDNode()); return split(0, 0, triangleCount); } std::pair split(size_t index, size_t begin, size_t end) { if (end - begin <= 2) { KDNode& node = (*nodes)[index]; node.leaf.axis = 3; node.leaf.triangleCount = end - begin; Extents e; for (size_t i = begin; i < end; ++i) { unsigned int tri = triangles[i].index; unsigned int i0 = indices[tri * 3 + 0]; unsigned int i1 = indices[tri * 3 + 1]; unsigned int i2 = indices[tri * 3 + 2]; node.leaf.triangles[i - begin] = tri; e.expandToContain(vertices[i0]); e.expandToContain(vertices[i1]); e.expandToContain(vertices[i2]); } return std::make_pair(1, e); } else { // gather midpoint stats Vector3 min = Vector3::maxFinite(); Vector3 max = Vector3::minFinite(); Vector3 avg = Vector3(); size_t size = end - begin; for (size_t i = begin; i < end; ++i) { const Vector3& mp = triangles[i].midpoint; min = min.min(mp); max = max.max(mp); avg += mp; } avg /= size; // partition triangles Vector3 ext = max - min; int axis; std::vector::iterator it; if (ext.x > ext.y && ext.x > ext.z) { axis = 0; it = std::partition(triangles.begin() + begin, triangles.begin() + end, TriangleAxisSplitter<0>(avg.x)); } else if (ext.y > ext.z) { axis = 1; it = std::partition(triangles.begin() + begin, triangles.begin() + end, TriangleAxisSplitter<1>(avg.y)); } else { axis = 2; it = std::partition(triangles.begin() + begin, triangles.begin() + end, TriangleAxisSplitter<2>(avg.z)); } // repartition in half to keep balance size_t pr = it - triangles.begin(); size_t partitioned = pr - begin; if (partitioned <= size / 4 || partitioned >= size - size / 4) pr = begin + size / 2; // recurse size_t childIndex = nodes->size(); nodes->push_back(KDNode()); nodes->push_back(KDNode()); auto ln = split(childIndex + 0, begin, pr); auto rn = split(childIndex + 1, pr, end); KDNode& node = (*nodes)[index]; node.branch.splits[0] = ln.second.max()[axis]; node.branch.splits[1] = rn.second.min()[axis]; node.branch.axis = axis; node.branch.childIndex = childIndex; Extents e = ln.second; e.expandToContain(rn.second); return std::make_pair(std::max(ln.first, rn.first) + 1, e); } } const Vector3* vertices; const unsigned int* indices; std::vector* nodes; std::vector triangles; }; void KDTree::build(const Vector3* vertexPositions, const unsigned char* vertexMaterials, size_t vertexCount, const unsigned int* indices, size_t triangleCount) { KDTreeBuilder builder; builder.vertices = vertexPositions; builder.indices = indices; builder.nodes = &this->nodes; auto p = builder.build(vertexCount, triangleCount); this->vertexPositions = vertexPositions; this->vertexMaterials = vertexMaterials; this->indices = indices; this->depth = p.first; this->extentsMin = p.second.min(); this->extentsMax = p.second.max(); } }