diff options
author | James Cook <james@lindenlab.com> | 2007-01-02 08:33:20 +0000 |
---|---|---|
committer | James Cook <james@lindenlab.com> | 2007-01-02 08:33:20 +0000 |
commit | 420b91db29485df39fd6e724e782c449158811cb (patch) | |
tree | b471a94563af914d3ed3edd3e856d21cb1b69945 /indra/newview/llvovolume.cpp |
Print done when done.
Diffstat (limited to 'indra/newview/llvovolume.cpp')
-rw-r--r-- | indra/newview/llvovolume.cpp | 2027 |
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; +} |