/** * @file llvolumeoctree.cpp * * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #include "linden_common.h" #include "llvolumeoctree.h" #include "llvector4a.h" bool LLLineSegmentBoxIntersect(const LLVector4a& start, const LLVector4a& end, const LLVector4a& center, const LLVector4a& size) { LLVector4a fAWdU; LLVector4a dir; LLVector4a diff; dir.setSub(end, start); dir.mul(0.5f); diff.setAdd(end,start); diff.mul(0.5f); diff.sub(center); fAWdU.setAbs(dir); LLVector4a rhs; rhs.setAdd(size, fAWdU); LLVector4a lhs; lhs.setAbs(diff); U32 grt = lhs.greaterThan(rhs).getGatheredBits(); if (grt & 0x7) { return false; } LLVector4a f; f.setCross3(dir, diff); f.setAbs(f); LLVector4a v0, v1; v0 = _mm_shuffle_ps(size, size,_MM_SHUFFLE(3,0,0,1)); v1 = _mm_shuffle_ps(fAWdU, fAWdU, _MM_SHUFFLE(3,1,2,2)); lhs.setMul(v0, v1); v0 = _mm_shuffle_ps(size, size, _MM_SHUFFLE(3,1,2,2)); v1 = _mm_shuffle_ps(fAWdU, fAWdU, _MM_SHUFFLE(3,0,0,1)); rhs.setMul(v0, v1); rhs.add(lhs); grt = f.greaterThan(rhs).getGatheredBits(); return (grt & 0x7) == 0; } LLVolumeOctreeListener::LLVolumeOctreeListener(LLOctreeNode* node) { node->addListener(this); } LLVolumeOctreeListener::~LLVolumeOctreeListener() { } void LLVolumeOctreeListener::handleChildAddition(const LLOctreeNode* parent, LLOctreeNode* child) { new LLVolumeOctreeListener(child); } LLOctreeTriangleRayIntersect::LLOctreeTriangleRayIntersect(const LLVector4a& start, const LLVector4a& dir, LLVolumeFace* face, F32* closest_t, LLVector4a* intersection,LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent) : mStart(start), mDir(dir), mIntersection(intersection), mTexCoord(tex_coord), mNormal(normal), mTangent(tangent), mFace(face), mClosestT(closest_t), mHitFace(false) { mEnd.setAdd(mStart, mDir); } void LLOctreeTriangleRayIntersect::traverse(const LLOctreeNode* node) { LLVolumeOctreeListener* vl = (LLVolumeOctreeListener*) node->getListener(0); if (LLLineSegmentBoxIntersect(mStart, mEnd, vl->mBounds[0], vl->mBounds[1])) { node->accept(this); for (U32 i = 0; i < node->getChildCount(); ++i) { traverse(node->getChild(i)); } } } void LLOctreeTriangleRayIntersect::visit(const LLOctreeNode* node) { for (typename LLOctreeNode::const_element_iter iter = node->getDataBegin(); iter != node->getDataEnd(); ++iter) { const LLVolumeTriangle* tri = *iter; F32 a, b, t; if (LLTriangleRayIntersect(*tri->mV[0], *tri->mV[1], *tri->mV[2], mStart, mDir, a, b, t)) { if ((t >= 0.f) && // if hit is after start (t <= 1.f) && // and before end (t < *mClosestT)) // and this hit is closer { *mClosestT = t; mHitFace = true; mHitTriangle = tri; if (mIntersection != NULL) { LLVector4a intersect = mDir; intersect.mul(*mClosestT); intersect.add(mStart); *mIntersection = intersect; } U32 idx0 = tri->mIndex[0]; U32 idx1 = tri->mIndex[1]; U32 idx2 = tri->mIndex[2]; if (mTexCoord != NULL && mFace->mTexCoords) { LLVector2* tc = (LLVector2*) mFace->mTexCoords; *mTexCoord = ((1.f - a - b) * tc[idx0] + a * tc[idx1] + b * tc[idx2]); } if (mNormal != NULL && mFace->mNormals) { LLVector4a* norm = mFace->mNormals; LLVector4a n1,n2,n3; n1 = norm[idx0]; n1.mul(1.f-a-b); n2 = norm[idx1]; n2.mul(a); n3 = norm[idx2]; n3.mul(b); n1.add(n2); n1.add(n3); *mNormal = n1; } if (mTangent != NULL && mFace->mTangents) { LLVector4a* tangents = mFace->mTangents; LLVector4a t1,t2,t3; t1 = tangents[idx0]; t1.mul(1.f-a-b); t2 = tangents[idx1]; t2.mul(a); t3 = tangents[idx2]; t3.mul(b); t1.add(t2); t1.add(t3); *mTangent = t1; } } } } } const LLVector4a& LLVolumeTriangle::getPositionGroup() const { return mPositionGroup; } const F32& LLVolumeTriangle::getBinRadius() const { return mRadius; } //TEST CODE void LLVolumeOctreeValidate::visit(const LLOctreeNode* branch) { LLVolumeOctreeListener* node = (LLVolumeOctreeListener*) branch->getListener(0); //make sure bounds matches extents LLVector4a& min = node->mExtents[0]; LLVector4a& max = node->mExtents[1]; LLVector4a& center = node->mBounds[0]; LLVector4a& size = node->mBounds[1]; LLVector4a test_min, test_max; test_min.setSub(center, size); test_max.setAdd(center, size); if (!test_min.equals3(min, 0.001f) || !test_max.equals3(max, 0.001f)) { LL_ERRS() << "Bad bounding box data found." << LL_ENDL; } test_min.sub(LLVector4a(0.001f)); test_max.add(LLVector4a(0.001f)); for (U32 i = 0; i < branch->getChildCount(); ++i) { LLVolumeOctreeListener* child = (LLVolumeOctreeListener*) branch->getChild(i)->getListener(0); //make sure all children fit inside this node if (child->mExtents[0].lessThan(test_min).areAnySet(LLVector4Logical::MASK_XYZ) || child->mExtents[1].greaterThan(test_max).areAnySet(LLVector4Logical::MASK_XYZ)) { LL_ERRS() << "Child protrudes from bounding box." << LL_ENDL; } } //children fit, check data for (typename LLOctreeNode::const_element_iter iter = branch->getDataBegin(); iter != branch->getDataEnd(); ++iter) { const LLVolumeTriangle* tri = *iter; //validate triangle for (U32 i = 0; i < 3; i++) { if (tri->mV[i]->greaterThan(test_max).areAnySet(LLVector4Logical::MASK_XYZ) || tri->mV[i]->lessThan(test_min).areAnySet(LLVector4Logical::MASK_XYZ)) { LL_ERRS() << "Triangle protrudes from node." << LL_ENDL; } } } }