Files
SingularityViewer/indra/llprimitive/llprimlinkinfo.h

400 lines
11 KiB
C++

/**
* @file llprimlinkinfo.h
* @author andrew@lindenlab.com
* @brief A template for determining which prims in a set are linkable
*
* $LicenseInfo:firstyear=2007&license=viewergpl$
*
* Copyright (c) 2007-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#ifndef LL_PRIM_LINK_INFO_H
#define LL_PRIM_LINK_INFO_H
// system includes
#include <iostream>
#include <map>
#include <list>
#include <vector>
// common includes
#include "stdtypes.h"
#include "v3math.h"
#include "llquaternion.h"
#include "llsphere.h"
const F32 MAX_OBJECT_SPAN = 54.f; // max distance from outside edge of an object to the farthest edge
const F32 OBJECT_SPAN_BONUS = 2.f; // infinitesimally small prims can always link up to this distance
const S32 MAX_PRIMS_PER_OBJECT = 256;
template < typename DATA_TYPE >
class LLPrimLinkInfo
{
public:
LLPrimLinkInfo();
LLPrimLinkInfo( DATA_TYPE data, const LLSphere& sphere );
~LLPrimLinkInfo();
void set( DATA_TYPE data, const LLSphere& sphere );
void append( DATA_TYPE data, const LLSphere& sphere );
void getData( std::list< DATA_TYPE >& data_list ) const;
F32 getDiameter() const;
LLVector3 getCenter() const;
// returns 'true' if this info can link with other_info
bool canLink( const LLPrimLinkInfo< DATA_TYPE >& other_info );
S32 getPrimCount() const { return mDataMap.size(); }
void mergeLinkableSet( typename std::list< LLPrimLinkInfo < DATA_TYPE > >& unlinked );
void transform(const LLVector3& position, const LLQuaternion& rotation);
private:
// returns number of merges made
S32 merge(LLPrimLinkInfo< DATA_TYPE >& other_info);
// returns number of collapses made
static S32 collapse(typename std::list< LLPrimLinkInfo < DATA_TYPE > >& unlinked );
void computeBoundingSphere();
// Internal utility to encapsulate the link rules
F32 get_max_linkable_span(const LLSphere& first, const LLSphere& second);
F32 get_span(const LLSphere& first, const LLSphere& second);
private:
std::map< DATA_TYPE, LLSphere > mDataMap;
LLSphere mBoundingSphere;
};
template < typename DATA_TYPE >
LLPrimLinkInfo< DATA_TYPE >::LLPrimLinkInfo()
: mBoundingSphere( LLVector3(0.f, 0.f, 0.f), 0.f )
{
}
template < typename DATA_TYPE >
LLPrimLinkInfo< DATA_TYPE >::LLPrimLinkInfo( DATA_TYPE data, const LLSphere& sphere)
: mBoundingSphere(sphere)
{
mDataMap[data] = sphere;
}
template < typename DATA_TYPE >
LLPrimLinkInfo< DATA_TYPE >::~LLPrimLinkInfo()
{
mDataMap.clear();
}
template < typename DATA_TYPE >
void LLPrimLinkInfo< DATA_TYPE>::set( DATA_TYPE data, const LLSphere& sphere )
{
if (!mDataMap.empty())
{
mDataMap.clear();
}
mDataMap[data] = sphere;
mBoundingSphere = sphere;
}
template < typename DATA_TYPE >
void LLPrimLinkInfo< DATA_TYPE>::append( DATA_TYPE data, const LLSphere& sphere )
{
mDataMap[data] = sphere;
if (!mBoundingSphere.contains(sphere))
{
computeBoundingSphere();
}
}
template < typename DATA_TYPE >
void LLPrimLinkInfo< DATA_TYPE >::getData( std::list< DATA_TYPE >& data_list) const
{
typename std::map< DATA_TYPE, LLSphere >::const_iterator map_itr;
for (map_itr = mDataMap.begin(); map_itr != mDataMap.end(); ++map_itr)
{
data_list.push_back(map_itr->first);
}
}
template < typename DATA_TYPE >
F32 LLPrimLinkInfo< DATA_TYPE >::getDiameter() const
{
return 2.f * mBoundingSphere.getRadius();
}
template < typename DATA_TYPE >
LLVector3 LLPrimLinkInfo< DATA_TYPE >::getCenter() const
{
return mBoundingSphere.getCenter();
}
template < typename DATA_TYPE >
F32 LLPrimLinkInfo< DATA_TYPE >::get_max_linkable_span(const LLSphere& first, const LLSphere& second)
{
F32 max_span = 3.f * (first.getRadius() + second.getRadius()) + OBJECT_SPAN_BONUS;
if (max_span > MAX_OBJECT_SPAN)
{
max_span = MAX_OBJECT_SPAN;
}
return max_span;
}
template < typename DATA_TYPE >
F32 LLPrimLinkInfo< DATA_TYPE >::get_span(const LLSphere& first, const LLSphere& second)
{
F32 span = (first.getCenter() - second.getCenter()).length()
+ first.getRadius() + second.getRadius();
return span;
}
// static
// returns 'true' if this info can link with any part of other_info
template < typename DATA_TYPE >
bool LLPrimLinkInfo< DATA_TYPE >::canLink(const LLPrimLinkInfo& other_info)
{
F32 max_span = get_max_linkable_span(mBoundingSphere, other_info.mBoundingSphere);
F32 span = get_span(mBoundingSphere, other_info.mBoundingSphere);
if (span <= max_span)
{
// The entire other_info fits inside the max span.
return TRUE;
}
else if (span > max_span + 2.f * other_info.mBoundingSphere.getRadius())
{
// there is no way any piece of other_info could link with this one
return FALSE;
}
// there may be a piece of other_info that is linkable
typename std::map< DATA_TYPE, LLSphere >::const_iterator map_itr;
for (map_itr = other_info.mDataMap.begin(); map_itr != other_info.mDataMap.end(); ++map_itr)
{
const LLSphere& other_sphere = (*map_itr).second;
max_span = get_max_linkable_span(mBoundingSphere, other_sphere);
span = get_span(mBoundingSphere, other_sphere);
if (span <= max_span)
{
// found one piece that is linkable
return TRUE;
}
}
return FALSE;
}
// merges elements of 'unlinked'
// returns number of links made (NOT final prim count, NOR linked prim count)
// and removes any linkable infos from 'unlinked'
template < typename DATA_TYPE >
void LLPrimLinkInfo< DATA_TYPE >::mergeLinkableSet(std::list< LLPrimLinkInfo< DATA_TYPE > > & unlinked)
{
bool linked_something = true;
while (linked_something)
{
linked_something = false;
typename std::list< LLPrimLinkInfo< DATA_TYPE > >::iterator other_itr = unlinked.begin();
while ( other_itr != unlinked.end()
&& getPrimCount() < MAX_PRIMS_PER_OBJECT )
{
S32 merge_count = merge(*other_itr);
if (merge_count > 0)
{
linked_something = true;
}
if (0 == (*other_itr).getPrimCount())
{
unlinked.erase(other_itr++);
}
else
{
++other_itr;
}
}
if (!linked_something
&& unlinked.size() > 1)
{
S32 collapse_count = collapse(unlinked);
if (collapse_count > 0)
{
linked_something = true;
}
}
}
}
// transforms all of the spheres into a new reference frame
template < typename DATA_TYPE >
void LLPrimLinkInfo< DATA_TYPE >::transform(const LLVector3& position, const LLQuaternion& rotation)
{
typename std::map< DATA_TYPE, LLSphere >::iterator map_itr;
for (map_itr = mDataMap.begin(); map_itr != mDataMap.end(); ++map_itr)
{
(*map_itr).second.setCenter((*map_itr).second.getCenter() * rotation + position);
}
mBoundingSphere.setCenter(mBoundingSphere.getCenter() * rotation + position);
}
// private
// returns number of links made
template < typename DATA_TYPE >
S32 LLPrimLinkInfo< DATA_TYPE >::merge(LLPrimLinkInfo& other_info)
{
S32 link_count = 0;
// F32 other_radius = other_info.mBoundingSphere.getRadius();
// other_info.computeBoundingSphere();
// if ( other_radius != other_info.mBoundingSphere.getRadius() )
// {
// LL_INFOS() << "Other bounding sphere changed!!" << LL_ENDL;
// }
// F32 this_radius = mBoundingSphere.getRadius();
// computeBoundingSphere();
// if ( this_radius != mBoundingSphere.getRadius() )
// {
// LL_INFOS() << "This bounding sphere changed!!" << LL_ENDL;
// }
F32 max_span = get_max_linkable_span(mBoundingSphere, other_info.mBoundingSphere);
// F32 center_dist = (mBoundingSphere.getCenter() - other_info.mBoundingSphere.getCenter()).length();
// LL_INFOS() << "objects are " << center_dist << "m apart" << LL_ENDL;
F32 span = get_span(mBoundingSphere, other_info.mBoundingSphere);
F32 span_limit = max_span + (2.f * other_info.mBoundingSphere.getRadius());
if (span > span_limit)
{
// there is no way any piece of other_info could link with this one
// LL_INFOS() << "span too large: " << span << " vs. " << span_limit << LL_ENDL;
return 0;
}
bool completely_linkable = (span <= max_span) ? true : false;
typename std::map< DATA_TYPE, LLSphere >::iterator map_itr = other_info.mDataMap.begin();
while (map_itr != other_info.mDataMap.end()
&& getPrimCount() < MAX_PRIMS_PER_OBJECT )
{
DATA_TYPE other_data = (*map_itr).first;
LLSphere& other_sphere = (*map_itr).second;
if (!completely_linkable)
{
max_span = get_max_linkable_span(mBoundingSphere, other_sphere);
F32 span = get_span(mBoundingSphere, other_sphere);
if (span > max_span)
{
++map_itr;
continue;
}
}
mDataMap[other_data] = other_sphere;
++link_count;
if (!mBoundingSphere.contains(other_sphere) )
{
computeBoundingSphere();
}
// remove from the other info
other_info.mDataMap.erase(map_itr++);
}
if (link_count > 0 && other_info.getPrimCount() > 0)
{
other_info.computeBoundingSphere();
}
return link_count;
}
// links any linkable elements of unlinked
template < typename DATA_TYPE >
S32 LLPrimLinkInfo< DATA_TYPE >::collapse(std::list< LLPrimLinkInfo< DATA_TYPE > > & unlinked)
{
S32 link_count = 0;
bool linked_something = true;
while (linked_something)
{
linked_something = false;
typename std::list< LLPrimLinkInfo< DATA_TYPE > >::iterator this_itr = unlinked.begin();
typename std::list< LLPrimLinkInfo< DATA_TYPE > >::iterator other_itr = this_itr;
++other_itr;
while ( other_itr != unlinked.end() )
{
S32 merge_count = (*this_itr).merge(*other_itr);
if (merge_count > 0)
{
linked_something = true;
link_count += merge_count;
}
if (0 == (*other_itr).getPrimCount())
{
unlinked.erase(other_itr++);
}
else
{
++other_itr;
}
}
}
return link_count;
}
template < typename DATA_TYPE >
void LLPrimLinkInfo< DATA_TYPE >::computeBoundingSphere()
{
std::vector< LLSphere > sphere_list;
typename std::map< DATA_TYPE, LLSphere >::const_iterator map_itr;
for (map_itr = mDataMap.begin(); map_itr != mDataMap.end(); ++map_itr)
{
sphere_list.push_back(map_itr->second);
}
mBoundingSphere = LLSphere::getBoundingSphere(sphere_list);
}
#endif