phoenix-firestorm/indra/newview/llmeshsearch.cpp

231 lines
7.5 KiB
C++

/**
* @file llmeshsearch.cpp
* @brief Search meshes efficiently
* @author Karl Stiefvater <qarl@qarl.com>
*
* $LicenseInfo:firstyear=2013&license=viewerlgpl$
* InWorldz Viewer Source Code
* Copyright (C) 2013, InWorldz, LLC.
*
* 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 or later.
*
* 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
*
* $/LicenseInfo$
*/
#include "llviewerprecompiledheaders.h"
#include "llmeshsearch.h"
#include "llrand.h"
#include "llvolume.h"
LLMeshSearch::LLMeshSearch(std::vector<LLVector3>& positions, std::vector<S32>& indices) :
mPositions(positions), mIndices(indices)
{
// compute bounding box
mBBox.setMin(mPositions[0]);
mBBox.setMax(mPositions[0]);
for (S32 i = 0; i < mPositions.size(); i++)
{
mBBox.addPoint(mPositions[i]);
}
// size of each voxel - emperically determined to give good performance
mVoxelSize = 0.05;
mWidth = (S32)ceil(mBBox.getExtent().mV[VX] / mVoxelSize);
mHeight = (S32)ceil(mBBox.getExtent().mV[VY] / mVoxelSize);
mDepth = (S32)ceil(mBBox.getExtent().mV[VZ] / mVoxelSize);
// the voxel table
mVoxels.resize(mWidth * mHeight * mDepth, NULL);
// insert triangles into voxel table
for (S32 triangle_index = 0; triangle_index < mIndices.size(); triangle_index += 3)
{
S32 index0 = mIndices[triangle_index];
S32 index1 = mIndices[triangle_index+1];
S32 index2 = mIndices[triangle_index+2];
LLVector3& position0 = mPositions[index0];
LLVector3& position1 = mPositions[index1];
LLVector3& position2 = mPositions[index2];
S32 x, y, z;
cellLocation(position0, x, y, z);
insertTriangle(triangle_index, x, y, z);
cellLocation(position1, x, y, z);
insertTriangle(triangle_index, x, y, z);
cellLocation(position2, x, y, z);
insertTriangle(triangle_index, x, y, z);
}
}
LLMeshSearch::~LLMeshSearch()
{
for (S32 i = 0; i < mVoxels.size(); i++)
{
if (mVoxels[i])
{
delete mVoxels[i];
}
}
}
// given a point in space, find it's entry coordinates in the voxel table
void LLMeshSearch::cellLocation(LLVector3& position, S32& x, S32& y, S32& z)
{
LLVector3 local_position = position - mBBox.getMin();
x = (S32)floor(local_position.mV[VX] / mVoxelSize);
y = (S32)floor(local_position.mV[VY] / mVoxelSize);
z = (S32)floor(local_position.mV[VZ] / mVoxelSize);
}
// insert a new triangle into the voxel table
void LLMeshSearch::insertTriangle(S32 triangle_index, S32 x, S32 y, S32 z)
{
S32 voxel_index = (x * mHeight + y) * mDepth + z;
std::vector<S32>* vector_pointer = mVoxels[voxel_index];
if (!vector_pointer)
{
vector_pointer = new std::vector<S32>;
mVoxels[voxel_index] = vector_pointer;
}
// don't insert dupes (which will only appear sequentially so this test catches them all)
if ((vector_pointer->size() == 0) || (vector_pointer->back() != triangle_index))
{
vector_pointer->push_back(triangle_index);
}
}
// find point on mesh which is closest to target mesh, within a given distance, return TRUE if found
BOOL LLMeshSearch::findClosestPoint(LLVector3 target, F32 within,
F32& distance, S32& index0, S32& index1, S32& index2, F32& bary_a, F32& bary_b)
{
BOOL found = FALSE;
F32 closest_distance_squared = powf(within, 2.0f);
S32 target_x, target_y, target_z;
cellLocation(target, target_x, target_y, target_z);
// cycle over all voxels
for (S32 x = 0; x < mWidth; x++)
{
F32 delta_x = llmax(abs(x-target_x)-1, 0) * mVoxelSize;
F32 delta_x_squared = delta_x * delta_x;
// if this plane is too far away, skip it
if ((delta_x_squared) > closest_distance_squared)
continue;
for (S32 y = 0; y < mHeight; y++)
{
F32 delta_y = llmax(abs(y-target_y)-1, 0) * mVoxelSize;
F32 delta_y_squared = delta_y * delta_y;
// if this line is too far away, skip it
if ((delta_x_squared + delta_y_squared) > closest_distance_squared)
continue;
for (S32 z = 0; z < mDepth; z++)
{
F32 delta_z = llmax(abs(z-target_z)-1, 0) * mVoxelSize;
F32 delta_z_squared = delta_z * delta_z;
// if this voxel is too far away, skip it
if ((delta_x_squared + delta_y_squared + delta_z_squared) > closest_distance_squared)
continue;
S32 voxel_index = (x * mHeight + y) * mDepth + z;
std::vector<S32>* vector_pointer = mVoxels[voxel_index];
// if the voxel is empty, skip it
if (!vector_pointer)
continue;
// cycle over all triangles
for (S32 j = 0; j < vector_pointer->size(); j++)
{
S32 triangle_index = (*vector_pointer)[j];
// indices for triangle corners
S32 this_index0 = mIndices[triangle_index+0];
S32 this_index1 = mIndices[triangle_index+1];
S32 this_index2 = mIndices[triangle_index+2];
// positions of triangle corners
LLVector3 this_position0 = mPositions[this_index0];
LLVector3 this_position1 = mPositions[this_index1];
LLVector3 this_position2 = mPositions[this_index2];
// barycentric coord of closest point
F32 this_bary_a;
F32 this_bary_b;
F32 this_distance_squared = LLTriangleClosestPoint(this_position0, this_position1, this_position2,
target, this_bary_a, this_bary_b);
if (this_distance_squared < closest_distance_squared)
{
found = TRUE;
// record corner indices of this triangle
index0 = this_index0;
index1 = this_index1;
index2 = this_index2;
// compute weights from barycentric coordinates
bary_a = this_bary_a;
bary_b = this_bary_b;
closest_distance_squared = this_distance_squared;
}
}
}
}
}
if (found)
{
distance = sqrt(closest_distance_squared);
}
return found;
}