1322 lines
27 KiB
C++
1322 lines
27 KiB
C++
/**
|
|
* @file lldrawable.cpp
|
|
* @brief LLDrawable class implementation
|
|
*
|
|
* Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
|
|
* $License$
|
|
*/
|
|
|
|
#include "llviewerprecompiledheaders.h"
|
|
|
|
#include "lldrawable.h"
|
|
|
|
// library includes
|
|
#include "material_codes.h"
|
|
|
|
// viewer includes
|
|
#include "llagparray.h"
|
|
#include "llagparray.inl"
|
|
#include "llcriticaldamp.h"
|
|
#include "llface.h"
|
|
#include "lllightconstants.h"
|
|
#include "llsky.h"
|
|
#include "llsurfacepatch.h"
|
|
#include "llviewercamera.h"
|
|
#include "llviewerregion.h"
|
|
#include "llvolume.h"
|
|
#include "llvoavatar.h"
|
|
#include "llvovolume.h"
|
|
#include "llvosurfacepatch.h" // for debugging
|
|
#include "llworld.h"
|
|
#include "pipeline.h"
|
|
#include "llspatialpartition.h"
|
|
#include "llviewerobjectlist.h"
|
|
#include "llviewerwindow.h"
|
|
|
|
const F32 MIN_INTERPOLATE_DISTANCE_SQUARED = 0.001f * 0.001f;
|
|
const F32 MAX_INTERPOLATE_DISTANCE_SQUARED = 10.f * 10.f;
|
|
const F32 OBJECT_DAMPING_TIME_CONSTANT = 0.06f;
|
|
const F32 MIN_SHADOW_CASTER_RADIUS = 2.0f;
|
|
|
|
////////////////////////
|
|
//
|
|
// Inline implementations.
|
|
//
|
|
//
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// Drawable code
|
|
//
|
|
//
|
|
|
|
// static
|
|
U32 LLDrawable::sCurVisible = 0;
|
|
U32 LLDrawable::sNumZombieDrawables = 0;
|
|
F32 LLDrawable::sCurPixelAngle = 0;
|
|
LLDynamicArrayPtr<LLPointer<LLDrawable> > LLDrawable::sDeadList;
|
|
|
|
// static
|
|
void LLDrawable::incrementVisible()
|
|
{
|
|
sCurVisible++;
|
|
sCurPixelAngle = (F32) gViewerWindow->getWindowDisplayHeight()/gCamera->getView();
|
|
}
|
|
void LLDrawable::init()
|
|
{
|
|
// mXform
|
|
mParent = NULL;
|
|
mRenderType = 0;
|
|
mCurrentScale = LLVector3(1,1,1);
|
|
mDistanceWRTCamera = 0.0f;
|
|
// mUVRect
|
|
mUVZ = 0.f;
|
|
// mLightSet
|
|
// mBlockSet
|
|
// mSavePos
|
|
mQuietCount = 0;
|
|
|
|
mState = 0;
|
|
mVObjp = NULL;
|
|
// mFaces
|
|
mSpatialGroupp = NULL;
|
|
mSpatialGroupOffset = -1;
|
|
mVisible = 0;
|
|
mRadius = 0.f;
|
|
mSunShadowFactor = 1.f;
|
|
|
|
mGeneration = -1;
|
|
mBinRadius = 1.f;
|
|
mSpatialBridge = NULL;
|
|
}
|
|
|
|
// static
|
|
void LLDrawable::initClass()
|
|
{
|
|
}
|
|
|
|
|
|
void LLDrawable::destroy()
|
|
{
|
|
if (isDead())
|
|
{
|
|
sNumZombieDrawables--;
|
|
}
|
|
|
|
std::for_each(mFaces.begin(), mFaces.end(), DeletePointer());
|
|
mFaces.clear();
|
|
|
|
/*
|
|
if (!(sNumZombieDrawables % 10))
|
|
{
|
|
llinfos << "- Zombie drawables: " << sNumZombieDrawables << llendl;
|
|
}
|
|
*/
|
|
}
|
|
|
|
void LLDrawable::markDead()
|
|
{
|
|
if (isDead())
|
|
{
|
|
llwarns << "Warning! Marking dead multiple times!" << llendl;
|
|
return;
|
|
}
|
|
|
|
if (mSpatialBridge)
|
|
{
|
|
mSpatialBridge->markDead();
|
|
mSpatialBridge = NULL;
|
|
}
|
|
|
|
sNumZombieDrawables++;
|
|
|
|
// We're dead. Free up all of our references to other objects
|
|
setState(DEAD);
|
|
cleanupReferences();
|
|
// sDeadList.put(this);
|
|
}
|
|
|
|
LLVOVolume* LLDrawable::getVOVolume() const
|
|
{
|
|
LLViewerObject* objectp = mVObjp;
|
|
if ( !isDead() && objectp && (objectp->getPCode() == LL_PCODE_VOLUME))
|
|
{
|
|
return ((LLVOVolume*)objectp);
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
BOOL LLDrawable::isLight() const
|
|
{
|
|
LLViewerObject* objectp = mVObjp;
|
|
if ( objectp && (objectp->getPCode() == LL_PCODE_VOLUME) && !isDead())
|
|
{
|
|
return ((LLVOVolume*)objectp)->getIsLight();
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
void LLDrawable::clearLightSet()
|
|
{
|
|
// Remove this object from any object which has it as a light
|
|
for (drawable_set_t::iterator iter = mLightSet.begin(); iter != mLightSet.end(); iter++)
|
|
{
|
|
LLDrawable *targetp = *iter;
|
|
if (targetp != this && !targetp->isDead())
|
|
{
|
|
targetp->mLightSet.erase(this);
|
|
gPipeline.markRelight(targetp);
|
|
}
|
|
}
|
|
mLightSet.clear();
|
|
}
|
|
|
|
void LLDrawable::cleanupReferences()
|
|
{
|
|
LLFastTimer t(LLFastTimer::FTM_PIPELINE);
|
|
|
|
std::for_each(mFaces.begin(), mFaces.end(), DeletePointer());
|
|
mFaces.clear();
|
|
|
|
clearLightSet();
|
|
|
|
gObjectList.removeDrawable(this);
|
|
|
|
mBlockSet.clear();
|
|
|
|
gPipeline.unlinkDrawable(this);
|
|
|
|
// Cleanup references to other objects
|
|
mVObjp = NULL;
|
|
mParent = NULL;
|
|
}
|
|
|
|
void LLDrawable::cleanupDeadDrawables()
|
|
{
|
|
/*
|
|
S32 i;
|
|
for (i = 0; i < sDeadList.count(); i++)
|
|
{
|
|
if (sDeadList[i]->getNumRefs() > 1)
|
|
{
|
|
llwarns << "Dead drawable has " << sDeadList[i]->getNumRefs() << " remaining refs" << llendl;
|
|
gPipeline.findReferences(sDeadList[i]);
|
|
}
|
|
}
|
|
*/
|
|
sDeadList.reset();
|
|
}
|
|
|
|
S32 LLDrawable::findReferences(LLDrawable *drawablep)
|
|
{
|
|
S32 count = 0;
|
|
if (mLightSet.count(drawablep) > 0)
|
|
{
|
|
llinfos << this << ": lightset reference" << llendl;
|
|
count++;
|
|
}
|
|
if (mBlockSet.count(drawablep) > 0)
|
|
{
|
|
llinfos << this << ": blockset reference" << llendl;
|
|
count++;
|
|
}
|
|
if (mParent == drawablep)
|
|
{
|
|
llinfos << this << ": parent reference" << llendl;
|
|
count++;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
#if 0
|
|
// SJB: This is SLOW, so we don't want to allow it (we don't currently use it)
|
|
void LLDrawable::removeFace(const S32 i)
|
|
{
|
|
LLFace *face= mFaces[i];
|
|
|
|
if (face)
|
|
{
|
|
mFaces.erase(mFaces.begin() + i);
|
|
delete face;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
LLFace* LLDrawable::addFace(LLDrawPool *poolp, LLViewerImage *texturep, const BOOL shared_geom)
|
|
{
|
|
LLMemType mt(LLMemType::MTYPE_DRAWABLE);
|
|
|
|
LLFace *face = new LLFace(this, mVObjp);
|
|
|
|
if (face)
|
|
{
|
|
mFaces.push_back(face);
|
|
face->setPool(poolp, texturep);
|
|
|
|
if (shared_geom)
|
|
{
|
|
face->setState(LLFace::SHARED_GEOM);
|
|
}
|
|
else if (!isVisible())
|
|
{
|
|
face->setState(LLFace::BACKLIST);
|
|
}
|
|
if (isState(UNLIT))
|
|
{
|
|
face->setState(LLFace::FULLBRIGHT);
|
|
}
|
|
}
|
|
return face;
|
|
}
|
|
|
|
void LLDrawable::setNumFaces(const S32 newFaces, LLDrawPool *poolp, LLViewerImage *texturep)
|
|
{
|
|
if (newFaces == (S32)mFaces.size())
|
|
{
|
|
return;
|
|
}
|
|
else if (newFaces < (S32)mFaces.size())
|
|
{
|
|
std::for_each(mFaces.begin() + newFaces, mFaces.end(), DeletePointer());
|
|
mFaces.erase(mFaces.begin() + newFaces, mFaces.end());
|
|
}
|
|
else // (newFaces > mFaces.size())
|
|
{
|
|
mFaces.reserve(newFaces);
|
|
for (int i = mFaces.size(); i<newFaces; i++)
|
|
{
|
|
addFace(poolp, texturep);
|
|
}
|
|
}
|
|
}
|
|
|
|
void LLDrawable::setNumFacesFast(const S32 newFaces, LLDrawPool *poolp, LLViewerImage *texturep)
|
|
{
|
|
if (newFaces <= (S32)mFaces.size() && newFaces >= (S32)mFaces.size()/2)
|
|
{
|
|
return;
|
|
}
|
|
else if (newFaces < (S32)mFaces.size())
|
|
{
|
|
std::for_each(mFaces.begin() + newFaces, mFaces.end(), DeletePointer());
|
|
mFaces.erase(mFaces.begin() + newFaces, mFaces.end());
|
|
}
|
|
else // (newFaces > mFaces.size())
|
|
{
|
|
mFaces.reserve(newFaces);
|
|
for (int i = mFaces.size(); i<newFaces; i++)
|
|
{
|
|
addFace(poolp, texturep);
|
|
}
|
|
}
|
|
}
|
|
|
|
void LLDrawable::mergeFaces(LLDrawable* src)
|
|
{
|
|
U32 face_count = mFaces.size() + src->mFaces.size();
|
|
|
|
mFaces.reserve(face_count);
|
|
for (U32 i = 0; i < src->mFaces.size(); i++)
|
|
{
|
|
LLFace* facep = src->mFaces[i];
|
|
facep->setDrawable(this);
|
|
mFaces.push_back(facep);
|
|
}
|
|
src->mFaces.clear();
|
|
}
|
|
|
|
void LLDrawable::deleteFaces(S32 offset, S32 count)
|
|
{
|
|
face_list_t::iterator face_begin = mFaces.begin() + offset;
|
|
face_list_t::iterator face_end = face_begin + count;
|
|
std::for_each(face_begin, face_end, DeletePointer());
|
|
mFaces.erase(face_begin, face_end);
|
|
}
|
|
|
|
void LLDrawable::update()
|
|
{
|
|
llerrs << "Shouldn't be called!" << llendl;
|
|
}
|
|
|
|
|
|
void LLDrawable::updateMaterial()
|
|
{
|
|
}
|
|
|
|
void LLDrawable::makeActive()
|
|
{
|
|
if (!isState(ACTIVE)) // && mGeneration > 0)
|
|
{
|
|
setState(ACTIVE);
|
|
|
|
if (!isRoot() && !mParent->isActive())
|
|
{
|
|
mParent->makeActive();
|
|
}
|
|
|
|
gPipeline.setActive(this, TRUE);
|
|
|
|
//all child objects must also be active
|
|
for (U32 i = 0; i < getChildCount(); i++)
|
|
{
|
|
getChild(i)->makeActive();
|
|
}
|
|
|
|
if (mVObjp->getPCode() == LL_PCODE_VOLUME)
|
|
{
|
|
if (mVObjp->getVolume()->getPathType() == LL_PCODE_PATH_FLEXIBLE)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
clearState(LLDrawable::LIGHTING_BUILT);
|
|
if (mVObjp->getPCode() == LL_PCODE_VOLUME)
|
|
{
|
|
gPipeline.markRebuild(this, LLDrawable::REBUILD_VOLUME, TRUE);
|
|
}
|
|
}
|
|
mQuietCount = 0;
|
|
}
|
|
|
|
|
|
void LLDrawable::makeStatic()
|
|
{
|
|
if (isState(ACTIVE))
|
|
{
|
|
clearState(ACTIVE);
|
|
gPipeline.setActive(this, FALSE);
|
|
|
|
if (mParent.notNull() && mParent->isActive())
|
|
{
|
|
llerrs << "Drawable became static with active parent!" << llendl;
|
|
}
|
|
|
|
S32 child_count = mVObjp->mChildList.size();
|
|
for (S32 child_num = 0; child_num < child_count; child_num++)
|
|
{
|
|
LLDrawable* child_drawable = mVObjp->mChildList[child_num]->mDrawable;
|
|
if (child_drawable)
|
|
{
|
|
child_drawable->makeStatic();
|
|
}
|
|
}
|
|
|
|
gPipeline.markRelight(this);
|
|
if (mVObjp->getPCode() == LL_PCODE_VOLUME)
|
|
{
|
|
gPipeline.markRebuild(this, LLDrawable::REBUILD_VOLUME, TRUE);
|
|
}
|
|
|
|
if (mSpatialBridge)
|
|
{
|
|
mSpatialBridge->markDead();
|
|
setSpatialBridge(NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Returns "distance" between target destination and resulting xfrom
|
|
F32 LLDrawable::updateXform(BOOL undamped)
|
|
{
|
|
BOOL damped = !undamped;
|
|
|
|
// Position
|
|
LLVector3 old_pos(mXform.getPosition());
|
|
LLVector3 target_pos;
|
|
if (mXform.isRoot())
|
|
{
|
|
// get root position in your agent's region
|
|
target_pos = mVObjp->getPositionAgent();
|
|
}
|
|
else
|
|
{
|
|
// parent-relative position
|
|
target_pos = mVObjp->getPosition();
|
|
}
|
|
|
|
// Rotation
|
|
LLQuaternion old_rot(mXform.getRotation());
|
|
LLQuaternion target_rot = mVObjp->getRotation();
|
|
//scaling
|
|
LLVector3 target_scale = mVObjp->getScale();
|
|
LLVector3 old_scale = mCurrentScale;
|
|
LLVector3 dest_scale = target_scale;
|
|
|
|
// Damping
|
|
F32 dist_squared = 0.f;
|
|
F32 scaled = 0.f;
|
|
|
|
if (damped && mDistanceWRTCamera > 0.0f)
|
|
{
|
|
F32 lerp_amt = llclamp(LLCriticalDamp::getInterpolant(OBJECT_DAMPING_TIME_CONSTANT), 0.f, 1.f);
|
|
LLVector3 new_pos = lerp(old_pos, target_pos, lerp_amt);
|
|
dist_squared = dist_vec_squared(new_pos, target_pos);
|
|
|
|
LLQuaternion new_rot = nlerp(lerp_amt, old_rot, target_rot);
|
|
dist_squared += (1.f - dot(new_rot, target_rot)) * 10.f;
|
|
|
|
LLVector3 new_scale = lerp(old_scale, target_scale, lerp_amt);
|
|
scaled = dist_vec_squared(new_scale, target_scale);
|
|
|
|
dist_squared += scaled;
|
|
F32 camdist2 = (mDistanceWRTCamera * mDistanceWRTCamera);
|
|
if ((dist_squared >= MIN_INTERPOLATE_DISTANCE_SQUARED * camdist2) &&
|
|
(dist_squared <= MAX_INTERPOLATE_DISTANCE_SQUARED))
|
|
{
|
|
// interpolate
|
|
target_pos = new_pos;
|
|
target_rot = new_rot;
|
|
target_scale = new_scale;
|
|
|
|
if (scaled >= MIN_INTERPOLATE_DISTANCE_SQUARED)
|
|
{
|
|
//scaling requires an immediate rebuild
|
|
gPipeline.markRebuild(this, LLDrawable::REBUILD_VOLUME, TRUE);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
// snap to final position
|
|
dist_squared = 0.0f;
|
|
}
|
|
}
|
|
|
|
// Update
|
|
mXform.setPosition(target_pos);
|
|
mXform.setRotation(target_rot);
|
|
mXform.setScale(LLVector3(1,1,1)); //no scale in drawable transforms (IT'S A RULE!)
|
|
mXform.updateMatrix();
|
|
|
|
mCurrentScale = target_scale;
|
|
|
|
if (!getVOVolume())
|
|
{
|
|
movePartition();
|
|
}
|
|
else if (mSpatialBridge)
|
|
{
|
|
gPipeline.markMoved(mSpatialBridge, FALSE);
|
|
}
|
|
else
|
|
{
|
|
//a child prim moved and needs its verts regenerated
|
|
gPipeline.markRebuild(this, LLDrawable::REBUILD_VOLUME, TRUE);
|
|
}
|
|
|
|
return dist_squared;
|
|
}
|
|
|
|
void LLDrawable::setRadius(F32 radius)
|
|
{
|
|
if (mRadius != radius)
|
|
{
|
|
mRadius = radius;
|
|
updateBinRadius();
|
|
if (!getVOVolume())
|
|
{
|
|
movePartition();
|
|
}
|
|
}
|
|
}
|
|
|
|
void LLDrawable::moveUpdatePipeline(BOOL moved)
|
|
{
|
|
makeActive();
|
|
|
|
// Update the face centers.
|
|
for (S32 i = 0; i < getNumFaces(); i++)
|
|
{
|
|
getFace(i)->updateCenterAgent();
|
|
}
|
|
|
|
if (moved || !isState(LLDrawable::BUILT)) // moved since last frame
|
|
{
|
|
LLVector3 tmp = mSavePos - mXform.getPositionW();
|
|
F32 dist = tmp.magVecSquared(); // moved since last _update_
|
|
|
|
if (dist > 1.0f || !isState(LLDrawable::BUILT) || isLight())
|
|
{
|
|
mSavePos = mXform.getPositionW();
|
|
gPipeline.markRelight(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
void LLDrawable::movePartition()
|
|
{
|
|
if (getSpatialGroup() || getVOVolume())
|
|
{
|
|
LLSpatialPartition* part = getSpatialPartition();
|
|
if (part)
|
|
{
|
|
part->move(this, getSpatialGroup());
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL LLDrawable::updateMove()
|
|
{
|
|
if (isDead())
|
|
{
|
|
llwarns << "Update move on dead drawable!" << llendl;
|
|
return TRUE;
|
|
}
|
|
|
|
if (mVObjp.isNull())
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
makeActive();
|
|
|
|
BOOL done;
|
|
|
|
if (isState(MOVE_UNDAMPED))
|
|
{
|
|
done = updateMoveUndamped();
|
|
}
|
|
else
|
|
{
|
|
done = updateMoveDamped();
|
|
}
|
|
return done;
|
|
}
|
|
|
|
BOOL LLDrawable::updateMoveUndamped()
|
|
{
|
|
F32 dist_squared = updateXform(TRUE);
|
|
|
|
mGeneration++;
|
|
|
|
if (!isState(LLDrawable::INVISIBLE))
|
|
{
|
|
BOOL moved = (dist_squared > 0.001f && dist_squared < 255.99f);
|
|
moveUpdatePipeline(moved);
|
|
mVObjp->updateText();
|
|
}
|
|
|
|
mVObjp->clearChanged(LLXform::MOVED);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL LLDrawable::updateMoveDamped()
|
|
{
|
|
F32 dist_squared = updateXform(FALSE);
|
|
|
|
mGeneration++;
|
|
|
|
if (!isState(LLDrawable::INVISIBLE))
|
|
{
|
|
BOOL moved = (dist_squared > 0.001f && dist_squared < 128.0f);
|
|
moveUpdatePipeline(moved);
|
|
mVObjp->updateText();
|
|
}
|
|
|
|
BOOL done_moving = (dist_squared == 0.0f) ? TRUE : FALSE;
|
|
|
|
if (done_moving)
|
|
{
|
|
mVObjp->clearChanged(LLXform::MOVED);
|
|
}
|
|
|
|
return done_moving;
|
|
}
|
|
|
|
void LLDrawable::updateDistance(LLCamera& camera)
|
|
{
|
|
if (mVObjp->isHUDAttachment())
|
|
{
|
|
mDistanceWRTCamera = 1.0f;
|
|
if (sCurVisible % 16 == 0)
|
|
{
|
|
mVObjp->updateLOD();
|
|
}
|
|
return;
|
|
}
|
|
|
|
LLVector3 pos(getPositionGroup());
|
|
|
|
pos -= camera.getOrigin();
|
|
mDistanceWRTCamera = pos.magVec();
|
|
|
|
//switch LOD with the spatial group to avoid artifacts
|
|
LLSpatialGroup* sg = getSpatialGroup();
|
|
if (!sg || sg->changeLOD())
|
|
{
|
|
mVObjp->updateLOD();
|
|
}
|
|
}
|
|
|
|
void LLDrawable::updateTexture()
|
|
{
|
|
LLMemType mt(LLMemType::MTYPE_DRAWABLE);
|
|
|
|
if (isDead())
|
|
{
|
|
llwarns << "Dead drawable updating texture!" << llendl;
|
|
return;
|
|
}
|
|
|
|
// *FIX: this updates textures on all faces in this drawable, not
|
|
// just the viewer object we care about
|
|
if (mVObjp->getNumTEs())
|
|
{
|
|
// For each face in this drawable, change the drawpool if necessary.
|
|
for (S32 i = 0; i < getNumFaces(); i++)
|
|
{
|
|
LLFace *facep = mFaces[i];
|
|
U32 pool_type = facep->getPool()->getType();
|
|
|
|
if ((pool_type == LLDrawPool::POOL_SIMPLE) ||
|
|
(pool_type == LLDrawPool::POOL_ALPHA) ||
|
|
(pool_type == LLDrawPool::POOL_HUD) ||
|
|
(pool_type == LLDrawPool::POOL_MEDIA) ||
|
|
(pool_type == LLDrawPool::POOL_BUMP))
|
|
{
|
|
LLViewerObject* objp = facep->getViewerObject();
|
|
S32 te_offset = facep->getTEOffset();
|
|
|
|
if (te_offset >= objp->getNumTEs()) // Shouldn't happen
|
|
{
|
|
llwarns << "TE offsets don't match!" << llendl;
|
|
facep->setTEOffset(-1);
|
|
continue;
|
|
}
|
|
|
|
LLDrawPool* poolp = NULL;
|
|
LLViewerImage* imagep = (te_offset >= 0) ? objp->getTEImage(te_offset) : facep->getTexture();
|
|
if (facep->isState(LLFace::HUD_RENDER))
|
|
{
|
|
poolp = gPipeline.getPool(LLDrawPool::POOL_HUD);
|
|
}
|
|
else if (te_offset >= 0)
|
|
{
|
|
// This face actually uses texture entries...
|
|
const LLTextureEntry* te = facep->getTextureEntry();
|
|
poolp = LLPipeline::getPoolFromTE(te, imagep);
|
|
}
|
|
else
|
|
{
|
|
// No texture entry for this face.
|
|
if (!imagep)
|
|
{
|
|
poolp = gPipeline.getPool(LLDrawPool::POOL_SIMPLE, NULL);
|
|
}
|
|
else if ((imagep->getComponents() == 4) || (imagep->getComponents() == 2))
|
|
{
|
|
poolp = gPipeline.getPool(LLDrawPool::POOL_ALPHA);
|
|
}
|
|
else
|
|
{
|
|
poolp = gPipeline.getPool(LLDrawPool::POOL_SIMPLE, imagep);
|
|
}
|
|
}
|
|
facep->setPool(poolp, imagep);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
BOOL LLDrawable::updateGeometry(BOOL priority)
|
|
{
|
|
llassert(mVObjp.notNull());
|
|
BOOL res = mVObjp->updateGeometry(this);
|
|
if (isState(REBUILD_LIGHTING))
|
|
{
|
|
updateLighting(priority ? FALSE : TRUE); // only do actual lighting for non priority updates
|
|
if (priority)
|
|
{
|
|
gPipeline.markRelight(this); // schedule non priority update
|
|
}
|
|
else
|
|
{
|
|
clearState(REBUILD_LIGHTING);
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
void LLDrawable::shiftPos(const LLVector3 &shift_vector)
|
|
{
|
|
if (isDead())
|
|
{
|
|
llwarns << "Shifting dead drawable" << llendl;
|
|
return;
|
|
}
|
|
|
|
if (mParent)
|
|
{
|
|
mXform.setPosition(mVObjp->getPosition());
|
|
}
|
|
else
|
|
{
|
|
mXform.setPosition(mVObjp->getPositionAgent());
|
|
}
|
|
|
|
mXform.setRotation(mVObjp->getRotation());
|
|
mXform.setScale(1,1,1);
|
|
mXform.updateMatrix();
|
|
|
|
if (isStatic() || // *FIX: don't know why this is happening, but
|
|
// some terrain patches are becoming active
|
|
// (earth quake, maybe?) DP
|
|
getRenderType() == LLPipeline::RENDER_TYPE_TERRAIN)
|
|
{
|
|
LLStrider<LLVector3> verticesp;
|
|
|
|
for (S32 i = 0; i < getNumFaces(); i++)
|
|
{
|
|
LLFace *facep = getFace(i);
|
|
facep->mCenterAgent += shift_vector;
|
|
facep->mExtents[0] += shift_vector;
|
|
facep->mExtents[1] += shift_vector;
|
|
|
|
if (facep->hasGeometry() && !facep->isState(LLFace::SHARED_GEOM))
|
|
{
|
|
S32 index = facep->getVertices(verticesp);
|
|
if (index >= 0)
|
|
{
|
|
S32 vertex_count = facep->getGeomCount();
|
|
for (S32 j = 0; j < vertex_count; j++)
|
|
{
|
|
*verticesp += shift_vector;
|
|
verticesp++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Update the face centers.
|
|
for (S32 i = 0; i < getNumFaces(); i++)
|
|
{
|
|
LLFace *facep = getFace(i);
|
|
facep->mCenterAgent += shift_vector;
|
|
}
|
|
}
|
|
|
|
//update spatial extents
|
|
if (!getVOVolume() || isStatic())
|
|
{
|
|
mExtents[0] += shift_vector;
|
|
mExtents[1] += shift_vector;
|
|
mPositionGroup += LLVector3d(shift_vector);
|
|
}
|
|
else if (mSpatialBridge)
|
|
{
|
|
mSpatialBridge->shiftPos(shift_vector);
|
|
}
|
|
|
|
mSavePos = mXform.getPositionW();
|
|
|
|
mVObjp->onShift(shift_vector);
|
|
}
|
|
|
|
const LLVector3& LLDrawable::getBounds(LLVector3& min, LLVector3& max) const
|
|
{
|
|
mXform.getMinMax(min,max);
|
|
return mXform.getPositionW();
|
|
}
|
|
|
|
const LLVector3* LLDrawable::getSpatialExtents() const
|
|
{
|
|
return mExtents;
|
|
}
|
|
|
|
void LLDrawable::setSpatialExtents(LLVector3 min, LLVector3 max)
|
|
{
|
|
LLVector3 size = max - min;
|
|
mExtents[0] = min;
|
|
mExtents[1] = max;
|
|
}
|
|
|
|
void LLDrawable::setPositionGroup(const LLVector3d& pos)
|
|
{
|
|
mPositionGroup.setVec(pos);
|
|
}
|
|
|
|
void LLDrawable::updateSpatialExtents()
|
|
{
|
|
if (mVObjp)
|
|
{
|
|
mVObjp->updateSpatialExtents(mExtents[0], mExtents[1]);
|
|
}
|
|
|
|
if (mSpatialBridge.notNull())
|
|
{
|
|
mPositionGroup.setVec(0,0,0);
|
|
}
|
|
}
|
|
|
|
|
|
void LLDrawable::updateBinRadius()
|
|
{
|
|
S32 binLOD = mVObjp ? mVObjp->getLOD() : 2;
|
|
static F64 detail_bins[] = { 8, 4, 2, 1 };
|
|
F32 radius = getVOVolume() && isStatic() ?
|
|
(mExtents[1]-mExtents[0]).magVec() : getRadius();
|
|
mBinRadius = detail_bins[binLOD] * llmax((F64) radius, (3-binLOD)*0.25);
|
|
}
|
|
|
|
void LLDrawable::updateLightSet()
|
|
{
|
|
if (isDead())
|
|
{
|
|
llwarns << "Updating light set for dead drawable!" << llendl;
|
|
return;
|
|
}
|
|
|
|
LLVOVolume* light = getVOVolume();
|
|
if (isLight() && light)
|
|
{
|
|
// mLightSet points to lit objects
|
|
for (drawable_set_t::iterator iter = mLightSet.begin(); iter != mLightSet.end(); iter++)
|
|
{
|
|
gPipeline.markRelight(*iter);
|
|
}
|
|
mLightSet.clear();
|
|
gPipeline.mObjectPartition->getObjects(getPositionAgent(), light->getLightRadius(), mLightSet);
|
|
for (drawable_set_t::iterator iter = mLightSet.begin(); iter != mLightSet.end(); iter++)
|
|
{
|
|
gPipeline.markRelight(*iter);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// mLightSet points to nearby lights
|
|
mLightSet.clear();
|
|
gPipeline.mObjectPartition->getLights(getPositionAgent(), getRadius(), mLightSet);
|
|
const U32 max_lights = 16;
|
|
if (mLightSet.size() > max_lights)
|
|
{
|
|
typedef std::set<std::pair<F32,LLPointer<LLDrawable> > > sorted_pair_set_t;
|
|
sorted_pair_set_t sorted_set;
|
|
for (drawable_set_t::iterator iter = mLightSet.begin(); iter != mLightSet.end(); iter++)
|
|
{
|
|
LLDrawable* drawable = *iter;
|
|
LLVector3 dvec = drawable->getPositionAgent() - getPositionAgent();
|
|
F32 dist2 = dvec.magVecSquared();
|
|
sorted_set.insert(std::make_pair(dist2, drawable));
|
|
}
|
|
mLightSet.clear();
|
|
S32 count = 0;
|
|
for (sorted_pair_set_t::iterator iter = sorted_set.begin(); iter != sorted_set.end(); iter++)
|
|
{
|
|
if (++count > 16)
|
|
break;
|
|
mLightSet.insert((*iter).second);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void LLDrawable::updateSpecialHoverCursor(BOOL enabled)
|
|
{
|
|
// TODO: maintain a list of objects that have special
|
|
// hover cursors, then use that list for per-frame
|
|
// hover cursor selection. JC
|
|
}
|
|
|
|
BOOL LLDrawable::updateLighting(BOOL do_lighting)
|
|
{
|
|
if (do_lighting)
|
|
{
|
|
if (gPipeline.getLightingDetail() >= 2 && (getLit() || isLight()))
|
|
{
|
|
LLFastTimer t(LLFastTimer::FTM_UPDATE_LIGHTS);
|
|
updateLightSet();
|
|
do_lighting = isLight() ? FALSE : TRUE;
|
|
}
|
|
else
|
|
{
|
|
do_lighting = FALSE;
|
|
}
|
|
}
|
|
if (gPipeline.getLightingDetail() >= 2)
|
|
{
|
|
LLFastTimer t(LLFastTimer::FTM_GEO_LIGHT);
|
|
if (mVObjp->updateLighting(do_lighting))
|
|
{
|
|
setState(LIGHTING_BUILT);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void LLDrawable::applyLightsAsPoint(LLColor4& result)
|
|
{
|
|
LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
|
|
|
|
LLVector3 point_agent(getPositionAgent());
|
|
LLVector3 normal(-gCamera->getXAxis()); // make point agent face camera
|
|
|
|
F32 sun_int = normal * gPipeline.mSunDir;
|
|
LLColor4 color(gSky.getTotalAmbientColor());
|
|
color += gPipeline.mSunDiffuse * sun_int;
|
|
|
|
for (drawable_set_t::iterator iter = mLightSet.begin();
|
|
iter != mLightSet.end(); ++iter)
|
|
{
|
|
LLDrawable* drawable = *iter;
|
|
LLVOVolume* light = drawable->getVOVolume();
|
|
if (!light)
|
|
{
|
|
continue;
|
|
}
|
|
LLColor4 light_color;
|
|
light->calcLightAtPoint(point_agent, normal, light_color);
|
|
color += light_color;
|
|
}
|
|
|
|
// Clamp the color...
|
|
color.mV[0] = llmax(color.mV[0], 0.f);
|
|
color.mV[1] = llmax(color.mV[1], 0.f);
|
|
color.mV[2] = llmax(color.mV[2], 0.f);
|
|
|
|
F32 max_color = llmax(color.mV[0], color.mV[1], color.mV[2]);
|
|
if (max_color > 1.f)
|
|
{
|
|
color *= 1.f/max_color;
|
|
}
|
|
|
|
result = color;
|
|
}
|
|
|
|
F32 LLDrawable::getVisibilityRadius() const
|
|
{
|
|
if (isDead())
|
|
{
|
|
return 0.f;
|
|
}
|
|
else if (isLight())
|
|
{
|
|
return llmax(getRadius(), getVOVolume()->getLightRadius());
|
|
}
|
|
else
|
|
{
|
|
return getRadius();
|
|
}
|
|
}
|
|
|
|
void LLDrawable::updateUVMinMax()
|
|
{
|
|
}
|
|
|
|
void LLDrawable::setSpatialGroup(LLSpatialGroup *groupp, const S32 offset)
|
|
{
|
|
mSpatialGroupp = groupp;
|
|
|
|
if (mSpatialGroupp)
|
|
{
|
|
mSpatialGroupOffset = offset;
|
|
}
|
|
else
|
|
{
|
|
mSpatialGroupOffset = -1;
|
|
}
|
|
}
|
|
|
|
LLSpatialPartition* LLDrawable::getSpatialPartition()
|
|
{
|
|
LLSpatialPartition* retval = NULL;
|
|
|
|
if (mVObjp->isHUDAttachment())
|
|
{ //HUD attachments don't get space partitioned
|
|
return NULL;
|
|
}
|
|
|
|
if (!mVObjp ||
|
|
!getVOVolume() ||
|
|
isStatic())
|
|
{
|
|
retval = gPipeline.mObjectPartition;
|
|
}
|
|
|
|
//must be an active volume
|
|
if (!retval && isRoot())
|
|
{
|
|
if (!mSpatialBridge)
|
|
{
|
|
setSpatialBridge(new LLSpatialBridge(this));
|
|
}
|
|
return mSpatialBridge->asPartition();
|
|
}
|
|
else if (!retval)
|
|
{
|
|
retval = getParent()->getSpatialPartition();
|
|
}
|
|
|
|
if (retval && mSpatialBridge.notNull())
|
|
{
|
|
mSpatialBridge->markDead();
|
|
setSpatialBridge(NULL);
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
//=======================================
|
|
// Spatial Partition Bridging Drawable
|
|
//=======================================
|
|
|
|
LLSpatialBridge::LLSpatialBridge(LLDrawable* root)
|
|
{
|
|
mDrawable = root;
|
|
root->setSpatialBridge(this);
|
|
|
|
mRenderType = mDrawable->mRenderType; //w00! magic!
|
|
|
|
mOctree->balance();
|
|
|
|
gPipeline.mObjectPartition->put(this);
|
|
}
|
|
|
|
LLSpatialBridge::~LLSpatialBridge()
|
|
{
|
|
if (getSpatialGroup())
|
|
{
|
|
gPipeline.mObjectPartition->remove(this, getSpatialGroup());
|
|
}
|
|
}
|
|
|
|
void LLSpatialBridge::updateSpatialExtents()
|
|
{
|
|
LLSpatialGroup* root = (LLSpatialGroup*) mOctree->getListener(0);
|
|
|
|
if (mOctree->getChildCount() > 0)
|
|
{
|
|
LLFastTimer ftm(LLFastTimer::FTM_CULL_REBOUND);
|
|
root->rebound();
|
|
}
|
|
|
|
LLXformMatrix* mat = mDrawable->getXform();
|
|
|
|
LLVector3 offset = root->mBounds[0];
|
|
LLVector3 size = root->mBounds[1];
|
|
|
|
LLVector3 center = LLVector3(0,0,0) * mat->getWorldMatrix();
|
|
LLQuaternion rotation = LLQuaternion(mat->getWorldMatrix());
|
|
|
|
offset *= rotation;
|
|
center += offset;
|
|
|
|
LLVector3 v[4];
|
|
//get 4 corners of bounding box
|
|
v[0] = (size * rotation);
|
|
v[1] = (LLVector3(-size.mV[0], -size.mV[1], size.mV[2]) * rotation);
|
|
v[2] = (LLVector3(size.mV[0], -size.mV[1], -size.mV[2]) * rotation);
|
|
v[3] = (LLVector3(-size.mV[0], size.mV[1], -size.mV[2]) * rotation);
|
|
|
|
LLVector3& newMin = mExtents[0];
|
|
LLVector3& newMax = mExtents[1];
|
|
|
|
newMin = newMax = center;
|
|
|
|
for (U32 i = 0; i < 4; i++)
|
|
{
|
|
for (U32 j = 0; j < 3; j++)
|
|
{
|
|
F32 delta = fabsf(v[i].mV[j]);
|
|
F32 min = center.mV[j] - delta;
|
|
F32 max = center.mV[j] + delta;
|
|
|
|
if (min < newMin.mV[j])
|
|
{
|
|
newMin.mV[j] = min;
|
|
}
|
|
|
|
if (max > newMax.mV[j])
|
|
{
|
|
newMax.mV[j] = max;
|
|
}
|
|
}
|
|
}
|
|
|
|
mPositionGroup.setVec((newMin + newMax) * 0.5f);
|
|
updateBinRadius();
|
|
}
|
|
|
|
void LLSpatialBridge::updateBinRadius()
|
|
{
|
|
F32 rad = ((mExtents[1]-mExtents[0])*0.5f).magVec();
|
|
mBinRadius = llmax(rad, 2.f);
|
|
mRadius = rad;
|
|
}
|
|
|
|
LLCamera LLSpatialBridge::transformCamera(LLCamera& camera)
|
|
{
|
|
LLCamera ret = camera;
|
|
LLXformMatrix* mat = mDrawable->getXform();
|
|
LLVector3 center = LLVector3(0,0,0) * mat->getWorldMatrix();
|
|
//LLQuaternion rotation = LLQuaternion(mat->getWorldMatrix());
|
|
|
|
//ret.rotate(~mat->getRotation());
|
|
LLVector3 delta = ret.getOrigin() - center;
|
|
delta *= ~mat->getRotation();
|
|
ret.setOrigin(delta);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void LLDrawable::setVisible(LLCamera& camera, std::vector<LLDrawable*>* results, BOOL for_select)
|
|
{
|
|
mVisible = sCurVisible;
|
|
}
|
|
|
|
void LLSpatialBridge::setVisible(LLCamera& camera_in, std::vector<LLDrawable*>* results, BOOL for_select)
|
|
{
|
|
LLVector3 center = (mExtents[0] + mExtents[1]) * 0.5f;
|
|
LLVector3 size = (mExtents[1]-mExtents[0]) * 0.5f;
|
|
|
|
if (camera_in.AABBInFrustum(center, size))
|
|
{
|
|
LLVector3 lookAt = center - camera_in.getOrigin();
|
|
F32 distSqr = lookAt.magVecSquared();
|
|
F32 objRad = size.magVecSquared();
|
|
|
|
if (objRad/distSqr < SG_MIN_DIST_RATIO*4)
|
|
{
|
|
return;
|
|
}
|
|
|
|
LLDrawable::setVisible(camera_in);
|
|
|
|
if (for_select)
|
|
{
|
|
results->push_back(mDrawable);
|
|
for (U32 i = 0; i < mDrawable->getChildCount(); i++)
|
|
{
|
|
results->push_back(mDrawable->getChild(i));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const LLVector3* extents = mDrawable->getSpatialExtents();
|
|
objRad = mDrawable->getRadius();
|
|
objRad *= objRad;
|
|
|
|
if (objRad/distSqr > SG_MIN_DIST_RATIO)
|
|
{
|
|
gPipeline.markNotCulled(mDrawable, camera_in);
|
|
}
|
|
|
|
for (U32 i = 0; i < mDrawable->getChildCount(); i++)
|
|
{
|
|
LLDrawable* child = mDrawable->getChild(i);
|
|
extents = child->getSpatialExtents();
|
|
objRad = child->getRadius();
|
|
objRad *= objRad;
|
|
|
|
if (objRad/distSqr > SG_MIN_DIST_RATIO)
|
|
{
|
|
gPipeline.markNotCulled(mDrawable->getChild(i), camera_in);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void LLSpatialBridge::updateDistance(LLCamera& camera_in)
|
|
{
|
|
LLCamera camera = transformCamera(camera_in);
|
|
|
|
mDrawable->updateDistance(camera);
|
|
|
|
for (U32 i = 0; i < mDrawable->getChildCount(); ++i)
|
|
{
|
|
mDrawable->getChild(i)->updateDistance(camera);
|
|
}
|
|
}
|
|
|
|
void LLSpatialBridge::makeActive()
|
|
{ //it is an error to make a spatial bridge active (it's already active)
|
|
llerrs << "makeActive called on spatial bridge" << llendl;
|
|
}
|
|
|
|
void LLSpatialBridge::makeStatic()
|
|
{
|
|
llerrs << "makeStatic called on spatial bridge" << llendl;
|
|
}
|
|
|
|
void LLSpatialBridge::move(LLDrawable *drawablep, LLSpatialGroup *curp, BOOL immediate)
|
|
{
|
|
LLSpatialPartition::move(drawablep, curp, immediate);
|
|
gPipeline.markMoved(this, FALSE);
|
|
}
|
|
|
|
BOOL LLSpatialBridge::updateMove()
|
|
{
|
|
mOctree->balance();
|
|
gPipeline.mObjectPartition->move(this, getSpatialGroup(), TRUE);
|
|
return TRUE;
|
|
}
|
|
|
|
void LLSpatialBridge::shiftPos(const LLVector3& vec)
|
|
{
|
|
mExtents[0] += vec;
|
|
mExtents[1] += vec;
|
|
mPositionGroup += LLVector3d(vec);
|
|
}
|
|
|
|
void LLSpatialBridge::cleanupReferences()
|
|
{
|
|
LLDrawable::cleanupReferences();
|
|
if (mDrawable)
|
|
{
|
|
mDrawable->setSpatialGroup(NULL, -1);
|
|
for (U32 i = 0; i < mDrawable->getChildCount(); i++)
|
|
{
|
|
LLDrawable* drawable = mDrawable->getChild(i);
|
|
if (drawable && drawable->getVOVolume())
|
|
{
|
|
drawable->setSpatialGroup(NULL, -1);
|
|
}
|
|
}
|
|
|
|
LLDrawable* drawablep = mDrawable;
|
|
mDrawable = NULL;
|
|
drawablep->setSpatialBridge(NULL);
|
|
}
|
|
}
|
|
|
|
const LLVector3 LLDrawable::getPositionAgent() const
|
|
{
|
|
if (getVOVolume())
|
|
{
|
|
if (isActive())
|
|
{
|
|
if (isRoot())
|
|
{
|
|
return LLVector3(0,0,0) * getWorldMatrix();
|
|
}
|
|
else
|
|
{
|
|
return mVObjp->getPosition() * getParent()->getWorldMatrix();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return mVObjp->getPositionAgent();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return getWorldPosition();
|
|
}
|
|
}
|
|
|