summaryrefslogtreecommitdiff
path: root/indra/newview/lldrawable.cpp
diff options
context:
space:
mode:
authorJames Cook <james@lindenlab.com>2007-01-02 08:33:20 +0000
committerJames Cook <james@lindenlab.com>2007-01-02 08:33:20 +0000
commit420b91db29485df39fd6e724e782c449158811cb (patch)
treeb471a94563af914d3ed3edd3e856d21cb1b69945 /indra/newview/lldrawable.cpp
Print done when done.
Diffstat (limited to 'indra/newview/lldrawable.cpp')
-rw-r--r--indra/newview/lldrawable.cpp1320
1 files changed, 1320 insertions, 0 deletions
diff --git a/indra/newview/lldrawable.cpp b/indra/newview/lldrawable.cpp
new file mode 100644
index 0000000000..a198886d37
--- /dev/null
+++ b/indra/newview/lldrawable.cpp
@@ -0,0 +1,1320 @@
+/**
+ * @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;
+ }
+
+ //FIXME: 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() || //DP FIXME: don't know why this is happening, but
+ //some terrain patches are becoming active
+ //(earth quake, maybe?)
+ 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();
+ }
+}
+