summaryrefslogtreecommitdiff
path: root/indra/newview/llvovolume.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/llvovolume.cpp
Print done when done.
Diffstat (limited to 'indra/newview/llvovolume.cpp')
-rw-r--r--indra/newview/llvovolume.cpp2027
1 files changed, 2027 insertions, 0 deletions
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
new file mode 100644
index 0000000000..03074d8a73
--- /dev/null
+++ b/indra/newview/llvovolume.cpp
@@ -0,0 +1,2027 @@
+/**
+ * @file llvovolume.cpp
+ * @brief LLVOVolume class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// A "volume" is a box, cylinder, sphere, or other primitive shape.
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llvovolume.h"
+
+#include "llviewercontrol.h"
+#include "lldir.h"
+#include "llflexibleobject.h"
+#include "llmaterialtable.h"
+#include "llprimitive.h"
+#include "llvolume.h"
+#include "llvolumemgr.h"
+#include "llvolumemessage.h"
+#include "material_codes.h"
+#include "message.h"
+#include "object_flags.h"
+
+#include "llagent.h"
+#include "lldrawable.h"
+#include "lldrawpoolsimple.h"
+#include "lldrawpoolbump.h"
+#include "llface.h"
+
+// TEMP HACK ventrella
+#include "llhudmanager.h"
+#include "llflexibleobject.h"
+#include "llanimalcontrols.h"
+
+#include "llsky.h"
+#include "llviewercamera.h"
+#include "llviewerimagelist.h"
+#include "llviewerregion.h"
+#include "llviewertextureanim.h"
+#include "llworld.h"
+#include "llselectmgr.h"
+#include "pipeline.h"
+
+const S32 MIN_QUIET_FRAMES_COALESCE = 30;
+
+//#define LLDEBUG_DISPLAY_LODS 1
+
+BOOL gAnimateTextures = TRUE;
+
+F32 LLVOVolume::sLODFactor = 1.f;
+F32 LLVOVolume::sLODSlopDistanceFactor = 0.5f; //Changing this to zero, effectively disables the LOD transition slop
+F32 LLVOVolume::sLODComplexityDistanceBias = 0.0f;//Changing this to zero makes all prims LOD equally regardless of complexity
+F32 LLVOVolume::sDistanceFactor = 1.0f;
+S32 LLVOVolume::sNumLODChanges = 0;
+
+LLVOVolume::LLVOVolume(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
+ : LLViewerObject(id, pcode, regionp),
+ mVolumeImpl(NULL)
+{
+ mRelativeXform.identity();
+ mRelativeXformInvTrans.identity();
+
+ mLOD = MIN_LOD;
+ mInited = FALSE;
+ mAllTEsSame = FALSE;
+ mTextureAnimp = NULL;
+ mGlobalVolume = FALSE;
+
+ mTextureAnimp = NULL;
+ mAllTEsSame = FALSE;
+ mVObjRadius = LLVector3(1,1,0.5f).magVec();
+ mNumFaces = 0;
+}
+
+LLVOVolume::~LLVOVolume()
+{
+ delete mTextureAnimp;
+ mTextureAnimp = NULL;
+ delete mVolumeImpl;
+ mVolumeImpl = NULL;
+}
+
+
+// static
+void LLVOVolume::initClass()
+{
+}
+
+
+U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys,
+ void **user_data,
+ U32 block_num, EObjectUpdateType update_type,
+ LLDataPacker *dp)
+{
+ LLColor4U color;
+
+ // Do base class updates...
+ U32 retval = LLViewerObject::processUpdateMessage(mesgsys, user_data, block_num, update_type, dp);
+
+ if (!dp)
+ {
+ if (update_type == OUT_FULL)
+ {
+ ////////////////////////////////
+ //
+ // Unpack texture animation data
+ //
+ //
+
+ if (mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_TextureAnim))
+ {
+ if (!mTextureAnimp)
+ {
+ mTextureAnimp = new LLViewerTextureAnim();
+ }
+ else
+ {
+ if (!(mTextureAnimp->mMode & LLTextureAnim::SMOOTH))
+ {
+ mTextureAnimp->reset();
+ }
+ }
+ mTextureAnimp->unpackTAMessage(mesgsys, block_num);
+ }
+ else
+ {
+ delete mTextureAnimp;
+ mTextureAnimp = NULL;
+ }
+
+ // Unpack volume data
+ LLVolumeParams volume_params;
+ LLVolumeMessage::unpackVolumeParams(&volume_params, mesgsys, _PREHASH_ObjectData, block_num);
+
+ if (setVolume(volume_params, 0))
+ {
+ markForUpdate(TRUE);
+ }
+ }
+
+ // Sigh, this needs to be done AFTER the volume is set as well, otherwise bad stuff happens...
+ ////////////////////////////
+ //
+ // Unpack texture entry data
+ //
+ if (unpackTEMessage(mesgsys, _PREHASH_ObjectData, block_num) & (TEM_CHANGE_TEXTURE|TEM_CHANGE_COLOR))
+ {
+ updateTEData();
+ }
+ }
+ else
+ {
+ // CORY TO DO: Figure out how to get the value here
+ if (update_type != OUT_TERSE_IMPROVED)
+ {
+ LLVolumeParams volume_params;
+ BOOL res = LLVolumeMessage::unpackVolumeParams(&volume_params, *dp);
+ if (!res)
+ {
+ llwarns << "Bogus volume parameters in object " << getID() << llendl;
+ llwarns << getRegion()->getOriginGlobal() << llendl;
+ }
+
+ if (setVolume(volume_params, 0))
+ {
+ markForUpdate(TRUE);
+ }
+ S32 res2 = unpackTEMessage(*dp);
+ if (TEM_INVALID == res2)
+ {
+ // Well, crap, there's something bogus in the data that we're unpacking.
+ dp->dumpBufferToLog();
+ llwarns << "Flushing cache files" << llendl;
+ char mask[LL_MAX_PATH];
+ sprintf(mask, "%s*.slc", gDirUtilp->getDirDelimiter().c_str());
+ gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE,"").c_str(),mask);
+// llerrs << "Bogus TE data in " << getID() << ", crashing!" << llendl;
+ llwarns << "Bogus TE data in " << getID() << llendl;
+ }
+ else if (res2 & (TEM_CHANGE_TEXTURE|TEM_CHANGE_COLOR))
+ {
+ updateTEData();
+ }
+
+ U32 value = dp->getPassFlags();
+
+ if (value & 0x40)
+ {
+ if (!mTextureAnimp)
+ {
+ mTextureAnimp = new LLViewerTextureAnim();
+ }
+ else
+ {
+ if (!(mTextureAnimp->mMode & LLTextureAnim::SMOOTH))
+ {
+ mTextureAnimp->reset();
+ }
+ }
+ mTextureAnimp->unpackTAMessage(*dp);
+ }
+ else
+ {
+ delete mTextureAnimp;
+ mTextureAnimp = NULL;
+ }
+ }
+ else
+ {
+ S32 texture_length = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_TextureEntry);
+ if (texture_length)
+ {
+ U8 tdpbuffer[1024];
+ LLDataPackerBinaryBuffer tdp(tdpbuffer, 1024);
+ mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_TextureEntry, tdpbuffer, 0, block_num);
+ if ( unpackTEMessage(tdp) & (TEM_CHANGE_TEXTURE|TEM_CHANGE_COLOR))
+ {
+ updateTEData();
+ }
+ }
+ }
+ }
+
+ return retval;
+}
+
+
+BOOL LLVOVolume::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
+{
+ LLViewerObject::idleUpdate(agent, world, time);
+
+ ///////////////////////
+ //
+ // Do texture animation stuff
+ //
+
+ if (mTextureAnimp && gAnimateTextures)
+ {
+ F32 off_s, off_t, scale_s, scale_t, rot;
+ S32 result;
+ if ((result = mTextureAnimp->animateTextures(off_s, off_t, scale_s, scale_t, rot)))
+ {
+ U8 has_bump = 0;
+ if (mTextureAnimp->mFace <= -1)
+ {
+ S32 face;
+ for (face = 0; face < getNumTEs(); face++)
+ {
+ if (result & LLViewerTextureAnim::TRANSLATE)
+ {
+ setTEOffset(face, off_s, off_t);
+ }
+ if (result & LLViewerTextureAnim::SCALE)
+ {
+ setTEScale(face, scale_s, scale_t);
+ }
+ if (result & LLViewerTextureAnim::ROTATE)
+ {
+ setTERotation(face, rot);
+ }
+ has_bump |= getTE(face)->getBumpmap();
+ }
+ }
+ else if (mTextureAnimp->mFace < getNumTEs())
+ {
+ if (result & LLViewerTextureAnim::TRANSLATE)
+ {
+ setTEOffset(mTextureAnimp->mFace, off_s, off_t);
+ }
+ if (result & LLViewerTextureAnim::SCALE)
+ {
+ setTEScale(mTextureAnimp->mFace, scale_s, scale_t);
+ }
+ if (result & LLViewerTextureAnim::ROTATE)
+ {
+ setTERotation(mTextureAnimp->mFace, rot);
+ }
+ has_bump |= getTE(mTextureAnimp->mFace)->getBumpmap();
+ }
+// mFaceMappingChanged = TRUE;
+ if (mDrawable->isVisible())
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD, TRUE);
+ }
+ }
+ }
+
+ // Dispatch to implementation
+ if (mVolumeImpl)
+ {
+ mVolumeImpl->doIdleUpdate(agent, world, time);
+ }
+
+ return TRUE;
+}
+
+void LLVOVolume::updateTextures(LLAgent &agent)
+{
+
+}
+
+//static
+F32 LLVOVolume::getTextureVirtualSize(const LLFace* face)
+{
+ //LLVector2 tdim = face->mTexExtents[1] - face->mTexExtents[0];
+ //F32 pixel_area = 1.f/llmin(llmax(tdim.mV[0] * tdim.mV[1], 1.f), 10.f);
+ LLVector3 cross_vec = (face->mExtents[1] - face->mExtents[0]);
+
+
+ LLVector3 lookAt = (face->getPositionAgent()-gCamera->getOrigin());
+ F32 dist = lookAt.normVec();
+
+ F32 face_area;
+
+ if (face->isState(LLFace::GLOBAL))
+ {
+ face_area = cross_vec.mV[0]*cross_vec.mV[1]*fabsf(lookAt.mV[2]) +
+ cross_vec.mV[1]*cross_vec.mV[2]*fabsf(lookAt.mV[0]) +
+ cross_vec.mV[0]*cross_vec.mV[2]*fabsf(lookAt.mV[1]);
+ }
+ else
+ {
+ face_area = cross_vec.mV[0]*cross_vec.mV[1] +
+ cross_vec.mV[1]*cross_vec.mV[2] +
+ cross_vec.mV[0]*cross_vec.mV[2];
+ }
+
+ if (face_area <= 0)
+ {
+ return 0.f;
+ }
+
+ F32 view = llmax(lookAt*gCamera->getAtAxis(), 0.5f);
+ F32 dist_ramp = dist * view/face_area;
+ //ramp down distance for things closer than 16 m * lookAt
+ dist /= dist_ramp;
+ dist *= dist;
+ dist *= dist_ramp;
+
+ F32 dist_ratio = face_area / llmax(dist, 0.1f);
+ F32 pixel_area = dist_ratio*gCamera->getScreenPixelArea();
+
+ return view*pixel_area;
+}
+
+void LLVOVolume::updateTextures(S32 lod)
+{
+ // Update the image levels of all textures...
+ // First we do some quick checks.
+
+ // This doesn't take into account whether the object is in front
+ // or behind...
+
+ if (LLViewerImage::sDontLoadVolumeTextures || mDrawable.isNull() || !mDrawable->isVisible())
+ {
+ return;
+ }
+
+ const S32 num_faces = mDrawable->getNumFaces();
+
+ for (S32 i = 0; i < num_faces; i++)
+ {
+ const LLFace* face = mDrawable->getFace(i);
+ const LLTextureEntry *te = face->getTextureEntry();
+ LLViewerImage *imagep = face->getTexture();
+
+ if (!imagep || !te)
+ {
+ continue;
+ }
+
+ F32 vsize;
+
+ if (isHUDAttachment())
+ {
+ vsize = (F32) (imagep->getWidth(0) * imagep->getHeight(0));
+ imagep->setBoostLevel(LLViewerImage::BOOST_HUD);
+ }
+ else
+ {
+ vsize = getTextureVirtualSize(face);
+ }
+
+ imagep->addTextureStats(vsize);
+
+
+ U8 bump = te->getBumpmap();
+ if( te && bump)
+ {
+ gBumpImageList.addTextureStats( bump, imagep->getID(), vsize, 1, 1);
+ }
+ }
+}
+
+BOOL LLVOVolume::isActive() const
+{
+ return !mStatic || mTextureAnimp || isAttachment() || (mVolumeImpl && mVolumeImpl->isActive());
+}
+
+BOOL LLVOVolume::setMaterial(const U8 material)
+{
+ BOOL res = LLViewerObject::setMaterial(material);
+ if (res)
+ {
+ // for deprecated LL_MCODE_LIGHT
+ if (mDrawable.notNull())
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_LIGHTING, TRUE);
+ }
+ }
+ return res;
+}
+
+void LLVOVolume::setTexture(const S32 face)
+{
+ llassert(face < getNumTEs());
+ LLViewerImage::bindTexture(getTEImage(face));
+}
+
+void LLVOVolume::setScale(const LLVector3 &scale, BOOL damped)
+{
+ if (scale != getScale())
+ {
+ // store local radius
+ LLViewerObject::setScale(scale);
+
+ if (mVolumeImpl)
+ {
+ mVolumeImpl->onSetScale(scale, damped);
+ }
+
+ updateRadius();
+
+ //since drawable transforms do not include scale, changing volume scale
+ //requires an immediate rebuild of volume verts.
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
+ }
+}
+
+LLFace* LLVOVolume::addFace(S32 f)
+{
+ const LLTextureEntry* te = getTE(f);
+ LLViewerImage* imagep = getTEImage(f);
+ LLDrawPool* poolp;
+ if (isHUDAttachment())
+ {
+ poolp = gPipeline.getPool(LLDrawPool::POOL_HUD);
+ }
+ else
+ {
+ poolp = LLPipeline::getPoolFromTE(te, imagep);
+ }
+ return mDrawable->addFace(poolp, imagep);
+}
+
+LLDrawable *LLVOVolume::createDrawable(LLPipeline *pipeline)
+{
+ pipeline->allocDrawable(this);
+ mDrawable->setRenderType(LLPipeline::RENDER_TYPE_VOLUME);
+
+ S32 max_tes_to_set = calcAllTEsSame() ? 1 : getNumTEs();
+ for (S32 i = 0; i < max_tes_to_set; i++)
+ {
+ LLFace* face = addFace(i);
+ // JC - should there be a setViewerObject(this) call here?
+ face->setTEOffset(i);
+ }
+ mNumFaces = max_tes_to_set;
+
+ if (isAttachment())
+ {
+ mDrawable->makeActive();
+ }
+
+ if (getIsLight())
+ {
+ // Add it to the pipeline mLightSet
+ gPipeline.setLight(mDrawable, TRUE);
+ }
+
+ updateRadius();
+ mDrawable->updateDistance(*gCamera);
+
+ return mDrawable;
+}
+
+
+BOOL LLVOVolume::setVolume(const LLVolumeParams &volume_params, const S32 detail, bool unique_volume)
+{
+ // Check if we need to change implementations
+ bool is_flexible = (volume_params.getPathParams().getCurveType() == LL_PCODE_PATH_FLEXIBLE);
+ if (is_flexible)
+ {
+ setParameterEntryInUse(LLNetworkData::PARAMS_FLEXIBLE, TRUE, false);
+ if (!mVolumeImpl)
+ {
+ LLFlexibleObjectData* data = (LLFlexibleObjectData*)getParameterEntry(LLNetworkData::PARAMS_FLEXIBLE);
+ mVolumeImpl = new LLVolumeImplFlexible(this, data);
+ }
+ }
+ else
+ {
+ // Mark the parameter not in use
+ setParameterEntryInUse(LLNetworkData::PARAMS_FLEXIBLE, FALSE, false);
+ if (mVolumeImpl)
+ {
+ delete mVolumeImpl;
+ mVolumeImpl = NULL;
+ if (mDrawable.notNull())
+ {
+ // Undo the damage we did to this matrix
+ mDrawable->updateXform(FALSE);
+ }
+ }
+ }
+ mGlobalVolume = (mVolumeImpl && mVolumeImpl->isVolumeGlobal());
+
+ //MSMSM Recompute LOD here in case the object was just created,
+ // its LOD might be incorrectly set to minumum detail...
+ calcLOD();
+
+ if (LLPrimitive::setVolume(volume_params, mLOD, (mVolumeImpl && mVolumeImpl->isVolumeUnique())))
+ {
+ mFaceMappingChanged = TRUE;
+
+ if (mVolumeImpl)
+ {
+ mVolumeImpl->onSetVolume(volume_params, detail);
+ }
+
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+F32 LLVOVolume::computeLODProfilePathComplexityBias(){
+ //compute a complexity cost from 0 to 1.0 where the 'simplest' prim has a cost of 0.0
+ // and the 'heaviest' prim has a cost of 1.0
+// LLVolume* volume = getVolume();
+ F32 complexity = 0.0f;
+// const LLVolumeParams& params = volume->getParams();
+// U8 type = volume->getPathType();
+// U8 pcode = this->getPCode();
+// U8 proftype = volume->getProfileType();
+
+ //if(params.getHollow()>0.0f){// || (proftype == 1) || (proftype == 0)){
+ //If it is hollow, or a cube/pyramid(subdivided), the complexity is roughly doubled
+ // complexity+=0.5f;
+ //}
+
+ if(this->getVolume()->getProfile().mParams.getCurveType()==LL_PCODE_PROFILE_SQUARE &&
+ this->getVolume()->getPath().mParams.getCurveType()==LL_PCODE_PATH_LINE)
+ {
+ //Object is a cube so bias it heavily since cubes are subdivided alot.
+// this->setDebugText("CUBE");
+ complexity += 1.0f;
+ }
+
+// if(params.getTwist() != params.getTwistBegin()){
+ //if there is twist.. the complexity is bumped
+// complexity+=0.25f;
+// }
+// if(type != LL_PCODE_PATH_LINE)//If the path is not a line it is more complex
+// complexity+=0.2f;
+ return complexity * sLODComplexityDistanceBias;
+}
+
+S32 LLVOVolume::computeLODDetail(F32 distance, F32 radius)
+{
+ S32 cur_detail;
+ // We've got LOD in the profile, and in the twist. Use radius.
+ F32 tan_angle = (LLVOVolume::sLODFactor*radius)/distance;
+ cur_detail = LLVolumeLODGroup::getDetailFromTan(tan_angle);
+ return cur_detail;
+}
+
+BOOL LLVOVolume::calcLOD()
+{
+ if (mDrawable.isNull())
+ {
+ return FALSE;
+ }
+ S32 cur_detail = 0;
+ /*if (isHUDAttachment())
+ {
+ cur_detail = LLVolumeLODGroup::NUM_LODS-1; // max detail
+ }
+ else*/
+ {
+ F32 radius = (mVolumep->mLODScaleBias.scaledVec(getScale())).magVec();
+ F32 distance = mDrawable->mDistanceWRTCamera;
+ distance *= sDistanceFactor;
+
+ F32 rampDist = LLVOVolume::sLODFactor * 2;
+
+ if (distance < rampDist)
+ {
+ // Boost LOD when you're REALLY close
+ distance *= 1.0f/rampDist;
+ distance *= distance;
+ distance *= rampDist;
+ }
+ else
+ {
+ //Now adjust the computed distance by some factor based on the geometric complexity of the primitive
+ distance += computeLODProfilePathComplexityBias();
+ }
+ // Compensate for field of view changing on FOV zoom.
+ distance *= gCamera->getView();
+
+ cur_detail = computeLODDetail(distance, radius);
+
+ //update textures with what the real LOD is
+ updateTextures(cur_detail);
+
+ if(cur_detail != mLOD)
+ {
+ // Here we test whether the LOD is increasing or decreasing to introduce a slop factor
+ if(cur_detail < mLOD)
+ {
+ // Viewer is moving away from the object
+ // so bias our LOD by adding a fixed amount to the distance.
+ // This will reduce the problem of LOD twitching when the
+ // user makes slight movements near the LOD transition threshhold.
+ F32 test_distance = distance - (distance*sLODSlopDistanceFactor/(1.0f+sLODFactor));
+ if(test_distance < 0.0f) test_distance = 0.0f;
+ S32 potential_detail = computeLODDetail( test_distance, radius );
+ if(potential_detail >= mLOD )
+ { //The LOD has truly not changed
+ cur_detail = mLOD;
+ }
+ }
+ }
+ }
+
+ if (cur_detail != mLOD)
+ {
+ mAppAngle = (F32) atan2( mDrawable->getRadius(), mDrawable->mDistanceWRTCamera) * RAD_TO_DEG;
+ mLOD = cur_detail;
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+BOOL LLVOVolume::updateLOD()
+{
+ if (mDrawable.isNull())
+ {
+ return FALSE;
+ }
+
+ BOOL lod_changed = calcLOD();
+
+#if LLDEBUG_DISPLAY_LODS
+ //MS Enable this to display LOD numbers on objects
+ std::ostringstream msg;
+ msg << cur_detail;//((cur_detail<mLOD)?"-":cur_detail==mLOD?"=":"+") << (int)cur_detail << " , " << mDrawable->mDistanceWRTCamera << " , " << ((LLVOVolume::sLODFactor*mVObjRadius)/mDrawable->mDistanceWRTCamera);
+ this->setDebugText(msg.str());
+#endif
+
+ if (lod_changed)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY, FALSE);
+ mLODChanged = TRUE;
+ }
+
+ return lod_changed;
+}
+
+BOOL LLVOVolume::setDrawableParent(LLDrawable* parentp)
+{
+ if (!LLViewerObject::setDrawableParent(parentp))
+ {
+ // no change in drawable parent
+ return FALSE;
+ }
+
+ if (!mDrawable->isRoot())
+ {
+ // parent is dynamic, so I'll need to share its drawable, must rebuild to share drawables
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY, TRUE);
+
+ if (mDrawable->isActive() && !parentp->isActive())
+ {
+ parentp->makeActive();
+ }
+ else if (mDrawable->isStatic() && parentp->isActive())
+ {
+ mDrawable->makeActive();
+ }
+ }
+
+ return TRUE;
+}
+
+void LLVOVolume::updateFaceFlags()
+{
+ for (S32 i = 0; i < getVolume()->getNumFaces(); i++)
+ {
+ LLFace *face = mDrawable->getFace(i + mFaceIndexOffset);
+ BOOL fullbright = getTE(i)->getFullbright();
+ face->clearState(LLFace::FULLBRIGHT | LLFace::HUD_RENDER | LLFace::LIGHT);
+
+ if (fullbright || (mMaterial == LL_MCODE_LIGHT))
+ {
+ face->setState(LLFace::FULLBRIGHT);
+ }
+ if (mDrawable->isLight())
+ {
+ face->setState(LLFace::LIGHT);
+ }
+ if (isHUDAttachment())
+ {
+ face->setState(LLFace::HUD_RENDER);
+ }
+ if (getAllTEsSame())
+ {
+ break; // only 1 face
+ }
+ }
+}
+
+// NOTE: regenFaces() MUST be followed by genTriangles()!
+void LLVOVolume::regenFaces()
+{
+ // remove existing faces
+ // use mDrawable->getVOVolume() in case of shared drawables
+ mDrawable->getVOVolume()->deleteFaces(this);
+ mFaceIndexOffset = mDrawable->getNumFaces();
+ // add new faces
+ mNumFaces = getAllTEsSame() ? 1 : getNumTEs();
+ for (S32 i = 0; i < mNumFaces; i++)
+ {
+ LLFace* facep = addFace(i);
+ facep->setViewerObject(this);
+ facep->setTEOffset(i);
+ }
+ // Need to do this as texture entries may not correspond to faces any more!
+ mDrawable->updateTexture();
+ gPipeline.markMaterialed(mDrawable);
+}
+
+BOOL LLVOVolume::genTriangles(BOOL force_global)
+{
+ BOOL res = TRUE;
+
+ LLVector3 min,max;
+
+ if (getAllTEsSame())
+ {
+ setupSingleFace(mFaceIndexOffset);
+ LLFace *face = mDrawable->getFace(mFaceIndexOffset);
+ S32 num_faces = getVolume()->getNumFaces();
+ res = face->genVolumeTriangles(*getVolume(), 0, num_faces-1,
+ mRelativeXform, mRelativeXformInvTrans,
+ mGlobalVolume | force_global);
+
+ if (mDrawable->isState(LLDrawable::REBUILD_VOLUME))
+ {
+ min = face->mExtents[0];
+ max = face->mExtents[1];
+ }
+ mWereAllTEsSame = TRUE;
+ }
+ else
+ {
+ for (S32 i = 0; i < getVolume()->getNumFaces(); i++)
+ {
+ LLFace *face = mDrawable->getFace(i + mFaceIndexOffset);
+ res &= face->genVolumeTriangles(*getVolume(), i,
+ mRelativeXform, mRelativeXformInvTrans,
+ mGlobalVolume | force_global);
+
+ if (mDrawable->isState(LLDrawable::REBUILD_VOLUME))
+ {
+ if (i == 0)
+ {
+ min = face->mExtents[0];
+ max = face->mExtents[1];
+ }
+ else
+ {
+ for (U32 i = 0; i < 3; i++)
+ {
+ if (face->mExtents[0].mV[i] < min.mV[i])
+ {
+ min.mV[i] = face->mExtents[0].mV[i];
+ }
+ if (face->mExtents[1].mV[i] > max.mV[i])
+ {
+ max.mV[i] = face->mExtents[1].mV[i];
+ }
+ }
+ }
+ }
+ }
+ mWereAllTEsSame = FALSE;
+ }
+
+ if (mDrawable->isState(LLDrawable::REBUILD_VOLUME))
+ {
+ mDrawable->setSpatialExtents(min,max);
+ if (!isVolumeGlobal())
+ {
+ mDrawable->setPositionGroup((min+max)*0.5f);
+ }
+ else
+ {
+ mDrawable->setPositionGroup(getPosition());
+ }
+
+ updateRadius();
+ mDrawable->updateBinRadius();
+ mDrawable->movePartition();
+ }
+
+ return res;
+}
+
+void LLVOVolume::updateRelativeXform(BOOL global_volume)
+{
+ if (mVolumeImpl)
+ {
+ mVolumeImpl->updateRelativeXform(global_volume);
+ return;
+ }
+
+ LLDrawable* drawable = mDrawable;
+
+ if (drawable->isActive())
+ {
+ // setup relative transforms
+ LLQuaternion delta_rot;
+ LLVector3 delta_pos, delta_scale;
+
+ //matrix from local space to parent relative/global space
+ delta_rot = drawable->isSpatialRoot() ? LLQuaternion() : mDrawable->getRotation();
+ delta_pos = drawable->isSpatialRoot() ? LLVector3(0,0,0) : mDrawable->getPosition();
+ delta_scale = mDrawable->getScale();
+
+ // Vertex transform (4x4)
+ LLVector3 x_axis = LLVector3(delta_scale.mV[VX], 0.f, 0.f) * delta_rot;
+ LLVector3 y_axis = LLVector3(0.f, delta_scale.mV[VY], 0.f) * delta_rot;
+ LLVector3 z_axis = LLVector3(0.f, 0.f, delta_scale.mV[VZ]) * delta_rot;
+
+ mRelativeXform.initRows(LLVector4(x_axis, 0.f),
+ LLVector4(y_axis, 0.f),
+ LLVector4(z_axis, 0.f),
+ LLVector4(delta_pos, 1.f));
+
+ x_axis.normVec();
+ y_axis.normVec();
+ z_axis.normVec();
+
+ mRelativeXformInvTrans.setRows(x_axis, y_axis, z_axis);
+ }
+ else
+ {
+ LLVector3 pos = getPosition();
+ LLVector3 scale = getScale();
+ LLQuaternion rot = getRotation();
+
+ if (mParent)
+ {
+ pos *= mParent->getRotation();
+ pos += mParent->getPosition();
+ rot *= mParent->getRotation();
+ }
+
+ LLViewerRegion* region = getRegion();
+ pos += region->getOriginAgent();
+
+ LLVector3 x_axis = LLVector3(scale.mV[VX], 0.f, 0.f) * rot;
+ LLVector3 y_axis = LLVector3(0.f, scale.mV[VY], 0.f) * rot;
+ LLVector3 z_axis = LLVector3(0.f, 0.f, scale.mV[VZ]) * rot;
+
+ mRelativeXform.initRows(LLVector4(x_axis, 0.f),
+ LLVector4(y_axis, 0.f),
+ LLVector4(z_axis, 0.f),
+ LLVector4(pos, 1.f));
+
+ x_axis.normVec();
+ y_axis.normVec();
+ z_axis.normVec();
+
+ mRelativeXformInvTrans.setRows(x_axis, y_axis, z_axis);
+ }
+}
+
+BOOL LLVOVolume::updateGeometry(LLDrawable *drawable)
+{
+ LLFastTimer t(LLFastTimer::FTM_UPDATE_PRIMITIVES);
+
+ if (mVolumeImpl != NULL)
+ {
+ LLFastTimer t(LLFastTimer::FTM_GEN_FLEX);
+ BOOL res = mVolumeImpl->doUpdateGeometry(drawable);
+ updateFaceFlags();
+ if (res)
+ {
+ drawable->clearState(LLDrawable::REBUILD_GEOMETRY);
+ }
+
+ return res;
+ }
+
+ BOOL compiled = FALSE;
+ BOOL change_shared = FALSE;
+
+ updateRelativeXform();
+
+ if (mDrawable.isNull()) // Not sure why this is happening, but it is...
+ {
+ return TRUE; // No update to complete
+ }
+
+ calcAllTEsSame();
+
+ if (mVolumeChanged || mFaceMappingChanged || change_shared)
+ {
+ compiled = TRUE;
+ mInited = TRUE;
+
+ {
+ LLFastTimer ftm(LLFastTimer::FTM_GEN_VOLUME);
+ LLVolumeParams volume_params = getVolume()->getParams();
+ setVolume(volume_params, 0);
+ }
+ drawable->setState(LLDrawable::REBUILD_GEOMETRY);
+ if (mVolumeChanged || change_shared)
+ {
+ drawable->setState(LLDrawable::REBUILD_LIGHTING);
+ }
+
+ {
+ LLFastTimer t(LLFastTimer::FTM_GEN_TRIANGLES);
+ regenFaces();
+ genTriangles(FALSE);
+ }
+ }
+ else if (mLODChanged)
+ {
+ LLPointer<LLVolume> old_volumep, new_volumep;
+ F32 old_lod, new_lod;
+
+ old_volumep = getVolume();
+ old_lod = old_volumep->getDetail();
+
+ {
+ LLFastTimer ftm(LLFastTimer::FTM_GEN_VOLUME);
+ LLVolumeParams volume_params = getVolume()->getParams();
+ setVolume(volume_params, 0);
+ }
+ new_volumep = getVolume();
+ new_lod = new_volumep->getDetail();
+
+ if (new_lod != old_lod)
+ {
+ compiled = TRUE;
+ sNumLODChanges += (getAllTEsSame() ? 1 : getVolume()->getNumFaces());
+
+ drawable->setState(LLDrawable::REBUILD_ALL); // for face->genVolumeTriangles()
+
+ {
+ LLFastTimer t(LLFastTimer::FTM_GEN_TRIANGLES);
+ if (new_volumep->getNumFaces() != old_volumep->getNumFaces())
+ {
+ regenFaces();
+ }
+ genTriangles(FALSE);
+ }
+ }
+ }
+ // it has its own drawable (it's moved) or it has changed UVs or it has changed xforms from global<->local
+ else
+ {
+ compiled = TRUE;
+ // All it did was move or we changed the texture coordinate offset
+ LLFastTimer t(LLFastTimer::FTM_GEN_TRIANGLES);
+ genTriangles(FALSE);
+ }
+
+ // Update face flags
+ updateFaceFlags();
+
+ if(compiled)
+ {
+ LLPipeline::sCompiles++;
+ }
+
+ mVolumeChanged = FALSE;
+ mLODChanged = FALSE;
+ mFaceMappingChanged = FALSE;
+
+ drawable->clearState(LLDrawable::REBUILD_GEOMETRY);
+
+ return TRUE;
+}
+
+BOOL LLVOVolume::isRootEdit() const
+{
+ if (mParent && !((LLViewerObject*)mParent)->isAvatar())
+ {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+void LLVOVolume::setTEImage(const U8 te, LLViewerImage *imagep)
+{
+// llinfos << "SetTEImage:" << llendl;
+ BOOL changed = (mTEImages[te] != imagep);
+ LLViewerObject::setTEImage(te, imagep);
+ if (mDrawable.notNull())
+ {
+ if (changed)
+ {
+ calcAllTEsSame();
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY, TRUE);
+ mFaceMappingChanged = TRUE;
+ }
+ }
+}
+
+S32 LLVOVolume::setTETexture(const U8 te, const LLUUID &uuid)
+{
+ BOOL changed = (uuid != getTE(te)->getID() || (uuid == LLUUID::null));
+
+ S32 res = LLViewerObject::setTETexture(te, uuid);
+ if (mDrawable.notNull())
+ {
+ if (changed)
+ {
+ calcAllTEsSame();
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY, TRUE);
+ mFaceMappingChanged = TRUE;
+ }
+ }
+ return res;
+}
+
+S32 LLVOVolume::setTEColor(const U8 te, const LLColor4 &color)
+{
+ BOOL changed = (color != getTE(te)->getColor());
+ S32 res = LLViewerObject::setTEColor(te, color);
+ if (mDrawable.notNull())
+ {
+ if (changed)
+ {
+ calcAllTEsSame();
+// mFaceMappingChanged = TRUE;
+ }
+ }
+ return res;
+}
+
+S32 LLVOVolume::setTEBumpmap(const U8 te, const U8 bumpmap)
+{
+ BOOL changed = (bumpmap != getTE(te)->getBumpmap());
+ S32 res = LLViewerObject::setTEBumpmap(te, bumpmap);
+ if (mDrawable.notNull())
+ {
+ if (changed)
+ {
+ calcAllTEsSame();
+ mFaceMappingChanged = TRUE;
+ }
+ }
+ return res;
+}
+
+S32 LLVOVolume::setTETexGen(const U8 te, const U8 texgen)
+{
+ BOOL changed = (texgen != getTE(te)->getTexGen());
+ S32 res = LLViewerObject::setTETexGen(te, texgen);
+ if (mDrawable.notNull())
+ {
+ if (changed)
+ {
+ calcAllTEsSame();
+ mFaceMappingChanged = TRUE;
+ }
+ }
+ return res;
+}
+
+S32 LLVOVolume::setTEShiny(const U8 te, const U8 shiny)
+{
+ BOOL changed = (shiny != getTE(te)->getShiny());
+ S32 res = LLViewerObject::setTEShiny(te, shiny);
+ if (mDrawable.notNull())
+ {
+ if (changed)
+ {
+ calcAllTEsSame();
+ mFaceMappingChanged = TRUE;
+ }
+ }
+ return res;
+}
+
+S32 LLVOVolume::setTEFullbright(const U8 te, const U8 fullbright)
+{
+ BOOL changed = (fullbright != getTE(te)->getFullbright());
+ S32 res = LLViewerObject::setTEFullbright(te, fullbright);
+ if (mDrawable.notNull())
+ {
+ if (changed)
+ {
+ calcAllTEsSame();
+ if (!mDrawable->isState(LLDrawable::REBUILD_VOLUME))
+ {
+ updateFaceFlags();
+ }
+ mFaceMappingChanged = TRUE;
+ }
+ }
+ return res;
+}
+
+S32 LLVOVolume::setTEMediaFlags(const U8 te, const U8 media_flags)
+{
+ bool changed = (media_flags != getTE(te)->getMediaFlags());
+ S32 res = LLViewerObject::setTEMediaFlags(te, media_flags);
+ if (mDrawable.notNull())
+ {
+ if (changed)
+ {
+ calcAllTEsSame();
+ mFaceMappingChanged = TRUE;
+ }
+ }
+ return res;
+}
+
+S32 LLVOVolume::setTEScale(const U8 te, const F32 s, const F32 t)
+{
+ F32 olds,oldt;
+ getTE(te)->getScale(&olds, &oldt);
+ bool changed = (s != olds || t != oldt);
+ S32 res = LLViewerObject::setTEScale(te, s, t);
+ if (mDrawable.notNull())
+ {
+ if (changed)
+ {
+ calcAllTEsSame();
+ mFaceMappingChanged = TRUE;
+ }
+ }
+ return res;
+}
+
+S32 LLVOVolume::setTEScaleS(const U8 te, const F32 s)
+{
+ F32 olds,oldt;
+ getTE(te)->getScale(&olds, &oldt);
+ bool changed = (s != olds);
+ S32 res = LLViewerObject::setTEScaleS(te, s);
+ if (mDrawable.notNull())
+ {
+ if (changed)
+ {
+ calcAllTEsSame();
+ mFaceMappingChanged = TRUE;
+ }
+ }
+ return res;
+}
+
+S32 LLVOVolume::setTEScaleT(const U8 te, const F32 t)
+{
+ F32 olds,oldt;
+ getTE(te)->getScale(&olds, &oldt);
+ bool changed = (t != oldt);
+ S32 res = LLViewerObject::setTEScaleT(te, t);
+ if (mDrawable.notNull())
+ {
+ if (changed)
+ {
+ calcAllTEsSame();
+ mFaceMappingChanged = TRUE;
+ }
+ }
+ return res;
+}
+
+void LLVOVolume::updateTEData()
+{
+ if (mDrawable.notNull())
+ {
+ calcAllTEsSame();
+ mFaceMappingChanged = TRUE;
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY, TRUE);
+ }
+}
+
+BOOL LLVOVolume::calcAllTEsSame()
+{
+ BOOL is_alpha = FALSE;
+ BOOL was_same = mAllTEsSame;
+ BOOL all_same = TRUE;
+ S32 num_tes = getNumTEs();
+
+ LLViewerImage *first_texturep = getTEImage(0);
+ if (!first_texturep)
+ {
+ return FALSE;
+ }
+
+ const LLTextureEntry *tep = getTE(0);
+ if (!tep)
+ {
+ llwarns << "Volume with zero textures!" << llendl;
+ return FALSE;
+ }
+
+ if (tep->getColor().mV[3] != 1.f)
+ {
+ is_alpha = TRUE;
+ }
+ const LLColor4 first_color = tep->getColor();
+ const U8 first_bump = tep->getBumpShinyFullbright();
+ const U8 first_media_flags = tep->getMediaTexGen();
+
+ if (first_texturep->getComponents() == 4)
+ {
+ is_alpha = TRUE;
+ }
+
+ F32 s_scale, t_scale;
+ tep->getScale(&s_scale, &t_scale);
+
+ for (S32 f = 1; f < num_tes; f++)
+ {
+ LLViewerImage *texturep = getTEImage(f);
+ if (texturep != first_texturep)
+ {
+ all_same = FALSE;
+ break;
+ }
+
+ tep = getTE(f);
+
+ if( tep->getBumpShinyFullbright() != first_bump )
+ {
+ all_same = FALSE;
+ break;
+ }
+
+ if (first_bump)
+ {
+ F32 cur_s, cur_t;
+ tep->getScale(&cur_s, &cur_t);
+ if ((cur_s != s_scale) || (cur_t != t_scale))
+ {
+ all_same = FALSE;
+ break;
+ }
+ }
+
+ if ((texturep->getComponents() == 4) || (tep->getColor().mV[3] != 1.f))
+ {
+ if (!is_alpha)
+ {
+ all_same = FALSE;
+ break;
+ }
+ }
+ else if (is_alpha)
+ {
+ all_same = FALSE;
+ break;
+ }
+
+ if (tep->getColor() != first_color)
+ {
+ all_same = FALSE;
+ break;
+ }
+
+ if (tep->getMediaTexGen() != first_media_flags)
+ {
+ all_same = FALSE;
+ break;
+ }
+ }
+
+ mAllTEsSame = all_same;
+ if (was_same != all_same)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_ALL, TRUE); // rebuild NOW
+ mFaceMappingChanged = TRUE;
+ }
+ return mAllTEsSame;
+}
+
+void LLVOVolume::setupSingleFace(S32 face_offset)
+{
+ S32 num_indices = 0;
+ S32 num_vertices = 0;
+
+ if (mDrawable.isNull())
+ {
+ llerrs << "setupSingleFace called with NULL mDrawable" << llendl;
+ }
+ if (face_offset >= mDrawable->getNumFaces())
+ {
+ llerrs << "setupSingleFace called with invalid face_offset" << llendl;
+ }
+
+ const S32 num_faces = getVolume()->getNumFaces();
+ for (S32 i = 0; i < num_faces; i++)
+ {
+ const LLVolumeFace &vf = getVolume()->getVolumeFace(i);
+ num_vertices += vf.mVertices.size();
+ num_indices += vf.mIndices.size();
+ }
+ LLFace *facep = mDrawable->getFace(face_offset);
+ facep->setSize(num_vertices, num_indices);
+}
+
+//----------------------------------------------------------------------------
+
+void LLVOVolume::setIsLight(BOOL is_light)
+{
+ if (is_light != getIsLight())
+ {
+ if (is_light)
+ {
+ setParameterEntryInUse(LLNetworkData::PARAMS_LIGHT, TRUE, true);
+ }
+ else
+ {
+ setParameterEntryInUse(LLNetworkData::PARAMS_LIGHT, FALSE, true);
+ }
+
+ if (is_light)
+ {
+ // Add it to the pipeline mLightSet
+ gPipeline.setLight(mDrawable, TRUE);
+ }
+ else
+ {
+ // Not a light. Remove it from the pipeline's light set.
+ gPipeline.setLight(mDrawable, FALSE);
+
+ // Remove this object from any object which has it as a light
+ if (mDrawable)
+ {
+ mDrawable->clearLightSet();
+ }
+ }
+ }
+}
+
+void LLVOVolume::setLightColor(const LLColor3& color)
+{
+ LLLightParams *param_block = (LLLightParams *)getParameterEntry(LLNetworkData::PARAMS_LIGHT);
+ if (param_block)
+ {
+ if (param_block->getColor() != color)
+ {
+ param_block->setColor(LLColor4(color, param_block->getColor().mV[3]));
+ parameterChanged(LLNetworkData::PARAMS_LIGHT, true);
+ }
+ }
+}
+
+void LLVOVolume::setLightIntensity(F32 intensity)
+{
+ LLLightParams *param_block = (LLLightParams *)getParameterEntry(LLNetworkData::PARAMS_LIGHT);
+ if (param_block)
+ {
+ if (param_block->getColor().mV[3] != intensity)
+ {
+ param_block->setColor(LLColor4(LLColor3(param_block->getColor()), intensity));
+ parameterChanged(LLNetworkData::PARAMS_LIGHT, true);
+ }
+ }
+}
+
+void LLVOVolume::setLightRadius(F32 radius)
+{
+ LLLightParams *param_block = (LLLightParams *)getParameterEntry(LLNetworkData::PARAMS_LIGHT);
+ if (param_block)
+ {
+ if (param_block->getRadius() != radius)
+ {
+ param_block->setRadius(radius);
+ parameterChanged(LLNetworkData::PARAMS_LIGHT, true);
+ }
+ }
+}
+
+void LLVOVolume::setLightFalloff(F32 falloff)
+{
+ LLLightParams *param_block = (LLLightParams *)getParameterEntry(LLNetworkData::PARAMS_LIGHT);
+ if (param_block)
+ {
+ if (param_block->getFalloff() != falloff)
+ {
+ param_block->setFalloff(falloff);
+ parameterChanged(LLNetworkData::PARAMS_LIGHT, true);
+ }
+ }
+}
+
+void LLVOVolume::setLightCutoff(F32 cutoff)
+{
+ LLLightParams *param_block = (LLLightParams *)getParameterEntry(LLNetworkData::PARAMS_LIGHT);
+ if (param_block)
+ {
+ if (param_block->getCutoff() != cutoff)
+ {
+ param_block->setCutoff(cutoff);
+ parameterChanged(LLNetworkData::PARAMS_LIGHT, true);
+ }
+ }
+}
+
+//----------------------------------------------------------------------------
+
+BOOL LLVOVolume::getIsLight() const
+{
+ return getParameterEntryInUse(LLNetworkData::PARAMS_LIGHT);
+}
+
+LLColor3 LLVOVolume::getLightBaseColor() const
+{
+ const LLLightParams *param_block = (const LLLightParams *)getParameterEntry(LLNetworkData::PARAMS_LIGHT);
+ if (param_block)
+ {
+ return LLColor3(param_block->getColor());
+ }
+ else
+ {
+ return LLColor3(1,1,1);
+ }
+}
+
+LLColor3 LLVOVolume::getLightColor() const
+{
+ const LLLightParams *param_block = (const LLLightParams *)getParameterEntry(LLNetworkData::PARAMS_LIGHT);
+ if (param_block)
+ {
+ return LLColor3(param_block->getColor()) * param_block->getColor().mV[3];
+ }
+ else
+ {
+ return LLColor3(1,1,1);
+ }
+}
+
+F32 LLVOVolume::getLightIntensity() const
+{
+ const LLLightParams *param_block = (const LLLightParams *)getParameterEntry(LLNetworkData::PARAMS_LIGHT);
+ if (param_block)
+ {
+ return param_block->getColor().mV[3];
+ }
+ else
+ {
+ return 1.f;
+ }
+}
+
+F32 LLVOVolume::getLightRadius() const
+{
+ const LLLightParams *param_block = (const LLLightParams *)getParameterEntry(LLNetworkData::PARAMS_LIGHT);
+ if (param_block)
+ {
+ return param_block->getRadius();
+ }
+ else
+ {
+ return 0.f;
+ }
+}
+
+F32 LLVOVolume::getLightFalloff() const
+{
+ const LLLightParams *param_block = (const LLLightParams *)getParameterEntry(LLNetworkData::PARAMS_LIGHT);
+ if (param_block)
+ {
+ return param_block->getFalloff();
+ }
+ else
+ {
+ return 0.f;
+ }
+}
+
+F32 LLVOVolume::getLightCutoff() const
+{
+ const LLLightParams *param_block = (const LLLightParams *)getParameterEntry(LLNetworkData::PARAMS_LIGHT);
+ if (param_block)
+ {
+ return param_block->getCutoff();
+ }
+ else
+ {
+ return 0.f;
+ }
+}
+
+//----------------------------------------------------------------------------
+
+// returns < 0 if inside radius
+F32 LLVOVolume::getLightDistance(const LLVector3& pos) const
+{
+ LLVector3 dpos = getRenderPosition() - pos;
+ F32 dist = dpos.magVec() - getLightRadius();
+ return dist;
+}
+
+// returns intensity, modifies color in result
+F32 LLVOVolume::calcLightAtPoint(const LLVector3& pos, const LLVector3& norm, LLColor4& result)
+{
+ if (!getIsLight())
+ {
+ return 0.0f;
+ }
+ F32 light_radius = getLightRadius();
+ LLVector3 light_pos = getRenderPosition();
+ LLVector3 light_dir = light_pos - pos;
+ F32 dist = light_dir.normVec();
+ F32 dp = norm * light_dir;
+ if ((gPipeline.getVertexShaderLevel(LLPipeline::SHADER_OBJECT) >= LLDrawPoolSimple::SHADER_LEVEL_LOCAL_LIGHTS))
+ {
+ if (dp <= 0)
+ {
+ result *= 0;
+ return 0;
+ }
+
+ if (dist >= light_radius)
+ {
+ result *= 0;
+ return 0;
+ }
+
+ F32 mag = 1.0f-(dist/light_radius);
+ mag = powf(mag, 0.75f);
+ mag *= dp;
+ result = getLightColor() * mag;
+ return mag;
+ }
+ else
+ {
+ F32 light_radius = getLightRadius();
+ LLVector3 light_pos = getRenderPosition();
+ LLVector3 light_dir = light_pos - pos;
+ F32 dist = light_dir.normVec();
+ F32 dp = norm * light_dir;
+ F32 atten = (1.f/.2f) / (light_radius); // 20% of brightness at radius
+ F32 falloff = 1.f / (dist * atten);
+ F32 mag = falloff * dp;
+ mag = llmax(mag, 0.0f);
+ result = getLightColor() * mag;
+ return mag;
+ }
+}
+
+BOOL LLVOVolume::updateLighting(BOOL do_lighting)
+{
+ LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
+
+ if (mDrawable->isStatic())
+ {
+ do_lighting = FALSE;
+ }
+
+ const LLMatrix4& mat_vert = mDrawable->getWorldMatrix();
+ const LLMatrix3& mat_normal = LLMatrix3(mDrawable->getWorldRotation());
+
+ LLVolume* volume = getVolume();
+ if (getAllTEsSame())
+ {
+ LLFace *face = mDrawable->getFace(mFaceIndexOffset);
+ S32 num_faces = volume->getNumFaces();
+ if (face && face->getGeomCount())
+ {
+ face->genLighting(volume, mDrawable, 0, num_faces-1, mat_vert, mat_normal, do_lighting);
+ }
+ }
+ else
+ {
+ for (S32 i = 0; i < volume->getNumFaces(); i++)
+ {
+ LLFace *face = mDrawable->getFace(i + mFaceIndexOffset);
+ if (face && face->getGeomCount())
+ {
+ face->genLighting(volume, mDrawable, i, i, mat_vert, mat_normal, do_lighting);
+ }
+ }
+ }
+ return TRUE;
+}
+
+//----------------------------------------------------------------------------
+
+BOOL LLVOVolume::isFlexible() const
+{
+ if (getParameterEntryInUse(LLNetworkData::PARAMS_FLEXIBLE))
+ {
+ if (getVolume()->getParams().getPathParams().getCurveType() != LL_PCODE_PATH_FLEXIBLE)
+ {
+ llwarns << "wtf" << llendl;
+ LLVolumeParams volume_params = getVolume()->getParams();
+ U8 profile_and_hole = volume_params.getProfileParams().getCurveType();
+ volume_params.setType(profile_and_hole, LL_PCODE_PATH_FLEXIBLE);
+ }
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+BOOL LLVOVolume::isVolumeGlobal() const
+{
+ if (mVolumeImpl)
+ {
+ return mVolumeImpl->isVolumeGlobal() ? TRUE : FALSE;
+ }
+ return FALSE;
+}
+
+BOOL LLVOVolume::canBeFlexible() const
+{
+ U8 path = getVolume()->getParams().getPathParams().getCurveType();
+ return (path == LL_PCODE_PATH_FLEXIBLE || path == LL_PCODE_PATH_LINE);
+}
+
+BOOL LLVOVolume::setIsFlexible(BOOL is_flexible)
+{
+ BOOL res = FALSE;
+ BOOL was_flexible = isFlexible();
+ LLVolumeParams volume_params;
+ if (is_flexible)
+ {
+ if (!was_flexible)
+ {
+ volume_params = getVolume()->getParams();
+ U8 profile_and_hole = volume_params.getProfileParams().getCurveType();
+ volume_params.setType(profile_and_hole, LL_PCODE_PATH_FLEXIBLE);
+ res = TRUE;
+ setFlags(FLAGS_USE_PHYSICS, FALSE);
+ setFlags(FLAGS_PHANTOM, TRUE);
+ setParameterEntryInUse(LLNetworkData::PARAMS_FLEXIBLE, TRUE, true);
+ if (mDrawable)
+ {
+ mDrawable->makeActive();
+ }
+ }
+ }
+ else
+ {
+ if (was_flexible)
+ {
+ volume_params = getVolume()->getParams();
+ U8 profile_and_hole = volume_params.getProfileParams().getCurveType();
+ volume_params.setType(profile_and_hole, LL_PCODE_PATH_LINE);
+ res = TRUE;
+ setFlags(FLAGS_PHANTOM, FALSE);
+ setParameterEntryInUse(LLNetworkData::PARAMS_FLEXIBLE, FALSE, true);
+ }
+ }
+ if (res)
+ {
+ res = setVolume(volume_params, 1);
+ if (res)
+ {
+ markForUpdate(TRUE);
+ }
+ }
+ return res;
+}
+
+//----------------------------------------------------------------------------
+
+void LLVOVolume::generateSilhouette(LLSelectNode* nodep, const LLVector3& view_point)
+{
+ LLVolume *volume = getVolume();
+
+ if (volume)
+ {
+ LLVector3 view_vector;
+ view_vector = view_point;
+
+ if (!isVolumeGlobal())
+ { //transform view vector into volume space
+ view_vector -= getRenderPosition();
+ LLQuaternion worldRot = getRenderRotation();
+ view_vector = view_vector * ~worldRot;
+ LLVector3 objScale = getScale();
+ LLVector3 invObjScale(1.f / objScale.mV[VX], 1.f / objScale.mV[VY], 1.f / objScale.mV[VZ]);
+ view_vector.scaleVec(invObjScale);
+ }
+
+ updateRelativeXform();
+ volume->generateSilhouetteVertices(nodep->mSilhouetteVertices, nodep->mSilhouetteNormals, nodep->mSilhouetteSegments, view_vector, mRelativeXform, mRelativeXformInvTrans);
+
+ nodep->mSilhouetteExists = TRUE;
+ }
+}
+
+void LLVOVolume::deleteFaces(LLVOVolume* childp)
+{
+ S32 face_count = childp->mNumFaces;
+ S32 start_index = childp->mFaceIndexOffset;
+ if (mDrawable.notNull())
+ {
+ mDrawable->deleteFaces(start_index, face_count);
+ }
+ if (mFaceIndexOffset > start_index)
+ {
+ mFaceIndexOffset -= face_count;
+ }
+
+ for (U32 i = 0; i < mChildList.size(); i++)
+ {
+ LLViewerObject* siblingp = mChildList[i];
+ if (siblingp != childp)
+ {
+ if (siblingp->getPCode() == LL_PCODE_VOLUME &&
+ ((LLVOVolume*)siblingp)->mFaceIndexOffset > start_index)
+ {
+ ((LLVOVolume*)siblingp)->mFaceIndexOffset -= face_count;
+ }
+ }
+ }
+ childp->mFaceIndexOffset = 0;
+ childp->mNumFaces = 0;
+}
+
+void LLVOVolume::updateRadius()
+{
+ if (mDrawable.isNull())
+ {
+ return;
+ }
+
+ mVObjRadius = getScale().magVec();
+ mDrawable->setRadius(mVObjRadius);
+}
+
+
+BOOL LLVOVolume::isAttachment() const
+{
+ if (mState == 0)
+ {
+ return FALSE;
+ }
+ else
+ {
+ return TRUE;
+ }
+}
+
+BOOL LLVOVolume::isHUDAttachment() const
+{
+ //HACK assume hud attachment points are in defined range
+ // since this range is constant for backwards compatibility reasons
+ // this is probably a reasonable assumption to make
+ S32 attachment_id = ATTACHMENT_ID_FROM_STATE(mState);
+ return ( attachment_id >= 31 && attachment_id <= 38 );
+}
+
+
+const LLMatrix4 LLVOVolume::getRenderMatrix() const
+{
+ if (mDrawable->isActive() && !mDrawable->isRoot())
+ {
+ return mDrawable->getParent()->getWorldMatrix();
+ }
+ return mDrawable->getWorldMatrix();
+}
+
+void LLVOVolume::writeCAL3D(apr_file_t* fp, std::string& path, std::string& file_base, S32 joint_num, LLVector3& pos, LLQuaternion& rot, S32& material_index, S32& texture_index, std::multimap<LLUUID, LLMaterialExportInfo*>& material_map)
+{
+ LLPointer<LLImageTGA> tga_image = new LLImageTGA;
+
+ if (mDrawable.isNull())
+ {
+ return;
+ }
+
+ LLVector3 final_pos = getPosition();
+ final_pos *= 100.f;
+
+ final_pos = final_pos * rot;
+ final_pos += pos;
+ LLQuaternion final_rot;
+ final_rot = getRotation() * rot;
+ LLMatrix4 transform;
+ transform.initAll(getScale(), final_rot, final_pos);
+
+ LLMatrix4 int_transpose_transform;
+ int_transpose_transform.initAll(LLVector3(1.f / getScale().mV[VX], 1.f / getScale().mV[VY], 1.f / getScale().mV[VZ]), final_rot, LLVector3::zero);
+
+ for (S32 i = 0; i < mDrawable->getNumFaces(); i++)
+ {
+ S32 vert_num = 0;
+ LLFace* facep = mDrawable->getFace(i);
+ LLDrawPool* poolp = facep->getPool();
+
+ const LLTextureEntry* tep = facep->getTextureEntry();
+ if (!tep)
+ {
+ continue;
+ }
+
+ S32 my_material = -1;
+ S32 my_texture = -1;
+ LLColor4 face_color = tep->getColor();
+
+ typedef std::multimap<LLUUID, LLMaterialExportInfo*>::iterator material_it_t;
+ std::pair<material_it_t, material_it_t> found_range = material_map.equal_range(tep->getID());
+ material_it_t material_it = found_range.first;
+
+ LLMaterialExportInfo* material_info = NULL;
+
+ while(material_it != material_map.end() && material_it != found_range.second)
+ {
+ // we've at least found a matching texture, so reuse it
+ my_texture = material_it->second->mTextureIndex;
+ if (material_it->second->mColor == face_color)
+ {
+ // we've found a matching material
+ material_info = material_it->second;
+ }
+ ++material_it;
+ }
+
+ if (material_info)
+ {
+ // material already exported, just reuse it
+ my_material = material_info->mMaterialIndex;
+ my_texture = material_info->mTextureIndex;
+ }
+ else
+ {
+ // reserve new material number
+ my_material = material_index++;
+
+ // if we didn't already find a matching texture...
+ if (my_texture == -1)
+ {
+ //...use the next available slot...
+ my_texture = texture_index++;
+
+ //...and export texture as image file
+ char filename[MAX_PATH];
+ sprintf(filename, "%s\\%s_material_tex_%d.tga", path.c_str(), file_base.c_str(), my_texture);
+
+ LLViewerImage* imagep = facep->getTexture();
+ if (imagep->getTexName() == 0)
+ {
+ llinfos << "No image data available for " << filename << llendl;
+ continue;
+ }
+ LLPointer<LLImageRaw> raw_image = new LLImageRaw;
+ imagep->readBackRaw(-1, raw_image);
+ BOOL success = tga_image->encode(raw_image);
+ success = tga_image->save(filename);
+ }
+
+ material_info = new LLMaterialExportInfo(my_material, my_texture, face_color);
+ material_map.insert(std::make_pair<LLUUID, LLMaterialExportInfo*>(tep->getID(), material_info));
+ }
+
+ apr_file_printf(fp, "\t<SUBMESH NUMVERTICES=\"%d\" NUMFACES=\"%d\" MATERIAL=\"%d\" NUMLODSTEPS=\"0\" NUMSPRINGS=\"0\" NUMTEXCOORDS=\"1\">\n",
+ facep->getGeomCount(), facep->getIndicesCount() / 3, my_material);
+
+ for (S32 vert_index = 0; vert_index < facep->getGeomCount(); vert_index++)
+ {
+ LLVector3 vert_pos = poolp->getVertex(facep->getGeomStart() + vert_index);
+ vert_pos *= 100.f;
+ vert_pos = vert_pos * transform;
+ LLVector3 vert_norm = poolp->getNormal(facep->getGeomStart() + vert_index);
+ vert_norm = vert_norm * int_transpose_transform;
+ LLVector2 vert_tc = poolp->getTexCoord(facep->getGeomStart() + vert_index, 0);
+ apr_file_printf(fp, " <VERTEX ID=\"%d\" NUMINFLUENCES=\"1\">\n", vert_num++);
+ apr_file_printf(fp, " <POS>%.4f %.4f %.4f</POS>\n", vert_pos.mV[VX], vert_pos.mV[VY], vert_pos.mV[VZ]);
+ apr_file_printf(fp, " <NORM>%.6f %.6f %.6f</NORM>\n", vert_norm.mV[VX], vert_norm.mV[VY], vert_norm.mV[VZ]);
+ apr_file_printf(fp, " <TEXCOORD>%.6f %.6f</TEXCOORD>\n", vert_tc.mV[VX], 1.f - vert_tc.mV[VY]);
+ apr_file_printf(fp, " <INFLUENCE ID=\"%d\">1.0</INFLUENCE>\n", joint_num + 1);
+ apr_file_printf(fp, " </VERTEX>\n");
+ }
+
+ for (U32 index_i = 0; index_i < facep->getIndicesCount(); index_i += 3)
+ {
+ U32 index_a = poolp->getIndex(facep->getIndicesStart() + index_i) - facep->getGeomStart();
+ U32 index_b = poolp->getIndex(facep->getIndicesStart() + index_i + 1) - facep->getGeomStart();
+ U32 index_c = poolp->getIndex(facep->getIndicesStart() + index_i + 2) - facep->getGeomStart();
+ apr_file_printf(fp, " <FACE VERTEXID=\"%d %d %d\" />\n", index_a, index_b, index_c);
+ }
+
+ apr_file_printf(fp, " </SUBMESH>\n");
+ }
+
+ for (U32 i = 0; i < mChildList.size(); i++)
+ {
+ ((LLVOVolume*)(LLViewerObject*)mChildList[i])->writeCAL3D(fp, path, file_base, joint_num, final_pos, final_rot, material_index, texture_index, material_map);
+ }
+}
+
+//static
+void LLVOVolume::preUpdateGeom()
+{
+ sNumLODChanges = 0;
+}
+
+void LLVOVolume::parameterChanged(U16 param_type, bool local_origin)
+{
+ LLViewerObject::parameterChanged(param_type, local_origin);
+}
+
+void LLVOVolume::parameterChanged(U16 param_type, LLNetworkData* data, BOOL in_use, bool local_origin)
+{
+ LLViewerObject::parameterChanged(param_type, data, in_use, local_origin);
+ if (mVolumeImpl)
+ {
+ mVolumeImpl->onParameterChanged(param_type, data, in_use, local_origin);
+ }
+ if (mDrawable.notNull())
+ {
+ BOOL is_light = getIsLight();
+ if (is_light != mDrawable->isState(LLDrawable::LIGHT))
+ {
+ gPipeline.setLight(mDrawable, is_light);
+ }
+ }
+}
+
+void LLVOVolume::updateSpatialExtents(LLVector3& newMin, LLVector3& newMax)
+{
+}
+
+const LLVector3 LLVOVolume::getPivotPositionAgent() const
+{
+ if (mVolumeImpl)
+ {
+ return mVolumeImpl->getPivotPosition();
+ }
+ return LLViewerObject::getPivotPositionAgent();
+}
+
+void LLVOVolume::onShift(const LLVector3 &shift_vector)
+{
+ if (mVolumeImpl)
+ {
+ mVolumeImpl->onShift(shift_vector);
+ }
+}
+
+const LLMatrix4& LLVOVolume::getWorldMatrix(LLXformMatrix* xform) const
+{
+ if (mVolumeImpl)
+ {
+ return mVolumeImpl->getWorldMatrix(xform);
+ }
+ return xform->getWorldMatrix();
+}
+
+LLVector3 LLVOVolume::agentPositionToVolume(const LLVector3& pos) const
+{
+ if (isVolumeGlobal())
+ {
+ return pos;
+ }
+
+ LLVector3 ret = pos - getRenderPosition();
+ ret = ret * ~getRenderRotation();
+ LLVector3 objScale = getScale();
+ LLVector3 invObjScale(1.f / objScale.mV[VX], 1.f / objScale.mV[VY], 1.f / objScale.mV[VZ]);
+ ret.scaleVec(invObjScale);
+
+ return ret;
+}
+
+LLVector3 LLVOVolume::agentDirectionToVolume(const LLVector3& dir) const
+{
+ return isVolumeGlobal() ? dir : (dir * ~getRenderRotation());
+}
+
+LLVector3 LLVOVolume::volumePositionToAgent(const LLVector3& dir) const
+{
+ LLVector3 ret = dir;
+ ret.scaleVec(getScale());
+ ret = ret * getRenderRotation();
+ ret += getRenderPosition();
+
+ return ret;
+}
+
+BOOL LLVOVolume::lineSegmentIntersect(const LLVector3& start, LLVector3& end) const
+{
+ LLVolume* volume = getVolume();
+ BOOL ret = FALSE;
+ if (volume)
+ {
+ LLVector3 v_start, v_end, v_dir;
+
+ v_start = agentPositionToVolume(start);
+ v_end = agentPositionToVolume(end);
+
+ if (LLLineSegmentAABB(v_start, v_end, volume->mBounds[0], volume->mBounds[1]))
+ {
+ if (volume->lineSegmentIntersect(v_start, v_end) >= 0)
+ {
+ end = volumePositionToAgent(v_end);
+ ret = TRUE;
+ }
+ }
+ }
+ return ret;
+}