summaryrefslogtreecommitdiff
path: root/indra
diff options
context:
space:
mode:
Diffstat (limited to 'indra')
-rw-r--r--indra/llmath/llmath.h7
-rw-r--r--indra/llwindow/llwindowmacosx.cpp1
-rw-r--r--indra/llwindow/llwindowsdl.cpp3
-rw-r--r--indra/llwindow/llwindowwin32.cpp1
-rw-r--r--indra/newview/app_settings/settings.xml84
-rw-r--r--indra/newview/llchathistory.cpp2
-rw-r--r--indra/newview/llfloatertools.cpp35
-rw-r--r--indra/newview/llfloatertools.h1
-rwxr-xr-xindra/newview/llmeshrepository.cpp9
-rwxr-xr-xindra/newview/llmeshrepository.h4
-rw-r--r--indra/newview/llselectmgr.cpp66
-rw-r--r--indra/newview/llspatialpartition.cpp116
-rw-r--r--indra/newview/llviewercontrol.cpp8
-rw-r--r--indra/newview/llviewerobjectlist.cpp4
-rw-r--r--indra/newview/llvoavatar.cpp54
-rw-r--r--indra/newview/llvoavatar.h2198
-rwxr-xr-xindra/newview/llvovolume.cpp9947
-rw-r--r--indra/newview/llvovolume.h18
-rw-r--r--indra/newview/pipeline.h1
-rw-r--r--indra/newview/skins/default/xui/en/menu_viewer.xml12
20 files changed, 6498 insertions, 6073 deletions
diff --git a/indra/llmath/llmath.h b/indra/llmath/llmath.h
index eea7c977fb..9297bcbac2 100644
--- a/indra/llmath/llmath.h
+++ b/indra/llmath/llmath.h
@@ -510,6 +510,13 @@ inline void ll_remove_outliers(std::vector<VEC_TYPE>& data, F32 k)
VEC_TYPE Q1 = data[data.size()/4];
VEC_TYPE Q3 = data[data.size()-data.size()/4-1];
+ if ((F32)(Q3-Q1) < 1.f)
+ {
+ // not enough variation to detect outliers
+ return;
+ }
+
+
VEC_TYPE min = (VEC_TYPE) ((F32) Q1-k * (F32) (Q3-Q1));
VEC_TYPE max = (VEC_TYPE) ((F32) Q3+k * (F32) (Q3-Q1));
diff --git a/indra/llwindow/llwindowmacosx.cpp b/indra/llwindow/llwindowmacosx.cpp
index cb2abc5bc0..4dd11541b9 100644
--- a/indra/llwindow/llwindowmacosx.cpp
+++ b/indra/llwindow/llwindowmacosx.cpp
@@ -1266,6 +1266,7 @@ BOOL LLWindowMacOSX::setSize(const LLCoordScreen size)
void LLWindowMacOSX::swapBuffers()
{
+ glFinish();
aglSwapBuffers(mContext);
}
diff --git a/indra/llwindow/llwindowsdl.cpp b/indra/llwindow/llwindowsdl.cpp
index b65287715c..e41aa9820f 100644
--- a/indra/llwindow/llwindowsdl.cpp
+++ b/indra/llwindow/llwindowsdl.cpp
@@ -984,7 +984,10 @@ BOOL LLWindowSDL::setSize(const LLCoordScreen size)
void LLWindowSDL::swapBuffers()
{
if (mWindow)
+ {
+ glFinish();
SDL_GL_SwapBuffers();
+ }
}
U32 LLWindowSDL::getFSAASamples()
diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp
index 551d487cc8..20286b4ca8 100644
--- a/indra/llwindow/llwindowwin32.cpp
+++ b/indra/llwindow/llwindowwin32.cpp
@@ -2865,6 +2865,7 @@ BOOL LLWindowWin32::resetDisplayResolution()
void LLWindowWin32::swapBuffers()
{
+ glFinish();
SwapBuffers(mhDC);
}
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 33541b559b..870ef5f399 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -7317,7 +7317,89 @@
<key>Value</key>
<integer>1</integer>
</map>
- <key>RenderAvatarLODFactor</key>
+ <key>RenderAvatarComplexityLimit</key>
+ <map>
+ <key>Comment</key>
+ <string>Max visual complexity of avatars in a scene</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>S32</string>
+ <key>Value</key>
+ <integer>-1</integer>
+ </map>
+ <key>RenderComplexityColorMin</key>
+ <map>
+ <key>Comment</key>
+ <string>Max visual complexity of avatars in a scene</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Color4</string>
+ <key>Value</key>
+ <array>
+ <real>0.0</real>
+ <real>0.0</real>
+ <real>1.0</real>
+ <real>0.5</real>
+ </array>
+ </map>
+ <key>RenderComplexityColorMid</key>
+ <map>
+ <key>Comment</key>
+ <string>Max visual complexity of avatars in a scene</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Color4</string>
+ <key>Value</key>
+ <array>
+ <real>0.0</real>
+ <real>1.0</real>
+ <real>0.0</real>
+ <real>0.5</real>
+ </array>
+ </map>
+ <key>RenderComplexityColorMax</key>
+ <map>
+ <key>Comment</key>
+ <string>Max visual complexity of avatars in a scene</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Color4</string>
+ <key>Value</key>
+ <array>
+ <real>1.0</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ <real>0.5</real>
+ </array>
+ </map>
+ <key>RenderComplexityThreshold</key>
+ <map>
+ <key>Comment</key>
+ <string>Only color objects higher than render threshold</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>S32</string>
+ <key>Value</key>
+ <integer>-1</integer>
+ </map>
+ <key>RenderComplexityStaticMax</key>
+ <map>
+ <key>Comment</key>
+ <string>Sets a static max value for scaling of RenderComplexity
+ display (-1 for dynamic scaling)</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>S32</string>
+ <key>Value</key>
+ <integer>-1</integer>
+ </map>
+ <key>RenderAvatarLODFactor</key>
<map>
<key>Comment</key>
<string>Controls level of detail of avatars (multiplier for current screen area when calculated level of detail)</string>
diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp
index c0c9ea1451..913d2f34b0 100644
--- a/indra/newview/llchathistory.cpp
+++ b/indra/newview/llchathistory.cpp
@@ -255,7 +255,7 @@ public:
mSourceType = chat.mSourceType;
//*TODO overly defensive thing, source type should be maintained out there
- if((chat.mFromID.isNull() && chat.mFromName.empty()) || chat.mFromName == SYSTEM_FROM && chat.mFromID.isNull())
+ if((chat.mFromID.isNull() && chat.mFromName.empty()) || (chat.mFromName == SYSTEM_FROM && chat.mFromID.isNull()))
{
mSourceType = CHAT_SOURCE_SYSTEM;
}
diff --git a/indra/newview/llfloatertools.cpp b/indra/newview/llfloatertools.cpp
index 00a0da3cde..e748802738 100644
--- a/indra/newview/llfloatertools.cpp
+++ b/indra/newview/llfloatertools.cpp
@@ -435,7 +435,8 @@ void LLFloaterTools::refresh()
if (sShowObjectCost)
{
std::string prim_cost_string;
- LLResMgr::getInstance()->getIntegerString(prim_cost_string, calcRenderCost());
+ S32 cost = LLSelectMgr::getInstance()->getSelection()->getSelectedObjectRenderCost();
+ LLResMgr::getInstance()->getIntegerString(prim_cost_string, cost);
getChild<LLUICtrl>("RenderingCost")->setTextArg("[COUNT]", prim_cost_string);
}
@@ -452,6 +453,7 @@ void LLFloaterTools::refresh()
F32 link_cost = LLSelectMgr::getInstance()->getSelection()->getSelectedLinksetCost();
S32 prim_count = LLSelectMgr::getInstance()->getSelection()->getObjectCount();
S32 link_count = LLSelectMgr::getInstance()->getSelection()->getRootObjectCount();
+ S32 draw_weight = LLSelectMgr::getInstance()->getSelection()->getSelectedObjectRenderCost();
LLStringUtil::format_map_t selection_args;
selection_args["OBJ_COUNT"] = llformat("%.1d", link_count);
@@ -480,7 +482,7 @@ void LLFloaterTools::refresh()
selection_info << ",";
childSetTextArg("selection_weight", "[PHYS_WEIGHT]", llformat("%.1f", link_phys_cost));
- childSetTextArg("selection_weight", "[DISP_WEIGHT]", llformat("%.1d", calcRenderCost()));
+ childSetTextArg("selection_weight", "[DISP_WEIGHT]", llformat("%.1d", draw_weight));
}
else
{
@@ -1014,35 +1016,6 @@ void LLFloaterTools::onClickGridOptions()
//floaterp->addDependentFloater(LLFloaterBuildOptions::getInstance(), FALSE);
}
-S32 LLFloaterTools::calcRenderCost()
-{
- S32 cost = 0;
- std::set<LLUUID> textures;
-
- for (LLObjectSelection::iterator selection_iter = LLSelectMgr::getInstance()->getSelection()->begin();
- selection_iter != LLSelectMgr::getInstance()->getSelection()->end();
- ++selection_iter)
- {
- LLSelectNode *select_node = *selection_iter;
- if (select_node)
- {
- LLViewerObject *vobj = select_node->getObject();
- if (vobj->getVolume())
- {
- LLVOVolume* volume = (LLVOVolume*) vobj;
-
- cost += volume->getRenderCost(textures);
- cost += textures.size() * LLVOVolume::ARC_TEXTURE_COST;
- textures.clear();
- }
- }
- }
-
-
- return cost;
-}
-
-
// static
void LLFloaterTools::setEditTool(void* tool_pointer)
{
diff --git a/indra/newview/llfloatertools.h b/indra/newview/llfloatertools.h
index 69636190fc..8c4cb721d3 100644
--- a/indra/newview/llfloatertools.h
+++ b/indra/newview/llfloatertools.h
@@ -113,7 +113,6 @@ private:
static bool multipleFacesSelectedConfirm(const LLSD& notification, const LLSD& response);
static void setObjectType( LLPCode pcode );
void onClickGridOptions();
- S32 calcRenderCost();
public:
LLButton *mBtnFocus;
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 90a0d5da59..bd18da2251 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -2505,7 +2505,7 @@ S32 LLMeshRepository::getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lo
return mThread->getActualMeshLOD(mesh_params, lod);
}
-const LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id, LLVOVolume* requesting_obj)
+const LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id, const LLVOVolume* requesting_obj)
{
if (mesh_id.notNull())
{
@@ -2761,7 +2761,7 @@ void LLMeshRepository::uploadError(LLSD& args)
}
//static
-F32 LLMeshRepository::getStreamingCost(LLSD& header, F32 radius, S32* bytes, S32* bytes_visible, S32 lod)
+F32 LLMeshRepository::getStreamingCost(LLSD& header, F32 radius, S32* bytes, S32* bytes_visible, S32 lod, F32 *unscaled_value)
{
F32 max_distance = 512.f;
@@ -2850,6 +2850,11 @@ F32 LLMeshRepository::getStreamingCost(LLSD& header, F32 radius, S32* bytes, S32
triangles_low*low_area +
triangles_lowest*lowest_area;
+ if (unscaled_value)
+ {
+ *unscaled_value = weighted_avg;
+ }
+
return weighted_avg/gSavedSettings.getU32("MeshTriangleBudget")*15000.f;
}
diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h
index 35a7314cd5..31b84ea0d9 100755
--- a/indra/newview/llmeshrepository.h
+++ b/indra/newview/llmeshrepository.h
@@ -445,7 +445,7 @@ public:
static U32 sCacheBytesWritten;
static U32 sPeakKbps;
- static F32 getStreamingCost(LLSD& header, F32 radius, S32* bytes = NULL, S32* visible_bytes = NULL, S32 detail = -1);
+ static F32 getStreamingCost(LLSD& header, F32 radius, S32* bytes = NULL, S32* visible_bytes = NULL, S32 detail = -1, F32 *unscaled_value = NULL);
LLMeshRepository();
@@ -464,7 +464,7 @@ public:
S32 getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod);
static S32 getActualMeshLOD(LLSD& header, S32 lod);
- const LLMeshSkinInfo* getSkinInfo(const LLUUID& mesh_id, LLVOVolume* requesting_obj);
+ const LLMeshSkinInfo* getSkinInfo(const LLUUID& mesh_id, const LLVOVolume* requesting_obj);
LLModel::Decomposition* getDecomposition(const LLUUID& mesh_id);
void fetchPhysicsShape(const LLUUID& mesh_id);
bool hasPhysicsShape(const LLUUID& mesh_id);
diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp
index 8fa4065fa6..1880bb87d3 100644
--- a/indra/newview/llselectmgr.cpp
+++ b/indra/newview/llselectmgr.cpp
@@ -6522,31 +6522,75 @@ U32 LLObjectSelection::getSelectedObjectTriangleCount()
return count;
}
-/*S32 LLObjectSelection::getSelectedObjectRenderCost()
+S32 LLObjectSelection::getSelectedObjectRenderCost()
{
S32 cost = 0;
LLVOVolume::texture_cost_t textures;
+ typedef std::set<LLUUID> uuid_list_t;
+ uuid_list_t computed_objects;
+
+ typedef std::list<LLPointer<LLViewerObject> > child_list_t;
+ typedef const child_list_t const_child_list_t;
+
+ // add render cost of complete linksets first, to get accurate texture counts
for (list_t::iterator iter = mList.begin(); iter != mList.end(); ++iter)
{
LLSelectNode* node = *iter;
+
LLVOVolume* object = (LLVOVolume*)node->getObject();
- if (object)
- {
- cost += object->getRenderCost(textures);
- }
-
- for (LLVOVolume::texture_cost_t::iterator iter = textures.begin(); iter != textures.end(); ++iter)
+ if (object && object->isRootEdit())
{
- // add the cost of each individual texture in the linkset
- cost += iter->second;
+ cost += object->getRenderCost(textures);
+ computed_objects.insert(object->getID());
+
+ const_child_list_t children = object->getChildren();
+ for (const_child_list_t::const_iterator child_iter = children.begin();
+ child_iter != children.end();
+ ++child_iter)
+ {
+ LLViewerObject* child_obj = *child_iter;
+ LLVOVolume *child = dynamic_cast<LLVOVolume*>( child_obj );
+ if (child)
+ {
+ cost += child->getRenderCost(textures);
+ computed_objects.insert(child->getID());
+ }
+ }
+
+ for (LLVOVolume::texture_cost_t::iterator iter = textures.begin(); iter != textures.end(); ++iter)
+ {
+ // add the cost of each individual texture in the linkset
+ cost += iter->second;
+ }
+
+ textures.clear();
}
- textures.clear();
}
+
+ // add any partial linkset objects, texture cost may be slightly misleading
+ for (list_t::iterator iter = mList.begin(); iter != mList.end(); ++iter)
+ {
+ LLSelectNode* node = *iter;
+ LLVOVolume* object = (LLVOVolume*)node->getObject();
+ if (object && computed_objects.find(object->getID()) == computed_objects.end() )
+ {
+ cost += object->getRenderCost(textures);
+ computed_objects.insert(object->getID());
+ }
+
+ for (LLVOVolume::texture_cost_t::iterator iter = textures.begin(); iter != textures.end(); ++iter)
+ {
+ // add the cost of each individual texture in the linkset
+ cost += iter->second;
+ }
+
+ textures.clear();
+ }
return cost;
-}*/
+}
//-----------------------------------------------------------------------------
diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp
index e23b431457..8654d6d635 100644
--- a/indra/newview/llspatialpartition.cpp
+++ b/indra/newview/llspatialpartition.cpp
@@ -2758,6 +2758,115 @@ void renderUpdateType(LLDrawable* drawablep)
}
}
+void renderComplexityDisplay(LLDrawable* drawablep)
+{
+ LLViewerObject* vobj = drawablep->getVObj();
+ if (!vobj)
+ {
+ return;
+ }
+
+ LLVOVolume *voVol = dynamic_cast<LLVOVolume*>(vobj);
+
+ if (!voVol)
+ {
+ return;
+ }
+
+ if (!voVol->isRoot())
+ {
+ return;
+ }
+
+ LLVOVolume::texture_cost_t textures;
+ F32 cost = (F32) voVol->getRenderCost(textures);
+
+ // add any child volumes
+ LLViewerObject::const_child_list_t children = voVol->getChildren();
+ for (LLViewerObject::const_child_list_t::const_iterator iter = children.begin(); iter != children.end(); ++iter)
+ {
+ const LLViewerObject *child = *iter;
+ const LLVOVolume *child_volume = dynamic_cast<const LLVOVolume*>(child);
+ if (child_volume)
+ {
+ cost += child_volume->getRenderCost(textures);
+ }
+ }
+
+ // add texture cost
+ for (LLVOVolume::texture_cost_t::iterator iter = textures.begin(); iter != textures.end(); ++iter)
+ {
+ // add the cost of each individual texture in the linkset
+ cost += iter->second;
+ }
+
+ F32 cost_max = (F32) LLVOVolume::getRenderComplexityMax();
+
+
+
+ // allow user to set a static color scale
+ if (gSavedSettings.getS32("RenderComplexityStaticMax") > 0)
+ {
+ cost_max = gSavedSettings.getS32("RenderComplexityStaticMax");
+ }
+
+ F32 cost_ratio = cost / cost_max;
+
+ // cap cost ratio at 1.0f in case cost_max is at a low threshold
+ cost_ratio = cost_ratio > 1.0f ? 1.0f : cost_ratio;
+
+ LLGLEnable blend(GL_BLEND);
+
+ LLColor4 color;
+ const LLColor4 color_min = gSavedSettings.getColor4("RenderComplexityColorMin");
+ const LLColor4 color_mid = gSavedSettings.getColor4("RenderComplexityColorMid");
+ const LLColor4 color_max = gSavedSettings.getColor4("RenderComplexityColorMax");
+
+ if (cost_ratio < 0.5f)
+ {
+ color = color_min * (1 - cost_ratio * 2) + color_mid * (cost_ratio * 2);
+ }
+ else
+ {
+ color = color_mid * (1 - (cost_ratio - 0.5) * 2) + color_max * ((cost_ratio - 0.5) * 2);
+ }
+
+ LLSD color_val = color.getValue();
+
+ // don't highlight objects below the threshold
+ if (cost > gSavedSettings.getS32("RenderComplexityThreshold"))
+ {
+ glColor4f(color[0],color[1],color[2],0.5f);
+
+
+ S32 num_faces = drawablep->getNumFaces();
+ if (num_faces)
+ {
+ for (S32 i = 0; i < num_faces; ++i)
+ {
+ pushVerts(drawablep->getFace(i), LLVertexBuffer::MAP_VERTEX);
+ }
+ }
+ LLViewerObject::const_child_list_t children = voVol->getChildren();
+ for (LLViewerObject::const_child_list_t::const_iterator iter = children.begin(); iter != children.end(); ++iter)
+ {
+ const LLViewerObject *child = *iter;
+ if (child)
+ {
+ num_faces = child->getNumFaces();
+ if (num_faces)
+ {
+ for (S32 i = 0; i < num_faces; ++i)
+ {
+ pushVerts(child->mDrawable->getFace(i), LLVertexBuffer::MAP_VERTEX);
+ }
+ }
+ }
+ }
+ }
+
+ voVol->setDebugText(llformat("%4.0f", cost));
+}
void renderBoundingBox(LLDrawable* drawable, BOOL set_color = TRUE)
{
@@ -3862,6 +3971,10 @@ public:
{
renderUpdateType(drawable);
}
+ if(gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_RENDER_COMPLEXITY))
+ {
+ renderComplexityDisplay(drawable);
+ }
LLVOAvatar* avatar = dynamic_cast<LLVOAvatar*>(drawable->getVObj().get());
@@ -4110,7 +4223,8 @@ void LLSpatialPartition::renderDebug()
LLPipeline::RENDER_DEBUG_AVATAR_VOLUME |
LLPipeline::RENDER_DEBUG_AGENT_TARGET |
//LLPipeline::RENDER_DEBUG_BUILD_QUEUE |
- LLPipeline::RENDER_DEBUG_SHADOW_FRUSTA))
+ LLPipeline::RENDER_DEBUG_SHADOW_FRUSTA |
+ LLPipeline::RENDER_DEBUG_RENDER_COMPLEXITY))
{
return;
}
diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp
index 2bafca70b6..0f0b7d7e78 100644
--- a/indra/newview/llviewercontrol.cpp
+++ b/indra/newview/llviewercontrol.cpp
@@ -142,7 +142,6 @@ static bool handleRenderPerfTestChanged(const LLSD& newvalue)
LLPipeline::RENDER_TYPE_WATER,
LLPipeline::RENDER_TYPE_PASS_GRASS,
LLPipeline::RENDER_TYPE_HUD,
- LLPipeline::RENDER_TYPE_PARTICLES,
LLPipeline::RENDER_TYPE_CLOUDS,
LLPipeline::RENDER_TYPE_HUD_PARTICLES,
LLPipeline::END_RENDER_TYPES);
@@ -158,7 +157,6 @@ static bool handleRenderPerfTestChanged(const LLSD& newvalue)
LLPipeline::RENDER_TYPE_WATER,
LLPipeline::RENDER_TYPE_PASS_GRASS,
LLPipeline::RENDER_TYPE_HUD,
- LLPipeline::RENDER_TYPE_PARTICLES,
LLPipeline::RENDER_TYPE_CLOUDS,
LLPipeline::RENDER_TYPE_HUD_PARTICLES,
LLPipeline::END_RENDER_TYPES);
@@ -168,6 +166,11 @@ static bool handleRenderPerfTestChanged(const LLSD& newvalue)
return true;
}
+bool handleRenderAvatarComplexityLimitChanged(const LLSD& newvalue)
+{
+ return true;
+}
+
bool handleRenderTransparentWaterChanged(const LLSD& newvalue)
{
LLWorld::getInstance()->updateWaterObjects();
@@ -611,6 +614,7 @@ void settings_setup_listeners()
gSavedSettings.getControl("WindLightUseAtmosShaders")->getSignal()->connect(boost::bind(&handleSetShaderChanged, _2));
gSavedSettings.getControl("RenderGammaFull")->getSignal()->connect(boost::bind(&handleSetShaderChanged, _2));
gSavedSettings.getControl("RenderAvatarMaxVisible")->getSignal()->connect(boost::bind(&handleAvatarMaxVisibleChanged, _2));
+ gSavedSettings.getControl("RenderAvatarComplexityLimit")->getSignal()->connect(boost::bind(&handleRenderAvatarComplexityLimitChanged, _2));
gSavedSettings.getControl("RenderVolumeLODFactor")->getSignal()->connect(boost::bind(&handleVolumeLODChanged, _2));
gSavedSettings.getControl("RenderAvatarLODFactor")->getSignal()->connect(boost::bind(&handleAvatarLODChanged, _2));
gSavedSettings.getControl("RenderAvatarPhysicsLODFactor")->getSignal()->connect(boost::bind(&handleAvatarPhysicsLODChanged, _2));
diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp
index f8aaded310..ce88c8759f 100644
--- a/indra/newview/llviewerobjectlist.cpp
+++ b/indra/newview/llviewerobjectlist.cpp
@@ -58,6 +58,7 @@
#include "llviewerregion.h"
#include "llviewerstats.h"
#include "llviewerstatsrecorder.h"
+#include "llvovolume.h"
#include "llvoavatarself.h"
#include "lltoolmgr.h"
#include "lltoolpie.h"
@@ -993,6 +994,9 @@ void LLViewerObjectList::update(LLAgent &agent, LLWorld &world)
mNumSizeCulled = 0;
mNumVisCulled = 0;
+ // update max computed render cost
+ LLVOVolume::updateRenderComplexity();
+
// compute all sorts of time-based stats
// don't factor frames that were paused into the stats
if (! mWasPaused)
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index b5be3cd552..2efc9060dd 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -2803,7 +2803,10 @@ void LLVOAvatar::idleUpdateLoadingEffect()
LLPartData::LL_PART_EMISSIVE_MASK | // LLPartData::LL_PART_FOLLOW_SRC_MASK |
LLPartData::LL_PART_TARGET_POS_MASK );
- setParticleSource(particle_parameters, getID());
+ if (!isTooComplex()) // do not generate particles for overly-complex avatars
+ {
+ setParticleSource(particle_parameters, getID());
+ }
}
}
}
@@ -6383,6 +6386,11 @@ BOOL LLVOAvatar::getIsCloud()
{
return TRUE;
}
+
+ if (isTooComplex())
+ {
+ return TRUE;
+ }
return FALSE;
}
@@ -6477,6 +6485,16 @@ BOOL LLVOAvatar::isFullyLoaded() const
return mFullyLoaded;
}
+bool LLVOAvatar::isTooComplex() const
+{
+ if (gSavedSettings.getS32("RenderAvatarComplexityLimit") > 0 && mVisualComplexity >= gSavedSettings.getS32("RenderAvatarComplexityLimit"))
+ {
+ return true;
+ }
+
+ return false;
+}
+
//-----------------------------------------------------------------------------
// findMotion()
@@ -8310,7 +8328,7 @@ void LLVOAvatar::getImpostorValues(LLVector4a* extents, LLVector3& angle, F32& d
void LLVOAvatar::idleUpdateRenderCost()
{
- static const U32 ARC_BODY_PART_COST = 20;
+ static const U32 ARC_BODY_PART_COST = 200;
static const U32 ARC_LIMIT = 2048;
static std::set<LLUUID> all_textures;
@@ -8321,7 +8339,7 @@ void LLVOAvatar::idleUpdateRenderCost()
}
U32 cost = 0;
- std::set<LLUUID> textures;
+ LLVOVolume::texture_cost_t textures;
for (U8 baked_index = 0; baked_index < BAKED_NUM_INDICES; baked_index++)
{
@@ -8336,6 +8354,7 @@ void LLVOAvatar::idleUpdateRenderCost()
}
}
+
for (attachment_map_t::const_iterator iter = mAttachmentPoints.begin();
iter != mAttachmentPoints.end();
++iter)
@@ -8348,6 +8367,7 @@ void LLVOAvatar::idleUpdateRenderCost()
const LLViewerObject* attached_object = (*attachment_iter);
if (attached_object && !attached_object->isHUDAttachment())
{
+ textures.clear();
const LLDrawable* drawable = attached_object->mDrawable;
if (drawable)
{
@@ -8355,6 +8375,25 @@ void LLVOAvatar::idleUpdateRenderCost()
if (volume)
{
cost += volume->getRenderCost(textures);
+
+ const_child_list_t children = volume->getChildren();
+ for (const_child_list_t::const_iterator child_iter = children.begin();
+ child_iter != children.end();
+ ++child_iter)
+ {
+ LLViewerObject* child_obj = *child_iter;
+ LLVOVolume *child = dynamic_cast<LLVOVolume*>( child_obj );
+ if (child)
+ {
+ cost += child->getRenderCost(textures);
+ }
+ }
+
+ for (LLVOVolume::texture_cost_t::iterator iter = textures.begin(); iter != textures.end(); ++iter)
+ {
+ // add the cost of each individual texture in the linkset
+ cost += iter->second;
+ }
}
}
}
@@ -8362,15 +8401,17 @@ void LLVOAvatar::idleUpdateRenderCost()
}
+
+
// Diagnostic output to identify all avatar-related textures.
// Does not affect rendering cost calculation.
// Could be wrapped in a debug option if output becomes problematic.
if (isSelf())
{
// print any attachment textures we didn't already know about.
- for (std::set<LLUUID>::iterator it = textures.begin(); it != textures.end(); ++it)
+ for (LLVOVolume::texture_cost_t::iterator it = textures.begin(); it != textures.end(); ++it)
{
- LLUUID image_id = *it;
+ LLUUID image_id = it->first;
if( image_id.isNull() || image_id == IMG_DEFAULT || image_id == IMG_DEFAULT_AVATAR)
continue;
if (all_textures.find(image_id) == all_textures.end())
@@ -8402,9 +8443,8 @@ void LLVOAvatar::idleUpdateRenderCost()
}
}
- cost += textures.size() * LLVOVolume::ARC_TEXTURE_COST;
-
setDebugText(llformat("%d", cost));
+ mVisualComplexity = cost;
F32 green = 1.f-llclamp(((F32) cost-(F32)ARC_LIMIT)/(F32)ARC_LIMIT, 0.f, 1.f);
F32 red = llmin((F32) cost/(F32)ARC_LIMIT, 1.f);
mText->setColor(LLColor4(red,green,0,1));
diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h
index 03c0498a2a..6f14b42513 100644
--- a/indra/newview/llvoavatar.h
+++ b/indra/newview/llvoavatar.h
@@ -1,1098 +1,1100 @@
-/**
- * @file llvoavatar.h
- * @brief Declaration of LLVOAvatar class which is a derivation of
- * LLViewerObject
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLVOAVATAR_H
-#define LL_LLVOAVATAR_H
-
-#include <map>
-#include <deque>
-#include <string>
-#include <vector>
-
-#include <boost/signals2.hpp>
-
-#include "imageids.h" // IMG_INVISIBLE
-#include "llchat.h"
-#include "lldrawpoolalpha.h"
-#include "llviewerobject.h"
-#include "llcharacter.h"
-#include "llviewerjointmesh.h"
-#include "llviewerjointattachment.h"
-#include "llrendertarget.h"
-#include "llvoavatardefines.h"
-#include "lltexglobalcolor.h"
-#include "lldriverparam.h"
-#include "material_codes.h" // LL_MCODE_END
-
-extern const LLUUID ANIM_AGENT_BODY_NOISE;
-extern const LLUUID ANIM_AGENT_BREATHE_ROT;
-extern const LLUUID ANIM_AGENT_PHYSICS_MOTION;
-extern const LLUUID ANIM_AGENT_EDITING;
-extern const LLUUID ANIM_AGENT_EYE;
-extern const LLUUID ANIM_AGENT_FLY_ADJUST;
-extern const LLUUID ANIM_AGENT_HAND_MOTION;
-extern const LLUUID ANIM_AGENT_HEAD_ROT;
-extern const LLUUID ANIM_AGENT_PELVIS_FIX;
-extern const LLUUID ANIM_AGENT_TARGET;
-extern const LLUUID ANIM_AGENT_WALK_ADJUST;
-
-class LLTexLayerSet;
-class LLVoiceVisualizer;
-class LLHUDNameTag;
-class LLHUDEffectSpiral;
-class LLTexGlobalColor;
-class LLVOAvatarBoneInfo;
-class LLVOAvatarSkeletonInfo;
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// LLVOAvatar
-//
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-class LLVOAvatar :
- public LLViewerObject,
- public LLCharacter,
- public boost::signals2::trackable
-{
-public:
- friend class LLVOAvatarSelf;
-protected:
- struct LLVOAvatarXmlInfo;
- struct LLMaskedMorph;
-
-/********************************************************************************
- ** **
- ** INITIALIZATION
- **/
-
-public:
- LLVOAvatar(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp);
- virtual void markDead();
- static void initClass(); // Initialize data that's only init'd once per class.
- static void cleanupClass(); // Cleanup data that's only init'd once per class.
- virtual void initInstance(); // Called after construction to initialize the class.
-protected:
- virtual ~LLVOAvatar();
- BOOL loadSkeletonNode();
- BOOL loadMeshNodes();
- virtual BOOL loadLayersets();
-
-/** Initialization
- ** **
- *******************************************************************************/
-
-/********************************************************************************
- ** **
- ** INHERITED
- **/
-
- //--------------------------------------------------------------------
- // LLViewerObject interface and related
- //--------------------------------------------------------------------
-public:
- virtual void updateGL();
- virtual LLVOAvatar* asAvatar();
- virtual U32 processUpdateMessage(LLMessageSystem *mesgsys,
- void **user_data,
- U32 block_num,
- const EObjectUpdateType update_type,
- LLDataPacker *dp);
- virtual BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
- virtual BOOL updateLOD();
- BOOL updateJointLODs();
- void updateLODRiggedAttachments( void );
- virtual BOOL isActive() const; // Whether this object needs to do an idleUpdate.
- virtual void updateTextures();
- virtual S32 setTETexture(const U8 te, const LLUUID& uuid); // If setting a baked texture, need to request it from a non-local sim.
- virtual void onShift(const LLVector4a& shift_vector);
- virtual U32 getPartitionType() const;
- virtual const LLVector3 getRenderPosition() const;
- virtual void updateDrawable(BOOL force_damped);
- virtual LLDrawable* createDrawable(LLPipeline *pipeline);
- virtual BOOL updateGeometry(LLDrawable *drawable);
- virtual void setPixelAreaAndAngle(LLAgent &agent);
- virtual void updateRegion(LLViewerRegion *regionp);
- virtual void updateSpatialExtents(LLVector4a& newMin, LLVector4a &newMax);
- virtual void getSpatialExtents(LLVector4a& newMin, LLVector4a& newMax);
- virtual BOOL lineSegmentIntersect(const LLVector3& start, const LLVector3& end,
- S32 face = -1, // which face to check, -1 = ALL_SIDES
- BOOL pick_transparent = FALSE,
- S32* face_hit = NULL, // which face was hit
- LLVector3* intersection = NULL, // return the intersection point
- LLVector2* tex_coord = NULL, // return the texture coordinates of the intersection point
- LLVector3* normal = NULL, // return the surface normal at the intersection point
- LLVector3* bi_normal = NULL); // return the surface bi-normal at the intersection point
- LLViewerObject* lineSegmentIntersectRiggedAttachments(const LLVector3& start, const LLVector3& end,
- S32 face = -1, // which face to check, -1 = ALL_SIDES
- BOOL pick_transparent = FALSE,
- S32* face_hit = NULL, // which face was hit
- LLVector3* intersection = NULL, // return the intersection point
- LLVector2* tex_coord = NULL, // return the texture coordinates of the intersection point
- LLVector3* normal = NULL, // return the surface normal at the intersection point
- LLVector3* bi_normal = NULL); // return the surface bi-normal at the intersection point
-
- //--------------------------------------------------------------------
- // LLCharacter interface and related
- //--------------------------------------------------------------------
-public:
- virtual LLVector3 getCharacterPosition();
- virtual LLQuaternion getCharacterRotation();
- virtual LLVector3 getCharacterVelocity();
- virtual LLVector3 getCharacterAngularVelocity();
- virtual LLJoint* getCharacterJoint(U32 num);
- virtual BOOL allocateCharacterJoints(U32 num);
-
- virtual LLUUID remapMotionID(const LLUUID& id);
- virtual BOOL startMotion(const LLUUID& id, F32 time_offset = 0.f);
- virtual BOOL stopMotion(const LLUUID& id, BOOL stop_immediate = FALSE);
- virtual void stopMotionFromSource(const LLUUID& source_id);
- virtual void requestStopMotion(LLMotion* motion);
- LLMotion* findMotion(const LLUUID& id) const;
- void startDefaultMotions();
- void dumpAnimationState();
-
- virtual LLJoint* getJoint(const std::string &name);
- virtual LLJoint* getRootJoint() { return &mRoot; }
-
- void resetJointPositions( void );
- void resetJointPositionsToDefault( void );
- void resetSpecificJointPosition( const std::string& name );
-
- virtual const char* getAnimationPrefix() { return "avatar"; }
- virtual const LLUUID& getID();
- virtual LLVector3 getVolumePos(S32 joint_index, LLVector3& volume_offset);
- virtual LLJoint* findCollisionVolume(U32 volume_id);
- virtual S32 getCollisionVolumeID(std::string &name);
- virtual void addDebugText(const std::string& text);
- virtual F32 getTimeDilation();
- virtual void getGround(const LLVector3 &inPos, LLVector3 &outPos, LLVector3 &outNorm);
- virtual F32 getPixelArea() const;
- virtual LLPolyMesh* getHeadMesh();
- virtual LLPolyMesh* getUpperBodyMesh();
- virtual LLVector3d getPosGlobalFromAgent(const LLVector3 &position);
- virtual LLVector3 getPosAgentFromGlobal(const LLVector3d &position);
- virtual void updateVisualParams();
-
-
-/** Inherited
- ** **
- *******************************************************************************/
-
-/********************************************************************************
- ** **
- ** STATE
- **/
-
-public:
- virtual bool isSelf() const { return false; } // True if this avatar is for this viewer's agent
- bool isBuilt() const { return mIsBuilt; }
-
-private: //aligned members
- LLVector4a mImpostorExtents[2];
-
-private:
- BOOL mSupportsAlphaLayers; // For backwards compatibility, TRUE for 1.23+ clients
-
- //--------------------------------------------------------------------
- // Updates
- //--------------------------------------------------------------------
-public:
- virtual BOOL updateCharacter(LLAgent &agent);
- void idleUpdateVoiceVisualizer(bool voice_enabled);
- void idleUpdateMisc(bool detailed_update);
- virtual void idleUpdateAppearanceAnimation();
- void idleUpdateLipSync(bool voice_enabled);
- void idleUpdateLoadingEffect();
- void idleUpdateWindEffect();
- void idleUpdateNameTag(const LLVector3& root_pos_last);
- void idleUpdateNameTagText(BOOL new_name);
- LLVector3 idleUpdateNameTagPosition(const LLVector3& root_pos_last);
- void idleUpdateNameTagAlpha(BOOL new_name, F32 alpha);
- LLColor4 getNameTagColor(bool is_friend);
- void clearNameTag();
- static void invalidateNameTag(const LLUUID& agent_id);
- // force all name tags to rebuild, useful when display names turned on/off
- static void invalidateNameTags();
- void addNameTagLine(const std::string& line, const LLColor4& color, S32 style, const LLFontGL* font);
- void idleUpdateRenderCost();
- void idleUpdateBelowWater();
-
- //--------------------------------------------------------------------
- // Static preferences (controlled by user settings/menus)
- //--------------------------------------------------------------------
-public:
- static S32 sRenderName;
- static BOOL sRenderGroupTitles;
- static U32 sMaxVisible; //(affected by control "RenderAvatarMaxVisible")
- static F32 sRenderDistance; //distance at which avatars will render.
- static BOOL sShowAnimationDebug; // show animation debug info
- static BOOL sUseImpostors; //use impostors for far away avatars
- static BOOL sShowFootPlane; // show foot collision plane reported by server
- static BOOL sShowCollisionVolumes; // show skeletal collision volumes
- static BOOL sVisibleInFirstPerson;
- static S32 sNumLODChangesThisFrame;
- static S32 sNumVisibleChatBubbles;
- static BOOL sDebugInvisible;
- static BOOL sShowAttachmentPoints;
- static F32 sLODFactor; // user-settable LOD factor
- static F32 sPhysicsLODFactor; // user-settable physics LOD factor
- static BOOL sJointDebug; // output total number of joints being touched for each avatar
- static BOOL sDebugAvatarRotation;
-
- //--------------------------------------------------------------------
- // Region state
- //--------------------------------------------------------------------
-public:
- LLHost getObjectHost() const;
-
- //--------------------------------------------------------------------
- // Loading state
- //--------------------------------------------------------------------
-public:
- BOOL isFullyLoaded() const;
- bool visualParamWeightsAreDefault();
-protected:
- virtual BOOL getIsCloud();
- BOOL updateIsFullyLoaded();
- BOOL processFullyLoadedChange(bool loading);
- void updateRuthTimer(bool loading);
- F32 calcMorphAmount();
-private:
- BOOL mFullyLoaded;
- BOOL mPreviousFullyLoaded;
- BOOL mFullyLoadedInitialized;
- S32 mFullyLoadedFrameCounter;
- LLFrameTimer mFullyLoadedTimer;
- LLFrameTimer mRuthTimer;
-protected:
- LLFrameTimer mInvisibleTimer;
-
-/** State
- ** **
- *******************************************************************************/
-
-/********************************************************************************
- ** **
- ** SKELETON
- **/
-
-public:
- void updateHeadOffset();
- F32 getPelvisToFoot() const { return mPelvisToFoot; }
- void setPelvisOffset( bool hasOffset, const LLVector3& translation, F32 offset ) ;
- bool hasPelvisOffset( void ) { return mHasPelvisOffset; }
- void postPelvisSetRecalc( void );
- void setPelvisOffset( F32 pelvixFixupAmount );
-
- bool mHasPelvisOffset;
- LLVector3 mPelvisOffset;
- F32 mLastPelvisToFoot;
- F32 mPelvisFixup;
- F32 mLastPelvisFixup;
-
- LLVector3 mHeadOffset; // current head position
- LLViewerJoint mRoot;
-protected:
- static BOOL parseSkeletonFile(const std::string& filename);
- void buildCharacter();
- virtual BOOL loadAvatar();
-
- BOOL setupBone(const LLVOAvatarBoneInfo* info, LLViewerJoint* parent, S32 &current_volume_num, S32 &current_joint_num);
- BOOL buildSkeleton(const LLVOAvatarSkeletonInfo *info);
-private:
- BOOL mIsBuilt; // state of deferred character building
- S32 mNumJoints;
- LLViewerJoint* mSkeleton;
-
- //--------------------------------------------------------------------
- // Pelvis height adjustment members.
- //--------------------------------------------------------------------
-public:
- LLVector3 mBodySize;
- S32 mLastSkeletonSerialNum;
-private:
- F32 mPelvisToFoot;
-
- //--------------------------------------------------------------------
- // Cached pointers to well known joints
- //--------------------------------------------------------------------
-public:
- LLViewerJoint* mPelvisp;
- LLViewerJoint* mTorsop;
- LLViewerJoint* mChestp;
- LLViewerJoint* mNeckp;
- LLViewerJoint* mHeadp;
- LLViewerJoint* mSkullp;
- LLViewerJoint* mEyeLeftp;
- LLViewerJoint* mEyeRightp;
- LLViewerJoint* mHipLeftp;
- LLViewerJoint* mHipRightp;
- LLViewerJoint* mKneeLeftp;
- LLViewerJoint* mKneeRightp;
- LLViewerJoint* mAnkleLeftp;
- LLViewerJoint* mAnkleRightp;
- LLViewerJoint* mFootLeftp;
- LLViewerJoint* mFootRightp;
- LLViewerJoint* mWristLeftp;
- LLViewerJoint* mWristRightp;
-
- //--------------------------------------------------------------------
- // XML parse tree
- //--------------------------------------------------------------------
-private:
- static LLXmlTree sXMLTree; // avatar config file
- static LLXmlTree sSkeletonXMLTree; // avatar skeleton file
-
-/** Skeleton
- ** **
- *******************************************************************************/
-
-/********************************************************************************
- ** **
- ** RENDERING
- **/
-
-public:
- U32 renderImpostor(LLColor4U color = LLColor4U(255,255,255,255), S32 diffuse_channel = 0);
- U32 renderRigid();
- U32 renderSkinned(EAvatarRenderPass pass);
- F32 getLastSkinTime() { return mLastSkinTime; }
- U32 renderSkinnedAttachments();
- U32 renderTransparent(BOOL first_pass);
- void renderCollisionVolumes();
- static void deleteCachedImages(bool clearAll=true);
- static void destroyGL();
- static void restoreGL();
- BOOL mIsDummy; // for special views
- S32 mSpecialRenderMode; // special lighting
-private:
- bool shouldAlphaMask();
-
- BOOL mNeedsSkin; // avatar has been animated and verts have not been updated
- F32 mLastSkinTime; //value of gFrameTimeSeconds at last skin update
-
- S32 mUpdatePeriod;
- S32 mNumInitFaces; //number of faces generated when creating the avatar drawable, does not inculde splitted faces due to long vertex buffer.
-
- //--------------------------------------------------------------------
- // Morph masks
- //--------------------------------------------------------------------
-public:
- BOOL morphMaskNeedsUpdate(LLVOAvatarDefines::EBakedTextureIndex index = LLVOAvatarDefines::BAKED_NUM_INDICES);
- void addMaskedMorph(LLVOAvatarDefines::EBakedTextureIndex index, LLPolyMorphTarget* morph_target, BOOL invert, std::string layer);
- void applyMorphMask(U8* tex_data, S32 width, S32 height, S32 num_components, LLVOAvatarDefines::EBakedTextureIndex index = LLVOAvatarDefines::BAKED_NUM_INDICES);
-
- //--------------------------------------------------------------------
- // Visibility
- //--------------------------------------------------------------------
-protected:
- void updateVisibility();
-private:
- U32 mVisibilityRank;
- BOOL mVisible;
-
- //--------------------------------------------------------------------
- // Shadowing
- //--------------------------------------------------------------------
-public:
- void updateShadowFaces();
- LLDrawable* mShadow;
-private:
- LLFace* mShadow0Facep;
- LLFace* mShadow1Facep;
- LLPointer<LLViewerTexture> mShadowImagep;
-
- //--------------------------------------------------------------------
- // Impostors
- //--------------------------------------------------------------------
-public:
- BOOL isImpostor() const;
- BOOL needsImpostorUpdate() const;
- const LLVector3& getImpostorOffset() const;
- const LLVector2& getImpostorDim() const;
- void getImpostorValues(LLVector4a* extents, LLVector3& angle, F32& distance) const;
- void cacheImpostorValues();
- void setImpostorDim(const LLVector2& dim);
- static void resetImpostors();
- static void updateImpostors();
- LLRenderTarget mImpostor;
- BOOL mNeedsImpostorUpdate;
-private:
- LLVector3 mImpostorOffset;
- LLVector2 mImpostorDim;
- BOOL mNeedsAnimUpdate;
- LLVector3 mImpostorAngle;
- F32 mImpostorDistance;
- F32 mImpostorPixelArea;
- LLVector3 mLastAnimExtents[2];
-
- //--------------------------------------------------------------------
- // Wind rippling in clothes
- //--------------------------------------------------------------------
-public:
- LLVector4 mWindVec;
- F32 mRipplePhase;
- BOOL mBelowWater;
-private:
- F32 mWindFreq;
- LLFrameTimer mRippleTimer;
- F32 mRippleTimeLast;
- LLVector3 mRippleAccel;
- LLVector3 mLastVel;
-
- //--------------------------------------------------------------------
- // Culling
- //--------------------------------------------------------------------
-public:
- static void cullAvatarsByPixelArea();
- BOOL isCulled() const { return mCulled; }
-private:
- BOOL mCulled;
-
- //--------------------------------------------------------------------
- // Freeze counter
- //--------------------------------------------------------------------
-public:
- static void updateFreezeCounter(S32 counter = 0);
-private:
- static S32 sFreezeCounter;
-
- //--------------------------------------------------------------------
- // Constants
- //--------------------------------------------------------------------
-public:
- virtual LLViewerTexture::EBoostLevel getAvatarBoostLevel() const { return LLViewerTexture::BOOST_AVATAR; }
- virtual LLViewerTexture::EBoostLevel getAvatarBakedBoostLevel() const { return LLViewerTexture::BOOST_AVATAR_BAKED; }
- virtual S32 getTexImageSize() const;
- virtual S32 getTexImageArea() const { return getTexImageSize()*getTexImageSize(); }
-
-/** Rendering
- ** **
- *******************************************************************************/
-
-/********************************************************************************
- ** **
- ** TEXTURES
- **/
-
- //--------------------------------------------------------------------
- // Loading status
- //--------------------------------------------------------------------
-public:
- virtual BOOL isTextureDefined(LLVOAvatarDefines::ETextureIndex type, U32 index = 0) const;
- virtual BOOL isTextureVisible(LLVOAvatarDefines::ETextureIndex type, U32 index = 0) const;
- virtual BOOL isTextureVisible(LLVOAvatarDefines::ETextureIndex type, LLWearable *wearable) const;
-
-protected:
- BOOL isFullyBaked();
- static BOOL areAllNearbyInstancesBaked(S32& grey_avatars);
-
- //--------------------------------------------------------------------
- // Baked textures
- //--------------------------------------------------------------------
-public:
- void releaseComponentTextures(); // ! BACKWARDS COMPATIBILITY !
-protected:
- static void onBakedTextureMasksLoaded(BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata);
- static void onInitialBakedTextureLoaded(BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata);
- static void onBakedTextureLoaded(BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata);
- virtual void removeMissingBakedTextures();
- void useBakedTexture(const LLUUID& id);
-
- typedef std::deque<LLMaskedMorph *> morph_list_t;
- struct BakedTextureData
- {
- LLUUID mLastTextureIndex;
- LLTexLayerSet* mTexLayerSet; // Only exists for self
- bool mIsLoaded;
- bool mIsUsed;
- LLVOAvatarDefines::ETextureIndex mTextureIndex;
- U32 mMaskTexName;
- // Stores pointers to the joint meshes that this baked texture deals with
- std::vector< LLViewerJointMesh * > mMeshes; // std::vector<LLViewerJointMesh> mJoints[i]->mMeshParts
- morph_list_t mMaskedMorphs;
- };
- typedef std::vector<BakedTextureData> bakedtexturedata_vec_t;
- bakedtexturedata_vec_t mBakedTextureDatas;
- LLLoadedCallbackEntry::source_callback_list_t mCallbackTextureList ;
- BOOL mLoadedCallbacksPaused;
- //--------------------------------------------------------------------
- // Local Textures
- //--------------------------------------------------------------------
-protected:
- virtual void setLocalTexture(LLVOAvatarDefines::ETextureIndex type, LLViewerTexture* tex, BOOL baked_version_exits, U32 index = 0);
- virtual void addLocalTextureStats(LLVOAvatarDefines::ETextureIndex type, LLViewerFetchedTexture* imagep, F32 texel_area_ratio, BOOL rendered, BOOL covered_by_baked, U32 index = 0);
- // MULTI-WEARABLE: make self-only?
- virtual void setBakedReady(LLVOAvatarDefines::ETextureIndex type, BOOL baked_version_exists, U32 index = 0);
-
- //--------------------------------------------------------------------
- // Texture accessors
- //--------------------------------------------------------------------
-private:
- virtual void setImage(const U8 te, LLViewerTexture *imagep, const U32 index);
- virtual LLViewerTexture* getImage(const U8 te, const U32 index) const;
-
- virtual const LLTextureEntry* getTexEntry(const U8 te_num) const;
- virtual void setTexEntry(const U8 index, const LLTextureEntry &te);
-
- void checkTextureLoading() ;
- //--------------------------------------------------------------------
- // Layers
- //--------------------------------------------------------------------
-protected:
- void deleteLayerSetCaches(bool clearAll = true);
- void addBakedTextureStats(LLViewerFetchedTexture* imagep, F32 pixel_area, F32 texel_area_ratio, S32 boost_level);
-
- //--------------------------------------------------------------------
- // Composites
- //--------------------------------------------------------------------
-public:
- virtual void invalidateComposite(LLTexLayerSet* layerset, BOOL upload_result);
- virtual void invalidateAll();
- virtual void setCompositeUpdatesEnabled(bool b) {}
- virtual void setCompositeUpdatesEnabled(U32 index, bool b) {}
- virtual bool isCompositeUpdateEnabled(U32 index) { return false; }
-
- //--------------------------------------------------------------------
- // Static texture/mesh/baked dictionary
- //--------------------------------------------------------------------
-public:
- static BOOL isIndexLocalTexture(LLVOAvatarDefines::ETextureIndex i);
- static BOOL isIndexBakedTexture(LLVOAvatarDefines::ETextureIndex i);
-private:
- static const LLVOAvatarDefines::LLVOAvatarDictionary *getDictionary() { return sAvatarDictionary; }
- static LLVOAvatarDefines::LLVOAvatarDictionary* sAvatarDictionary;
- static LLVOAvatarSkeletonInfo* sAvatarSkeletonInfo;
- static LLVOAvatarXmlInfo* sAvatarXmlInfo;
-
- //--------------------------------------------------------------------
- // Messaging
- //--------------------------------------------------------------------
-public:
- void onFirstTEMessageReceived();
-private:
- BOOL mFirstTEMessageReceived;
- BOOL mFirstAppearanceMessageReceived;
-
-/** Textures
- ** **
- *******************************************************************************/
-
-/********************************************************************************
- ** **
- ** MESHES
- **/
-
-public:
- void updateMeshTextures();
- void updateSexDependentLayerSets(BOOL upload_bake);
- void dirtyMesh(); // Dirty the avatar mesh
- void updateMeshData();
-protected:
- void releaseMeshData();
- virtual void restoreMeshData();
-private:
- void dirtyMesh(S32 priority); // Dirty the avatar mesh, with priority
- S32 mDirtyMesh; // 0 -- not dirty, 1 -- morphed, 2 -- LOD
- BOOL mMeshTexturesDirty;
-
- typedef std::multimap<std::string, LLPolyMesh*> polymesh_map_t;
- polymesh_map_t mMeshes;
- std::vector<LLViewerJoint *> mMeshLOD;
-
- //--------------------------------------------------------------------
- // Destroy invisible mesh
- //--------------------------------------------------------------------
-protected:
- BOOL mMeshValid;
- LLFrameTimer mMeshInvisibleTime;
-
-/** Meshes
- ** **
- *******************************************************************************/
-
-/********************************************************************************
- ** **
- ** APPEARANCE
- **/
-
-public:
- void processAvatarAppearance(LLMessageSystem* mesgsys);
- void hideSkirt();
- void startAppearanceAnimation();
-
- //--------------------------------------------------------------------
- // Appearance morphing
- //--------------------------------------------------------------------
-public:
- BOOL getIsAppearanceAnimating() const { return mAppearanceAnimating; }
-private:
- BOOL mAppearanceAnimating;
- LLFrameTimer mAppearanceMorphTimer;
- F32 mLastAppearanceBlendTime;
-
- //--------------------------------------------------------------------
- // Clothing colors (convenience functions to access visual parameters)
- //--------------------------------------------------------------------
-public:
- void setClothesColor(LLVOAvatarDefines::ETextureIndex te, const LLColor4& new_color, BOOL upload_bake);
- LLColor4 getClothesColor(LLVOAvatarDefines::ETextureIndex te);
- static BOOL teToColorParams(LLVOAvatarDefines::ETextureIndex te, U32 *param_name);
-
- //--------------------------------------------------------------------
- // Global colors
- //--------------------------------------------------------------------
-public:
- LLColor4 getGlobalColor(const std::string& color_name ) const;
- void onGlobalColorChanged(const LLTexGlobalColor* global_color, BOOL upload_bake);
-private:
- LLTexGlobalColor* mTexSkinColor;
- LLTexGlobalColor* mTexHairColor;
- LLTexGlobalColor* mTexEyeColor;
-
- //--------------------------------------------------------------------
- // Visibility
- //--------------------------------------------------------------------
-public:
- BOOL isVisible() const;
- void setVisibilityRank(U32 rank);
- U32 getVisibilityRank() const { return mVisibilityRank; } // unused
- static S32 sNumVisibleAvatars; // Number of instances of this class
- static LLColor4 getDummyColor();
-/** Appearance
- ** **
- *******************************************************************************/
-
-/********************************************************************************
- ** **
- ** WEARABLES
- **/
-
-public:
- virtual BOOL isWearingWearableType(LLWearableType::EType type ) const;
-
- //--------------------------------------------------------------------
- // Attachments
- //--------------------------------------------------------------------
-public:
- void clampAttachmentPositions();
- virtual const LLViewerJointAttachment* attachObject(LLViewerObject *viewer_object);
- virtual BOOL detachObject(LLViewerObject *viewer_object);
- void cleanupAttachedMesh( LLViewerObject* pVO );
- static LLVOAvatar* findAvatarFromAttachment(LLViewerObject* obj);
-protected:
- LLViewerJointAttachment* getTargetAttachmentPoint(LLViewerObject* viewer_object);
- void lazyAttach();
- void rebuildRiggedAttachments( void );
-
- //--------------------------------------------------------------------
- // Map of attachment points, by ID
- //--------------------------------------------------------------------
-public:
- S32 getAttachmentCount(); // Warning: order(N) not order(1) // currently used only by -self
- typedef std::map<S32, LLViewerJointAttachment*> attachment_map_t;
- attachment_map_t mAttachmentPoints;
- std::vector<LLPointer<LLViewerObject> > mPendingAttachment;
-
- //--------------------------------------------------------------------
- // HUD functions
- //--------------------------------------------------------------------
-public:
- BOOL hasHUDAttachment() const;
- LLBBox getHUDBBox() const;
- void rebuildHUD();
- void resetHUDAttachments();
- BOOL canAttachMoreObjects() const;
- BOOL canAttachMoreObjects(U32 n) const;
-protected:
- U32 getNumAttachments() const; // O(N), not O(1)
-
-/** Wearables
- ** **
- *******************************************************************************/
-
-/********************************************************************************
- ** **
- ** ACTIONS
- **/
-
- //--------------------------------------------------------------------
- // Animations
- //--------------------------------------------------------------------
-public:
- BOOL isAnyAnimationSignaled(const LLUUID *anim_array, const S32 num_anims) const;
- void processAnimationStateChanges();
-protected:
- BOOL processSingleAnimationStateChange(const LLUUID &anim_id, BOOL start);
- void resetAnimations();
-private:
- LLTimer mAnimTimer;
- F32 mTimeLast;
-
- //--------------------------------------------------------------------
- // Animation state data
- //--------------------------------------------------------------------
-public:
- typedef std::map<LLUUID, S32>::iterator AnimIterator;
- std::map<LLUUID, S32> mSignaledAnimations; // requested state of Animation name/value
- std::map<LLUUID, S32> mPlayingAnimations; // current state of Animation name/value
-
- typedef std::multimap<LLUUID, LLUUID> AnimationSourceMap;
- typedef AnimationSourceMap::iterator AnimSourceIterator;
- AnimationSourceMap mAnimationSources; // object ids that triggered anim ids
-
- //--------------------------------------------------------------------
- // Chat
- //--------------------------------------------------------------------
-public:
- void addChat(const LLChat& chat);
- void clearChat();
- void startTyping() { mTyping = TRUE; mTypingTimer.reset(); }
- void stopTyping() { mTyping = FALSE; }
-private:
- BOOL mVisibleChat;
-
- //--------------------------------------------------------------------
- // Lip synch morphs
- //--------------------------------------------------------------------
-private:
- bool mLipSyncActive; // we're morphing for lip sync
- LLVisualParam* mOohMorph; // cached pointers morphs for lip sync
- LLVisualParam* mAahMorph; // cached pointers morphs for lip sync
-
- //--------------------------------------------------------------------
- // Flight
- //--------------------------------------------------------------------
-public:
- BOOL mInAir;
- LLFrameTimer mTimeInAir;
-
-/** Actions
- ** **
- *******************************************************************************/
-
-/********************************************************************************
- ** **
- ** PHYSICS
- **/
-
-private:
- F32 mSpeedAccum; // measures speed (for diagnostics mostly).
- BOOL mTurning; // controls hysteresis on avatar rotation
- F32 mSpeed; // misc. animation repeated state
-
- //--------------------------------------------------------------------
- // Collision volumes
- //--------------------------------------------------------------------
-public:
- S32 mNumCollisionVolumes;
- LLViewerJointCollisionVolume* mCollisionVolumes;
-protected:
- BOOL allocateCollisionVolumes(U32 num);
-
- //--------------------------------------------------------------------
- // Dimensions
- //--------------------------------------------------------------------
-public:
- void resolveHeightGlobal(const LLVector3d &inPos, LLVector3d &outPos, LLVector3 &outNorm);
- bool distanceToGround( const LLVector3d &startPoint, LLVector3d &collisionPoint, F32 distToIntersectionAlongRay );
- void resolveHeightAgent(const LLVector3 &inPos, LLVector3 &outPos, LLVector3 &outNorm);
- void resolveRayCollisionAgent(const LLVector3d start_pt, const LLVector3d end_pt, LLVector3d &out_pos, LLVector3 &out_norm);
- void slamPosition(); // Slam position to transmitted position (for teleport);
-protected:
- void computeBodySize();
-
- //--------------------------------------------------------------------
- // Material being stepped on
- //--------------------------------------------------------------------
-private:
- BOOL mStepOnLand;
- U8 mStepMaterial;
- LLVector3 mStepObjectVelocity;
-
-/** Physics
- ** **
- *******************************************************************************/
-
-/********************************************************************************
- ** **
- ** HIERARCHY
- **/
-
-public:
- virtual BOOL setParent(LLViewerObject* parent);
- virtual void addChild(LLViewerObject *childp);
- virtual void removeChild(LLViewerObject *childp);
-
- //--------------------------------------------------------------------
- // Sitting
- //--------------------------------------------------------------------
-public:
- void sitDown(BOOL bSitting);
- BOOL isSitting(){return mIsSitting;}
- void sitOnObject(LLViewerObject *sit_object);
- void getOffObject();
-private:
- // set this property only with LLVOAvatar::sitDown method
- BOOL mIsSitting;
-
-/** Hierarchy
- ** **
- *******************************************************************************/
-
-/********************************************************************************
- ** **
- ** NAME
- **/
-
-public:
- std::string getFullname() const; // Returns "FirstName LastName"
-protected:
- static void getAnimLabels(LLDynamicArray<std::string>* labels);
- static void getAnimNames(LLDynamicArray<std::string>* names);
-private:
- std::string mNameString; // UTF-8 title + name + status
- std::string mTitle;
- bool mNameAway;
- bool mNameBusy;
- bool mNameMute;
- bool mNameAppearance;
- bool mNameFriend;
- bool mNameCloud;
- F32 mNameAlpha;
- BOOL mRenderGroupTitles;
-
- //--------------------------------------------------------------------
- // Display the name (then optionally fade it out)
- //--------------------------------------------------------------------
-public:
- LLFrameTimer mChatTimer;
- LLPointer<LLHUDNameTag> mNameText;
-private:
- LLFrameTimer mTimeVisible;
- std::deque<LLChat> mChats;
- BOOL mTyping;
- LLFrameTimer mTypingTimer;
-
-/** Name
- ** **
- *******************************************************************************/
-
-/********************************************************************************
- ** **
- ** SOUNDS
- **/
-
- //--------------------------------------------------------------------
- // Voice visualizer
- //--------------------------------------------------------------------
-public:
- // Responsible for detecting the user's voice signal (and when the
- // user speaks, it puts a voice symbol over the avatar's head) and gesticulations
- LLPointer<LLVoiceVisualizer> mVoiceVisualizer;
- int mCurrentGesticulationLevel;
-
- //--------------------------------------------------------------------
- // Step sound
- //--------------------------------------------------------------------
-protected:
- const LLUUID& getStepSound() const;
-private:
- // Global table of sound ids per material, and the ground
- const static LLUUID sStepSounds[LL_MCODE_END];
- const static LLUUID sStepSoundOnLand;
-
- //--------------------------------------------------------------------
- // Foot step state (for generating sounds)
- //--------------------------------------------------------------------
-public:
- void setFootPlane(const LLVector4 &plane) { mFootPlane = plane; }
- LLVector4 mFootPlane;
-private:
- BOOL mWasOnGroundLeft;
- BOOL mWasOnGroundRight;
-
-/** Sounds
- ** **
- *******************************************************************************/
-
-/********************************************************************************
- ** **
- ** DIAGNOSTICS
- **/
-
- //--------------------------------------------------------------------
- // General
- //--------------------------------------------------------------------
-public:
- static void dumpArchetypeXML(void*);
- static void dumpBakedStatus();
- const std::string getBakedStatusForPrintout() const;
- void dumpAvatarTEs(const std::string& context) const;
-
- static F32 sUnbakedTime; // Total seconds with >=1 unbaked avatars
- static F32 sUnbakedUpdateTime; // Last time stats were updated (to prevent multiple updates per frame)
- static F32 sGreyTime; // Total seconds with >=1 grey avatars
- static F32 sGreyUpdateTime; // Last time stats were updated (to prevent multiple updates per frame)
-protected:
- S32 getUnbakedPixelAreaRank();
- BOOL mHasGrey;
-private:
- F32 mMinPixelArea;
- F32 mMaxPixelArea;
- F32 mAdjustedPixelArea;
- std::string mDebugText;
-
-
- //--------------------------------------------------------------------
- // Avatar Rez Metrics
- //--------------------------------------------------------------------
-public:
- F32 debugGetExistenceTimeElapsedF32() const { return mDebugExistenceTimer.getElapsedTimeF32(); }
-protected:
- LLFrameTimer mRuthDebugTimer; // For tracking how long it takes for av to rez
- LLFrameTimer mDebugExistenceTimer; // Debugging for how long the avatar has been in memory.
-
-/** Diagnostics
- ** **
- *******************************************************************************/
-
-/********************************************************************************
- ** **
- ** SUPPORT CLASSES
- **/
-
-protected: // Shared with LLVOAvatarSelf
-
- struct LLVOAvatarXmlInfo
- {
- LLVOAvatarXmlInfo();
- ~LLVOAvatarXmlInfo();
-
- BOOL parseXmlSkeletonNode(LLXmlTreeNode* root);
- BOOL parseXmlMeshNodes(LLXmlTreeNode* root);
- BOOL parseXmlColorNodes(LLXmlTreeNode* root);
- BOOL parseXmlLayerNodes(LLXmlTreeNode* root);
- BOOL parseXmlDriverNodes(LLXmlTreeNode* root);
- BOOL parseXmlMorphNodes(LLXmlTreeNode* root);
-
- struct LLVOAvatarMeshInfo
- {
- typedef std::pair<LLPolyMorphTargetInfo*,BOOL> morph_info_pair_t;
- typedef std::vector<morph_info_pair_t> morph_info_list_t;
-
- LLVOAvatarMeshInfo() : mLOD(0), mMinPixelArea(.1f) {}
- ~LLVOAvatarMeshInfo()
- {
- morph_info_list_t::iterator iter;
- for (iter = mPolyMorphTargetInfoList.begin(); iter != mPolyMorphTargetInfoList.end(); iter++)
- {
- delete iter->first;
- }
- mPolyMorphTargetInfoList.clear();
- }
-
- std::string mType;
- S32 mLOD;
- std::string mMeshFileName;
- std::string mReferenceMeshName;
- F32 mMinPixelArea;
- morph_info_list_t mPolyMorphTargetInfoList;
- };
- typedef std::vector<LLVOAvatarMeshInfo*> mesh_info_list_t;
- mesh_info_list_t mMeshInfoList;
-
- typedef std::vector<LLPolySkeletalDistortionInfo*> skeletal_distortion_info_list_t;
- skeletal_distortion_info_list_t mSkeletalDistortionInfoList;
-
- struct LLVOAvatarAttachmentInfo
- {
- LLVOAvatarAttachmentInfo()
- : mGroup(-1), mAttachmentID(-1), mPieMenuSlice(-1), mVisibleFirstPerson(FALSE),
- mIsHUDAttachment(FALSE), mHasPosition(FALSE), mHasRotation(FALSE) {}
- std::string mName;
- std::string mJointName;
- LLVector3 mPosition;
- LLVector3 mRotationEuler;
- S32 mGroup;
- S32 mAttachmentID;
- S32 mPieMenuSlice;
- BOOL mVisibleFirstPerson;
- BOOL mIsHUDAttachment;
- BOOL mHasPosition;
- BOOL mHasRotation;
- };
- typedef std::vector<LLVOAvatarAttachmentInfo*> attachment_info_list_t;
- attachment_info_list_t mAttachmentInfoList;
-
- LLTexGlobalColorInfo *mTexSkinColorInfo;
- LLTexGlobalColorInfo *mTexHairColorInfo;
- LLTexGlobalColorInfo *mTexEyeColorInfo;
-
- typedef std::vector<LLTexLayerSetInfo*> layer_info_list_t;
- layer_info_list_t mLayerInfoList;
-
- typedef std::vector<LLDriverParamInfo*> driver_info_list_t;
- driver_info_list_t mDriverInfoList;
-
- struct LLVOAvatarMorphInfo
- {
- LLVOAvatarMorphInfo()
- : mInvert(FALSE) {}
- std::string mName;
- std::string mRegion;
- std::string mLayer;
- BOOL mInvert;
- };
-
- typedef std::vector<LLVOAvatarMorphInfo*> morph_info_list_t;
- morph_info_list_t mMorphMaskInfoList;
- };
-
- struct LLMaskedMorph
- {
- LLMaskedMorph(LLPolyMorphTarget *morph_target, BOOL invert, std::string layer) :
- mMorphTarget(morph_target),
- mInvert(invert),
- mLayer(layer)
- {
- morph_target->addPendingMorphMask();
- }
-
- LLPolyMorphTarget *mMorphTarget;
- BOOL mInvert;
- std::string mLayer;
- };
-
-/** Support classes
- ** **
- *******************************************************************************/
-
-}; // LLVOAvatar
-extern const F32 SELF_ADDITIONAL_PRI;
-extern const S32 MAX_TEXTURE_VIRTURE_SIZE_RESET_INTERVAL;
-
-#endif // LL_VO_AVATAR_H
+/**
+ * @file llvoavatar.h
+ * @brief Declaration of LLVOAvatar class which is a derivation of
+ * LLViewerObject
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLVOAVATAR_H
+#define LL_LLVOAVATAR_H
+
+#include <map>
+#include <deque>
+#include <string>
+#include <vector>
+
+#include <boost/signals2.hpp>
+
+#include "imageids.h" // IMG_INVISIBLE
+#include "llchat.h"
+#include "lldrawpoolalpha.h"
+#include "llviewerobject.h"
+#include "llcharacter.h"
+#include "llviewerjointmesh.h"
+#include "llviewerjointattachment.h"
+#include "llrendertarget.h"
+#include "llvoavatardefines.h"
+#include "lltexglobalcolor.h"
+#include "lldriverparam.h"
+#include "material_codes.h" // LL_MCODE_END
+
+extern const LLUUID ANIM_AGENT_BODY_NOISE;
+extern const LLUUID ANIM_AGENT_BREATHE_ROT;
+extern const LLUUID ANIM_AGENT_PHYSICS_MOTION;
+extern const LLUUID ANIM_AGENT_EDITING;
+extern const LLUUID ANIM_AGENT_EYE;
+extern const LLUUID ANIM_AGENT_FLY_ADJUST;
+extern const LLUUID ANIM_AGENT_HAND_MOTION;
+extern const LLUUID ANIM_AGENT_HEAD_ROT;
+extern const LLUUID ANIM_AGENT_PELVIS_FIX;
+extern const LLUUID ANIM_AGENT_TARGET;
+extern const LLUUID ANIM_AGENT_WALK_ADJUST;
+
+class LLTexLayerSet;
+class LLVoiceVisualizer;
+class LLHUDNameTag;
+class LLHUDEffectSpiral;
+class LLTexGlobalColor;
+class LLVOAvatarBoneInfo;
+class LLVOAvatarSkeletonInfo;
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// LLVOAvatar
+//
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLVOAvatar :
+ public LLViewerObject,
+ public LLCharacter,
+ public boost::signals2::trackable
+{
+public:
+ friend class LLVOAvatarSelf;
+protected:
+ struct LLVOAvatarXmlInfo;
+ struct LLMaskedMorph;
+
+/********************************************************************************
+ ** **
+ ** INITIALIZATION
+ **/
+
+public:
+ LLVOAvatar(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp);
+ virtual void markDead();
+ static void initClass(); // Initialize data that's only init'd once per class.
+ static void cleanupClass(); // Cleanup data that's only init'd once per class.
+ virtual void initInstance(); // Called after construction to initialize the class.
+protected:
+ virtual ~LLVOAvatar();
+ BOOL loadSkeletonNode();
+ BOOL loadMeshNodes();
+ virtual BOOL loadLayersets();
+
+/** Initialization
+ ** **
+ *******************************************************************************/
+
+/********************************************************************************
+ ** **
+ ** INHERITED
+ **/
+
+ //--------------------------------------------------------------------
+ // LLViewerObject interface and related
+ //--------------------------------------------------------------------
+public:
+ virtual void updateGL();
+ virtual LLVOAvatar* asAvatar();
+ virtual U32 processUpdateMessage(LLMessageSystem *mesgsys,
+ void **user_data,
+ U32 block_num,
+ const EObjectUpdateType update_type,
+ LLDataPacker *dp);
+ virtual BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
+ virtual BOOL updateLOD();
+ BOOL updateJointLODs();
+ void updateLODRiggedAttachments( void );
+ virtual BOOL isActive() const; // Whether this object needs to do an idleUpdate.
+ virtual void updateTextures();
+ virtual S32 setTETexture(const U8 te, const LLUUID& uuid); // If setting a baked texture, need to request it from a non-local sim.
+ virtual void onShift(const LLVector4a& shift_vector);
+ virtual U32 getPartitionType() const;
+ virtual const LLVector3 getRenderPosition() const;
+ virtual void updateDrawable(BOOL force_damped);
+ virtual LLDrawable* createDrawable(LLPipeline *pipeline);
+ virtual BOOL updateGeometry(LLDrawable *drawable);
+ virtual void setPixelAreaAndAngle(LLAgent &agent);
+ virtual void updateRegion(LLViewerRegion *regionp);
+ virtual void updateSpatialExtents(LLVector4a& newMin, LLVector4a &newMax);
+ virtual void getSpatialExtents(LLVector4a& newMin, LLVector4a& newMax);
+ virtual BOOL lineSegmentIntersect(const LLVector3& start, const LLVector3& end,
+ S32 face = -1, // which face to check, -1 = ALL_SIDES
+ BOOL pick_transparent = FALSE,
+ S32* face_hit = NULL, // which face was hit
+ LLVector3* intersection = NULL, // return the intersection point
+ LLVector2* tex_coord = NULL, // return the texture coordinates of the intersection point
+ LLVector3* normal = NULL, // return the surface normal at the intersection point
+ LLVector3* bi_normal = NULL); // return the surface bi-normal at the intersection point
+ LLViewerObject* lineSegmentIntersectRiggedAttachments(const LLVector3& start, const LLVector3& end,
+ S32 face = -1, // which face to check, -1 = ALL_SIDES
+ BOOL pick_transparent = FALSE,
+ S32* face_hit = NULL, // which face was hit
+ LLVector3* intersection = NULL, // return the intersection point
+ LLVector2* tex_coord = NULL, // return the texture coordinates of the intersection point
+ LLVector3* normal = NULL, // return the surface normal at the intersection point
+ LLVector3* bi_normal = NULL); // return the surface bi-normal at the intersection point
+
+ //--------------------------------------------------------------------
+ // LLCharacter interface and related
+ //--------------------------------------------------------------------
+public:
+ virtual LLVector3 getCharacterPosition();
+ virtual LLQuaternion getCharacterRotation();
+ virtual LLVector3 getCharacterVelocity();
+ virtual LLVector3 getCharacterAngularVelocity();
+ virtual LLJoint* getCharacterJoint(U32 num);
+ virtual BOOL allocateCharacterJoints(U32 num);
+
+ virtual LLUUID remapMotionID(const LLUUID& id);
+ virtual BOOL startMotion(const LLUUID& id, F32 time_offset = 0.f);
+ virtual BOOL stopMotion(const LLUUID& id, BOOL stop_immediate = FALSE);
+ virtual void stopMotionFromSource(const LLUUID& source_id);
+ virtual void requestStopMotion(LLMotion* motion);
+ LLMotion* findMotion(const LLUUID& id) const;
+ void startDefaultMotions();
+ void dumpAnimationState();
+
+ virtual LLJoint* getJoint(const std::string &name);
+ virtual LLJoint* getRootJoint() { return &mRoot; }
+
+ void resetJointPositions( void );
+ void resetJointPositionsToDefault( void );
+ void resetSpecificJointPosition( const std::string& name );
+
+ virtual const char* getAnimationPrefix() { return "avatar"; }
+ virtual const LLUUID& getID();
+ virtual LLVector3 getVolumePos(S32 joint_index, LLVector3& volume_offset);
+ virtual LLJoint* findCollisionVolume(U32 volume_id);
+ virtual S32 getCollisionVolumeID(std::string &name);
+ virtual void addDebugText(const std::string& text);
+ virtual F32 getTimeDilation();
+ virtual void getGround(const LLVector3 &inPos, LLVector3 &outPos, LLVector3 &outNorm);
+ virtual F32 getPixelArea() const;
+ virtual LLPolyMesh* getHeadMesh();
+ virtual LLPolyMesh* getUpperBodyMesh();
+ virtual LLVector3d getPosGlobalFromAgent(const LLVector3 &position);
+ virtual LLVector3 getPosAgentFromGlobal(const LLVector3d &position);
+ virtual void updateVisualParams();
+
+
+/** Inherited
+ ** **
+ *******************************************************************************/
+
+/********************************************************************************
+ ** **
+ ** STATE
+ **/
+
+public:
+ virtual bool isSelf() const { return false; } // True if this avatar is for this viewer's agent
+ bool isBuilt() const { return mIsBuilt; }
+
+private: //aligned members
+ LLVector4a mImpostorExtents[2];
+
+private:
+ BOOL mSupportsAlphaLayers; // For backwards compatibility, TRUE for 1.23+ clients
+
+ //--------------------------------------------------------------------
+ // Updates
+ //--------------------------------------------------------------------
+public:
+ virtual BOOL updateCharacter(LLAgent &agent);
+ void idleUpdateVoiceVisualizer(bool voice_enabled);
+ void idleUpdateMisc(bool detailed_update);
+ virtual void idleUpdateAppearanceAnimation();
+ void idleUpdateLipSync(bool voice_enabled);
+ void idleUpdateLoadingEffect();
+ void idleUpdateWindEffect();
+ void idleUpdateNameTag(const LLVector3& root_pos_last);
+ void idleUpdateNameTagText(BOOL new_name);
+ LLVector3 idleUpdateNameTagPosition(const LLVector3& root_pos_last);
+ void idleUpdateNameTagAlpha(BOOL new_name, F32 alpha);
+ LLColor4 getNameTagColor(bool is_friend);
+ void clearNameTag();
+ static void invalidateNameTag(const LLUUID& agent_id);
+ // force all name tags to rebuild, useful when display names turned on/off
+ static void invalidateNameTags();
+ void addNameTagLine(const std::string& line, const LLColor4& color, S32 style, const LLFontGL* font);
+ void idleUpdateRenderCost();
+ void idleUpdateBelowWater();
+
+ //--------------------------------------------------------------------
+ // Static preferences (controlled by user settings/menus)
+ //--------------------------------------------------------------------
+public:
+ static S32 sRenderName;
+ static BOOL sRenderGroupTitles;
+ static U32 sMaxVisible; //(affected by control "RenderAvatarMaxVisible")
+ static F32 sRenderDistance; //distance at which avatars will render.
+ static BOOL sShowAnimationDebug; // show animation debug info
+ static BOOL sUseImpostors; //use impostors for far away avatars
+ static BOOL sShowFootPlane; // show foot collision plane reported by server
+ static BOOL sShowCollisionVolumes; // show skeletal collision volumes
+ static BOOL sVisibleInFirstPerson;
+ static S32 sNumLODChangesThisFrame;
+ static S32 sNumVisibleChatBubbles;
+ static BOOL sDebugInvisible;
+ static BOOL sShowAttachmentPoints;
+ static F32 sLODFactor; // user-settable LOD factor
+ static F32 sPhysicsLODFactor; // user-settable physics LOD factor
+ static BOOL sJointDebug; // output total number of joints being touched for each avatar
+ static BOOL sDebugAvatarRotation;
+
+ //--------------------------------------------------------------------
+ // Region state
+ //--------------------------------------------------------------------
+public:
+ LLHost getObjectHost() const;
+
+ //--------------------------------------------------------------------
+ // Loading state
+ //--------------------------------------------------------------------
+public:
+ BOOL isFullyLoaded() const;
+ bool isTooComplex() const;
+ bool visualParamWeightsAreDefault();
+protected:
+ virtual BOOL getIsCloud();
+ BOOL updateIsFullyLoaded();
+ BOOL processFullyLoadedChange(bool loading);
+ void updateRuthTimer(bool loading);
+ F32 calcMorphAmount();
+private:
+ BOOL mFullyLoaded;
+ BOOL mPreviousFullyLoaded;
+ BOOL mFullyLoadedInitialized;
+ S32 mFullyLoadedFrameCounter;
+ S32 mVisualComplexity;
+ LLFrameTimer mFullyLoadedTimer;
+ LLFrameTimer mRuthTimer;
+protected:
+ LLFrameTimer mInvisibleTimer;
+
+/** State
+ ** **
+ *******************************************************************************/
+
+/********************************************************************************
+ ** **
+ ** SKELETON
+ **/
+
+public:
+ void updateHeadOffset();
+ F32 getPelvisToFoot() const { return mPelvisToFoot; }
+ void setPelvisOffset( bool hasOffset, const LLVector3& translation, F32 offset ) ;
+ bool hasPelvisOffset( void ) { return mHasPelvisOffset; }
+ void postPelvisSetRecalc( void );
+ void setPelvisOffset( F32 pelvixFixupAmount );
+
+ bool mHasPelvisOffset;
+ LLVector3 mPelvisOffset;
+ F32 mLastPelvisToFoot;
+ F32 mPelvisFixup;
+ F32 mLastPelvisFixup;
+
+ LLVector3 mHeadOffset; // current head position
+ LLViewerJoint mRoot;
+protected:
+ static BOOL parseSkeletonFile(const std::string& filename);
+ void buildCharacter();
+ virtual BOOL loadAvatar();
+
+ BOOL setupBone(const LLVOAvatarBoneInfo* info, LLViewerJoint* parent, S32 &current_volume_num, S32 &current_joint_num);
+ BOOL buildSkeleton(const LLVOAvatarSkeletonInfo *info);
+private:
+ BOOL mIsBuilt; // state of deferred character building
+ S32 mNumJoints;
+ LLViewerJoint* mSkeleton;
+
+ //--------------------------------------------------------------------
+ // Pelvis height adjustment members.
+ //--------------------------------------------------------------------
+public:
+ LLVector3 mBodySize;
+ S32 mLastSkeletonSerialNum;
+private:
+ F32 mPelvisToFoot;
+
+ //--------------------------------------------------------------------
+ // Cached pointers to well known joints
+ //--------------------------------------------------------------------
+public:
+ LLViewerJoint* mPelvisp;
+ LLViewerJoint* mTorsop;
+ LLViewerJoint* mChestp;
+ LLViewerJoint* mNeckp;
+ LLViewerJoint* mHeadp;
+ LLViewerJoint* mSkullp;
+ LLViewerJoint* mEyeLeftp;
+ LLViewerJoint* mEyeRightp;
+ LLViewerJoint* mHipLeftp;
+ LLViewerJoint* mHipRightp;
+ LLViewerJoint* mKneeLeftp;
+ LLViewerJoint* mKneeRightp;
+ LLViewerJoint* mAnkleLeftp;
+ LLViewerJoint* mAnkleRightp;
+ LLViewerJoint* mFootLeftp;
+ LLViewerJoint* mFootRightp;
+ LLViewerJoint* mWristLeftp;
+ LLViewerJoint* mWristRightp;
+
+ //--------------------------------------------------------------------
+ // XML parse tree
+ //--------------------------------------------------------------------
+private:
+ static LLXmlTree sXMLTree; // avatar config file
+ static LLXmlTree sSkeletonXMLTree; // avatar skeleton file
+
+/** Skeleton
+ ** **
+ *******************************************************************************/
+
+/********************************************************************************
+ ** **
+ ** RENDERING
+ **/
+
+public:
+ U32 renderImpostor(LLColor4U color = LLColor4U(255,255,255,255), S32 diffuse_channel = 0);
+ U32 renderRigid();
+ U32 renderSkinned(EAvatarRenderPass pass);
+ F32 getLastSkinTime() { return mLastSkinTime; }
+ U32 renderSkinnedAttachments();
+ U32 renderTransparent(BOOL first_pass);
+ void renderCollisionVolumes();
+ static void deleteCachedImages(bool clearAll=true);
+ static void destroyGL();
+ static void restoreGL();
+ BOOL mIsDummy; // for special views
+ S32 mSpecialRenderMode; // special lighting
+private:
+ bool shouldAlphaMask();
+
+ BOOL mNeedsSkin; // avatar has been animated and verts have not been updated
+ F32 mLastSkinTime; //value of gFrameTimeSeconds at last skin update
+
+ S32 mUpdatePeriod;
+ S32 mNumInitFaces; //number of faces generated when creating the avatar drawable, does not inculde splitted faces due to long vertex buffer.
+
+ //--------------------------------------------------------------------
+ // Morph masks
+ //--------------------------------------------------------------------
+public:
+ BOOL morphMaskNeedsUpdate(LLVOAvatarDefines::EBakedTextureIndex index = LLVOAvatarDefines::BAKED_NUM_INDICES);
+ void addMaskedMorph(LLVOAvatarDefines::EBakedTextureIndex index, LLPolyMorphTarget* morph_target, BOOL invert, std::string layer);
+ void applyMorphMask(U8* tex_data, S32 width, S32 height, S32 num_components, LLVOAvatarDefines::EBakedTextureIndex index = LLVOAvatarDefines::BAKED_NUM_INDICES);
+
+ //--------------------------------------------------------------------
+ // Visibility
+ //--------------------------------------------------------------------
+protected:
+ void updateVisibility();
+private:
+ U32 mVisibilityRank;
+ BOOL mVisible;
+
+ //--------------------------------------------------------------------
+ // Shadowing
+ //--------------------------------------------------------------------
+public:
+ void updateShadowFaces();
+ LLDrawable* mShadow;
+private:
+ LLFace* mShadow0Facep;
+ LLFace* mShadow1Facep;
+ LLPointer<LLViewerTexture> mShadowImagep;
+
+ //--------------------------------------------------------------------
+ // Impostors
+ //--------------------------------------------------------------------
+public:
+ BOOL isImpostor() const;
+ BOOL needsImpostorUpdate() const;
+ const LLVector3& getImpostorOffset() const;
+ const LLVector2& getImpostorDim() const;
+ void getImpostorValues(LLVector4a* extents, LLVector3& angle, F32& distance) const;
+ void cacheImpostorValues();
+ void setImpostorDim(const LLVector2& dim);
+ static void resetImpostors();
+ static void updateImpostors();
+ LLRenderTarget mImpostor;
+ BOOL mNeedsImpostorUpdate;
+private:
+ LLVector3 mImpostorOffset;
+ LLVector2 mImpostorDim;
+ BOOL mNeedsAnimUpdate;
+ LLVector3 mImpostorAngle;
+ F32 mImpostorDistance;
+ F32 mImpostorPixelArea;
+ LLVector3 mLastAnimExtents[2];
+
+ //--------------------------------------------------------------------
+ // Wind rippling in clothes
+ //--------------------------------------------------------------------
+public:
+ LLVector4 mWindVec;
+ F32 mRipplePhase;
+ BOOL mBelowWater;
+private:
+ F32 mWindFreq;
+ LLFrameTimer mRippleTimer;
+ F32 mRippleTimeLast;
+ LLVector3 mRippleAccel;
+ LLVector3 mLastVel;
+
+ //--------------------------------------------------------------------
+ // Culling
+ //--------------------------------------------------------------------
+public:
+ static void cullAvatarsByPixelArea();
+ BOOL isCulled() const { return mCulled; }
+private:
+ BOOL mCulled;
+
+ //--------------------------------------------------------------------
+ // Freeze counter
+ //--------------------------------------------------------------------
+public:
+ static void updateFreezeCounter(S32 counter = 0);
+private:
+ static S32 sFreezeCounter;
+
+ //--------------------------------------------------------------------
+ // Constants
+ //--------------------------------------------------------------------
+public:
+ virtual LLViewerTexture::EBoostLevel getAvatarBoostLevel() const { return LLViewerTexture::BOOST_AVATAR; }
+ virtual LLViewerTexture::EBoostLevel getAvatarBakedBoostLevel() const { return LLViewerTexture::BOOST_AVATAR_BAKED; }
+ virtual S32 getTexImageSize() const;
+ virtual S32 getTexImageArea() const { return getTexImageSize()*getTexImageSize(); }
+
+/** Rendering
+ ** **
+ *******************************************************************************/
+
+/********************************************************************************
+ ** **
+ ** TEXTURES
+ **/
+
+ //--------------------------------------------------------------------
+ // Loading status
+ //--------------------------------------------------------------------
+public:
+ virtual BOOL isTextureDefined(LLVOAvatarDefines::ETextureIndex type, U32 index = 0) const;
+ virtual BOOL isTextureVisible(LLVOAvatarDefines::ETextureIndex type, U32 index = 0) const;
+ virtual BOOL isTextureVisible(LLVOAvatarDefines::ETextureIndex type, LLWearable *wearable) const;
+
+protected:
+ BOOL isFullyBaked();
+ static BOOL areAllNearbyInstancesBaked(S32& grey_avatars);
+
+ //--------------------------------------------------------------------
+ // Baked textures
+ //--------------------------------------------------------------------
+public:
+ void releaseComponentTextures(); // ! BACKWARDS COMPATIBILITY !
+protected:
+ static void onBakedTextureMasksLoaded(BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata);
+ static void onInitialBakedTextureLoaded(BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata);
+ static void onBakedTextureLoaded(BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata);
+ virtual void removeMissingBakedTextures();
+ void useBakedTexture(const LLUUID& id);
+
+ typedef std::deque<LLMaskedMorph *> morph_list_t;
+ struct BakedTextureData
+ {
+ LLUUID mLastTextureIndex;
+ LLTexLayerSet* mTexLayerSet; // Only exists for self
+ bool mIsLoaded;
+ bool mIsUsed;
+ LLVOAvatarDefines::ETextureIndex mTextureIndex;
+ U32 mMaskTexName;
+ // Stores pointers to the joint meshes that this baked texture deals with
+ std::vector< LLViewerJointMesh * > mMeshes; // std::vector<LLViewerJointMesh> mJoints[i]->mMeshParts
+ morph_list_t mMaskedMorphs;
+ };
+ typedef std::vector<BakedTextureData> bakedtexturedata_vec_t;
+ bakedtexturedata_vec_t mBakedTextureDatas;
+ LLLoadedCallbackEntry::source_callback_list_t mCallbackTextureList ;
+ BOOL mLoadedCallbacksPaused;
+ //--------------------------------------------------------------------
+ // Local Textures
+ //--------------------------------------------------------------------
+protected:
+ virtual void setLocalTexture(LLVOAvatarDefines::ETextureIndex type, LLViewerTexture* tex, BOOL baked_version_exits, U32 index = 0);
+ virtual void addLocalTextureStats(LLVOAvatarDefines::ETextureIndex type, LLViewerFetchedTexture* imagep, F32 texel_area_ratio, BOOL rendered, BOOL covered_by_baked, U32 index = 0);
+ // MULTI-WEARABLE: make self-only?
+ virtual void setBakedReady(LLVOAvatarDefines::ETextureIndex type, BOOL baked_version_exists, U32 index = 0);
+
+ //--------------------------------------------------------------------
+ // Texture accessors
+ //--------------------------------------------------------------------
+private:
+ virtual void setImage(const U8 te, LLViewerTexture *imagep, const U32 index);
+ virtual LLViewerTexture* getImage(const U8 te, const U32 index) const;
+
+ virtual const LLTextureEntry* getTexEntry(const U8 te_num) const;
+ virtual void setTexEntry(const U8 index, const LLTextureEntry &te);
+
+ void checkTextureLoading() ;
+ //--------------------------------------------------------------------
+ // Layers
+ //--------------------------------------------------------------------
+protected:
+ void deleteLayerSetCaches(bool clearAll = true);
+ void addBakedTextureStats(LLViewerFetchedTexture* imagep, F32 pixel_area, F32 texel_area_ratio, S32 boost_level);
+
+ //--------------------------------------------------------------------
+ // Composites
+ //--------------------------------------------------------------------
+public:
+ virtual void invalidateComposite(LLTexLayerSet* layerset, BOOL upload_result);
+ virtual void invalidateAll();
+ virtual void setCompositeUpdatesEnabled(bool b) {}
+ virtual void setCompositeUpdatesEnabled(U32 index, bool b) {}
+ virtual bool isCompositeUpdateEnabled(U32 index) { return false; }
+
+ //--------------------------------------------------------------------
+ // Static texture/mesh/baked dictionary
+ //--------------------------------------------------------------------
+public:
+ static BOOL isIndexLocalTexture(LLVOAvatarDefines::ETextureIndex i);
+ static BOOL isIndexBakedTexture(LLVOAvatarDefines::ETextureIndex i);
+private:
+ static const LLVOAvatarDefines::LLVOAvatarDictionary *getDictionary() { return sAvatarDictionary; }
+ static LLVOAvatarDefines::LLVOAvatarDictionary* sAvatarDictionary;
+ static LLVOAvatarSkeletonInfo* sAvatarSkeletonInfo;
+ static LLVOAvatarXmlInfo* sAvatarXmlInfo;
+
+ //--------------------------------------------------------------------
+ // Messaging
+ //--------------------------------------------------------------------
+public:
+ void onFirstTEMessageReceived();
+private:
+ BOOL mFirstTEMessageReceived;
+ BOOL mFirstAppearanceMessageReceived;
+
+/** Textures
+ ** **
+ *******************************************************************************/
+
+/********************************************************************************
+ ** **
+ ** MESHES
+ **/
+
+public:
+ void updateMeshTextures();
+ void updateSexDependentLayerSets(BOOL upload_bake);
+ void dirtyMesh(); // Dirty the avatar mesh
+ void updateMeshData();
+protected:
+ void releaseMeshData();
+ virtual void restoreMeshData();
+private:
+ void dirtyMesh(S32 priority); // Dirty the avatar mesh, with priority
+ S32 mDirtyMesh; // 0 -- not dirty, 1 -- morphed, 2 -- LOD
+ BOOL mMeshTexturesDirty;
+
+ typedef std::multimap<std::string, LLPolyMesh*> polymesh_map_t;
+ polymesh_map_t mMeshes;
+ std::vector<LLViewerJoint *> mMeshLOD;
+
+ //--------------------------------------------------------------------
+ // Destroy invisible mesh
+ //--------------------------------------------------------------------
+protected:
+ BOOL mMeshValid;
+ LLFrameTimer mMeshInvisibleTime;
+
+/** Meshes
+ ** **
+ *******************************************************************************/
+
+/********************************************************************************
+ ** **
+ ** APPEARANCE
+ **/
+
+public:
+ void processAvatarAppearance(LLMessageSystem* mesgsys);
+ void hideSkirt();
+ void startAppearanceAnimation();
+
+ //--------------------------------------------------------------------
+ // Appearance morphing
+ //--------------------------------------------------------------------
+public:
+ BOOL getIsAppearanceAnimating() const { return mAppearanceAnimating; }
+private:
+ BOOL mAppearanceAnimating;
+ LLFrameTimer mAppearanceMorphTimer;
+ F32 mLastAppearanceBlendTime;
+
+ //--------------------------------------------------------------------
+ // Clothing colors (convenience functions to access visual parameters)
+ //--------------------------------------------------------------------
+public:
+ void setClothesColor(LLVOAvatarDefines::ETextureIndex te, const LLColor4& new_color, BOOL upload_bake);
+ LLColor4 getClothesColor(LLVOAvatarDefines::ETextureIndex te);
+ static BOOL teToColorParams(LLVOAvatarDefines::ETextureIndex te, U32 *param_name);
+
+ //--------------------------------------------------------------------
+ // Global colors
+ //--------------------------------------------------------------------
+public:
+ LLColor4 getGlobalColor(const std::string& color_name ) const;
+ void onGlobalColorChanged(const LLTexGlobalColor* global_color, BOOL upload_bake);
+private:
+ LLTexGlobalColor* mTexSkinColor;
+ LLTexGlobalColor* mTexHairColor;
+ LLTexGlobalColor* mTexEyeColor;
+
+ //--------------------------------------------------------------------
+ // Visibility
+ //--------------------------------------------------------------------
+public:
+ BOOL isVisible() const;
+ void setVisibilityRank(U32 rank);
+ U32 getVisibilityRank() const { return mVisibilityRank; } // unused
+ static S32 sNumVisibleAvatars; // Number of instances of this class
+ static LLColor4 getDummyColor();
+/** Appearance
+ ** **
+ *******************************************************************************/
+
+/********************************************************************************
+ ** **
+ ** WEARABLES
+ **/
+
+public:
+ virtual BOOL isWearingWearableType(LLWearableType::EType type ) const;
+
+ //--------------------------------------------------------------------
+ // Attachments
+ //--------------------------------------------------------------------
+public:
+ void clampAttachmentPositions();
+ virtual const LLViewerJointAttachment* attachObject(LLViewerObject *viewer_object);
+ virtual BOOL detachObject(LLViewerObject *viewer_object);
+ void cleanupAttachedMesh( LLViewerObject* pVO );
+ static LLVOAvatar* findAvatarFromAttachment(LLViewerObject* obj);
+protected:
+ LLViewerJointAttachment* getTargetAttachmentPoint(LLViewerObject* viewer_object);
+ void lazyAttach();
+ void rebuildRiggedAttachments( void );
+
+ //--------------------------------------------------------------------
+ // Map of attachment points, by ID
+ //--------------------------------------------------------------------
+public:
+ S32 getAttachmentCount(); // Warning: order(N) not order(1) // currently used only by -self
+ typedef std::map<S32, LLViewerJointAttachment*> attachment_map_t;
+ attachment_map_t mAttachmentPoints;
+ std::vector<LLPointer<LLViewerObject> > mPendingAttachment;
+
+ //--------------------------------------------------------------------
+ // HUD functions
+ //--------------------------------------------------------------------
+public:
+ BOOL hasHUDAttachment() const;
+ LLBBox getHUDBBox() const;
+ void rebuildHUD();
+ void resetHUDAttachments();
+ BOOL canAttachMoreObjects() const;
+ BOOL canAttachMoreObjects(U32 n) const;
+protected:
+ U32 getNumAttachments() const; // O(N), not O(1)
+
+/** Wearables
+ ** **
+ *******************************************************************************/
+
+/********************************************************************************
+ ** **
+ ** ACTIONS
+ **/
+
+ //--------------------------------------------------------------------
+ // Animations
+ //--------------------------------------------------------------------
+public:
+ BOOL isAnyAnimationSignaled(const LLUUID *anim_array, const S32 num_anims) const;
+ void processAnimationStateChanges();
+protected:
+ BOOL processSingleAnimationStateChange(const LLUUID &anim_id, BOOL start);
+ void resetAnimations();
+private:
+ LLTimer mAnimTimer;
+ F32 mTimeLast;
+
+ //--------------------------------------------------------------------
+ // Animation state data
+ //--------------------------------------------------------------------
+public:
+ typedef std::map<LLUUID, S32>::iterator AnimIterator;
+ std::map<LLUUID, S32> mSignaledAnimations; // requested state of Animation name/value
+ std::map<LLUUID, S32> mPlayingAnimations; // current state of Animation name/value
+
+ typedef std::multimap<LLUUID, LLUUID> AnimationSourceMap;
+ typedef AnimationSourceMap::iterator AnimSourceIterator;
+ AnimationSourceMap mAnimationSources; // object ids that triggered anim ids
+
+ //--------------------------------------------------------------------
+ // Chat
+ //--------------------------------------------------------------------
+public:
+ void addChat(const LLChat& chat);
+ void clearChat();
+ void startTyping() { mTyping = TRUE; mTypingTimer.reset(); }
+ void stopTyping() { mTyping = FALSE; }
+private:
+ BOOL mVisibleChat;
+
+ //--------------------------------------------------------------------
+ // Lip synch morphs
+ //--------------------------------------------------------------------
+private:
+ bool mLipSyncActive; // we're morphing for lip sync
+ LLVisualParam* mOohMorph; // cached pointers morphs for lip sync
+ LLVisualParam* mAahMorph; // cached pointers morphs for lip sync
+
+ //--------------------------------------------------------------------
+ // Flight
+ //--------------------------------------------------------------------
+public:
+ BOOL mInAir;
+ LLFrameTimer mTimeInAir;
+
+/** Actions
+ ** **
+ *******************************************************************************/
+
+/********************************************************************************
+ ** **
+ ** PHYSICS
+ **/
+
+private:
+ F32 mSpeedAccum; // measures speed (for diagnostics mostly).
+ BOOL mTurning; // controls hysteresis on avatar rotation
+ F32 mSpeed; // misc. animation repeated state
+
+ //--------------------------------------------------------------------
+ // Collision volumes
+ //--------------------------------------------------------------------
+public:
+ S32 mNumCollisionVolumes;
+ LLViewerJointCollisionVolume* mCollisionVolumes;
+protected:
+ BOOL allocateCollisionVolumes(U32 num);
+
+ //--------------------------------------------------------------------
+ // Dimensions
+ //--------------------------------------------------------------------
+public:
+ void resolveHeightGlobal(const LLVector3d &inPos, LLVector3d &outPos, LLVector3 &outNorm);
+ bool distanceToGround( const LLVector3d &startPoint, LLVector3d &collisionPoint, F32 distToIntersectionAlongRay );
+ void resolveHeightAgent(const LLVector3 &inPos, LLVector3 &outPos, LLVector3 &outNorm);
+ void resolveRayCollisionAgent(const LLVector3d start_pt, const LLVector3d end_pt, LLVector3d &out_pos, LLVector3 &out_norm);
+ void slamPosition(); // Slam position to transmitted position (for teleport);
+protected:
+ void computeBodySize();
+
+ //--------------------------------------------------------------------
+ // Material being stepped on
+ //--------------------------------------------------------------------
+private:
+ BOOL mStepOnLand;
+ U8 mStepMaterial;
+ LLVector3 mStepObjectVelocity;
+
+/** Physics
+ ** **
+ *******************************************************************************/
+
+/********************************************************************************
+ ** **
+ ** HIERARCHY
+ **/
+
+public:
+ virtual BOOL setParent(LLViewerObject* parent);
+ virtual void addChild(LLViewerObject *childp);
+ virtual void removeChild(LLViewerObject *childp);
+
+ //--------------------------------------------------------------------
+ // Sitting
+ //--------------------------------------------------------------------
+public:
+ void sitDown(BOOL bSitting);
+ BOOL isSitting(){return mIsSitting;}
+ void sitOnObject(LLViewerObject *sit_object);
+ void getOffObject();
+private:
+ // set this property only with LLVOAvatar::sitDown method
+ BOOL mIsSitting;
+
+/** Hierarchy
+ ** **
+ *******************************************************************************/
+
+/********************************************************************************
+ ** **
+ ** NAME
+ **/
+
+public:
+ std::string getFullname() const; // Returns "FirstName LastName"
+protected:
+ static void getAnimLabels(LLDynamicArray<std::string>* labels);
+ static void getAnimNames(LLDynamicArray<std::string>* names);
+private:
+ std::string mNameString; // UTF-8 title + name + status
+ std::string mTitle;
+ bool mNameAway;
+ bool mNameBusy;
+ bool mNameMute;
+ bool mNameAppearance;
+ bool mNameFriend;
+ bool mNameCloud;
+ F32 mNameAlpha;
+ BOOL mRenderGroupTitles;
+
+ //--------------------------------------------------------------------
+ // Display the name (then optionally fade it out)
+ //--------------------------------------------------------------------
+public:
+ LLFrameTimer mChatTimer;
+ LLPointer<LLHUDNameTag> mNameText;
+private:
+ LLFrameTimer mTimeVisible;
+ std::deque<LLChat> mChats;
+ BOOL mTyping;
+ LLFrameTimer mTypingTimer;
+
+/** Name
+ ** **
+ *******************************************************************************/
+
+/********************************************************************************
+ ** **
+ ** SOUNDS
+ **/
+
+ //--------------------------------------------------------------------
+ // Voice visualizer
+ //--------------------------------------------------------------------
+public:
+ // Responsible for detecting the user's voice signal (and when the
+ // user speaks, it puts a voice symbol over the avatar's head) and gesticulations
+ LLPointer<LLVoiceVisualizer> mVoiceVisualizer;
+ int mCurrentGesticulationLevel;
+
+ //--------------------------------------------------------------------
+ // Step sound
+ //--------------------------------------------------------------------
+protected:
+ const LLUUID& getStepSound() const;
+private:
+ // Global table of sound ids per material, and the ground
+ const static LLUUID sStepSounds[LL_MCODE_END];
+ const static LLUUID sStepSoundOnLand;
+
+ //--------------------------------------------------------------------
+ // Foot step state (for generating sounds)
+ //--------------------------------------------------------------------
+public:
+ void setFootPlane(const LLVector4 &plane) { mFootPlane = plane; }
+ LLVector4 mFootPlane;
+private:
+ BOOL mWasOnGroundLeft;
+ BOOL mWasOnGroundRight;
+
+/** Sounds
+ ** **
+ *******************************************************************************/
+
+/********************************************************************************
+ ** **
+ ** DIAGNOSTICS
+ **/
+
+ //--------------------------------------------------------------------
+ // General
+ //--------------------------------------------------------------------
+public:
+ static void dumpArchetypeXML(void*);
+ static void dumpBakedStatus();
+ const std::string getBakedStatusForPrintout() const;
+ void dumpAvatarTEs(const std::string& context) const;
+
+ static F32 sUnbakedTime; // Total seconds with >=1 unbaked avatars
+ static F32 sUnbakedUpdateTime; // Last time stats were updated (to prevent multiple updates per frame)
+ static F32 sGreyTime; // Total seconds with >=1 grey avatars
+ static F32 sGreyUpdateTime; // Last time stats were updated (to prevent multiple updates per frame)
+protected:
+ S32 getUnbakedPixelAreaRank();
+ BOOL mHasGrey;
+private:
+ F32 mMinPixelArea;
+ F32 mMaxPixelArea;
+ F32 mAdjustedPixelArea;
+ std::string mDebugText;
+
+
+ //--------------------------------------------------------------------
+ // Avatar Rez Metrics
+ //--------------------------------------------------------------------
+public:
+ F32 debugGetExistenceTimeElapsedF32() const { return mDebugExistenceTimer.getElapsedTimeF32(); }
+protected:
+ LLFrameTimer mRuthDebugTimer; // For tracking how long it takes for av to rez
+ LLFrameTimer mDebugExistenceTimer; // Debugging for how long the avatar has been in memory.
+
+/** Diagnostics
+ ** **
+ *******************************************************************************/
+
+/********************************************************************************
+ ** **
+ ** SUPPORT CLASSES
+ **/
+
+protected: // Shared with LLVOAvatarSelf
+
+ struct LLVOAvatarXmlInfo
+ {
+ LLVOAvatarXmlInfo();
+ ~LLVOAvatarXmlInfo();
+
+ BOOL parseXmlSkeletonNode(LLXmlTreeNode* root);
+ BOOL parseXmlMeshNodes(LLXmlTreeNode* root);
+ BOOL parseXmlColorNodes(LLXmlTreeNode* root);
+ BOOL parseXmlLayerNodes(LLXmlTreeNode* root);
+ BOOL parseXmlDriverNodes(LLXmlTreeNode* root);
+ BOOL parseXmlMorphNodes(LLXmlTreeNode* root);
+
+ struct LLVOAvatarMeshInfo
+ {
+ typedef std::pair<LLPolyMorphTargetInfo*,BOOL> morph_info_pair_t;
+ typedef std::vector<morph_info_pair_t> morph_info_list_t;
+
+ LLVOAvatarMeshInfo() : mLOD(0), mMinPixelArea(.1f) {}
+ ~LLVOAvatarMeshInfo()
+ {
+ morph_info_list_t::iterator iter;
+ for (iter = mPolyMorphTargetInfoList.begin(); iter != mPolyMorphTargetInfoList.end(); iter++)
+ {
+ delete iter->first;
+ }
+ mPolyMorphTargetInfoList.clear();
+ }
+
+ std::string mType;
+ S32 mLOD;
+ std::string mMeshFileName;
+ std::string mReferenceMeshName;
+ F32 mMinPixelArea;
+ morph_info_list_t mPolyMorphTargetInfoList;
+ };
+ typedef std::vector<LLVOAvatarMeshInfo*> mesh_info_list_t;
+ mesh_info_list_t mMeshInfoList;
+
+ typedef std::vector<LLPolySkeletalDistortionInfo*> skeletal_distortion_info_list_t;
+ skeletal_distortion_info_list_t mSkeletalDistortionInfoList;
+
+ struct LLVOAvatarAttachmentInfo
+ {
+ LLVOAvatarAttachmentInfo()
+ : mGroup(-1), mAttachmentID(-1), mPieMenuSlice(-1), mVisibleFirstPerson(FALSE),
+ mIsHUDAttachment(FALSE), mHasPosition(FALSE), mHasRotation(FALSE) {}
+ std::string mName;
+ std::string mJointName;
+ LLVector3 mPosition;
+ LLVector3 mRotationEuler;
+ S32 mGroup;
+ S32 mAttachmentID;
+ S32 mPieMenuSlice;
+ BOOL mVisibleFirstPerson;
+ BOOL mIsHUDAttachment;
+ BOOL mHasPosition;
+ BOOL mHasRotation;
+ };
+ typedef std::vector<LLVOAvatarAttachmentInfo*> attachment_info_list_t;
+ attachment_info_list_t mAttachmentInfoList;
+
+ LLTexGlobalColorInfo *mTexSkinColorInfo;
+ LLTexGlobalColorInfo *mTexHairColorInfo;
+ LLTexGlobalColorInfo *mTexEyeColorInfo;
+
+ typedef std::vector<LLTexLayerSetInfo*> layer_info_list_t;
+ layer_info_list_t mLayerInfoList;
+
+ typedef std::vector<LLDriverParamInfo*> driver_info_list_t;
+ driver_info_list_t mDriverInfoList;
+
+ struct LLVOAvatarMorphInfo
+ {
+ LLVOAvatarMorphInfo()
+ : mInvert(FALSE) {}
+ std::string mName;
+ std::string mRegion;
+ std::string mLayer;
+ BOOL mInvert;
+ };
+
+ typedef std::vector<LLVOAvatarMorphInfo*> morph_info_list_t;
+ morph_info_list_t mMorphMaskInfoList;
+ };
+
+ struct LLMaskedMorph
+ {
+ LLMaskedMorph(LLPolyMorphTarget *morph_target, BOOL invert, std::string layer) :
+ mMorphTarget(morph_target),
+ mInvert(invert),
+ mLayer(layer)
+ {
+ morph_target->addPendingMorphMask();
+ }
+
+ LLPolyMorphTarget *mMorphTarget;
+ BOOL mInvert;
+ std::string mLayer;
+ };
+
+/** Support classes
+ ** **
+ *******************************************************************************/
+
+}; // LLVOAvatar
+extern const F32 SELF_ADDITIONAL_PRI;
+extern const S32 MAX_TEXTURE_VIRTURE_SIZE_RESET_INTERVAL;
+
+#endif // LL_VO_AVATAR_H
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index b097ebcc1d..959bce63b1 100755
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -1,4910 +1,5037 @@
-/**
- * @file llvovolume.cpp
- * @brief LLVOVolume class implementation
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-// A "volume" is a box, cylinder, sphere, or other primitive shape.
-
-#include "llviewerprecompiledheaders.h"
-
-#include "llvovolume.h"
-
-#include <sstream>
-
-#include "llviewercontrol.h"
-#include "lldir.h"
-#include "llflexibleobject.h"
-#include "llfloatertools.h"
-#include "llmaterialtable.h"
-#include "llprimitive.h"
-#include "llvolume.h"
-#include "llvolumeoctree.h"
-#include "llvolumemgr.h"
-#include "llvolumemessage.h"
-#include "material_codes.h"
-#include "message.h"
-#include "llpluginclassmedia.h" // for code in the mediaEvent handler
-#include "object_flags.h"
-#include "llagentconstants.h"
-#include "lldrawable.h"
-#include "lldrawpoolavatar.h"
-#include "lldrawpoolbump.h"
-#include "llface.h"
-#include "llspatialpartition.h"
-#include "llhudmanager.h"
-#include "llflexibleobject.h"
-#include "llsky.h"
-#include "lltexturefetch.h"
-#include "llvector4a.h"
-#include "llviewercamera.h"
-#include "llviewertexturelist.h"
-#include "llviewerobjectlist.h"
-#include "llviewerregion.h"
-#include "llviewertextureanim.h"
-#include "llworld.h"
-#include "llselectmgr.h"
-#include "pipeline.h"
-#include "llsdutil.h"
-#include "llmatrix4a.h"
-#include "llmediaentry.h"
-#include "llmediadataclient.h"
-#include "llmeshrepository.h"
-#include "llagent.h"
-#include "llviewermediafocus.h"
-#include "lldatapacker.h"
-#include "llviewershadermgr.h"
-#include "llvoavatar.h"
-#include "llvocache.h"
-
-const S32 MIN_QUIET_FRAMES_COALESCE = 30;
-const F32 FORCE_SIMPLE_RENDER_AREA = 512.f;
-const F32 FORCE_CULL_AREA = 8.f;
-const F32 MAX_LOD_DISTANCE = 24.f;
-
-
-BOOL gAnimateTextures = TRUE;
-//extern BOOL gHideSelectedObjects;
-
-F32 LLVOVolume::sLODFactor = 1.f;
-F32 LLVOVolume::sLODSlopDistanceFactor = 0.5f; //Changing this to zero, effectively disables the LOD transition slop
-F32 LLVOVolume::sDistanceFactor = 1.0f;
-S32 LLVOVolume::sNumLODChanges = 0;
-LLPointer<LLObjectMediaDataClient> LLVOVolume::sObjectMediaClient = NULL;
-LLPointer<LLObjectMediaNavigateClient> LLVOVolume::sObjectMediaNavigateClient = NULL;
-
-static LLFastTimer::DeclareTimer FTM_GEN_TRIANGLES("Generate Triangles");
-static LLFastTimer::DeclareTimer FTM_GEN_VOLUME("Generate Volumes");
-static LLFastTimer::DeclareTimer FTM_VOLUME_TEXTURES("Volume Textures");
-
-// Implementation class of LLMediaDataClientObject. See llmediadataclient.h
-class LLMediaDataClientObjectImpl : public LLMediaDataClientObject
-{
-public:
- LLMediaDataClientObjectImpl(LLVOVolume *obj, bool isNew) : mObject(obj), mNew(isNew)
- {
- mObject->addMDCImpl();
- }
- ~LLMediaDataClientObjectImpl()
- {
- mObject->removeMDCImpl();
- }
-
- virtual U8 getMediaDataCount() const
- { return mObject->getNumTEs(); }
-
- virtual LLSD getMediaDataLLSD(U8 index) const
- {
- LLSD result;
- LLTextureEntry *te = mObject->getTE(index);
- if (NULL != te)
- {
- llassert((te->getMediaData() != NULL) == te->hasMedia());
- if (te->getMediaData() != NULL)
- {
- result = te->getMediaData()->asLLSD();
- // XXX HACK: workaround bug in asLLSD() where whitelist is not set properly
- // See DEV-41949
- if (!result.has(LLMediaEntry::WHITELIST_KEY))
- {
- result[LLMediaEntry::WHITELIST_KEY] = LLSD::emptyArray();
- }
- }
- }
- return result;
- }
- virtual bool isCurrentMediaUrl(U8 index, const std::string &url) const
- {
- LLTextureEntry *te = mObject->getTE(index);
- if (te)
- {
- if (te->getMediaData())
- {
- return (te->getMediaData()->getCurrentURL() == url);
- }
- }
- return url.empty();
- }
-
- virtual LLUUID getID() const
- { return mObject->getID(); }
-
- virtual void mediaNavigateBounceBack(U8 index)
- { mObject->mediaNavigateBounceBack(index); }
-
- virtual bool hasMedia() const
- { return mObject->hasMedia(); }
-
- virtual void updateObjectMediaData(LLSD const &data, const std::string &version_string)
- { mObject->updateObjectMediaData(data, version_string); }
-
- virtual F64 getMediaInterest() const
- {
- F64 interest = mObject->getTotalMediaInterest();
- if (interest < (F64)0.0)
- {
- // media interest not valid yet, try pixel area
- interest = mObject->getPixelArea();
- // HACK: force recalculation of pixel area if interest is the "magic default" of 1024.
- if (interest == 1024.f)
- {
- const_cast<LLVOVolume*>(static_cast<LLVOVolume*>(mObject))->setPixelAreaAndAngle(gAgent);
- interest = mObject->getPixelArea();
- }
- }
- return interest;
- }
-
- virtual bool isInterestingEnough() const
- {
- return LLViewerMedia::isInterestingEnough(mObject, getMediaInterest());
- }
-
- virtual std::string getCapabilityUrl(const std::string &name) const
- { return mObject->getRegion()->getCapability(name); }
-
- virtual bool isDead() const
- { return mObject->isDead(); }
-
- virtual U32 getMediaVersion() const
- { return LLTextureEntry::getVersionFromMediaVersionString(mObject->getMediaURL()); }
-
- virtual bool isNew() const
- { return mNew; }
-
-private:
- LLPointer<LLVOVolume> mObject;
- bool mNew;
-};
-
-
-LLVOVolume::LLVOVolume(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
- : LLViewerObject(id, pcode, regionp),
- mVolumeImpl(NULL)
-{
- mTexAnimMode = 0;
- mRelativeXform.setIdentity();
- mRelativeXformInvTrans.setIdentity();
-
- mFaceMappingChanged = FALSE;
- mLOD = MIN_LOD;
- mTextureAnimp = NULL;
- mVolumeChanged = FALSE;
- mVObjRadius = LLVector3(1,1,0.5f).length();
- mNumFaces = 0;
- mLODChanged = FALSE;
- mSculptChanged = FALSE;
- mSpotLightPriority = 0.f;
-
- mMediaImplList.resize(getNumTEs());
- mLastFetchedMediaVersion = -1;
- mIndexInTex = 0;
- mMDCImplCount = 0;
-}
-
-LLVOVolume::~LLVOVolume()
-{
- delete mTextureAnimp;
- mTextureAnimp = NULL;
- delete mVolumeImpl;
- mVolumeImpl = NULL;
-
- if(!mMediaImplList.empty())
- {
- for(U32 i = 0 ; i < mMediaImplList.size() ; i++)
- {
- if(mMediaImplList[i].notNull())
- {
- mMediaImplList[i]->removeObject(this) ;
- }
- }
- }
-}
-
-void LLVOVolume::markDead()
-{
- if (!mDead)
- {
- if(getMDCImplCount() > 0)
- {
- LLMediaDataClientObject::ptr_t obj = new LLMediaDataClientObjectImpl(const_cast<LLVOVolume*>(this), false);
- if (sObjectMediaClient) sObjectMediaClient->removeFromQueue(obj);
- if (sObjectMediaNavigateClient) sObjectMediaNavigateClient->removeFromQueue(obj);
- }
-
- // Detach all media impls from this object
- for(U32 i = 0 ; i < mMediaImplList.size() ; i++)
- {
- removeMediaImpl(i);
- }
-
- if (mSculptTexture.notNull())
- {
- mSculptTexture->removeVolume(this);
- }
- }
-
- LLViewerObject::markDead();
-}
-
-
-// static
-void LLVOVolume::initClass()
-{
- // gSavedSettings better be around
- if (gSavedSettings.getBOOL("PrimMediaMasterEnabled"))
- {
- const F32 queue_timer_delay = gSavedSettings.getF32("PrimMediaRequestQueueDelay");
- const F32 retry_timer_delay = gSavedSettings.getF32("PrimMediaRetryTimerDelay");
- const U32 max_retries = gSavedSettings.getU32("PrimMediaMaxRetries");
- const U32 max_sorted_queue_size = gSavedSettings.getU32("PrimMediaMaxSortedQueueSize");
- const U32 max_round_robin_queue_size = gSavedSettings.getU32("PrimMediaMaxRoundRobinQueueSize");
- sObjectMediaClient = new LLObjectMediaDataClient(queue_timer_delay, retry_timer_delay, max_retries,
- max_sorted_queue_size, max_round_robin_queue_size);
- sObjectMediaNavigateClient = new LLObjectMediaNavigateClient(queue_timer_delay, retry_timer_delay,
- max_retries, max_sorted_queue_size, max_round_robin_queue_size);
- }
-}
-
-// static
-void LLVOVolume::cleanupClass()
-{
- sObjectMediaClient = NULL;
- sObjectMediaNavigateClient = NULL;
-}
-
-U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys,
- void **user_data,
- U32 block_num, EObjectUpdateType update_type,
- LLDataPacker *dp)
-{
- LLColor4U color;
- const S32 teDirtyBits = (TEM_CHANGE_TEXTURE|TEM_CHANGE_COLOR|TEM_CHANGE_MEDIA);
-
- // Do base class updates...
- U32 retval = LLViewerObject::processUpdateMessage(mesgsys, user_data, block_num, update_type, dp);
-
- LLUUID sculpt_id;
- U8 sculpt_type = 0;
- if (isSculpted())
- {
- LLSculptParams *sculpt_params = (LLSculptParams *)getParameterEntry(LLNetworkData::PARAMS_SCULPT);
- sculpt_id = sculpt_params->getSculptTexture();
- sculpt_type = sculpt_params->getSculptType();
- }
-
- 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();
- }
- }
- mTexAnimMode = 0;
- mTextureAnimp->unpackTAMessage(mesgsys, block_num);
- }
- else
- {
- if (mTextureAnimp)
- {
- delete mTextureAnimp;
- mTextureAnimp = NULL;
- gPipeline.markTextured(mDrawable);
- mFaceMappingChanged = TRUE;
- mTexAnimMode = 0;
- }
- }
-
- // Unpack volume data
- LLVolumeParams volume_params;
- LLVolumeMessage::unpackVolumeParams(&volume_params, mesgsys, _PREHASH_ObjectData, block_num);
- volume_params.setSculptID(sculpt_id, sculpt_type);
-
- 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
- //
-
- S32 result = unpackTEMessage(mesgsys, _PREHASH_ObjectData, block_num);
- if (result & teDirtyBits)
- {
- updateTEData();
- }
- if (result & TEM_CHANGE_MEDIA)
- {
- retval |= MEDIA_FLAGS_CHANGED;
- }
- }
- 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;
- }
-
- volume_params.setSculptID(sculpt_id, sculpt_type);
-
- if (setVolume(volume_params, 0))
- {
- markForUpdate(TRUE);
- }
- S32 res2 = unpackTEMessage(*dp);
- if (TEM_INVALID == res2)
- {
- // There's something bogus in the data that we're unpacking.
- dp->dumpBufferToLog();
- llwarns << "Flushing cache files" << llendl;
-
- if(LLVOCache::hasInstance() && getRegion())
- {
- LLVOCache::getInstance()->removeEntry(getRegion()->getHandle()) ;
- }
-
- llwarns << "Bogus TE data in " << getID() << llendl;
- }
- else
- {
- if (res2 & teDirtyBits)
- {
- updateTEData();
- }
- if (res2 & TEM_CHANGE_MEDIA)
- {
- retval |= MEDIA_FLAGS_CHANGED;
- }
- }
-
- U32 value = dp->getPassFlags();
-
- if (value & 0x40)
- {
- if (!mTextureAnimp)
- {
- mTextureAnimp = new LLViewerTextureAnim();
- }
- else
- {
- if (!(mTextureAnimp->mMode & LLTextureAnim::SMOOTH))
- {
- mTextureAnimp->reset();
- }
- }
- mTexAnimMode = 0;
- mTextureAnimp->unpackTAMessage(*dp);
- }
- else if (mTextureAnimp)
- {
- delete mTextureAnimp;
- mTextureAnimp = NULL;
- gPipeline.markTextured(mDrawable);
- mFaceMappingChanged = TRUE;
- mTexAnimMode = 0;
- }
- }
- 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);
- S32 result = unpackTEMessage(tdp);
- if (result & teDirtyBits)
- {
- updateTEData();
- }
- if (result & TEM_CHANGE_MEDIA)
- {
- retval |= MEDIA_FLAGS_CHANGED;
- }
- }
- }
- }
- if (retval & (MEDIA_URL_REMOVED | MEDIA_URL_ADDED | MEDIA_URL_UPDATED | MEDIA_FLAGS_CHANGED))
- {
- // If only the media URL changed, and it isn't a media version URL,
- // ignore it
- if ( ! ( retval & (MEDIA_URL_ADDED | MEDIA_URL_UPDATED) &&
- mMedia && ! mMedia->mMediaURL.empty() &&
- ! LLTextureEntry::isMediaVersionString(mMedia->mMediaURL) ) )
- {
- // If the media changed at all, request new media data
- LL_DEBUGS("MediaOnAPrim") << "Media update: " << getID() << ": retval=" << retval << " Media URL: " <<
- ((mMedia) ? mMedia->mMediaURL : std::string("")) << LL_ENDL;
- requestMediaDataUpdate(retval & MEDIA_FLAGS_CHANGED);
- }
- else {
- LL_INFOS("MediaOnAPrim") << "Ignoring media update for: " << getID() << " Media URL: " <<
- ((mMedia) ? mMedia->mMediaURL : std::string("")) << LL_ENDL;
- }
- }
- // ...and clean up any media impls
- cleanUpMediaImpls();
-
- return retval;
-}
-
-
-void LLVOVolume::animateTextures()
-{
- F32 off_s = 0.f, off_t = 0.f, scale_s = 1.f, scale_t = 1.f, rot = 0.f;
- S32 result = mTextureAnimp->animateTextures(off_s, off_t, scale_s, scale_t, rot);
-
- if (result)
- {
- if (!mTexAnimMode)
- {
- mFaceMappingChanged = TRUE;
- gPipeline.markTextured(mDrawable);
- }
- mTexAnimMode = result | mTextureAnimp->mMode;
-
- S32 start=0, end=mDrawable->getNumFaces()-1;
- if (mTextureAnimp->mFace >= 0 && mTextureAnimp->mFace <= end)
- {
- start = end = mTextureAnimp->mFace;
- }
-
- for (S32 i = start; i <= end; i++)
- {
- LLFace* facep = mDrawable->getFace(i);
- if(facep->getVirtualSize() <= MIN_TEX_ANIM_SIZE && facep->mTextureMatrix) continue;
-
- const LLTextureEntry* te = facep->getTextureEntry();
-
- if (!te)
- {
- continue;
- }
-
- if (!(result & LLViewerTextureAnim::ROTATE))
- {
- te->getRotation(&rot);
- }
- if (!(result & LLViewerTextureAnim::TRANSLATE))
- {
- te->getOffset(&off_s,&off_t);
- }
- if (!(result & LLViewerTextureAnim::SCALE))
- {
- te->getScale(&scale_s, &scale_t);
- }
-
- if (!facep->mTextureMatrix)
- {
- facep->mTextureMatrix = new LLMatrix4();
- }
-
- LLMatrix4& tex_mat = *facep->mTextureMatrix;
- tex_mat.setIdentity();
- LLVector3 trans ;
-
- if(facep->isAtlasInUse())
- {
- //
- //if use atlas for animated texture
- //apply the following transform to the animation matrix.
- //
-
- F32 tcoord_xoffset = 0.f ;
- F32 tcoord_yoffset = 0.f ;
- F32 tcoord_xscale = 1.f ;
- F32 tcoord_yscale = 1.f ;
- if(facep->isAtlasInUse())
- {
- const LLVector2* tmp = facep->getTexCoordOffset() ;
- tcoord_xoffset = tmp->mV[0] ;
- tcoord_yoffset = tmp->mV[1] ;
-
- tmp = facep->getTexCoordScale() ;
- tcoord_xscale = tmp->mV[0] ;
- tcoord_yscale = tmp->mV[1] ;
- }
- trans.set(LLVector3(tcoord_xoffset + tcoord_xscale * (off_s+0.5f), tcoord_yoffset + tcoord_yscale * (off_t+0.5f), 0.f));
-
- tex_mat.translate(LLVector3(-(tcoord_xoffset + tcoord_xscale * 0.5f), -(tcoord_yoffset + tcoord_yscale * 0.5f), 0.f));
- }
- else //non atlas
- {
- trans.set(LLVector3(off_s+0.5f, off_t+0.5f, 0.f));
- tex_mat.translate(LLVector3(-0.5f, -0.5f, 0.f));
- }
-
- LLVector3 scale(scale_s, scale_t, 1.f);
- LLQuaternion quat;
- quat.setQuat(rot, 0, 0, -1.f);
-
- tex_mat.rotate(quat);
-
- LLMatrix4 mat;
- mat.initAll(scale, LLQuaternion(), LLVector3());
- tex_mat *= mat;
-
- tex_mat.translate(trans);
- }
- }
- else
- {
- if (mTexAnimMode && mTextureAnimp->mRate == 0)
- {
- U8 start, count;
-
- if (mTextureAnimp->mFace == -1)
- {
- start = 0;
- count = getNumTEs();
- }
- else
- {
- start = (U8) mTextureAnimp->mFace;
- count = 1;
- }
-
- for (S32 i = start; i < start + count; i++)
- {
- if (mTexAnimMode & LLViewerTextureAnim::TRANSLATE)
- {
- setTEOffset(i, mTextureAnimp->mOffS, mTextureAnimp->mOffT);
- }
- if (mTexAnimMode & LLViewerTextureAnim::SCALE)
- {
- setTEScale(i, mTextureAnimp->mScaleS, mTextureAnimp->mScaleT);
- }
- if (mTexAnimMode & LLViewerTextureAnim::ROTATE)
- {
- setTERotation(i, mTextureAnimp->mRot);
- }
- }
-
- gPipeline.markTextured(mDrawable);
- mFaceMappingChanged = TRUE;
- mTexAnimMode = 0;
- }
- }
-}
-BOOL LLVOVolume::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
-{
- LLViewerObject::idleUpdate(agent, world, time);
-
- static LLFastTimer::DeclareTimer ftm("Volume");
- LLFastTimer t(ftm);
-
- if (mDead || mDrawable.isNull())
- {
- return TRUE;
- }
-
- ///////////////////////
- //
- // Do texture animation stuff
- //
-
- if (mTextureAnimp && gAnimateTextures)
- {
- animateTextures();
- }
-
- // Dispatch to implementation
- if (mVolumeImpl)
- {
- mVolumeImpl->doIdleUpdate(agent, world, time);
- }
-
- const S32 MAX_ACTIVE_OBJECT_QUIET_FRAMES = 40;
-
- if (mDrawable->isActive())
- {
- if (mDrawable->isRoot() &&
- mDrawable->mQuietCount++ > MAX_ACTIVE_OBJECT_QUIET_FRAMES &&
- (!mDrawable->getParent() || !mDrawable->getParent()->isActive()))
- {
- mDrawable->makeStatic();
- }
- }
-
- return TRUE;
-}
-
-void LLVOVolume::updateTextures()
-{
- const F32 TEXTURE_AREA_REFRESH_TIME = 5.f; // seconds
- if (mTextureUpdateTimer.getElapsedTimeF32() > TEXTURE_AREA_REFRESH_TIME)
- {
- updateTextureVirtualSize();
- }
-}
-
-BOOL LLVOVolume::isVisible() const
-{
- if(mDrawable.notNull() && mDrawable->isVisible())
- {
- return TRUE ;
- }
-
- if(isAttachment())
- {
- LLViewerObject* objp = (LLViewerObject*)getParent() ;
- while(objp && !objp->isAvatar())
- {
- objp = (LLViewerObject*)objp->getParent() ;
- }
-
- return objp && objp->mDrawable.notNull() && objp->mDrawable->isVisible() ;
- }
-
- return FALSE ;
-}
-
-void LLVOVolume::updateTextureVirtualSize()
-{
- LLFastTimer ftm(FTM_VOLUME_TEXTURES);
- // Update the pixel area of all faces
-
- if(!isVisible())
- {
- return ;
- }
-
- if (!gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_SIMPLE))
- {
- return;
- }
-
- static LLCachedControl<bool> dont_load_textures(gSavedSettings,"TextureDisable");
-
- if (dont_load_textures || LLAppViewer::getTextureFetch()->mDebugPause) // || !mDrawable->isVisible())
- {
- return;
- }
-
- mTextureUpdateTimer.reset();
-
- F32 old_area = mPixelArea;
- mPixelArea = 0.f;
-
- const S32 num_faces = mDrawable->getNumFaces();
- F32 min_vsize=999999999.f, max_vsize=0.f;
- LLViewerCamera* camera = LLViewerCamera::getInstance();
- for (S32 i = 0; i < num_faces; i++)
- {
- LLFace* face = mDrawable->getFace(i);
- const LLTextureEntry *te = face->getTextureEntry();
- LLViewerTexture *imagep = face->getTexture();
- if (!imagep || !te ||
- face->mExtents[0].equals3(face->mExtents[1]))
- {
- continue;
- }
-
- F32 vsize;
- F32 old_size = face->getVirtualSize();
-
- if (isHUDAttachment())
- {
- F32 area = (F32) camera->getScreenPixelArea();
- vsize = area;
- imagep->setBoostLevel(LLViewerTexture::BOOST_HUD);
- face->setPixelArea(area); // treat as full screen
- face->setVirtualSize(vsize);
- }
- else
- {
- vsize = face->getTextureVirtualSize();
- }
-
- mPixelArea = llmax(mPixelArea, face->getPixelArea());
-
- if (face->mTextureMatrix != NULL)
- {
- if ((vsize < MIN_TEX_ANIM_SIZE && old_size > MIN_TEX_ANIM_SIZE) ||
- (vsize > MIN_TEX_ANIM_SIZE && old_size < MIN_TEX_ANIM_SIZE))
- {
- gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD, FALSE);
- }
- }
-
- if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_AREA))
- {
- if (vsize < min_vsize) min_vsize = vsize;
- if (vsize > max_vsize) max_vsize = vsize;
- }
- else if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY))
- {
- LLViewerFetchedTexture* img = LLViewerTextureManager::staticCastToFetchedTexture(imagep) ;
- if(img)
- {
- F32 pri = img->getDecodePriority();
- pri = llmax(pri, 0.0f);
- if (pri < min_vsize) min_vsize = pri;
- if (pri > max_vsize) max_vsize = pri;
- }
- }
- else if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_FACE_AREA))
- {
- F32 pri = mPixelArea;
- if (pri < min_vsize) min_vsize = pri;
- if (pri > max_vsize) max_vsize = pri;
- }
- }
-
- if (isSculpted())
- {
- LLSculptParams *sculpt_params = (LLSculptParams *)getParameterEntry(LLNetworkData::PARAMS_SCULPT);
- LLUUID id = sculpt_params->getSculptTexture();
-
- updateSculptTexture();
-
-
-
- if (mSculptTexture.notNull())
- {
- mSculptTexture->setBoostLevel(llmax((S32)mSculptTexture->getBoostLevel(),
- (S32)LLViewerTexture::BOOST_SCULPTED));
- mSculptTexture->setForSculpt() ;
-
- if(!mSculptTexture->isCachedRawImageReady())
- {
- S32 lod = llmin(mLOD, 3);
- F32 lodf = ((F32)(lod + 1.0f)/4.f);
- F32 tex_size = lodf * LLViewerTexture::sMaxSculptRez ;
- mSculptTexture->addTextureStats(2.f * tex_size * tex_size, FALSE);
-
- //if the sculpty very close to the view point, load first
- {
- LLVector3 lookAt = getPositionAgent() - camera->getOrigin();
- F32 dist = lookAt.normVec() ;
- F32 cos_angle_to_view_dir = lookAt * camera->getXAxis() ;
- mSculptTexture->setAdditionalDecodePriority(0.8f * LLFace::calcImportanceToCamera(cos_angle_to_view_dir, dist)) ;
- }
- }
-
- S32 texture_discard = mSculptTexture->getDiscardLevel(); //try to match the texture
- S32 current_discard = getVolume() ? getVolume()->getSculptLevel() : -2 ;
-
- if (texture_discard >= 0 && //texture has some data available
- (texture_discard < current_discard || //texture has more data than last rebuild
- current_discard < 0)) //no previous rebuild
- {
- gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, FALSE);
- mSculptChanged = TRUE;
- }
-
- if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_SCULPTED))
- {
- setDebugText(llformat("T%d C%d V%d\n%dx%d",
- texture_discard, current_discard, getVolume()->getSculptLevel(),
- mSculptTexture->getHeight(), mSculptTexture->getWidth()));
- }
- }
-
- }
-
- if (getLightTextureID().notNull())
- {
- LLLightImageParams* params = (LLLightImageParams*) getParameterEntry(LLNetworkData::PARAMS_LIGHT_IMAGE);
- LLUUID id = params->getLightTexture();
- mLightTexture = LLViewerTextureManager::getFetchedTexture(id);
- if (mLightTexture.notNull())
- {
- F32 rad = getLightRadius();
- mLightTexture->addTextureStats(gPipeline.calcPixelArea(getPositionAgent(),
- LLVector3(rad,rad,rad),
- *camera));
- }
- }
-
- if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_AREA))
- {
- setDebugText(llformat("%.0f:%.0f", (F32) sqrt(min_vsize),(F32) sqrt(max_vsize)));
- }
- else if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY))
- {
- setDebugText(llformat("%.0f:%.0f", (F32) sqrt(min_vsize),(F32) sqrt(max_vsize)));
- }
- else if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_FACE_AREA))
- {
- setDebugText(llformat("%.0f:%.0f", (F32) sqrt(min_vsize),(F32) sqrt(max_vsize)));
- }
-
- if (mPixelArea == 0)
- { //flexi phasing issues make this happen
- mPixelArea = old_area;
- }
-}
-
-BOOL LLVOVolume::isActive() const
-{
- return !mStatic || mTextureAnimp || (mVolumeImpl && mVolumeImpl->isActive()) ||
- (mDrawable.notNull() && mDrawable->isActive());
-}
-
-BOOL LLVOVolume::setMaterial(const U8 material)
-{
- BOOL res = LLViewerObject::setMaterial(material);
-
- return res;
-}
-
-void LLVOVolume::setTexture(const S32 face)
-{
- llassert(face < getNumTEs());
- gGL.getTexUnit(0)->bind(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_POSITION, TRUE);
- }
-}
-
-LLFace* LLVOVolume::addFace(S32 f)
-{
- const LLTextureEntry* te = getTE(f);
- LLViewerTexture* imagep = getTEImage(f);
- return mDrawable->addFace(te, imagep);
-}
-
-LLDrawable *LLVOVolume::createDrawable(LLPipeline *pipeline)
-{
- pipeline->allocDrawable(this);
-
- mDrawable->setRenderType(LLPipeline::RENDER_TYPE_VOLUME);
-
- S32 max_tes_to_set = getNumTEs();
- for (S32 i = 0; i < max_tes_to_set; i++)
- {
- addFace(i);
- }
- mNumFaces = max_tes_to_set;
-
- if (isAttachment())
- {
- mDrawable->makeActive();
- }
-
- if (getIsLight())
- {
- // Add it to the pipeline mLightSet
- gPipeline.setLight(mDrawable, TRUE);
- }
-
- updateRadius();
- bool force_update = true; // avoid non-alpha mDistance update being optimized away
- mDrawable->updateDistance(*LLViewerCamera::getInstance(), force_update);
-
- return mDrawable;
-}
-
-BOOL LLVOVolume::setVolume(const LLVolumeParams &params_in, const S32 detail, bool unique_volume)
-{
- LLVolumeParams volume_params = params_in;
-
- S32 last_lod = mVolumep.notNull() ? LLVolumeLODGroup::getVolumeDetailFromScale(mVolumep->getDetail()) : -1;
- S32 lod = mLOD;
-
- BOOL is404 = FALSE;
-
- if (isSculpted())
- {
- // if it's a mesh
- if ((volume_params.getSculptType() & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH)
- { //meshes might not have all LODs, get the force detail to best existing LOD
- LLUUID mesh_id = volume_params.getSculptID();
-
- lod = gMeshRepo.getActualMeshLOD(volume_params, lod);
- if (lod == -1)
- {
- is404 = TRUE;
- lod = 0;
- }
- }
- }
-
- // 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);
- }
- }
- }
-
- if (is404)
- {
- setIcon(LLViewerTextureManager::getFetchedTextureFromFile("icons/Inv_Mesh.png", TRUE, LLViewerTexture::BOOST_UI));
- //render prim proxy when mesh loading attempts give up
- volume_params.setSculptID(LLUUID::null, LL_SCULPT_TYPE_NONE);
-
- }
-
- if ((LLPrimitive::setVolume(volume_params, lod, (mVolumeImpl && mVolumeImpl->isVolumeUnique()))) || mSculptChanged)
- {
- mFaceMappingChanged = TRUE;
-
- if (mVolumeImpl)
- {
- mVolumeImpl->onSetVolume(volume_params, mLOD);
- }
-
- updateSculptTexture();
-
- if (isSculpted())
- {
- updateSculptTexture();
- // if it's a mesh
- if ((volume_params.getSculptType() & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH)
- {
- if (!getVolume()->isMeshAssetLoaded())
- {
- //load request not yet issued, request pipeline load this mesh
- LLUUID asset_id = volume_params.getSculptID();
- S32 available_lod = gMeshRepo.loadMesh(this, volume_params, lod, last_lod);
- if (available_lod != lod)
- {
- LLPrimitive::setVolume(volume_params, available_lod);
- }
- }
-
- }
- else // otherwise is sculptie
- {
- if (mSculptTexture.notNull())
- {
- sculpt();
- }
- }
- }
-
- return TRUE;
- }
-
- return FALSE;
-}
-
-void LLVOVolume::updateSculptTexture()
-{
- LLPointer<LLViewerFetchedTexture> old_sculpt = mSculptTexture;
-
- if (isSculpted() && !isMesh())
- {
- LLSculptParams *sculpt_params = (LLSculptParams *)getParameterEntry(LLNetworkData::PARAMS_SCULPT);
- LLUUID id = sculpt_params->getSculptTexture();
- if (id.notNull())
- {
- mSculptTexture = LLViewerTextureManager::getFetchedTexture(id, TRUE, LLViewerTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
- }
- }
- else
- {
- mSculptTexture = NULL;
- }
-
- if (mSculptTexture != old_sculpt)
- {
- if (old_sculpt.notNull())
- {
- old_sculpt->removeVolume(this);
- }
- if (mSculptTexture.notNull())
- {
- mSculptTexture->addVolume(this);
- }
- }
-
-}
-
-void LLVOVolume::notifyMeshLoaded()
-{
- mSculptChanged = TRUE;
- gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY, TRUE);
-}
-
-// sculpt replaces generate() for sculpted surfaces
-void LLVOVolume::sculpt()
-{
- if (mSculptTexture.notNull())
- {
- U16 sculpt_height = 0;
- U16 sculpt_width = 0;
- S8 sculpt_components = 0;
- const U8* sculpt_data = NULL;
-
- S32 discard_level = mSculptTexture->getDiscardLevel() ;
- LLImageRaw* raw_image = mSculptTexture->getCachedRawImage() ;
-
- S32 max_discard = mSculptTexture->getMaxDiscardLevel();
- if (discard_level > max_discard)
- discard_level = max_discard; // clamp to the best we can do
-
- S32 current_discard = getVolume()->getSculptLevel() ;
- if(current_discard < -2)
- {
- llwarns << "WARNING!!: Current discard of sculpty at " << current_discard
- << " is less than -2." << llendl;
-
- // corrupted volume... don't update the sculpty
- return;
- }
- else if (current_discard > MAX_DISCARD_LEVEL)
- {
- llwarns << "WARNING!!: Current discard of sculpty at " << current_discard
- << " is more than than allowed max of " << MAX_DISCARD_LEVEL << llendl;
-
- // corrupted volume... don't update the sculpty
- return;
- }
-
- if (current_discard == discard_level) // no work to do here
- return;
-
- if(!raw_image)
- {
- llassert(discard_level < 0) ;
-
- sculpt_width = 0;
- sculpt_height = 0;
- sculpt_data = NULL ;
-
- if(LLViewerTextureManager::sTesterp)
- {
- LLViewerTextureManager::sTesterp->updateGrayTextureBinding();
- }
- }
- else
- {
- sculpt_height = raw_image->getHeight();
- sculpt_width = raw_image->getWidth();
- sculpt_components = raw_image->getComponents();
-
- sculpt_data = raw_image->getData();
-
- if(LLViewerTextureManager::sTesterp)
- {
- mSculptTexture->updateBindStatsForTester() ;
- }
- }
- getVolume()->sculpt(sculpt_width, sculpt_height, sculpt_components, sculpt_data, discard_level);
-
- //notify rebuild any other VOVolumes that reference this sculpty volume
- for (S32 i = 0; i < mSculptTexture->getNumVolumes(); ++i)
- {
- LLVOVolume* volume = (*(mSculptTexture->getVolumeList()))[i];
- if (volume != this && volume->getVolume() == getVolume())
- {
- gPipeline.markRebuild(volume->mDrawable, LLDrawable::REBUILD_GEOMETRY, FALSE);
- }
- }
- }
-}
-
-S32 LLVOVolume::computeLODDetail(F32 distance, F32 radius)
-{
- S32 cur_detail;
- if (LLPipeline::sDynamicLOD)
- {
- // We've got LOD in the profile, and in the twist. Use radius.
- F32 tan_angle = (LLVOVolume::sLODFactor*radius)/distance;
- cur_detail = LLVolumeLODGroup::getDetailFromTan(llround(tan_angle, 0.01f));
- }
- else
- {
- cur_detail = llclamp((S32) (sqrtf(radius)*LLVOVolume::sLODFactor*4.f), 0, 3);
- }
- return cur_detail;
-}
-
-BOOL LLVOVolume::calcLOD()
-{
- if (mDrawable.isNull())
- {
- return FALSE;
- }
-
- S32 cur_detail = 0;
-
- F32 radius;
- F32 distance;
-
- if (mDrawable->isState(LLDrawable::RIGGED))
- {
- LLVOAvatar* avatar = getAvatar();
- distance = avatar->mDrawable->mDistanceWRTCamera;
- radius = avatar->getBinRadius();
- }
- else
- {
- distance = mDrawable->mDistanceWRTCamera;
- radius = getVolume()->mLODScaleBias.scaledVec(getScale()).length();
- }
-
- //hold onto unmodified distance for debugging
- //F32 debug_distance = distance;
-
- 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;
- }
-
- // DON'T Compensate for field of view changing on FOV zoom.
- distance *= F_PI/3.f;
-
- cur_detail = computeLODDetail(llround(distance, 0.01f),
- llround(radius, 0.01f));
-
-
- if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_LOD_INFO))
- {
- //setDebugText(llformat("%.2f:%.2f, %d", debug_distance, radius, cur_detail));
-
- setDebugText(llformat("%d", mDrawable->getFace(0)->getTextureIndex()));
- }
-
- if (cur_detail != mLOD)
- {
- mAppAngle = llround((F32) atan2( mDrawable->getRadius(), mDrawable->mDistanceWRTCamera) * RAD_TO_DEG, 0.01f);
- mLOD = cur_detail;
- return TRUE;
- }
- else
- {
- return FALSE;
- }
-}
-
-BOOL LLVOVolume::updateLOD()
-{
- if (mDrawable.isNull())
- {
- return FALSE;
- }
-
- BOOL lod_changed = calcLOD();
-
- if (lod_changed)
- {
- gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, FALSE);
- mLODChanged = TRUE;
- }
- else
- {
- F32 new_radius = getBinRadius();
- F32 old_radius = mDrawable->getBinRadius();
- if (new_radius < old_radius * 0.9f || new_radius > old_radius*1.1f)
- {
- gPipeline.markPartitionMove(mDrawable);
- }
- }
-
- lod_changed = lod_changed || LLViewerObject::updateLOD();
-
- return lod_changed;
-}
-
-BOOL LLVOVolume::setDrawableParent(LLDrawable* parentp)
-{
- if (!LLViewerObject::setDrawableParent(parentp))
- {
- // no change in drawable parent
- return FALSE;
- }
-
- if (!mDrawable->isRoot())
- {
- // rebuild vertices in parent relative space
- gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, 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);
- if (!face)
- {
- return;
- }
-
- 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);
- }
- }
-}
-
-BOOL LLVOVolume::setParent(LLViewerObject* parent)
-{
- BOOL ret = FALSE ;
- if (parent != getParent())
- {
- ret = LLViewerObject::setParent(parent);
- if (ret && mDrawable)
- {
- gPipeline.markMoved(mDrawable);
- gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
- }
- }
-
- return ret ;
-}
-
-// NOTE: regenFaces() MUST be followed by genTriangles()!
-void LLVOVolume::regenFaces()
-{
- // remove existing faces
- BOOL count_changed = mNumFaces != getNumTEs();
-
- if (count_changed)
- {
- deleteFaces();
- // add new faces
- mNumFaces = getNumTEs();
- }
-
- for (S32 i = 0; i < mNumFaces; i++)
- {
- LLFace* facep = count_changed ? addFace(i) : mDrawable->getFace(i);
- facep->setTEOffset(i);
- facep->setTexture(getTEImage(i));
- facep->setViewerObject(this);
-
- // If the face had media on it, this will have broken the link between the LLViewerMediaTexture and the face.
- // Re-establish the link.
- if((int)mMediaImplList.size() > i)
- {
- if(mMediaImplList[i])
- {
- LLViewerMediaTexture* media_tex = LLViewerTextureManager::findMediaTexture(mMediaImplList[i]->getMediaTextureID()) ;
- if(media_tex)
- {
- media_tex->addMediaToFace(facep) ;
- }
- }
- }
- }
-
- if (!count_changed)
- {
- updateFaceFlags();
- }
-}
-
-BOOL LLVOVolume::genBBoxes(BOOL force_global)
-{
- BOOL res = TRUE;
-
- LLVector4a min,max;
-
- min.clear();
- max.clear();
-
- BOOL rebuild = mDrawable->isState(LLDrawable::REBUILD_VOLUME | LLDrawable::REBUILD_POSITION | LLDrawable::REBUILD_RIGGED);
-
-// bool rigged = false;
- LLVolume* volume = mRiggedVolume;
- if (!volume)
- {
- volume = getVolume();
- }
-
- for (S32 i = 0; i < getVolume()->getNumVolumeFaces(); i++)
- {
- LLFace *face = mDrawable->getFace(i);
- if (!face)
- {
- continue;
- }
- res &= face->genVolumeBBoxes(*volume, i,
- mRelativeXform, mRelativeXformInvTrans,
- (mVolumeImpl && mVolumeImpl->isVolumeGlobal()) || force_global);
-
- if (rebuild)
- {
- if (i == 0)
- {
- min = face->mExtents[0];
- max = face->mExtents[1];
- }
- else
- {
- min.setMin(min, face->mExtents[0]);
- max.setMax(max, face->mExtents[1]);
- }
- }
- }
-
- if (rebuild)
- {
- mDrawable->setSpatialExtents(min,max);
- min.add(max);
- min.mul(0.5f);
- mDrawable->setPositionGroup(min);
- }
-
- updateRadius();
- mDrawable->movePartition();
-
- return res;
-}
-
-void LLVOVolume::preRebuild()
-{
- if (mVolumeImpl != NULL)
- {
- mVolumeImpl->preRebuild();
- }
-}
-
-void LLVOVolume::updateRelativeXform()
-{
- if (mVolumeImpl)
- {
- mVolumeImpl->updateRelativeXform();
- return;
- }
-
- LLDrawable* drawable = mDrawable;
-
- if (drawable->isState(LLDrawable::RIGGED) && mRiggedVolume.notNull())
- { //rigged volume (which is in agent space) is used for generating bounding boxes etc
- //inverse of render matrix should go to partition space
- mRelativeXform = getRenderMatrix();
-
- F32* dst = (F32*) mRelativeXformInvTrans.mMatrix;
- F32* src = (F32*) mRelativeXform.mMatrix;
- dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2];
- dst[3] = src[4]; dst[4] = src[5]; dst[5] = src[6];
- dst[6] = src[8]; dst[7] = src[9]; dst[8] = src[10];
-
- mRelativeXform.invert();
- mRelativeXformInvTrans.transpose();
- }
- else 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));
-
-
- // compute inverse transpose for normals
- // mRelativeXformInvTrans.setRows(x_axis, y_axis, z_axis);
- // mRelativeXformInvTrans.invert();
- // mRelativeXformInvTrans.setRows(x_axis, y_axis, z_axis);
- // grumble - invert is NOT a matrix invert, so we do it by hand:
-
- LLMatrix3 rot_inverse = LLMatrix3(~delta_rot);
-
- LLMatrix3 scale_inverse;
- scale_inverse.setRows(LLVector3(1.0, 0.0, 0.0) / delta_scale.mV[VX],
- LLVector3(0.0, 1.0, 0.0) / delta_scale.mV[VY],
- LLVector3(0.0, 0.0, 1.0) / delta_scale.mV[VZ]);
-
-
- mRelativeXformInvTrans = rot_inverse * scale_inverse;
-
- mRelativeXformInvTrans.transpose();
- }
- 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));
-
- // compute inverse transpose for normals
- LLMatrix3 rot_inverse = LLMatrix3(~rot);
-
- LLMatrix3 scale_inverse;
- scale_inverse.setRows(LLVector3(1.0, 0.0, 0.0) / scale.mV[VX],
- LLVector3(0.0, 1.0, 0.0) / scale.mV[VY],
- LLVector3(0.0, 0.0, 1.0) / scale.mV[VZ]);
-
-
- mRelativeXformInvTrans = rot_inverse * scale_inverse;
-
- mRelativeXformInvTrans.transpose();
- }
-}
-
-static LLFastTimer::DeclareTimer FTM_GEN_FLEX("Generate Flexies");
-static LLFastTimer::DeclareTimer FTM_UPDATE_PRIMITIVES("Update Primitives");
-static LLFastTimer::DeclareTimer FTM_UPDATE_RIGGED_VOLUME("Update Rigged");
-
-BOOL LLVOVolume::updateGeometry(LLDrawable *drawable)
-{
- LLFastTimer t(FTM_UPDATE_PRIMITIVES);
-
- if (mDrawable->isState(LLDrawable::REBUILD_RIGGED))
- {
- {
- LLFastTimer t(FTM_UPDATE_RIGGED_VOLUME);
- updateRiggedVolume();
- }
- genBBoxes(FALSE);
- mDrawable->clearState(LLDrawable::REBUILD_RIGGED);
- }
-
- if (mVolumeImpl != NULL)
- {
- BOOL res;
- {
- LLFastTimer t(FTM_GEN_FLEX);
- res = mVolumeImpl->doUpdateGeometry(drawable);
- }
- updateFaceFlags();
- return res;
- }
-
- dirtySpatialGroup(drawable->isState(LLDrawable::IN_REBUILD_Q1));
-
- BOOL compiled = FALSE;
-
- updateRelativeXform();
-
- if (mDrawable.isNull()) // Not sure why this is happening, but it is...
- {
- return TRUE; // No update to complete
- }
-
- if (mVolumeChanged || mFaceMappingChanged )
- {
- compiled = TRUE;
-
- if (mVolumeChanged)
- {
- LLFastTimer ftm(FTM_GEN_VOLUME);
- LLVolumeParams volume_params = getVolume()->getParams();
- setVolume(volume_params, 0);
- drawable->setState(LLDrawable::REBUILD_VOLUME);
- }
-
- {
- LLFastTimer t(FTM_GEN_TRIANGLES);
- regenFaces();
- genBBoxes(FALSE);
- }
- }
- else if ((mLODChanged) || (mSculptChanged))
- {
- LLVolume *old_volumep, *new_volumep;
- F32 old_lod, new_lod;
- S32 old_num_faces, new_num_faces ;
-
- old_volumep = getVolume();
- old_lod = old_volumep->getDetail();
- old_num_faces = old_volumep->getNumFaces() ;
- old_volumep = NULL ;
-
- {
- LLFastTimer ftm(FTM_GEN_VOLUME);
- LLVolumeParams volume_params = getVolume()->getParams();
- setVolume(volume_params, 0);
- }
-
- new_volumep = getVolume();
- new_lod = new_volumep->getDetail();
- new_num_faces = new_volumep->getNumFaces() ;
- new_volumep = NULL ;
-
- if ((new_lod != old_lod) || mSculptChanged)
- {
- compiled = TRUE;
- sNumLODChanges += new_num_faces ;
-
- drawable->setState(LLDrawable::REBUILD_VOLUME); // for face->genVolumeTriangles()
-
- {
- LLFastTimer t(FTM_GEN_TRIANGLES);
- if (new_num_faces != old_num_faces)
- {
- regenFaces();
- }
- genBBoxes(FALSE);
-
- if (mSculptChanged)
- { //changes in sculpt maps can thrash an object bounding box without
- //triggering a spatial group bounding box update -- force spatial group
- //to update bounding boxes
- LLSpatialGroup* group = mDrawable->getSpatialGroup();
- if (group)
- {
- group->unbound();
- }
- }
- }
- }
- }
- // 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(FTM_GEN_TRIANGLES);
- genBBoxes(FALSE);
- }
-
- // Update face flags
- updateFaceFlags();
-
- if(compiled)
- {
- LLPipeline::sCompiles++;
- }
-
- mVolumeChanged = FALSE;
- mLODChanged = FALSE;
- mSculptChanged = FALSE;
- mFaceMappingChanged = FALSE;
-
- return LLViewerObject::updateGeometry(drawable);
-}
-
-void LLVOVolume::updateFaceSize(S32 idx)
-{
- LLFace* facep = mDrawable->getFace(idx);
- if (idx >= getVolume()->getNumVolumeFaces())
- {
- facep->setSize(0,0, true);
- }
- else
- {
- const LLVolumeFace& vol_face = getVolume()->getVolumeFace(idx);
- facep->setSize(vol_face.mNumVertices, vol_face.mNumIndices,
- true); // <--- volume faces should be padded for 16-byte alignment
-
- }
-}
-
-BOOL LLVOVolume::isRootEdit() const
-{
- if (mParent && !((LLViewerObject*)mParent)->isAvatar())
- {
- return FALSE;
- }
- return TRUE;
-}
-
-//virtual
-void LLVOVolume::setNumTEs(const U8 num_tes)
-{
- const U8 old_num_tes = getNumTEs() ;
-
- if(old_num_tes && old_num_tes < num_tes) //new faces added
- {
- LLViewerObject::setNumTEs(num_tes) ;
-
- if(mMediaImplList.size() >= old_num_tes && mMediaImplList[old_num_tes -1].notNull())//duplicate the last media textures if exists.
- {
- mMediaImplList.resize(num_tes) ;
- const LLTextureEntry* te = getTE(old_num_tes - 1) ;
- for(U8 i = old_num_tes; i < num_tes ; i++)
- {
- setTE(i, *te) ;
- mMediaImplList[i] = mMediaImplList[old_num_tes -1] ;
- }
- mMediaImplList[old_num_tes -1]->setUpdated(TRUE) ;
- }
- }
- else if(old_num_tes > num_tes && mMediaImplList.size() > num_tes) //old faces removed
- {
- U8 end = mMediaImplList.size() ;
- for(U8 i = num_tes; i < end ; i++)
- {
- removeMediaImpl(i) ;
- }
- mMediaImplList.resize(num_tes) ;
-
- LLViewerObject::setNumTEs(num_tes) ;
- }
- else
- {
- LLViewerObject::setNumTEs(num_tes) ;
- }
-
- return ;
-}
-
-void LLVOVolume::setTEImage(const U8 te, LLViewerTexture *imagep)
-{
- BOOL changed = (mTEImages[te] != imagep);
- LLViewerObject::setTEImage(te, imagep);
- if (changed)
- {
- gPipeline.markTextured(mDrawable);
- mFaceMappingChanged = TRUE;
- }
-}
-
-S32 LLVOVolume::setTETexture(const U8 te, const LLUUID &uuid)
-{
- S32 res = LLViewerObject::setTETexture(te, uuid);
- if (res)
- {
- gPipeline.markTextured(mDrawable);
- mFaceMappingChanged = TRUE;
- }
- return res;
-}
-
-S32 LLVOVolume::setTEColor(const U8 te, const LLColor3& color)
-{
- return setTEColor(te, LLColor4(color));
-}
-
-S32 LLVOVolume::setTEColor(const U8 te, const LLColor4& color)
-{
- S32 retval = 0;
- const LLTextureEntry *tep = getTE(te);
- if (!tep)
- {
- llwarns << "No texture entry for te " << (S32)te << ", object " << mID << llendl;
- }
- else if (color != tep->getColor())
- {
- if (color.mV[3] != tep->getColor().mV[3])
- {
- gPipeline.markTextured(mDrawable);
- }
- retval = LLPrimitive::setTEColor(te, color);
- if (mDrawable.notNull() && retval)
- {
- // These should only happen on updates which are not the initial update.
- mDrawable->setState(LLDrawable::REBUILD_COLOR);
- dirtyMesh();
- }
- }
-
- return retval;
-}
-
-S32 LLVOVolume::setTEBumpmap(const U8 te, const U8 bumpmap)
-{
- S32 res = LLViewerObject::setTEBumpmap(te, bumpmap);
- if (res)
- {
- gPipeline.markTextured(mDrawable);
- mFaceMappingChanged = TRUE;
- }
- return res;
-}
-
-S32 LLVOVolume::setTETexGen(const U8 te, const U8 texgen)
-{
- S32 res = LLViewerObject::setTETexGen(te, texgen);
- if (res)
- {
- gPipeline.markTextured(mDrawable);
- mFaceMappingChanged = TRUE;
- }
- return res;
-}
-
-S32 LLVOVolume::setTEMediaTexGen(const U8 te, const U8 media)
-{
- S32 res = LLViewerObject::setTEMediaTexGen(te, media);
- if (res)
- {
- gPipeline.markTextured(mDrawable);
- mFaceMappingChanged = TRUE;
- }
- return res;
-}
-
-S32 LLVOVolume::setTEShiny(const U8 te, const U8 shiny)
-{
- S32 res = LLViewerObject::setTEShiny(te, shiny);
- if (res)
- {
- gPipeline.markTextured(mDrawable);
- mFaceMappingChanged = TRUE;
- }
- return res;
-}
-
-S32 LLVOVolume::setTEFullbright(const U8 te, const U8 fullbright)
-{
- S32 res = LLViewerObject::setTEFullbright(te, fullbright);
- if (res)
- {
- gPipeline.markTextured(mDrawable);
- mFaceMappingChanged = TRUE;
- }
- return res;
-}
-
-S32 LLVOVolume::setTEBumpShinyFullbright(const U8 te, const U8 bump)
-{
- S32 res = LLViewerObject::setTEBumpShinyFullbright(te, bump);
- if (res)
- {
- gPipeline.markTextured(mDrawable);
- mFaceMappingChanged = TRUE;
- }
- return res;
-}
-
-S32 LLVOVolume::setTEMediaFlags(const U8 te, const U8 media_flags)
-{
- S32 res = LLViewerObject::setTEMediaFlags(te, media_flags);
- if (res)
- {
- gPipeline.markTextured(mDrawable);
- mFaceMappingChanged = TRUE;
- }
- return res;
-}
-
-S32 LLVOVolume::setTEGlow(const U8 te, const F32 glow)
-{
- S32 res = LLViewerObject::setTEGlow(te, glow);
- if (res)
- {
- gPipeline.markTextured(mDrawable);
- mFaceMappingChanged = TRUE;
- }
- return res;
-}
-
-S32 LLVOVolume::setTEScale(const U8 te, const F32 s, const F32 t)
-{
- S32 res = LLViewerObject::setTEScale(te, s, t);
- if (res)
- {
- gPipeline.markTextured(mDrawable);
- mFaceMappingChanged = TRUE;
- }
- return res;
-}
-
-S32 LLVOVolume::setTEScaleS(const U8 te, const F32 s)
-{
- S32 res = LLViewerObject::setTEScaleS(te, s);
- if (res)
- {
- gPipeline.markTextured(mDrawable);
- mFaceMappingChanged = TRUE;
- }
- return res;
-}
-
-S32 LLVOVolume::setTEScaleT(const U8 te, const F32 t)
-{
- S32 res = LLViewerObject::setTEScaleT(te, t);
- if (res)
- {
- gPipeline.markTextured(mDrawable);
- mFaceMappingChanged = TRUE;
- }
- return res;
-}
-
-void LLVOVolume::updateTEData()
-{
- /*if (mDrawable.notNull())
- {
- mFaceMappingChanged = TRUE;
- gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_MATERIAL, TRUE);
- }*/
-}
-
-bool LLVOVolume::hasMedia() const
-{
- bool result = false;
- const U8 numTEs = getNumTEs();
- for (U8 i = 0; i < numTEs; i++)
- {
- const LLTextureEntry* te = getTE(i);
- if(te->hasMedia())
- {
- result = true;
- break;
- }
- }
- return result;
-}
-
-LLVector3 LLVOVolume::getApproximateFaceNormal(U8 face_id)
-{
- LLVolume* volume = getVolume();
- LLVector4a result;
- result.clear();
-
- LLVector3 ret;
-
- if (volume && face_id < volume->getNumVolumeFaces())
- {
- const LLVolumeFace& face = volume->getVolumeFace(face_id);
- for (S32 i = 0; i < (S32)face.mNumVertices; ++i)
- {
- result.add(face.mNormals[i]);
- }
-
- LLVector3 ret(result.getF32ptr());
- ret = volumeDirectionToAgent(ret);
- ret.normVec();
- }
-
- return ret;
-}
-
-void LLVOVolume::requestMediaDataUpdate(bool isNew)
-{
- if (sObjectMediaClient)
- sObjectMediaClient->fetchMedia(new LLMediaDataClientObjectImpl(this, isNew));
-}
-
-bool LLVOVolume::isMediaDataBeingFetched() const
-{
- // I know what I'm doing by const_casting this away: this is just
- // a wrapper class that is only going to do a lookup.
- return (sObjectMediaClient) ? sObjectMediaClient->isInQueue(new LLMediaDataClientObjectImpl(const_cast<LLVOVolume*>(this), false)) : false;
-}
-
-void LLVOVolume::cleanUpMediaImpls()
-{
- // Iterate through our TEs and remove any Impls that are no longer used
- const U8 numTEs = getNumTEs();
- for (U8 i = 0; i < numTEs; i++)
- {
- const LLTextureEntry* te = getTE(i);
- if( ! te->hasMedia())
- {
- // Delete the media IMPL!
- removeMediaImpl(i) ;
- }
- }
-}
-
-void LLVOVolume::updateObjectMediaData(const LLSD &media_data_array, const std::string &media_version)
-{
- // media_data_array is an array of media entry maps
- // media_version is the version string in the response.
- U32 fetched_version = LLTextureEntry::getVersionFromMediaVersionString(media_version);
-
- // Only update it if it is newer!
- if ( (S32)fetched_version > mLastFetchedMediaVersion)
- {
- mLastFetchedMediaVersion = fetched_version;
- //llinfos << "updating:" << this->getID() << " " << ll_pretty_print_sd(media_data_array) << llendl;
-
- LLSD::array_const_iterator iter = media_data_array.beginArray();
- LLSD::array_const_iterator end = media_data_array.endArray();
- U8 texture_index = 0;
- for (; iter != end; ++iter, ++texture_index)
- {
- syncMediaData(texture_index, *iter, false/*merge*/, false/*ignore_agent*/);
- }
- }
-}
-
-void LLVOVolume::syncMediaData(S32 texture_index, const LLSD &media_data, bool merge, bool ignore_agent)
-{
- if(mDead)
- {
- // If the object has been marked dead, don't process media updates.
- return;
- }
-
- LLTextureEntry *te = getTE(texture_index);
- if(!te)
- {
- return ;
- }
-
- LL_DEBUGS("MediaOnAPrim") << "BEFORE: texture_index = " << texture_index
- << " hasMedia = " << te->hasMedia() << " : "
- << ((NULL == te->getMediaData()) ? "NULL MEDIA DATA" : ll_pretty_print_sd(te->getMediaData()->asLLSD())) << llendl;
-
- std::string previous_url;
- LLMediaEntry* mep = te->getMediaData();
- if(mep)
- {
- // Save the "current url" from before the update so we can tell if
- // it changes.
- previous_url = mep->getCurrentURL();
- }
-
- if (merge)
- {
- te->mergeIntoMediaData(media_data);
- }
- else {
- // XXX Question: what if the media data is undefined LLSD, but the
- // update we got above said that we have media flags?? Here we clobber
- // that, assuming the data from the service is more up-to-date.
- te->updateMediaData(media_data);
- }
-
- mep = te->getMediaData();
- if(mep)
- {
- bool update_from_self = false;
- if (!ignore_agent)
- {
- LLUUID updating_agent = LLTextureEntry::getAgentIDFromMediaVersionString(getMediaURL());
- update_from_self = (updating_agent == gAgent.getID());
- }
- viewer_media_t media_impl = LLViewerMedia::updateMediaImpl(mep, previous_url, update_from_self);
-
- addMediaImpl(media_impl, texture_index) ;
- }
- else
- {
- removeMediaImpl(texture_index);
- }
-
- LL_DEBUGS("MediaOnAPrim") << "AFTER: texture_index = " << texture_index
- << " hasMedia = " << te->hasMedia() << " : "
- << ((NULL == te->getMediaData()) ? "NULL MEDIA DATA" : ll_pretty_print_sd(te->getMediaData()->asLLSD())) << llendl;
-}
-
-void LLVOVolume::mediaNavigateBounceBack(U8 texture_index)
-{
- // Find the media entry for this navigate
- const LLMediaEntry* mep = NULL;
- viewer_media_t impl = getMediaImpl(texture_index);
- LLTextureEntry *te = getTE(texture_index);
- if(te)
- {
- mep = te->getMediaData();
- }
-
- if (mep && impl)
- {
- std::string url = mep->getCurrentURL();
- // Look for a ":", if not there, assume "http://"
- if (!url.empty() && std::string::npos == url.find(':'))
- {
- url = "http://" + url;
- }
- // If the url we're trying to "bounce back" to is either empty or not
- // allowed by the whitelist, try the home url. If *that* doesn't work,
- // set the media as failed and unload it
- if (url.empty() || !mep->checkCandidateUrl(url))
- {
- url = mep->getHomeURL();
- // Look for a ":", if not there, assume "http://"
- if (!url.empty() && std::string::npos == url.find(':'))
- {
- url = "http://" + url;
- }
- }
- if (url.empty() || !mep->checkCandidateUrl(url))
- {
- // The url to navigate back to is not good, and we have nowhere else
- // to go.
- LL_WARNS("MediaOnAPrim") << "FAILED to bounce back URL \"" << url << "\" -- unloading impl" << LL_ENDL;
- impl->setMediaFailed(true);
- }
- else {
- // Okay, navigate now
- LL_INFOS("MediaOnAPrim") << "bouncing back to URL: " << url << LL_ENDL;
- impl->navigateTo(url, "", false, true);
- }
- }
-}
-
-bool LLVOVolume::hasMediaPermission(const LLMediaEntry* media_entry, MediaPermType perm_type)
-{
- // NOTE: This logic ALMOST duplicates the logic in the server (in particular, in llmediaservice.cpp).
- if (NULL == media_entry ) return false; // XXX should we assert here?
-
- // The agent has permissions if:
- // - world permissions are on, or
- // - group permissions are on, and agent_id is in the group, or
- // - agent permissions are on, and agent_id is the owner
-
- // *NOTE: We *used* to check for modify permissions here (i.e. permissions were
- // granted if permModify() was true). However, this doesn't make sense in the
- // viewer: we don't want to show controls or allow interaction if the author
- // has deemed it so. See DEV-42115.
-
- U8 media_perms = (perm_type == MEDIA_PERM_INTERACT) ? media_entry->getPermsInteract() : media_entry->getPermsControl();
-
- // World permissions
- if (0 != (media_perms & LLMediaEntry::PERM_ANYONE))
- {
- return true;
- }
-
- // Group permissions
- else if (0 != (media_perms & LLMediaEntry::PERM_GROUP))
- {
- LLPermissions* obj_perm = LLSelectMgr::getInstance()->findObjectPermissions(this);
- if (obj_perm && gAgent.isInGroup(obj_perm->getGroup()))
- {
- return true;
- }
- }
-
- // Owner permissions
- else if (0 != (media_perms & LLMediaEntry::PERM_OWNER) && permYouOwner())
- {
- return true;
- }
-
- return false;
-
-}
-
-void LLVOVolume::mediaNavigated(LLViewerMediaImpl *impl, LLPluginClassMedia* plugin, std::string new_location)
-{
- bool block_navigation = false;
- // FIXME: if/when we allow the same media impl to be used by multiple faces, the logic here will need to be fixed
- // to deal with multiple face indices.
- int face_index = getFaceIndexWithMediaImpl(impl, -1);
-
- // Find the media entry for this navigate
- LLMediaEntry* mep = NULL;
- LLTextureEntry *te = getTE(face_index);
- if(te)
- {
- mep = te->getMediaData();
- }
-
- if(mep)
- {
- if(!mep->checkCandidateUrl(new_location))
- {
- block_navigation = true;
- }
- if (!block_navigation && !hasMediaPermission(mep, MEDIA_PERM_INTERACT))
- {
- block_navigation = true;
- }
- }
- else
- {
- LL_WARNS("MediaOnAPrim") << "Couldn't find media entry!" << LL_ENDL;
- }
-
- if(block_navigation)
- {
- LL_INFOS("MediaOnAPrim") << "blocking navigate to URI " << new_location << LL_ENDL;
-
- // "bounce back" to the current URL from the media entry
- mediaNavigateBounceBack(face_index);
- }
- else if (sObjectMediaNavigateClient)
- {
-
- LL_DEBUGS("MediaOnAPrim") << "broadcasting navigate with URI " << new_location << LL_ENDL;
-
- sObjectMediaNavigateClient->navigate(new LLMediaDataClientObjectImpl(this, false), face_index, new_location);
- }
-}
-
-void LLVOVolume::mediaEvent(LLViewerMediaImpl *impl, LLPluginClassMedia* plugin, LLViewerMediaObserver::EMediaEvent event)
-{
- switch(event)
- {
-
- case LLViewerMediaObserver::MEDIA_EVENT_LOCATION_CHANGED:
- {
- switch(impl->getNavState())
- {
- case LLViewerMediaImpl::MEDIANAVSTATE_FIRST_LOCATION_CHANGED:
- {
- // This is the first location changed event after the start of a non-server-directed nav. It may need to be broadcast or bounced back.
- mediaNavigated(impl, plugin, plugin->getLocation());
- }
- break;
-
- case LLViewerMediaImpl::MEDIANAVSTATE_FIRST_LOCATION_CHANGED_SPURIOUS:
- // This navigate didn't change the current URL.
- LL_DEBUGS("MediaOnAPrim") << " NOT broadcasting navigate (spurious)" << LL_ENDL;
- break;
-
- case LLViewerMediaImpl::MEDIANAVSTATE_SERVER_FIRST_LOCATION_CHANGED:
- // This is the first location changed event after the start of a server-directed nav. Don't broadcast it.
- LL_INFOS("MediaOnAPrim") << " NOT broadcasting navigate (server-directed)" << LL_ENDL;
- break;
-
- default:
- // This is a subsequent location-changed due to a redirect. Don't broadcast.
- LL_INFOS("MediaOnAPrim") << " NOT broadcasting navigate (redirect)" << LL_ENDL;
- break;
- }
- }
- break;
-
- case LLViewerMediaObserver::MEDIA_EVENT_NAVIGATE_COMPLETE:
- {
- switch(impl->getNavState())
- {
- case LLViewerMediaImpl::MEDIANAVSTATE_COMPLETE_BEFORE_LOCATION_CHANGED:
- {
- // This is the first location changed event after the start of a non-server-directed nav. It may need to be broadcast or bounced back.
- mediaNavigated(impl, plugin, plugin->getNavigateURI());
- }
- break;
-
- case LLViewerMediaImpl::MEDIANAVSTATE_COMPLETE_BEFORE_LOCATION_CHANGED_SPURIOUS:
- // This navigate didn't change the current URL.
- LL_DEBUGS("MediaOnAPrim") << " NOT broadcasting navigate (spurious)" << LL_ENDL;
- break;
-
- case LLViewerMediaImpl::MEDIANAVSTATE_SERVER_COMPLETE_BEFORE_LOCATION_CHANGED:
- // This is the the navigate complete event from a server-directed nav. Don't broadcast it.
- LL_INFOS("MediaOnAPrim") << " NOT broadcasting navigate (server-directed)" << LL_ENDL;
- break;
-
- default:
- // For all other states, the navigate should have been handled by LOCATION_CHANGED events already.
- break;
- }
- }
- break;
-
- default:
- break;
- }
-
-}
-
-void LLVOVolume::sendMediaDataUpdate()
-{
- if (sObjectMediaClient)
- sObjectMediaClient->updateMedia(new LLMediaDataClientObjectImpl(this, false));
-}
-
-void LLVOVolume::removeMediaImpl(S32 texture_index)
-{
- if(mMediaImplList.size() <= (U32)texture_index || mMediaImplList[texture_index].isNull())
- {
- return ;
- }
-
- //make the face referencing to mMediaImplList[texture_index] to point back to the old texture.
- if(mDrawable && texture_index < mDrawable->getNumFaces())
- {
- LLFace* facep = mDrawable->getFace(texture_index) ;
- if(facep)
- {
- LLViewerMediaTexture* media_tex = LLViewerTextureManager::findMediaTexture(mMediaImplList[texture_index]->getMediaTextureID()) ;
- if(media_tex)
- {
- media_tex->removeMediaFromFace(facep) ;
- }
- }
- }
-
- //check if some other face(s) of this object reference(s)to this media impl.
- S32 i ;
- S32 end = (S32)mMediaImplList.size() ;
- for(i = 0; i < end ; i++)
- {
- if( i != texture_index && mMediaImplList[i] == mMediaImplList[texture_index])
- {
- break ;
- }
- }
-
- if(i == end) //this object does not need this media impl.
- {
- mMediaImplList[texture_index]->removeObject(this) ;
- }
-
- mMediaImplList[texture_index] = NULL ;
- return ;
-}
-
-void LLVOVolume::addMediaImpl(LLViewerMediaImpl* media_impl, S32 texture_index)
-{
- if((S32)mMediaImplList.size() < texture_index + 1)
- {
- mMediaImplList.resize(texture_index + 1) ;
- }
-
- if(mMediaImplList[texture_index].notNull())
- {
- if(mMediaImplList[texture_index] == media_impl)
- {
- return ;
- }
-
- removeMediaImpl(texture_index) ;
- }
-
- mMediaImplList[texture_index] = media_impl;
- media_impl->addObject(this) ;
-
- //add the face to show the media if it is in playing
- if(mDrawable)
- {
- LLFace* facep = mDrawable->getFace(texture_index) ;
- if(facep)
- {
- LLViewerMediaTexture* media_tex = LLViewerTextureManager::findMediaTexture(mMediaImplList[texture_index]->getMediaTextureID()) ;
- if(media_tex)
- {
- media_tex->addMediaToFace(facep) ;
- }
- }
- else //the face is not available now, start media on this face later.
- {
- media_impl->setUpdated(TRUE) ;
- }
- }
- return ;
-}
-
-viewer_media_t LLVOVolume::getMediaImpl(U8 face_id) const
-{
- if(mMediaImplList.size() > face_id)
- {
- return mMediaImplList[face_id];
- }
- return NULL;
-}
-
-F64 LLVOVolume::getTotalMediaInterest() const
-{
- // If this object is currently focused, this object has "high" interest
- if (LLViewerMediaFocus::getInstance()->getFocusedObjectID() == getID())
- return F64_MAX;
-
- F64 interest = (F64)-1.0; // means not interested;
-
- // If this object is selected, this object has "high" interest, but since
- // there can be more than one, we still add in calculated impl interest
- // XXX Sadly, 'contains()' doesn't take a const :(
- if (LLSelectMgr::getInstance()->getSelection()->contains(const_cast<LLVOVolume*>(this)))
- interest = F64_MAX / 2.0;
-
- int i = 0;
- const int end = getNumTEs();
- for ( ; i < end; ++i)
- {
- const viewer_media_t &impl = getMediaImpl(i);
- if (!impl.isNull())
- {
- if (interest == (F64)-1.0) interest = (F64)0.0;
- interest += impl->getInterest();
- }
- }
- return interest;
-}
-
-S32 LLVOVolume::getFaceIndexWithMediaImpl(const LLViewerMediaImpl* media_impl, S32 start_face_id)
-{
- S32 end = (S32)mMediaImplList.size() ;
- for(S32 face_id = start_face_id + 1; face_id < end; face_id++)
- {
- if(mMediaImplList[face_id] == media_impl)
- {
- return face_id ;
- }
- }
- return -1 ;
-}
-
-//----------------------------------------------------------------------------
-
-void LLVOVolume::setLightTextureID(LLUUID id)
-{
- if (id.notNull())
- {
- if (!hasLightTexture())
- {
- setParameterEntryInUse(LLNetworkData::PARAMS_LIGHT_IMAGE, TRUE, true);
- }
- LLLightImageParams* param_block = (LLLightImageParams*) getParameterEntry(LLNetworkData::PARAMS_LIGHT_IMAGE);
- if (param_block && param_block->getLightTexture() != id)
- {
- param_block->setLightTexture(id);
- parameterChanged(LLNetworkData::PARAMS_LIGHT_IMAGE, true);
- }
- }
- else
- {
- if (hasLightTexture())
- {
- setParameterEntryInUse(LLNetworkData::PARAMS_LIGHT_IMAGE, FALSE, true);
- mLightTexture = NULL;
- }
- }
-}
-
-void LLVOVolume::setSpotLightParams(LLVector3 params)
-{
- LLLightImageParams* param_block = (LLLightImageParams*) getParameterEntry(LLNetworkData::PARAMS_LIGHT_IMAGE);
- if (param_block && param_block->getParams() != params)
- {
- param_block->setParams(params);
- parameterChanged(LLNetworkData::PARAMS_LIGHT_IMAGE, true);
- }
-}
-
-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);
- }
- }
-}
-
-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);
- gPipeline.markTextured(mDrawable);
- mFaceMappingChanged = 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);
- }
-}
-
-LLUUID LLVOVolume::getLightTextureID() const
-{
- if (getParameterEntryInUse(LLNetworkData::PARAMS_LIGHT_IMAGE))
- {
- const LLLightImageParams *param_block = (const LLLightImageParams *)getParameterEntry(LLNetworkData::PARAMS_LIGHT_IMAGE);
- if (param_block)
- {
- return param_block->getLightTexture();
- }
- }
-
- return LLUUID::null;
-}
-
-
-LLVector3 LLVOVolume::getSpotLightParams() const
-{
- if (getParameterEntryInUse(LLNetworkData::PARAMS_LIGHT_IMAGE))
- {
- const LLLightImageParams *param_block = (const LLLightImageParams *)getParameterEntry(LLNetworkData::PARAMS_LIGHT_IMAGE);
- if (param_block)
- {
- return param_block->getParams();
- }
- }
-
- return LLVector3();
-}
-
-F32 LLVOVolume::getSpotLightPriority() const
-{
- return mSpotLightPriority;
-}
-
-void LLVOVolume::updateSpotLightPriority()
-{
- LLVector3 pos = mDrawable->getPositionAgent();
- LLVector3 at(0,0,-1);
- at *= getRenderRotation();
-
- F32 r = getLightRadius()*0.5f;
-
- pos += at * r;
-
- at = LLViewerCamera::getInstance()->getAtAxis();
-
- pos -= at * r;
-
- mSpotLightPriority = gPipeline.calcPixelArea(pos, LLVector3(r,r,r), *LLViewerCamera::getInstance());
-
- if (mLightTexture.notNull())
- {
- mLightTexture->addTextureStats(mSpotLightPriority);
- mLightTexture->setBoostLevel(LLViewerTexture::BOOST_CLOUDS);
- }
-}
-
-
-bool LLVOVolume::isLightSpotlight() const
-{
- LLLightImageParams* params = (LLLightImageParams*) getParameterEntry(LLNetworkData::PARAMS_LIGHT_IMAGE);
- if (params)
- {
- return params->isLightSpotlight();
- }
- return false;
-}
-
-
-LLViewerTexture* LLVOVolume::getLightTexture()
-{
- LLUUID id = getLightTextureID();
-
- if (id.notNull())
- {
- if (mLightTexture.isNull() || id != mLightTexture->getID())
- {
- mLightTexture = LLViewerTextureManager::getFetchedTexture(id);
- }
- }
- else
- {
- mLightTexture = NULL;
- }
-
- return mLightTexture;
-}
-
-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;
- }
-}
-
-U32 LLVOVolume::getVolumeInterfaceID() const
-{
- if (mVolumeImpl)
- {
- return mVolumeImpl->getID();
- }
-
- return 0;
-}
-
-BOOL LLVOVolume::isFlexible() const
-{
- if (getParameterEntryInUse(LLNetworkData::PARAMS_FLEXIBLE))
- {
- LLVolume* volume = getVolume();
- if (volume && volume->getParams().getPathParams().getCurveType() != LL_PCODE_PATH_FLEXIBLE)
- {
- 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::isSculpted() const
-{
- if (getParameterEntryInUse(LLNetworkData::PARAMS_SCULPT))
- {
- return TRUE;
- }
-
- return FALSE;
-}
-
-BOOL LLVOVolume::isMesh() const
-{
- if (isSculpted())
- {
- LLSculptParams *sculpt_params = (LLSculptParams *)getParameterEntry(LLNetworkData::PARAMS_SCULPT);
- U8 sculpt_type = sculpt_params->getSculptType();
-
- if ((sculpt_type & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH)
- // mesh is a mesh
- {
- return TRUE;
- }
- }
-
- return FALSE;
-}
-
-BOOL LLVOVolume::hasLightTexture() const
-{
- if (getParameterEntryInUse(LLNetworkData::PARAMS_LIGHT_IMAGE))
- {
- return TRUE;
- }
-
- return FALSE;
-}
-
-BOOL LLVOVolume::isVolumeGlobal() const
-{
- if (mVolumeImpl)
- {
- return mVolumeImpl->isVolumeGlobal() ? TRUE : FALSE;
- }
- else if (mRiggedVolume.notNull())
- {
- return TRUE;
- }
-
- 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;
-
- //transform view vector into volume space
- view_vector -= getRenderPosition();
- mDrawable->mDistanceWRTCamera = view_vector.length();
- LLQuaternion worldRot = getRenderRotation();
- view_vector = view_vector * ~worldRot;
- if (!isVolumeGlobal())
- {
- 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();
- LLMatrix4 trans_mat = mRelativeXform;
- if (mDrawable->isStatic())
- {
- trans_mat.translate(getRegion()->getOriginAgent());
- }
-
- volume->generateSilhouetteVertices(nodep->mSilhouetteVertices, nodep->mSilhouetteNormals, view_vector, trans_mat, mRelativeXformInvTrans, nodep->getTESelectMask());
-
- nodep->mSilhouetteExists = TRUE;
- }
-}
-
-void LLVOVolume::deleteFaces()
-{
- S32 face_count = mNumFaces;
- if (mDrawable.notNull())
- {
- mDrawable->deleteFaces(0, face_count);
- }
-
- mNumFaces = 0;
-}
-
-void LLVOVolume::updateRadius()
-{
- if (mDrawable.isNull())
- {
- return;
- }
-
- mVObjRadius = getScale().length();
- mDrawable->setRadius(mVObjRadius);
-}
-
-
-BOOL LLVOVolume::isAttachment() const
-{
- return mState != 0 ;
-}
-
-BOOL LLVOVolume::isHUDAttachment() const
-{
- // *NOTE: we 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();
-}
-
-// Returns a base cost and adds textures to passed in set.
-// total cost is returned value + 5 * size of the resulting set.
-// Cannot include cost of textures, as they may be re-used in linked
-// children, and cost should only be increased for unique textures -Nyx
-U32 LLVOVolume::getRenderCost(std::set<LLUUID> &textures) const
-{
- // base cost of each prim should be 10 points
- static const U32 ARC_PRIM_COST = 10;
- // per-prim costs
- static const U32 ARC_INVISI_COST = 1;
- static const U32 ARC_SHINY_COST = 1;
- static const U32 ARC_GLOW_COST = 1;
- static const U32 ARC_FLEXI_COST = 8;
- static const U32 ARC_PARTICLE_COST = 16;
- static const U32 ARC_BUMP_COST = 4;
-
- // per-face costs
- static const U32 ARC_PLANAR_COST = 1;
- static const U32 ARC_ANIM_TEX_COST = 4;
- static const U32 ARC_ALPHA_COST = 4;
-
- U32 shame = ARC_PRIM_COST;
-
- U32 invisi = 0;
- U32 shiny = 0;
- U32 glow = 0;
- U32 alpha = 0;
- U32 flexi = 0;
- U32 animtex = 0;
- U32 particles = 0;
- U32 scale = 0;
- U32 bump = 0;
- U32 planar = 0;
-
- if (isFlexible())
- {
- flexi = 1;
- }
- if (isParticleSource())
- {
- particles = 1;
- }
-
- const LLVector3& sc = getScale();
- scale += (U32) sc.mV[0] + (U32) sc.mV[1] + (U32) sc.mV[2];
-
- const LLDrawable* drawablep = mDrawable;
-
- if (isSculpted())
- {
- const LLSculptParams *sculpt_params = (LLSculptParams *) getParameterEntry(LLNetworkData::PARAMS_SCULPT);
- LLUUID sculpt_id = sculpt_params->getSculptTexture();
- textures.insert(sculpt_id);
- }
-
- for (S32 i = 0; i < drawablep->getNumFaces(); ++i)
- {
- const LLFace* face = drawablep->getFace(i);
- const LLTextureEntry* te = face->getTextureEntry();
- const LLViewerTexture* img = face->getTexture();
-
- if (img)
- {
- textures.insert(img->getID());
- }
-
- if (face->getPoolType() == LLDrawPool::POOL_ALPHA)
- {
- alpha++;
- }
- else if (img && img->getPrimaryFormat() == GL_ALPHA)
- {
- invisi = 1;
- }
-
- if (te)
- {
- if (te->getBumpmap())
- {
- bump = 1;
- }
- if (te->getShiny())
- {
- shiny = 1;
- }
- if (te->getGlow() > 0.f)
- {
- glow = 1;
- }
- if (face->mTextureMatrix != NULL)
- {
- animtex++;
- }
- if (te->getTexGen())
- {
- planar++;
- }
- }
- }
-
-
- shame += invisi * ARC_INVISI_COST;
- shame += shiny * ARC_SHINY_COST;
- shame += glow * ARC_GLOW_COST;
- shame += alpha * ARC_ALPHA_COST;
- shame += flexi * ARC_FLEXI_COST;
- shame += animtex * ARC_ANIM_TEX_COST;
- shame += particles * ARC_PARTICLE_COST;
- shame += bump * ARC_BUMP_COST;
- shame += planar * ARC_PLANAR_COST;
- shame += scale;
-
- LLViewerObject::const_child_list_t& child_list = getChildren();
- for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin();
- iter != child_list.end();
- ++iter)
- {
- const LLViewerObject* child_objectp = *iter;
- const LLDrawable* child_drawablep = child_objectp->mDrawable;
- if (child_drawablep)
- {
- const LLVOVolume* child_volumep = child_drawablep->getVOVolume();
- if (child_volumep)
- {
- shame += child_volumep->getRenderCost(textures);
- }
- }
- }
-
- return shame;
-
-}
-
-F32 LLVOVolume::getStreamingCost(S32* bytes, S32* visible_bytes)
-{
- F32 radius = getScale().length()*0.5f;
-
- if (isMesh())
- {
- LLSD& header = gMeshRepo.getMeshHeader(getVolume()->getParams().getSculptID());
-
- return LLMeshRepository::getStreamingCost(header, radius, bytes, visible_bytes, mLOD);
- }
- else
- {
- LLVolume* volume = getVolume();
- S32 counts[4];
- LLVolume::getLoDTriangleCounts(volume->getParams(), counts);
-
- LLSD header;
- header["lowest_lod"]["size"] = counts[0] * 10;
- header["low_lod"]["size"] = counts[1] * 10;
- header["medium_lod"]["size"] = counts[2] * 10;
- header["high_lod"]["size"] = counts[3] * 10;
-
- return LLMeshRepository::getStreamingCost(header, radius);
- }
-}
-
-U32 LLVOVolume::getTriangleCount()
-{
- U32 count = 0;
- LLVolume* volume = getVolume();
- if (volume)
- {
- count = volume->getNumTriangles();
- }
-
- return count;
-}
-
-U32 LLVOVolume::getHighLODTriangleCount()
-{
- U32 ret = 0;
-
- LLVolume* volume = getVolume();
-
- if (!isSculpted())
- {
- LLVolume* ref = LLPrimitive::getVolumeManager()->refVolume(volume->getParams(), 3);
- ret = ref->getNumTriangles();
- LLPrimitive::getVolumeManager()->unrefVolume(ref);
- }
- else if (isMesh())
- {
- LLVolume* ref = LLPrimitive::getVolumeManager()->refVolume(volume->getParams(), 3);
- if (!ref->isMeshAssetLoaded() || ref->getNumVolumeFaces() == 0)
- {
- gMeshRepo.loadMesh(this, volume->getParams(), LLModel::LOD_HIGH);
- }
- ret = ref->getNumTriangles();
- LLPrimitive::getVolumeManager()->unrefVolume(ref);
- }
- else
- { //default sculpts have a constant number of triangles
- ret = 31*2*31; //31 rows of 31 columns of quads for a 32x32 vertex patch
- }
-
- return ret;
-}
-
-//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::setSelected(BOOL sel)
-{
- LLViewerObject::setSelected(sel);
- if (mDrawable.notNull())
- {
- markForUpdate(TRUE);
- }
-}
-
-void LLVOVolume::updateSpatialExtents(LLVector4a& newMin, LLVector4a& newMax)
-{
-}
-
-F32 LLVOVolume::getBinRadius()
-{
- F32 radius;
-
- F32 scale = 1.f;
-
- S32 size_factor = llmax(gSavedSettings.getS32("OctreeStaticObjectSizeFactor"), 1);
- S32 attachment_size_factor = llmax(gSavedSettings.getS32("OctreeAttachmentSizeFactor"), 1);
- LLVector3 distance_factor = gSavedSettings.getVector3("OctreeDistanceFactor");
- LLVector3 alpha_distance_factor = gSavedSettings.getVector3("OctreeAlphaDistanceFactor");
- const LLVector4a* ext = mDrawable->getSpatialExtents();
-
- BOOL shrink_wrap = mDrawable->isAnimating();
- BOOL alpha_wrap = FALSE;
-
- if (!isHUDAttachment())
- {
- for (S32 i = 0; i < mDrawable->getNumFaces(); i++)
- {
- LLFace* face = mDrawable->getFace(i);
- if (face->getPoolType() == LLDrawPool::POOL_ALPHA &&
- !face->canRenderAsMask())
- {
- alpha_wrap = TRUE;
- break;
- }
- }
- }
- else
- {
- shrink_wrap = FALSE;
- }
-
- if (alpha_wrap)
- {
- LLVector3 bounds = getScale();
- radius = llmin(bounds.mV[1], bounds.mV[2]);
- radius = llmin(radius, bounds.mV[0]);
- radius *= 0.5f;
- radius *= 1.f+mDrawable->mDistanceWRTCamera*alpha_distance_factor[1];
- radius += mDrawable->mDistanceWRTCamera*alpha_distance_factor[0];
- }
- else if (shrink_wrap)
- {
- LLVector4a rad;
- rad.setSub(ext[1], ext[0]);
-
- radius = rad.getLength3().getF32()*0.5f;
- }
- else if (mDrawable->isStatic())
- {
- radius = llmax((S32) mDrawable->getRadius(), 1)*size_factor;
- radius *= 1.f + mDrawable->mDistanceWRTCamera * distance_factor[1];
- radius += mDrawable->mDistanceWRTCamera * distance_factor[0];
- }
- else if (mDrawable->getVObj()->isAttachment())
- {
- radius = llmax((S32) mDrawable->getRadius(),1)*attachment_size_factor;
- }
- else
- {
- radius = mDrawable->getRadius();
- radius *= 1.f + mDrawable->mDistanceWRTCamera * distance_factor[1];
- radius += mDrawable->mDistanceWRTCamera * distance_factor[0];
- }
-
- return llclamp(radius*scale, 0.5f, 256.f);
-}
-
-const LLVector3 LLVOVolume::getPivotPositionAgent() const
-{
- if (mVolumeImpl)
- {
- return mVolumeImpl->getPivotPosition();
- }
- return LLViewerObject::getPivotPositionAgent();
-}
-
-void LLVOVolume::onShift(const LLVector4a &shift_vector)
-{
- if (mVolumeImpl)
- {
- mVolumeImpl->onShift(shift_vector);
- }
-
- updateRelativeXform();
-}
-
-const LLMatrix4& LLVOVolume::getWorldMatrix(LLXformMatrix* xform) const
-{
- if (mVolumeImpl)
- {
- return mVolumeImpl->getWorldMatrix(xform);
- }
- return xform->getWorldMatrix();
-}
-
-LLVector3 LLVOVolume::agentPositionToVolume(const LLVector3& pos) const
-{
- LLVector3 ret = pos - getRenderPosition();
- ret = ret * ~getRenderRotation();
- LLVector3 objScale = isVolumeGlobal() ? LLVector3(1,1,1) : 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
-{
- LLVector3 ret = dir * ~getRenderRotation();
-
- LLVector3 objScale = isVolumeGlobal() ? LLVector3(1,1,1) : getScale();
- ret.scaleVec(objScale);
-
- return ret;
-}
-
-LLVector3 LLVOVolume::volumePositionToAgent(const LLVector3& dir) const
-{
- LLVector3 ret = dir;
- LLVector3 objScale = isVolumeGlobal() ? LLVector3(1,1,1) : getScale();
- ret.scaleVec(objScale);
- ret = ret * getRenderRotation();
- ret += getRenderPosition();
-
- return ret;
-}
-
-LLVector3 LLVOVolume::volumeDirectionToAgent(const LLVector3& dir) const
-{
- LLVector3 ret = dir;
- LLVector3 objScale = isVolumeGlobal() ? LLVector3(1,1,1) : getScale();
- LLVector3 invObjScale(1.f / objScale.mV[VX], 1.f / objScale.mV[VY], 1.f / objScale.mV[VZ]);
- ret.scaleVec(invObjScale);
- ret = ret * getRenderRotation();
-
- return ret;
-}
-
-
-BOOL LLVOVolume::lineSegmentIntersect(const LLVector3& start, const LLVector3& end, S32 face, BOOL pick_transparent, S32 *face_hitp,
- LLVector3* intersection,LLVector2* tex_coord, LLVector3* normal, LLVector3* bi_normal)
-
-{
- if (!mbCanSelect
- || mDrawable->isDead()
- || !gPipeline.hasRenderType(mDrawable->getRenderType()))
- {
- return FALSE;
- }
-
- BOOL ret = FALSE;
-
- LLVolume* volume = getVolume();
-
- bool transform = true;
-
- if (mDrawable->isState(LLDrawable::RIGGED))
- {
- if (LLFloater::isVisible(gFloaterTools) && getAvatar()->isSelf())
- {
- updateRiggedVolume();
- genBBoxes(FALSE);
- volume = mRiggedVolume;
- transform = false;
- }
- else
- { //cannot pick rigged attachments on other avatars or when not in build mode
- return FALSE;
- }
- }
-
- if (volume)
- {
- LLVector3 v_start, v_end, v_dir;
-
- if (transform)
- {
- v_start = agentPositionToVolume(start);
- v_end = agentPositionToVolume(end);
- }
- else
- {
- v_start = start;
- v_end = end;
- }
-
- LLVector3 p;
- LLVector3 n;
- LLVector2 tc;
- LLVector3 bn;
-
- if (intersection != NULL)
- {
- p = *intersection;
- }
-
- if (tex_coord != NULL)
- {
- tc = *tex_coord;
- }
-
- if (normal != NULL)
- {
- n = *normal;
- }
-
- if (bi_normal != NULL)
- {
- bn = *bi_normal;
- }
-
- S32 face_hit = -1;
-
- S32 start_face, end_face;
- if (face == -1)
- {
- start_face = 0;
- end_face = volume->getNumVolumeFaces();
- }
- else
- {
- start_face = face;
- end_face = face+1;
- }
-
- bool special_cursor = specialHoverCursor();
- for (S32 i = start_face; i < end_face; ++i)
- {
- if (!special_cursor && !pick_transparent && getTE(i)->getColor().mV[3] == 0.f)
- { //don't attempt to pick completely transparent faces unless
- //pick_transparent is true
- continue;
- }
-
- face_hit = volume->lineSegmentIntersect(v_start, v_end, i,
- &p, &tc, &n, &bn);
-
- if (face_hit >= 0 && mDrawable->getNumFaces() > face_hit)
- {
- LLFace* face = mDrawable->getFace(face_hit);
-
- if (pick_transparent || !face->getTexture() || !face->getTexture()->hasGLTexture() || face->getTexture()->getMask(face->surfaceToTexture(tc, p, n)))
- {
- v_end = p;
- if (face_hitp != NULL)
- {
- *face_hitp = face_hit;
- }
-
- if (intersection != NULL)
- {
- if (transform)
- {
- *intersection = volumePositionToAgent(p); // must map back to agent space
- }
- else
- {
- *intersection = p;
- }
- }
-
- if (normal != NULL)
- {
- if (transform)
- {
- *normal = volumeDirectionToAgent(n);
- }
- else
- {
- *normal = n;
- }
-
- (*normal).normVec();
- }
-
- if (bi_normal != NULL)
- {
- if (transform)
- {
- *bi_normal = volumeDirectionToAgent(bn);
- }
- else
- {
- *bi_normal = bn;
- }
- (*bi_normal).normVec();
- }
-
- if (tex_coord != NULL)
- {
- *tex_coord = tc;
- }
-
- ret = TRUE;
- }
- }
- }
- }
-
- return ret;
-}
-
-bool LLVOVolume::treatAsRigged()
-{
- return LLFloater::isVisible(gFloaterTools) &&
- isAttachment() &&
- getAvatar() &&
- getAvatar()->isSelf() &&
- mDrawable.notNull() &&
- mDrawable->isState(LLDrawable::RIGGED);
-}
-
-LLRiggedVolume* LLVOVolume::getRiggedVolume()
-{
- return mRiggedVolume;
-}
-
-void LLVOVolume::clearRiggedVolume()
-{
- if (mRiggedVolume.notNull())
- {
- mRiggedVolume = NULL;
- updateRelativeXform();
- }
-}
-
-void LLVOVolume::updateRiggedVolume()
-{
- //Update mRiggedVolume to match current animation frame of avatar.
- //Also update position/size in octree.
-
- if (!treatAsRigged())
- {
- clearRiggedVolume();
-
- return;
- }
-
- LLVolume* volume = getVolume();
-
- const LLMeshSkinInfo* skin = gMeshRepo.getSkinInfo(volume->getParams().getSculptID(), this);
-
- if (!skin)
- {
- clearRiggedVolume();
- return;
- }
-
- LLVOAvatar* avatar = getAvatar();
-
- if (!avatar)
- {
- clearRiggedVolume();
- return;
- }
-
- if (!mRiggedVolume)
- {
- LLVolumeParams p;
- mRiggedVolume = new LLRiggedVolume(p);
- updateRelativeXform();
- }
-
- mRiggedVolume->update(skin, avatar, volume);
-
-}
-
-static LLFastTimer::DeclareTimer FTM_SKIN_RIGGED("Skin");
-static LLFastTimer::DeclareTimer FTM_RIGGED_OCTREE("Octree");
-
-void LLRiggedVolume::update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, const LLVolume* volume)
-{
- bool copy = false;
- if (volume->getNumVolumeFaces() != getNumVolumeFaces())
- {
- copy = true;
- }
-
- for (S32 i = 0; i < volume->getNumVolumeFaces() && !copy; ++i)
- {
- const LLVolumeFace& src_face = volume->getVolumeFace(i);
- const LLVolumeFace& dst_face = getVolumeFace(i);
-
- if (src_face.mNumIndices != dst_face.mNumIndices ||
- src_face.mNumVertices != dst_face.mNumVertices)
- {
- copy = true;
- }
- }
-
- if (copy)
- {
- copyVolumeFaces(volume);
- }
-
- //build matrix palette
- LLMatrix4a mp[64];
- LLMatrix4* mat = (LLMatrix4*) mp;
-
- for (U32 j = 0; j < skin->mJointNames.size(); ++j)
- {
- LLJoint* joint = avatar->getJoint(skin->mJointNames[j]);
- if (joint)
- {
- mat[j] = skin->mInvBindMatrix[j];
- mat[j] *= joint->getWorldMatrix();
- }
- }
-
- for (S32 i = 0; i < volume->getNumVolumeFaces(); ++i)
- {
- const LLVolumeFace& vol_face = volume->getVolumeFace(i);
-
- LLVolumeFace& dst_face = mVolumeFaces[i];
-
- LLVector4a* weight = vol_face.mWeights;
-
- LLMatrix4a bind_shape_matrix;
- bind_shape_matrix.loadu(skin->mBindShapeMatrix);
-
- LLVector4a* pos = dst_face.mPositions;
-
- {
- LLFastTimer t(FTM_SKIN_RIGGED);
-
- for (U32 j = 0; j < dst_face.mNumVertices; ++j)
- {
- LLMatrix4a final_mat;
- final_mat.clear();
-
- S32 idx[4];
-
- LLVector4 wght;
-
- F32 scale = 0.f;
- for (U32 k = 0; k < 4; k++)
- {
- F32 w = weight[j][k];
-
- idx[k] = (S32) floorf(w);
- wght[k] = w - floorf(w);
- scale += wght[k];
- }
-
- wght *= 1.f/scale;
-
- for (U32 k = 0; k < 4; k++)
- {
- F32 w = wght[k];
-
- LLMatrix4a src;
- src.setMul(mp[idx[k]], w);
-
- final_mat.add(src);
- }
-
-
- LLVector4a& v = vol_face.mPositions[j];
- LLVector4a t;
- LLVector4a dst;
- bind_shape_matrix.affineTransform(v, t);
- final_mat.affineTransform(t, dst);
- pos[j] = dst;
- }
-
- //update bounding box
- LLVector4a& min = dst_face.mExtents[0];
- LLVector4a& max = dst_face.mExtents[1];
-
- min = pos[0];
- max = pos[1];
-
- for (U32 j = 1; j < dst_face.mNumVertices; ++j)
- {
- min.setMin(min, pos[j]);
- max.setMax(max, pos[j]);
- }
-
- dst_face.mCenter->setAdd(dst_face.mExtents[0], dst_face.mExtents[1]);
- dst_face.mCenter->mul(0.5f);
-
- }
-
- {
- LLFastTimer t(FTM_RIGGED_OCTREE);
- delete dst_face.mOctree;
- dst_face.mOctree = NULL;
-
- LLVector4a size;
- size.setSub(dst_face.mExtents[1], dst_face.mExtents[0]);
- size.splat(size.getLength3().getF32()*0.5f);
-
- dst_face.createOctree(1.f);
- }
- }
-}
-
-U32 LLVOVolume::getPartitionType() const
-{
- if (isHUDAttachment())
- {
- return LLViewerRegion::PARTITION_HUD;
- }
-
- return LLViewerRegion::PARTITION_VOLUME;
-}
-
-LLVolumePartition::LLVolumePartition()
-: LLSpatialPartition(LLVOVolume::VERTEX_DATA_MASK, TRUE, GL_DYNAMIC_DRAW_ARB)
-{
- mLODPeriod = 32;
- mDepthMask = FALSE;
- mDrawableType = LLPipeline::RENDER_TYPE_VOLUME;
- mPartitionType = LLViewerRegion::PARTITION_VOLUME;
- mSlopRatio = 0.25f;
- mBufferUsage = GL_DYNAMIC_DRAW_ARB;
-}
-
-LLVolumeBridge::LLVolumeBridge(LLDrawable* drawablep)
-: LLSpatialBridge(drawablep, TRUE, LLVOVolume::VERTEX_DATA_MASK)
-{
- mDepthMask = FALSE;
- mLODPeriod = 32;
- mDrawableType = LLPipeline::RENDER_TYPE_VOLUME;
- mPartitionType = LLViewerRegion::PARTITION_BRIDGE;
-
- mBufferUsage = GL_DYNAMIC_DRAW_ARB;
-
- mSlopRatio = 0.25f;
-}
-
-bool can_batch_texture(LLFace* facep)
-{
- if (facep->getTextureEntry()->getBumpmap())
- { //bump maps aren't worked into texture batching yet
- return false;
- }
-
- if (facep->getTexture() && facep->getTexture()->getPrimaryFormat() == GL_ALPHA)
- { //can't batch invisiprims
- return false;
- }
-
- if (facep->isState(LLFace::TEXTURE_ANIM) && facep->getVirtualSize() > MIN_TEX_ANIM_SIZE)
- { //texture animation breaks batches
- return false;
- }
-
- return true;
-}
-
-void LLVolumeGeometryManager::registerFace(LLSpatialGroup* group, LLFace* facep, U32 type)
-{
- LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
-
- if (facep->getViewerObject()->isSelected() && LLSelectMgr::getInstance()->mHideSelectedObjects)
- {
- return;
- }
-
- //add face to drawmap
- LLSpatialGroup::drawmap_elem_t& draw_vec = group->mDrawMap[type];
-
- S32 idx = draw_vec.size()-1;
-
- BOOL fullbright = (type == LLRenderPass::PASS_FULLBRIGHT) ||
- (type == LLRenderPass::PASS_INVISIBLE) ||
- (type == LLRenderPass::PASS_ALPHA && facep->isState(LLFace::FULLBRIGHT));
-
- if (!fullbright && type != LLRenderPass::PASS_GLOW && !facep->getVertexBuffer()->hasDataType(LLVertexBuffer::TYPE_NORMAL))
- {
- llwarns << "Non fullbright face has no normals!" << llendl;
- return;
- }
-
- const LLMatrix4* tex_mat = NULL;
- if (facep->isState(LLFace::TEXTURE_ANIM) && facep->getVirtualSize() > MIN_TEX_ANIM_SIZE)
- {
- tex_mat = facep->mTextureMatrix;
- }
-
- const LLMatrix4* model_mat = NULL;
-
- LLDrawable* drawable = facep->getDrawable();
- if (drawable->isActive())
- {
- model_mat = &(drawable->getRenderMatrix());
- }
- else
- {
- model_mat = &(drawable->getRegion()->mRenderMatrix);
- if (model_mat->isIdentity())
- {
- model_mat = NULL;
- }
- }
-
- U8 bump = (type == LLRenderPass::PASS_BUMP || type == LLRenderPass::PASS_POST_BUMP) ? facep->getTextureEntry()->getBumpmap() : 0;
-
- LLViewerTexture* tex = facep->getTexture();
-
- U8 index = facep->getTextureIndex();
-
- bool batchable = false;
-
- if (index < 255 && idx >= 0)
- {
- if (index < draw_vec[idx]->mTextureList.size())
- {
- if (draw_vec[idx]->mTextureList[index].isNull())
- {
- batchable = true;
- draw_vec[idx]->mTextureList[index] = tex;
- }
- else if (draw_vec[idx]->mTextureList[index] == tex)
- { //this face's texture index can be used with this batch
- batchable = true;
- }
- }
- else
- { //texture list can be expanded to fit this texture index
- batchable = true;
- }
- }
-
- U8 glow = (U8) (facep->getTextureEntry()->getGlow() * 255);
-
- if (idx >= 0 &&
- draw_vec[idx]->mVertexBuffer == facep->getVertexBuffer() &&
- draw_vec[idx]->mEnd == facep->getGeomIndex()-1 &&
- (LLPipeline::sTextureBindTest || draw_vec[idx]->mTexture == tex || batchable) &&
-#if LL_DARWIN
- draw_vec[idx]->mEnd - draw_vec[idx]->mStart + facep->getGeomCount() <= (U32) gGLManager.mGLMaxVertexRange &&
- draw_vec[idx]->mCount + facep->getIndicesCount() <= (U32) gGLManager.mGLMaxIndexRange &&
-#endif
- draw_vec[idx]->mGlowColor.mV[3] == glow &&
- draw_vec[idx]->mFullbright == fullbright &&
- draw_vec[idx]->mBump == bump &&
- draw_vec[idx]->mTextureMatrix == tex_mat &&
- draw_vec[idx]->mModelMatrix == model_mat)
- {
- draw_vec[idx]->mCount += facep->getIndicesCount();
- draw_vec[idx]->mEnd += facep->getGeomCount();
- draw_vec[idx]->mVSize = llmax(draw_vec[idx]->mVSize, facep->getVirtualSize());
-
- if (index >= draw_vec[idx]->mTextureList.size())
- {
- draw_vec[idx]->mTextureList.resize(index+1);
- draw_vec[idx]->mTextureList[index] = tex;
- }
- draw_vec[idx]->validate();
- update_min_max(draw_vec[idx]->mExtents[0], draw_vec[idx]->mExtents[1], facep->mExtents[0]);
- update_min_max(draw_vec[idx]->mExtents[0], draw_vec[idx]->mExtents[1], facep->mExtents[1]);
- }
- else
- {
- U32 start = facep->getGeomIndex();
- U32 end = start + facep->getGeomCount()-1;
- U32 offset = facep->getIndicesStart();
- U32 count = facep->getIndicesCount();
- LLPointer<LLDrawInfo> draw_info = new LLDrawInfo(start,end,count,offset, tex,
- facep->getVertexBuffer(), fullbright, bump);
- draw_info->mGroup = group;
- draw_info->mVSize = facep->getVirtualSize();
- draw_vec.push_back(draw_info);
- draw_info->mTextureMatrix = tex_mat;
- draw_info->mModelMatrix = model_mat;
- draw_info->mGlowColor.setVec(0,0,0,glow);
- if (type == LLRenderPass::PASS_ALPHA)
- { //for alpha sorting
- facep->setDrawInfo(draw_info);
- }
- draw_info->mExtents[0] = facep->mExtents[0];
- draw_info->mExtents[1] = facep->mExtents[1];
-
- if (LLPipeline::sUseTriStrips)
- {
- draw_info->mDrawMode = LLRender::TRIANGLE_STRIP;
- }
-
- if (index < 255)
- { //initialize texture list for texture batching
- draw_info->mTextureList.resize(index+1);
- draw_info->mTextureList[index] = tex;
- }
- draw_info->validate();
- }
-}
-
-void LLVolumeGeometryManager::getGeometry(LLSpatialGroup* group)
-{
-
-}
-
-static LLFastTimer::DeclareTimer FTM_REBUILD_VOLUME_VB("Volume");
-static LLFastTimer::DeclareTimer FTM_REBUILD_VBO("VBO Rebuilt");
-
-static LLDrawPoolAvatar* get_avatar_drawpool(LLViewerObject* vobj)
-{
- LLVOAvatar* avatar = vobj->getAvatar();
-
- if (avatar)
- {
- LLDrawable* drawable = avatar->mDrawable;
- if (drawable && drawable->getNumFaces() > 0)
- {
- LLFace* face = drawable->getFace(0);
- if (face)
- {
- LLDrawPool* drawpool = face->getPool();
- if (drawpool)
- {
- if (drawpool->getType() == LLDrawPool::POOL_AVATAR)
- {
- return (LLDrawPoolAvatar*) drawpool;
- }
- }
- }
- }
- }
-
- return NULL;
-}
-
-void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
-{
- if (group->changeLOD())
- {
- group->mLastUpdateDistance = group->mDistance;
- }
-
- group->mLastUpdateViewAngle = group->mViewAngle;
-
- if (!group->isState(LLSpatialGroup::GEOM_DIRTY | LLSpatialGroup::ALPHA_DIRTY))
- {
- if (group->isState(LLSpatialGroup::MESH_DIRTY) && !LLPipeline::sDelayVBUpdate)
- {
- LLFastTimer ftm(FTM_REBUILD_VBO);
- LLFastTimer ftm2(FTM_REBUILD_VOLUME_VB);
-
- rebuildMesh(group);
- }
- return;
- }
-
- group->mBuilt = 1.f;
- LLFastTimer ftm(FTM_REBUILD_VBO);
-
- LLFastTimer ftm2(FTM_REBUILD_VOLUME_VB);
-
- group->clearDrawMap();
-
- mFaceList.clear();
-
- std::vector<LLFace*> fullbright_faces;
- std::vector<LLFace*> bump_faces;
- std::vector<LLFace*> simple_faces;
-
- std::vector<LLFace*> alpha_faces;
- U32 useage = group->mSpatialPartition->mBufferUsage;
-
- U32 max_vertices = (gSavedSettings.getS32("RenderMaxVBOSize")*1024)/LLVertexBuffer::calcVertexSize(group->mSpatialPartition->mVertexDataMask);
- U32 max_total = (gSavedSettings.getS32("RenderMaxNodeSize")*1024)/LLVertexBuffer::calcVertexSize(group->mSpatialPartition->mVertexDataMask);
- max_vertices = llmin(max_vertices, (U32) 65535);
-
- U32 cur_total = 0;
-
- //get all the faces into a list
- for (LLSpatialGroup::element_iter drawable_iter = group->getData().begin(); drawable_iter != group->getData().end(); ++drawable_iter)
- {
- LLDrawable* drawablep = *drawable_iter;
-
- if (drawablep->isDead() || drawablep->isState(LLDrawable::FORCE_INVISIBLE) )
- {
- continue;
- }
-
- if (drawablep->isAnimating())
- { //fall back to stream draw for animating verts
- useage = GL_STREAM_DRAW_ARB;
- }
-
- LLVOVolume* vobj = drawablep->getVOVolume();
-
- if (vobj->isMesh() &&
- (vobj->getVolume() && !vobj->getVolume()->isMeshAssetLoaded() || !gMeshRepo.meshRezEnabled()))
- {
- continue;
- }
-
- llassert_always(vobj);
- vobj->updateTextureVirtualSize();
- vobj->preRebuild();
-
- drawablep->clearState(LLDrawable::HAS_ALPHA);
-
- bool rigged = vobj->isAttachment() &&
- vobj->isMesh() &&
- gMeshRepo.getSkinInfo(vobj->getVolume()->getParams().getSculptID(), vobj);
-
- bool bake_sunlight = LLPipeline::sBakeSunlight && drawablep->isStatic();
-
- bool is_rigged = false;
-
- //for each face
- for (S32 i = 0; i < drawablep->getNumFaces(); i++)
- {
- LLFace* facep = drawablep->getFace(i);
-
- //ALWAYS null out vertex buffer on rebuild -- if the face lands in a render
- // batch, it will recover its vertex buffer reference from the spatial group
- facep->setVertexBuffer(NULL);
-
- //sum up face verts and indices
- drawablep->updateFaceSize(i);
-
-
-
- if (rigged)
- {
- if (!facep->isState(LLFace::RIGGED))
- { //completely reset vertex buffer
- facep->clearVertexBuffer();
- }
-
- facep->setState(LLFace::RIGGED);
- is_rigged = true;
-
- //get drawpool of avatar with rigged face
- LLDrawPoolAvatar* pool = get_avatar_drawpool(vobj);
-
- //Determine if we've received skininfo that contains an
- //alternate bind matrix - if it does then apply the translational component
- //to the joints of the avatar.
- LLVOAvatar* pAvatarVO = vobj->getAvatar();
- bool pelvisGotSet = false;
-
- if ( pAvatarVO )
- {
- LLUUID currentId = vobj->getVolume()->getParams().getSculptID();
- const LLMeshSkinInfo* pSkinData = gMeshRepo.getSkinInfo( currentId, vobj );
-
- if ( pSkinData )
- {
- const int bindCnt = pSkinData->mAlternateBindMatrix.size();
- if ( bindCnt > 0 )
- {
- const int jointCnt = pSkinData->mJointNames.size();
- const F32 pelvisZOffset = pSkinData->mPelvisOffset;
- bool fullRig = (jointCnt>=20) ? true : false;
- if ( fullRig )
- {
- for ( int i=0; i<jointCnt; ++i )
- {
- std::string lookingForJoint = pSkinData->mJointNames[i].c_str();
- //llinfos<<"joint name "<<lookingForJoint.c_str()<<llendl;
- LLJoint* pJoint = pAvatarVO->getJoint( lookingForJoint );
- if ( pJoint && pJoint->getId() != currentId )
- {
- pJoint->setId( currentId );
- const LLVector3& jointPos = pSkinData->mAlternateBindMatrix[i].getTranslation();
- //Set the joint position
- pJoint->storeCurrentXform( jointPos );
- //If joint is a pelvis then handle old/new pelvis to foot values
- if ( lookingForJoint == "mPelvis" )
- {
- pJoint->storeCurrentXform( jointPos );
- if ( !pAvatarVO->hasPelvisOffset() )
- {
- pAvatarVO->setPelvisOffset( true, jointPos, pelvisZOffset );
- //Trigger to rebuild viewer AV
- pelvisGotSet = true;
- }
- }
- }
- }
- }
- }
- }
- }
- //If we've set the pelvis to a new position we need to also rebuild some information that the
- //viewer does at launch (e.g. body size etc.)
- if ( pelvisGotSet )
- {
- pAvatarVO->postPelvisSetRecalc();
- }
-
- if (pool)
- {
- const LLTextureEntry* te = facep->getTextureEntry();
-
- //remove face from old pool if it exists
- LLDrawPool* old_pool = facep->getPool();
- if (old_pool && old_pool->getType() == LLDrawPool::POOL_AVATAR)
- {
- ((LLDrawPoolAvatar*) old_pool)->removeRiggedFace(facep);
- }
-
- //add face to new pool
- LLViewerTexture* tex = facep->getTexture();
- U32 type = gPipeline.getPoolTypeFromTE(te, tex);
-
- if (type == LLDrawPool::POOL_ALPHA)
- {
- if (te->getFullbright())
- {
- pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_FULLBRIGHT_ALPHA);
- }
- else
- {
- pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_ALPHA);
- }
- }
- else if (te->getShiny())
- {
- if (te->getFullbright())
- {
- pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_FULLBRIGHT_SHINY);
- }
- else
- {
- if (LLPipeline::sRenderDeferred)
- {
- pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_SIMPLE);
- }
- else
- {
- pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_SHINY);
- }
- }
- }
- else
- {
- if (te->getFullbright())
- {
- pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_FULLBRIGHT);
- }
- else
- {
- pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_SIMPLE);
- }
- }
-
- if (te->getGlow())
- {
- pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_GLOW);
- }
-
- if (LLPipeline::sRenderDeferred)
- {
- if (type != LLDrawPool::POOL_ALPHA && !te->getFullbright())
- {
- if (te->getBumpmap())
- {
- pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_DEFERRED_BUMP);
- }
- else
- {
- pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_DEFERRED_SIMPLE);
- }
- }
- }
- }
-
- continue;
- }
- else
- {
- if (facep->isState(LLFace::RIGGED))
- { //face is not rigged but used to be, remove from rigged face pool
- LLDrawPoolAvatar* pool = (LLDrawPoolAvatar*) facep->getPool();
- if (pool)
- {
- pool->removeRiggedFace(facep);
- }
- facep->clearState(LLFace::RIGGED);
- }
- }
-
- if (cur_total > max_total || facep->getIndicesCount() <= 0 || facep->getGeomCount() <= 0)
- {
- facep->clearVertexBuffer();
- continue;
- }
-
- cur_total += facep->getGeomCount();
-
- if (facep->hasGeometry() && facep->getPixelArea() > FORCE_CULL_AREA)
- {
- const LLTextureEntry* te = facep->getTextureEntry();
- LLViewerTexture* tex = facep->getTexture();
-
- if (facep->isState(LLFace::TEXTURE_ANIM))
- {
- if (!vobj->mTexAnimMode)
- {
- facep->clearState(LLFace::TEXTURE_ANIM);
- }
- }
-
- BOOL force_simple = (facep->getPixelArea() < FORCE_SIMPLE_RENDER_AREA);
- U32 type = gPipeline.getPoolTypeFromTE(te, tex);
- if (type != LLDrawPool::POOL_ALPHA && force_simple)
- {
- type = LLDrawPool::POOL_SIMPLE;
- }
- facep->setPoolType(type);
-
- if (vobj->isHUDAttachment())
- {
- facep->setState(LLFace::FULLBRIGHT);
- }
-
- if (vobj->mTextureAnimp && vobj->mTexAnimMode)
- {
- if (vobj->mTextureAnimp->mFace <= -1)
- {
- S32 face;
- for (face = 0; face < vobj->getNumTEs(); face++)
- {
- drawablep->getFace(face)->setState(LLFace::TEXTURE_ANIM);
- }
- }
- else if (vobj->mTextureAnimp->mFace < vobj->getNumTEs())
- {
- drawablep->getFace(vobj->mTextureAnimp->mFace)->setState(LLFace::TEXTURE_ANIM);
- }
- }
-
- if (type == LLDrawPool::POOL_ALPHA)
- {
- if (facep->canRenderAsMask())
- { //can be treated as alpha mask
- simple_faces.push_back(facep);
- }
- else
- {
- drawablep->setState(LLDrawable::HAS_ALPHA);
- alpha_faces.push_back(facep);
- }
- }
- else
- {
- if (drawablep->isState(LLDrawable::REBUILD_VOLUME))
- {
- facep->mLastUpdateTime = gFrameTimeSeconds;
- }
-
- if (gPipeline.canUseWindLightShadersOnObjects()
- && LLPipeline::sRenderBump)
- {
- if (te->getBumpmap())
- { //needs normal + binormal
- bump_faces.push_back(facep);
- }
- else if (te->getShiny() || !te->getFullbright())
- { //needs normal
- simple_faces.push_back(facep);
- }
- else
- { //doesn't need normal
- facep->setState(LLFace::FULLBRIGHT);
- fullbright_faces.push_back(facep);
- }
- }
- else
- {
- if (te->getBumpmap() && LLPipeline::sRenderBump)
- { //needs normal + binormal
- bump_faces.push_back(facep);
- }
- else if ((te->getShiny() && LLPipeline::sRenderBump) ||
- !(te->getFullbright() || bake_sunlight))
- { //needs normal
- simple_faces.push_back(facep);
- }
- else
- { //doesn't need normal
- facep->setState(LLFace::FULLBRIGHT);
- fullbright_faces.push_back(facep);
- }
- }
- }
- }
- else
- { //face has no renderable geometry
- facep->clearVertexBuffer();
- }
- }
-
- if (is_rigged)
- {
- drawablep->setState(LLDrawable::RIGGED);
- }
- else
- {
- drawablep->clearState(LLDrawable::RIGGED);
- }
- }
-
- group->mBufferUsage = useage;
-
- //PROCESS NON-ALPHA FACES
- U32 simple_mask = LLVertexBuffer::MAP_TEXCOORD0 | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_COLOR;
- U32 alpha_mask = simple_mask | 0x80000000; //hack to give alpha verts their own VBO
- U32 bump_mask = LLVertexBuffer::MAP_TEXCOORD0 | LLVertexBuffer::MAP_TEXCOORD1 | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_COLOR;
- U32 fullbright_mask = LLVertexBuffer::MAP_TEXCOORD0 | LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_COLOR;
-
- bool batch_textures = LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_OBJECT) > 1;
-
- if (batch_textures)
- {
- bump_mask |= LLVertexBuffer::MAP_BINORMAL;
- genDrawInfo(group, simple_mask | LLVertexBuffer::MAP_TEXTURE_INDEX, simple_faces, FALSE, TRUE);
- genDrawInfo(group, fullbright_mask | LLVertexBuffer::MAP_TEXTURE_INDEX, fullbright_faces, FALSE, TRUE);
- genDrawInfo(group, bump_mask | LLVertexBuffer::MAP_TEXTURE_INDEX, bump_faces, FALSE, TRUE);
- genDrawInfo(group, alpha_mask | LLVertexBuffer::MAP_TEXTURE_INDEX, alpha_faces, TRUE, TRUE);
- }
- else
- {
- genDrawInfo(group, simple_mask, simple_faces);
- genDrawInfo(group, fullbright_mask, fullbright_faces);
- genDrawInfo(group, bump_mask, bump_faces, FALSE, TRUE);
- genDrawInfo(group, alpha_mask, alpha_faces, TRUE);
- }
-
-
- if (!LLPipeline::sDelayVBUpdate)
- {
- //drawables have been rebuilt, clear rebuild status
- for (LLSpatialGroup::element_iter drawable_iter = group->getData().begin(); drawable_iter != group->getData().end(); ++drawable_iter)
- {
- LLDrawable* drawablep = *drawable_iter;
- drawablep->clearState(LLDrawable::REBUILD_ALL);
- }
- }
-
- group->mLastUpdateTime = gFrameTimeSeconds;
- group->mBuilt = 1.f;
- group->clearState(LLSpatialGroup::GEOM_DIRTY | LLSpatialGroup::ALPHA_DIRTY);
-
- if (LLPipeline::sDelayVBUpdate)
- {
- group->setState(LLSpatialGroup::MESH_DIRTY | LLSpatialGroup::NEW_DRAWINFO);
- }
-
- mFaceList.clear();
-}
-
-static LLFastTimer::DeclareTimer FTM_VOLUME_GEOM("Volume Geometry");
-static LLFastTimer::DeclareTimer FTM_VOLUME_GEOM_PARTIAL("Terse Rebuild");
-
-void LLVolumeGeometryManager::rebuildMesh(LLSpatialGroup* group)
-{
- llassert(group);
- if (group && group->isState(LLSpatialGroup::MESH_DIRTY) && !group->isState(LLSpatialGroup::GEOM_DIRTY))
- {
- LLFastTimer tm(FTM_VOLUME_GEOM);
- S32 num_mapped_veretx_buffer = LLVertexBuffer::sMappedCount ;
-
- group->mBuilt = 1.f;
-
- std::set<LLVertexBuffer*> mapped_buffers;
-
- for (LLSpatialGroup::element_iter drawable_iter = group->getData().begin(); drawable_iter != group->getData().end(); ++drawable_iter)
- {
- LLFastTimer t(FTM_VOLUME_GEOM_PARTIAL);
- LLDrawable* drawablep = *drawable_iter;
-
- if (!drawablep->isDead() && drawablep->isState(LLDrawable::REBUILD_ALL) )
- {
- LLVOVolume* vobj = drawablep->getVOVolume();
- vobj->preRebuild();
-
- LLVolume* volume = vobj->getVolume();
- for (S32 i = 0; i < drawablep->getNumFaces(); ++i)
- {
- LLFace* face = drawablep->getFace(i);
- if (face)
- {
- LLVertexBuffer* buff = face->getVertexBuffer();
- if (buff)
- {
- face->getGeometryVolume(*volume, face->getTEOffset(),
- vobj->getRelativeXform(), vobj->getRelativeXformInvTrans(), face->getGeomIndex());
-
- if (buff->isLocked())
- {
- mapped_buffers.insert(buff);
- }
- }
- }
- }
-
- drawablep->clearState(LLDrawable::REBUILD_ALL);
- }
- }
-
- for (std::set<LLVertexBuffer*>::iterator iter = mapped_buffers.begin(); iter != mapped_buffers.end(); ++iter)
- {
- (*iter)->setBuffer(0);
- }
-
- // don't forget alpha
- if(group != NULL &&
- !group->mVertexBuffer.isNull() &&
- group->mVertexBuffer->isLocked())
- {
- group->mVertexBuffer->setBuffer(0);
- }
-
- //if not all buffers are unmapped
- if(num_mapped_veretx_buffer != LLVertexBuffer::sMappedCount)
- {
- llwarns << "Not all mapped vertex buffers are unmapped!" << llendl ;
- for (LLSpatialGroup::element_iter drawable_iter = group->getData().begin(); drawable_iter != group->getData().end(); ++drawable_iter)
- {
- LLDrawable* drawablep = *drawable_iter;
- for (S32 i = 0; i < drawablep->getNumFaces(); ++i)
- {
- LLFace* face = drawablep->getFace(i);
- LLVertexBuffer* buff = face->getVertexBuffer();
- if (face && buff && buff->isLocked())
- {
- buff->setBuffer(0) ;
- }
- }
- }
- }
-
- group->clearState(LLSpatialGroup::MESH_DIRTY | LLSpatialGroup::NEW_DRAWINFO);
- }
-
- llassert(!group || !group->isState(LLSpatialGroup::NEW_DRAWINFO));
-}
-
-struct CompareBatchBreakerModified
-{
- bool operator()(const LLFace* const& lhs, const LLFace* const& rhs)
- {
- const LLTextureEntry* lte = lhs->getTextureEntry();
- const LLTextureEntry* rte = rhs->getTextureEntry();
-
- if (lte->getBumpmap() != rte->getBumpmap())
- {
- return lte->getBumpmap() < rte->getBumpmap();
- }
- else if (lte->getFullbright() != rte->getFullbright())
- {
- return lte->getFullbright() < rte->getFullbright();
- }
- else if (lte->getGlow() != rte->getGlow())
- {
- return lte->getGlow() < rte->getGlow();
- }
- else
- {
- return lhs->getTexture() < rhs->getTexture();
- }
-
- }
-};
-
-void LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, std::vector<LLFace*>& faces, BOOL distance_sort, BOOL batch_textures)
-{
- //calculate maximum number of vertices to store in a single buffer
- U32 max_vertices = (gSavedSettings.getS32("RenderMaxVBOSize")*1024)/LLVertexBuffer::calcVertexSize(group->mSpatialPartition->mVertexDataMask);
- max_vertices = llmin(max_vertices, (U32) 65535);
-
- if (!distance_sort)
- {
- //sort faces by things that break batches
- std::sort(faces.begin(), faces.end(), CompareBatchBreakerModified());
- }
- else
- {
- //sort faces by distance
- std::sort(faces.begin(), faces.end(), LLFace::CompareDistanceGreater());
- }
-
- std::vector<LLFace*>::iterator face_iter = faces.begin();
-
- LLSpatialGroup::buffer_map_t buffer_map;
-
- LLViewerTexture* last_tex = NULL;
- S32 buffer_index = 0;
-
- if (distance_sort)
- {
- buffer_index = -1;
- }
-
- S32 texture_index_channels = gGLManager.mNumTextureImageUnits-1; //always reserve one for shiny for now just for simplicity
-
- if (gGLManager.mGLVersion < 3.1f)
- {
- texture_index_channels = 1;
- }
-
- if (LLPipeline::sRenderDeferred && distance_sort)
- {
- texture_index_channels = gDeferredAlphaProgram.mFeatures.mIndexedTextureChannels;
- }
-
- texture_index_channels = llmin(texture_index_channels, (S32) gSavedSettings.getU32("RenderMaxTextureIndex"));
-
-
- while (face_iter != faces.end())
- {
- //pull off next face
- LLFace* facep = *face_iter;
- LLViewerTexture* tex = facep->getTexture();
-
- if (distance_sort)
- {
- tex = NULL;
- }
-
- if (last_tex == tex)
- {
- buffer_index++;
- }
- else
- {
- last_tex = tex;
- buffer_index = 0;
- }
-
- bool bake_sunlight = LLPipeline::sBakeSunlight && facep->getDrawable()->isStatic();
-
- U32 index_count = facep->getIndicesCount();
- U32 geom_count = facep->getGeomCount();
-
- //sum up vertices needed for this render batch
- std::vector<LLFace*>::iterator i = face_iter;
- ++i;
-
- std::vector<LLViewerTexture*> texture_list;
-
- if (batch_textures)
- {
- U8 cur_tex = 0;
- facep->setTextureIndex(cur_tex);
- texture_list.push_back(tex);
-
- //if (can_batch_texture(facep))
- {
- while (i != faces.end())
- {
- facep = *i;
- if (facep->getTexture() != tex)
- {
- if (distance_sort)
- { //textures might be out of order, see if texture exists in current batch
- bool found = false;
- for (U32 tex_idx = 0; tex_idx < texture_list.size(); ++tex_idx)
- {
- if (facep->getTexture() == texture_list[tex_idx])
- {
- cur_tex = tex_idx;
- found = true;
- break;
- }
- }
-
- if (!found)
- {
- cur_tex = texture_list.size();
- }
- }
- else
- {
- cur_tex++;
- }
-
- if (!can_batch_texture(facep))
- { //face is bump mapped or has an animated texture matrix -- can't
- //batch more than 1 texture at a time
- break;
- }
-
- if (cur_tex >= texture_index_channels)
- { //cut batches when index channels are depleted
- break;
- }
-
- tex = facep->getTexture();
-
- texture_list.push_back(tex);
- }
-
- if (geom_count + facep->getGeomCount() > max_vertices)
- { //cut batches on geom count too big
- break;
- }
-
- ++i;
- index_count += facep->getIndicesCount();
- geom_count += facep->getGeomCount();
-
- facep->setTextureIndex(cur_tex);
- }
- }
-
- tex = texture_list[0];
- }
- else
- {
- while (i != faces.end() &&
- (LLPipeline::sTextureBindTest || (distance_sort || (*i)->getTexture() == tex)))
- {
- facep = *i;
-
-
- //face has no texture index
- facep->mDrawInfo = NULL;
- facep->setTextureIndex(255);
-
- if (geom_count + facep->getGeomCount() > max_vertices)
- { //cut batches on geom count too big
- break;
- }
-
- ++i;
- index_count += facep->getIndicesCount();
- geom_count += facep->getGeomCount();
- }
- }
-
- //create/delete/resize vertex buffer if needed
- LLVertexBuffer* buffer = NULL;
- LLSpatialGroup::buffer_texture_map_t::iterator found_iter = group->mBufferMap[mask].find(*face_iter);
-
- if (found_iter != group->mBufferMap[mask].end())
- {
- if ((U32) buffer_index < found_iter->second.size())
- {
- buffer = found_iter->second[buffer_index];
- }
- }
-
- if (!buffer)
- { //create new buffer if needed
- buffer = createVertexBuffer(mask,
- group->mBufferUsage);
- buffer->allocateBuffer(geom_count, index_count, TRUE);
- }
- else
- { //resize pre-existing buffer
- if (LLVertexBuffer::sEnableVBOs && buffer->getUsage() != group->mBufferUsage ||
- buffer->getTypeMask() != mask)
- {
- buffer = createVertexBuffer(mask,
- group->mBufferUsage);
- buffer->allocateBuffer(geom_count, index_count, TRUE);
- }
- else
- {
- buffer->resizeBuffer(geom_count, index_count);
- }
- }
-
- buffer_map[mask][*face_iter].push_back(buffer);
-
- //add face geometry
-
- U32 indices_index = 0;
- U16 index_offset = 0;
-
- while (face_iter < i)
- { //update face indices for new buffer
- facep = *face_iter;
- facep->setIndicesIndex(indices_index);
- facep->setGeomIndex(index_offset);
- facep->setVertexBuffer(buffer);
-
- if (batch_textures && facep->getTextureIndex() == 255)
- {
- llerrs << "Invalid texture index." << llendl;
- }
-
- {
- //for debugging, set last time face was updated vs moved
- facep->updateRebuildFlags();
-
- if (!LLPipeline::sDelayVBUpdate)
- { //copy face geometry into vertex buffer
- LLDrawable* drawablep = facep->getDrawable();
- LLVOVolume* vobj = drawablep->getVOVolume();
- LLVolume* volume = vobj->getVolume();
-
- U32 te_idx = facep->getTEOffset();
-
- facep->getGeometryVolume(*volume, te_idx,
- vobj->getRelativeXform(), vobj->getRelativeXformInvTrans(), index_offset);
- }
- }
-
- index_offset += facep->getGeomCount();
- indices_index += facep->getIndicesCount();
-
-
- //append face to appropriate render batch
-
- BOOL force_simple = facep->getPixelArea() < FORCE_SIMPLE_RENDER_AREA;
- BOOL fullbright = facep->isState(LLFace::FULLBRIGHT);
- if ((mask & LLVertexBuffer::MAP_NORMAL) == 0)
- { //paranoia check to make sure GL doesn't try to read non-existant normals
- fullbright = TRUE;
- }
-
- const LLTextureEntry* te = facep->getTextureEntry();
- tex = facep->getTexture();
-
- BOOL is_alpha = (facep->getPoolType() == LLDrawPool::POOL_ALPHA) ? TRUE : FALSE;
-
- if (is_alpha)
- {
- // can we safely treat this as an alpha mask?
- if (facep->canRenderAsMask())
- {
- if (te->getFullbright() || LLPipeline::sNoAlpha)
- {
- registerFace(group, facep, LLRenderPass::PASS_FULLBRIGHT_ALPHA_MASK);
- }
- else
- {
- registerFace(group, facep, LLRenderPass::PASS_ALPHA_MASK);
- }
- }
- else
- {
- registerFace(group, facep, LLRenderPass::PASS_ALPHA);
- }
-
- if (LLPipeline::sRenderDeferred)
- {
- registerFace(group, facep, LLRenderPass::PASS_ALPHA_SHADOW);
- }
- }
- else if (gPipeline.canUseVertexShaders()
- && group->mSpatialPartition->mPartitionType != LLViewerRegion::PARTITION_HUD
- && LLPipeline::sRenderBump
- && te->getShiny())
- { //shiny
- if (tex->getPrimaryFormat() == GL_ALPHA)
- { //invisiprim+shiny
- registerFace(group, facep, LLRenderPass::PASS_INVISI_SHINY);
- registerFace(group, facep, LLRenderPass::PASS_INVISIBLE);
- }
- else if (LLPipeline::sRenderDeferred)
- { //deferred rendering
- if (te->getFullbright())
- { //register in post deferred fullbright shiny pass
- registerFace(group, facep, LLRenderPass::PASS_FULLBRIGHT_SHINY);
- if (te->getBumpmap())
- { //register in post deferred bump pass
- registerFace(group, facep, LLRenderPass::PASS_POST_BUMP);
- }
- }
- else if (te->getBumpmap())
- { //register in deferred bump pass
- registerFace(group, facep, LLRenderPass::PASS_BUMP);
- }
- else
- { //register in deferred simple pass (deferred simple includes shiny)
- llassert(mask & LLVertexBuffer::MAP_NORMAL);
- registerFace(group, facep, LLRenderPass::PASS_SIMPLE);
- }
- }
- else if (fullbright)
- { //not deferred, register in standard fullbright shiny pass
- registerFace(group, facep, LLRenderPass::PASS_FULLBRIGHT_SHINY);
- }
- else
- { //not deferred or fullbright, register in standard shiny pass
- registerFace(group, facep, LLRenderPass::PASS_SHINY);
- }
- }
- else
- { //not alpha and not shiny
- if (!is_alpha && tex->getPrimaryFormat() == GL_ALPHA)
- { //invisiprim
- registerFace(group, facep, LLRenderPass::PASS_INVISIBLE);
- }
- else if (fullbright || bake_sunlight)
- { //fullbright
- registerFace(group, facep, LLRenderPass::PASS_FULLBRIGHT);
- if (LLPipeline::sRenderDeferred && LLPipeline::sRenderBump && te->getBumpmap())
- { //if this is the deferred render and a bump map is present, register in post deferred bump
- registerFace(group, facep, LLRenderPass::PASS_POST_BUMP);
- }
- }
- else
- {
- if (LLPipeline::sRenderDeferred && LLPipeline::sRenderBump && te->getBumpmap())
- { //non-shiny or fullbright deferred bump
- registerFace(group, facep, LLRenderPass::PASS_BUMP);
- }
- else
- { //all around simple
- llassert(mask & LLVertexBuffer::MAP_NORMAL);
- registerFace(group, facep, LLRenderPass::PASS_SIMPLE);
- }
- }
-
- //not sure why this is here -- shiny HUD attachments maybe? -- davep 5/11/2010
- if (!is_alpha && te->getShiny() && LLPipeline::sRenderBump)
- {
- registerFace(group, facep, LLRenderPass::PASS_SHINY);
- }
- }
-
- //not sure why this is here, and looks like it might cause bump mapped objects to get rendered redundantly -- davep 5/11/2010
- if (!is_alpha && !LLPipeline::sRenderDeferred)
- {
- llassert((mask & LLVertexBuffer::MAP_NORMAL) || fullbright);
- facep->setPoolType((fullbright) ? LLDrawPool::POOL_FULLBRIGHT : LLDrawPool::POOL_SIMPLE);
-
- if (!force_simple && te->getBumpmap() && LLPipeline::sRenderBump)
- {
- registerFace(group, facep, LLRenderPass::PASS_BUMP);
- }
- }
-
- if (!is_alpha && LLPipeline::sRenderGlow && te->getGlow() > 0.f)
- {
- registerFace(group, facep, LLRenderPass::PASS_GLOW);
- }
-
- ++face_iter;
- }
-
- buffer->setBuffer(0);
- }
-
- group->mBufferMap[mask].clear();
- for (LLSpatialGroup::buffer_texture_map_t::iterator i = buffer_map[mask].begin(); i != buffer_map[mask].end(); ++i)
- {
- group->mBufferMap[mask][i->first] = i->second;
- }
-}
-
-void LLGeometryManager::addGeometryCount(LLSpatialGroup* group, U32 &vertex_count, U32 &index_count)
-{
- //initialize to default usage for this partition
- U32 usage = group->mSpatialPartition->mBufferUsage;
-
- //clear off any old faces
- mFaceList.clear();
-
- //for each drawable
- for (LLSpatialGroup::element_iter drawable_iter = group->getData().begin(); drawable_iter != group->getData().end(); ++drawable_iter)
- {
- LLDrawable* drawablep = *drawable_iter;
-
- if (drawablep->isDead())
- {
- continue;
- }
-
- if (drawablep->isAnimating())
- { //fall back to stream draw for animating verts
- usage = GL_STREAM_DRAW_ARB;
- }
-
- //for each face
- for (S32 i = 0; i < drawablep->getNumFaces(); i++)
- {
- //sum up face verts and indices
- drawablep->updateFaceSize(i);
- LLFace* facep = drawablep->getFace(i);
- if (facep->hasGeometry() && facep->getPixelArea() > FORCE_CULL_AREA)
- {
- vertex_count += facep->getGeomCount();
- index_count += facep->getIndicesCount();
- llassert(facep->getIndicesCount() < 65536);
- //remember face (for sorting)
- mFaceList.push_back(facep);
- }
- else
- {
- facep->clearVertexBuffer();
- }
- }
- }
-
- group->mBufferUsage = usage;
-}
-
-LLHUDPartition::LLHUDPartition()
-{
- mPartitionType = LLViewerRegion::PARTITION_HUD;
- mDrawableType = LLPipeline::RENDER_TYPE_HUD;
- mSlopRatio = 0.f;
- mLODPeriod = 1;
-}
-
-void LLHUDPartition::shift(const LLVector4a &offset)
-{
- //HUD objects don't shift with region crossing. That would be silly.
-}
-
-
+/**
+ * @file llvovolume.cpp
+ * @brief LLVOVolume class implementation
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+// A "volume" is a box, cylinder, sphere, or other primitive shape.
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llvovolume.h"
+
+#include <sstream>
+
+#include "llviewercontrol.h"
+#include "lldir.h"
+#include "llflexibleobject.h"
+#include "llfloatertools.h"
+#include "llmaterialtable.h"
+#include "llprimitive.h"
+#include "llvolume.h"
+#include "llvolumeoctree.h"
+#include "llvolumemgr.h"
+#include "llvolumemessage.h"
+#include "material_codes.h"
+#include "message.h"
+#include "llpluginclassmedia.h" // for code in the mediaEvent handler
+#include "object_flags.h"
+#include "llagentconstants.h"
+#include "lldrawable.h"
+#include "lldrawpoolavatar.h"
+#include "lldrawpoolbump.h"
+#include "llface.h"
+#include "llspatialpartition.h"
+#include "llhudmanager.h"
+#include "llflexibleobject.h"
+#include "llsky.h"
+#include "lltexturefetch.h"
+#include "llvector4a.h"
+#include "llviewercamera.h"
+#include "llviewertexturelist.h"
+#include "llviewerobjectlist.h"
+#include "llviewerregion.h"
+#include "llviewertextureanim.h"
+#include "llworld.h"
+#include "llselectmgr.h"
+#include "pipeline.h"
+#include "llsdutil.h"
+#include "llmatrix4a.h"
+#include "llmediaentry.h"
+#include "llmediadataclient.h"
+#include "llmeshrepository.h"
+#include "llagent.h"
+#include "llviewermediafocus.h"
+#include "lldatapacker.h"
+#include "llviewershadermgr.h"
+#include "llvoavatar.h"
+#include "llvocache.h"
+
+const S32 MIN_QUIET_FRAMES_COALESCE = 30;
+const F32 FORCE_SIMPLE_RENDER_AREA = 512.f;
+const F32 FORCE_CULL_AREA = 8.f;
+const F32 MAX_LOD_DISTANCE = 24.f;
+
+
+BOOL gAnimateTextures = TRUE;
+//extern BOOL gHideSelectedObjects;
+
+F32 LLVOVolume::sLODFactor = 1.f;
+F32 LLVOVolume::sLODSlopDistanceFactor = 0.5f; //Changing this to zero, effectively disables the LOD transition slop
+F32 LLVOVolume::sDistanceFactor = 1.0f;
+S32 LLVOVolume::sNumLODChanges = 0;
+S32 LLVOVolume::mRenderComplexity_last = 0;
+S32 LLVOVolume::mRenderComplexity_current = 0;
+LLPointer<LLObjectMediaDataClient> LLVOVolume::sObjectMediaClient = NULL;
+LLPointer<LLObjectMediaNavigateClient> LLVOVolume::sObjectMediaNavigateClient = NULL;
+
+static LLFastTimer::DeclareTimer FTM_GEN_TRIANGLES("Generate Triangles");
+static LLFastTimer::DeclareTimer FTM_GEN_VOLUME("Generate Volumes");
+static LLFastTimer::DeclareTimer FTM_VOLUME_TEXTURES("Volume Textures");
+
+// Implementation class of LLMediaDataClientObject. See llmediadataclient.h
+class LLMediaDataClientObjectImpl : public LLMediaDataClientObject
+{
+public:
+ LLMediaDataClientObjectImpl(LLVOVolume *obj, bool isNew) : mObject(obj), mNew(isNew)
+ {
+ mObject->addMDCImpl();
+ }
+ ~LLMediaDataClientObjectImpl()
+ {
+ mObject->removeMDCImpl();
+ }
+
+ virtual U8 getMediaDataCount() const
+ { return mObject->getNumTEs(); }
+
+ virtual LLSD getMediaDataLLSD(U8 index) const
+ {
+ LLSD result;
+ LLTextureEntry *te = mObject->getTE(index);
+ if (NULL != te)
+ {
+ llassert((te->getMediaData() != NULL) == te->hasMedia());
+ if (te->getMediaData() != NULL)
+ {
+ result = te->getMediaData()->asLLSD();
+ // XXX HACK: workaround bug in asLLSD() where whitelist is not set properly
+ // See DEV-41949
+ if (!result.has(LLMediaEntry::WHITELIST_KEY))
+ {
+ result[LLMediaEntry::WHITELIST_KEY] = LLSD::emptyArray();
+ }
+ }
+ }
+ return result;
+ }
+ virtual bool isCurrentMediaUrl(U8 index, const std::string &url) const
+ {
+ LLTextureEntry *te = mObject->getTE(index);
+ if (te)
+ {
+ if (te->getMediaData())
+ {
+ return (te->getMediaData()->getCurrentURL() == url);
+ }
+ }
+ return url.empty();
+ }
+
+ virtual LLUUID getID() const
+ { return mObject->getID(); }
+
+ virtual void mediaNavigateBounceBack(U8 index)
+ { mObject->mediaNavigateBounceBack(index); }
+
+ virtual bool hasMedia() const
+ { return mObject->hasMedia(); }
+
+ virtual void updateObjectMediaData(LLSD const &data, const std::string &version_string)
+ { mObject->updateObjectMediaData(data, version_string); }
+
+ virtual F64 getMediaInterest() const
+ {
+ F64 interest = mObject->getTotalMediaInterest();
+ if (interest < (F64)0.0)
+ {
+ // media interest not valid yet, try pixel area
+ interest = mObject->getPixelArea();
+ // HACK: force recalculation of pixel area if interest is the "magic default" of 1024.
+ if (interest == 1024.f)
+ {
+ const_cast<LLVOVolume*>(static_cast<LLVOVolume*>(mObject))->setPixelAreaAndAngle(gAgent);
+ interest = mObject->getPixelArea();
+ }
+ }
+ return interest;
+ }
+
+ virtual bool isInterestingEnough() const
+ {
+ return LLViewerMedia::isInterestingEnough(mObject, getMediaInterest());
+ }
+
+ virtual std::string getCapabilityUrl(const std::string &name) const
+ { return mObject->getRegion()->getCapability(name); }
+
+ virtual bool isDead() const
+ { return mObject->isDead(); }
+
+ virtual U32 getMediaVersion() const
+ { return LLTextureEntry::getVersionFromMediaVersionString(mObject->getMediaURL()); }
+
+ virtual bool isNew() const
+ { return mNew; }
+
+private:
+ LLPointer<LLVOVolume> mObject;
+ bool mNew;
+};
+
+
+LLVOVolume::LLVOVolume(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
+ : LLViewerObject(id, pcode, regionp),
+ mVolumeImpl(NULL)
+{
+ mTexAnimMode = 0;
+ mRelativeXform.setIdentity();
+ mRelativeXformInvTrans.setIdentity();
+
+ mFaceMappingChanged = FALSE;
+ mLOD = MIN_LOD;
+ mTextureAnimp = NULL;
+ mVolumeChanged = FALSE;
+ mVObjRadius = LLVector3(1,1,0.5f).length();
+ mNumFaces = 0;
+ mLODChanged = FALSE;
+ mSculptChanged = FALSE;
+ mSpotLightPriority = 0.f;
+
+ mMediaImplList.resize(getNumTEs());
+ mLastFetchedMediaVersion = -1;
+ mIndexInTex = 0;
+ mMDCImplCount = 0;
+}
+
+LLVOVolume::~LLVOVolume()
+{
+ delete mTextureAnimp;
+ mTextureAnimp = NULL;
+ delete mVolumeImpl;
+ mVolumeImpl = NULL;
+
+ if(!mMediaImplList.empty())
+ {
+ for(U32 i = 0 ; i < mMediaImplList.size() ; i++)
+ {
+ if(mMediaImplList[i].notNull())
+ {
+ mMediaImplList[i]->removeObject(this) ;
+ }
+ }
+ }
+}
+
+void LLVOVolume::markDead()
+{
+ if (!mDead)
+ {
+ if(getMDCImplCount() > 0)
+ {
+ LLMediaDataClientObject::ptr_t obj = new LLMediaDataClientObjectImpl(const_cast<LLVOVolume*>(this), false);
+ if (sObjectMediaClient) sObjectMediaClient->removeFromQueue(obj);
+ if (sObjectMediaNavigateClient) sObjectMediaNavigateClient->removeFromQueue(obj);
+ }
+
+ // Detach all media impls from this object
+ for(U32 i = 0 ; i < mMediaImplList.size() ; i++)
+ {
+ removeMediaImpl(i);
+ }
+
+ if (mSculptTexture.notNull())
+ {
+ mSculptTexture->removeVolume(this);
+ }
+ }
+
+ LLViewerObject::markDead();
+}
+
+
+// static
+void LLVOVolume::initClass()
+{
+ // gSavedSettings better be around
+ if (gSavedSettings.getBOOL("PrimMediaMasterEnabled"))
+ {
+ const F32 queue_timer_delay = gSavedSettings.getF32("PrimMediaRequestQueueDelay");
+ const F32 retry_timer_delay = gSavedSettings.getF32("PrimMediaRetryTimerDelay");
+ const U32 max_retries = gSavedSettings.getU32("PrimMediaMaxRetries");
+ const U32 max_sorted_queue_size = gSavedSettings.getU32("PrimMediaMaxSortedQueueSize");
+ const U32 max_round_robin_queue_size = gSavedSettings.getU32("PrimMediaMaxRoundRobinQueueSize");
+ sObjectMediaClient = new LLObjectMediaDataClient(queue_timer_delay, retry_timer_delay, max_retries,
+ max_sorted_queue_size, max_round_robin_queue_size);
+ sObjectMediaNavigateClient = new LLObjectMediaNavigateClient(queue_timer_delay, retry_timer_delay,
+ max_retries, max_sorted_queue_size, max_round_robin_queue_size);
+ }
+}
+
+// static
+void LLVOVolume::cleanupClass()
+{
+ sObjectMediaClient = NULL;
+ sObjectMediaNavigateClient = NULL;
+}
+
+U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys,
+ void **user_data,
+ U32 block_num, EObjectUpdateType update_type,
+ LLDataPacker *dp)
+{
+ LLColor4U color;
+ const S32 teDirtyBits = (TEM_CHANGE_TEXTURE|TEM_CHANGE_COLOR|TEM_CHANGE_MEDIA);
+
+ // Do base class updates...
+ U32 retval = LLViewerObject::processUpdateMessage(mesgsys, user_data, block_num, update_type, dp);
+
+ LLUUID sculpt_id;
+ U8 sculpt_type = 0;
+ if (isSculpted())
+ {
+ LLSculptParams *sculpt_params = (LLSculptParams *)getParameterEntry(LLNetworkData::PARAMS_SCULPT);
+ sculpt_id = sculpt_params->getSculptTexture();
+ sculpt_type = sculpt_params->getSculptType();
+ }
+
+ 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();
+ }
+ }
+ mTexAnimMode = 0;
+ mTextureAnimp->unpackTAMessage(mesgsys, block_num);
+ }
+ else
+ {
+ if (mTextureAnimp)
+ {
+ delete mTextureAnimp;
+ mTextureAnimp = NULL;
+ gPipeline.markTextured(mDrawable);
+ mFaceMappingChanged = TRUE;
+ mTexAnimMode = 0;
+ }
+ }
+
+ // Unpack volume data
+ LLVolumeParams volume_params;
+ LLVolumeMessage::unpackVolumeParams(&volume_params, mesgsys, _PREHASH_ObjectData, block_num);
+ volume_params.setSculptID(sculpt_id, sculpt_type);
+
+ 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
+ //
+
+ S32 result = unpackTEMessage(mesgsys, _PREHASH_ObjectData, block_num);
+ if (result & teDirtyBits)
+ {
+ updateTEData();
+ }
+ if (result & TEM_CHANGE_MEDIA)
+ {
+ retval |= MEDIA_FLAGS_CHANGED;
+ }
+ }
+ 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;
+ }
+
+ volume_params.setSculptID(sculpt_id, sculpt_type);
+
+ if (setVolume(volume_params, 0))
+ {
+ markForUpdate(TRUE);
+ }
+ S32 res2 = unpackTEMessage(*dp);
+ if (TEM_INVALID == res2)
+ {
+ // There's something bogus in the data that we're unpacking.
+ dp->dumpBufferToLog();
+ llwarns << "Flushing cache files" << llendl;
+
+ if(LLVOCache::hasInstance() && getRegion())
+ {
+ LLVOCache::getInstance()->removeEntry(getRegion()->getHandle()) ;
+ }
+
+ llwarns << "Bogus TE data in " << getID() << llendl;
+ }
+ else
+ {
+ if (res2 & teDirtyBits)
+ {
+ updateTEData();
+ }
+ if (res2 & TEM_CHANGE_MEDIA)
+ {
+ retval |= MEDIA_FLAGS_CHANGED;
+ }
+ }
+
+ U32 value = dp->getPassFlags();
+
+ if (value & 0x40)
+ {
+ if (!mTextureAnimp)
+ {
+ mTextureAnimp = new LLViewerTextureAnim();
+ }
+ else
+ {
+ if (!(mTextureAnimp->mMode & LLTextureAnim::SMOOTH))
+ {
+ mTextureAnimp->reset();
+ }
+ }
+ mTexAnimMode = 0;
+ mTextureAnimp->unpackTAMessage(*dp);
+ }
+ else if (mTextureAnimp)
+ {
+ delete mTextureAnimp;
+ mTextureAnimp = NULL;
+ gPipeline.markTextured(mDrawable);
+ mFaceMappingChanged = TRUE;
+ mTexAnimMode = 0;
+ }
+ }
+ 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);
+ S32 result = unpackTEMessage(tdp);
+ if (result & teDirtyBits)
+ {
+ updateTEData();
+ }
+ if (result & TEM_CHANGE_MEDIA)
+ {
+ retval |= MEDIA_FLAGS_CHANGED;
+ }
+ }
+ }
+ }
+ if (retval & (MEDIA_URL_REMOVED | MEDIA_URL_ADDED | MEDIA_URL_UPDATED | MEDIA_FLAGS_CHANGED))
+ {
+ // If only the media URL changed, and it isn't a media version URL,
+ // ignore it
+ if ( ! ( retval & (MEDIA_URL_ADDED | MEDIA_URL_UPDATED) &&
+ mMedia && ! mMedia->mMediaURL.empty() &&
+ ! LLTextureEntry::isMediaVersionString(mMedia->mMediaURL) ) )
+ {
+ // If the media changed at all, request new media data
+ LL_DEBUGS("MediaOnAPrim") << "Media update: " << getID() << ": retval=" << retval << " Media URL: " <<
+ ((mMedia) ? mMedia->mMediaURL : std::string("")) << LL_ENDL;
+ requestMediaDataUpdate(retval & MEDIA_FLAGS_CHANGED);
+ }
+ else {
+ LL_INFOS("MediaOnAPrim") << "Ignoring media update for: " << getID() << " Media URL: " <<
+ ((mMedia) ? mMedia->mMediaURL : std::string("")) << LL_ENDL;
+ }
+ }
+ // ...and clean up any media impls
+ cleanUpMediaImpls();
+
+ return retval;
+}
+
+
+void LLVOVolume::animateTextures()
+{
+ F32 off_s = 0.f, off_t = 0.f, scale_s = 1.f, scale_t = 1.f, rot = 0.f;
+ S32 result = mTextureAnimp->animateTextures(off_s, off_t, scale_s, scale_t, rot);
+
+ if (result)
+ {
+ if (!mTexAnimMode)
+ {
+ mFaceMappingChanged = TRUE;
+ gPipeline.markTextured(mDrawable);
+ }
+ mTexAnimMode = result | mTextureAnimp->mMode;
+
+ S32 start=0, end=mDrawable->getNumFaces()-1;
+ if (mTextureAnimp->mFace >= 0 && mTextureAnimp->mFace <= end)
+ {
+ start = end = mTextureAnimp->mFace;
+ }
+
+ for (S32 i = start; i <= end; i++)
+ {
+ LLFace* facep = mDrawable->getFace(i);
+ if(facep->getVirtualSize() <= MIN_TEX_ANIM_SIZE && facep->mTextureMatrix) continue;
+
+ const LLTextureEntry* te = facep->getTextureEntry();
+
+ if (!te)
+ {
+ continue;
+ }
+
+ if (!(result & LLViewerTextureAnim::ROTATE))
+ {
+ te->getRotation(&rot);
+ }
+ if (!(result & LLViewerTextureAnim::TRANSLATE))
+ {
+ te->getOffset(&off_s,&off_t);
+ }
+ if (!(result & LLViewerTextureAnim::SCALE))
+ {
+ te->getScale(&scale_s, &scale_t);
+ }
+
+ if (!facep->mTextureMatrix)
+ {
+ facep->mTextureMatrix = new LLMatrix4();
+ }
+
+ LLMatrix4& tex_mat = *facep->mTextureMatrix;
+ tex_mat.setIdentity();
+ LLVector3 trans ;
+
+ if(facep->isAtlasInUse())
+ {
+ //
+ //if use atlas for animated texture
+ //apply the following transform to the animation matrix.
+ //
+
+ F32 tcoord_xoffset = 0.f ;
+ F32 tcoord_yoffset = 0.f ;
+ F32 tcoord_xscale = 1.f ;
+ F32 tcoord_yscale = 1.f ;
+ if(facep->isAtlasInUse())
+ {
+ const LLVector2* tmp = facep->getTexCoordOffset() ;
+ tcoord_xoffset = tmp->mV[0] ;
+ tcoord_yoffset = tmp->mV[1] ;
+
+ tmp = facep->getTexCoordScale() ;
+ tcoord_xscale = tmp->mV[0] ;
+ tcoord_yscale = tmp->mV[1] ;
+ }
+ trans.set(LLVector3(tcoord_xoffset + tcoord_xscale * (off_s+0.5f), tcoord_yoffset + tcoord_yscale * (off_t+0.5f), 0.f));
+
+ tex_mat.translate(LLVector3(-(tcoord_xoffset + tcoord_xscale * 0.5f), -(tcoord_yoffset + tcoord_yscale * 0.5f), 0.f));
+ }
+ else //non atlas
+ {
+ trans.set(LLVector3(off_s+0.5f, off_t+0.5f, 0.f));
+ tex_mat.translate(LLVector3(-0.5f, -0.5f, 0.f));
+ }
+
+ LLVector3 scale(scale_s, scale_t, 1.f);
+ LLQuaternion quat;
+ quat.setQuat(rot, 0, 0, -1.f);
+
+ tex_mat.rotate(quat);
+
+ LLMatrix4 mat;
+ mat.initAll(scale, LLQuaternion(), LLVector3());
+ tex_mat *= mat;
+
+ tex_mat.translate(trans);
+ }
+ }
+ else
+ {
+ if (mTexAnimMode && mTextureAnimp->mRate == 0)
+ {
+ U8 start, count;
+
+ if (mTextureAnimp->mFace == -1)
+ {
+ start = 0;
+ count = getNumTEs();
+ }
+ else
+ {
+ start = (U8) mTextureAnimp->mFace;
+ count = 1;
+ }
+
+ for (S32 i = start; i < start + count; i++)
+ {
+ if (mTexAnimMode & LLViewerTextureAnim::TRANSLATE)
+ {
+ setTEOffset(i, mTextureAnimp->mOffS, mTextureAnimp->mOffT);
+ }
+ if (mTexAnimMode & LLViewerTextureAnim::SCALE)
+ {
+ setTEScale(i, mTextureAnimp->mScaleS, mTextureAnimp->mScaleT);
+ }
+ if (mTexAnimMode & LLViewerTextureAnim::ROTATE)
+ {
+ setTERotation(i, mTextureAnimp->mRot);
+ }
+ }
+
+ gPipeline.markTextured(mDrawable);
+ mFaceMappingChanged = TRUE;
+ mTexAnimMode = 0;
+ }
+ }
+}
+BOOL LLVOVolume::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
+{
+ LLViewerObject::idleUpdate(agent, world, time);
+
+ static LLFastTimer::DeclareTimer ftm("Volume");
+ LLFastTimer t(ftm);
+
+ if (mDead || mDrawable.isNull())
+ {
+ return TRUE;
+ }
+
+ ///////////////////////
+ //
+ // Do texture animation stuff
+ //
+
+ if (mTextureAnimp && gAnimateTextures)
+ {
+ animateTextures();
+ }
+
+ // Dispatch to implementation
+ if (mVolumeImpl)
+ {
+ mVolumeImpl->doIdleUpdate(agent, world, time);
+ }
+
+ const S32 MAX_ACTIVE_OBJECT_QUIET_FRAMES = 40;
+
+ if (mDrawable->isActive())
+ {
+ if (mDrawable->isRoot() &&
+ mDrawable->mQuietCount++ > MAX_ACTIVE_OBJECT_QUIET_FRAMES &&
+ (!mDrawable->getParent() || !mDrawable->getParent()->isActive()))
+ {
+ mDrawable->makeStatic();
+ }
+ }
+
+ return TRUE;
+}
+
+void LLVOVolume::updateTextures()
+{
+ const F32 TEXTURE_AREA_REFRESH_TIME = 5.f; // seconds
+ if (mTextureUpdateTimer.getElapsedTimeF32() > TEXTURE_AREA_REFRESH_TIME)
+ {
+ updateTextureVirtualSize();
+ }
+}
+
+BOOL LLVOVolume::isVisible() const
+{
+ if(mDrawable.notNull() && mDrawable->isVisible())
+ {
+ return TRUE ;
+ }
+
+ if(isAttachment())
+ {
+ LLViewerObject* objp = (LLViewerObject*)getParent() ;
+ while(objp && !objp->isAvatar())
+ {
+ objp = (LLViewerObject*)objp->getParent() ;
+ }
+
+ return objp && objp->mDrawable.notNull() && objp->mDrawable->isVisible() ;
+ }
+
+ return FALSE ;
+}
+
+void LLVOVolume::updateTextureVirtualSize()
+{
+ LLFastTimer ftm(FTM_VOLUME_TEXTURES);
+ // Update the pixel area of all faces
+
+ if(!isVisible())
+ {
+ return ;
+ }
+
+ if (!gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_SIMPLE))
+ {
+ return;
+ }
+
+ static LLCachedControl<bool> dont_load_textures(gSavedSettings,"TextureDisable");
+
+ if (dont_load_textures || LLAppViewer::getTextureFetch()->mDebugPause) // || !mDrawable->isVisible())
+ {
+ return;
+ }
+
+ mTextureUpdateTimer.reset();
+
+ F32 old_area = mPixelArea;
+ mPixelArea = 0.f;
+
+ const S32 num_faces = mDrawable->getNumFaces();
+ F32 min_vsize=999999999.f, max_vsize=0.f;
+ LLViewerCamera* camera = LLViewerCamera::getInstance();
+ for (S32 i = 0; i < num_faces; i++)
+ {
+ LLFace* face = mDrawable->getFace(i);
+ const LLTextureEntry *te = face->getTextureEntry();
+ LLViewerTexture *imagep = face->getTexture();
+ if (!imagep || !te ||
+ face->mExtents[0].equals3(face->mExtents[1]))
+ {
+ continue;
+ }
+
+ F32 vsize;
+ F32 old_size = face->getVirtualSize();
+
+ if (isHUDAttachment())
+ {
+ F32 area = (F32) camera->getScreenPixelArea();
+ vsize = area;
+ imagep->setBoostLevel(LLViewerTexture::BOOST_HUD);
+ face->setPixelArea(area); // treat as full screen
+ face->setVirtualSize(vsize);
+ }
+ else
+ {
+ vsize = face->getTextureVirtualSize();
+ }
+
+ mPixelArea = llmax(mPixelArea, face->getPixelArea());
+
+ if (face->mTextureMatrix != NULL)
+ {
+ if ((vsize < MIN_TEX_ANIM_SIZE && old_size > MIN_TEX_ANIM_SIZE) ||
+ (vsize > MIN_TEX_ANIM_SIZE && old_size < MIN_TEX_ANIM_SIZE))
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD, FALSE);
+ }
+ }
+
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_AREA))
+ {
+ if (vsize < min_vsize) min_vsize = vsize;
+ if (vsize > max_vsize) max_vsize = vsize;
+ }
+ else if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY))
+ {
+ LLViewerFetchedTexture* img = LLViewerTextureManager::staticCastToFetchedTexture(imagep) ;
+ if(img)
+ {
+ F32 pri = img->getDecodePriority();
+ pri = llmax(pri, 0.0f);
+ if (pri < min_vsize) min_vsize = pri;
+ if (pri > max_vsize) max_vsize = pri;
+ }
+ }
+ else if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_FACE_AREA))
+ {
+ F32 pri = mPixelArea;
+ if (pri < min_vsize) min_vsize = pri;
+ if (pri > max_vsize) max_vsize = pri;
+ }
+ }
+
+ if (isSculpted())
+ {
+ LLSculptParams *sculpt_params = (LLSculptParams *)getParameterEntry(LLNetworkData::PARAMS_SCULPT);
+ LLUUID id = sculpt_params->getSculptTexture();
+
+ updateSculptTexture();
+
+
+
+ if (mSculptTexture.notNull())
+ {
+ mSculptTexture->setBoostLevel(llmax((S32)mSculptTexture->getBoostLevel(),
+ (S32)LLViewerTexture::BOOST_SCULPTED));
+ mSculptTexture->setForSculpt() ;
+
+ if(!mSculptTexture->isCachedRawImageReady())
+ {
+ S32 lod = llmin(mLOD, 3);
+ F32 lodf = ((F32)(lod + 1.0f)/4.f);
+ F32 tex_size = lodf * LLViewerTexture::sMaxSculptRez ;
+ mSculptTexture->addTextureStats(2.f * tex_size * tex_size, FALSE);
+
+ //if the sculpty very close to the view point, load first
+ {
+ LLVector3 lookAt = getPositionAgent() - camera->getOrigin();
+ F32 dist = lookAt.normVec() ;
+ F32 cos_angle_to_view_dir = lookAt * camera->getXAxis() ;
+ mSculptTexture->setAdditionalDecodePriority(0.8f * LLFace::calcImportanceToCamera(cos_angle_to_view_dir, dist)) ;
+ }
+ }
+
+ S32 texture_discard = mSculptTexture->getDiscardLevel(); //try to match the texture
+ S32 current_discard = getVolume() ? getVolume()->getSculptLevel() : -2 ;
+
+ if (texture_discard >= 0 && //texture has some data available
+ (texture_discard < current_discard || //texture has more data than last rebuild
+ current_discard < 0)) //no previous rebuild
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, FALSE);
+ mSculptChanged = TRUE;
+ }
+
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_SCULPTED))
+ {
+ setDebugText(llformat("T%d C%d V%d\n%dx%d",
+ texture_discard, current_discard, getVolume()->getSculptLevel(),
+ mSculptTexture->getHeight(), mSculptTexture->getWidth()));
+ }
+ }
+
+ }
+
+ if (getLightTextureID().notNull())
+ {
+ LLLightImageParams* params = (LLLightImageParams*) getParameterEntry(LLNetworkData::PARAMS_LIGHT_IMAGE);
+ LLUUID id = params->getLightTexture();
+ mLightTexture = LLViewerTextureManager::getFetchedTexture(id);
+ if (mLightTexture.notNull())
+ {
+ F32 rad = getLightRadius();
+ mLightTexture->addTextureStats(gPipeline.calcPixelArea(getPositionAgent(),
+ LLVector3(rad,rad,rad),
+ *camera));
+ }
+ }
+
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_AREA))
+ {
+ setDebugText(llformat("%.0f:%.0f", (F32) sqrt(min_vsize),(F32) sqrt(max_vsize)));
+ }
+ else if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY))
+ {
+ setDebugText(llformat("%.0f:%.0f", (F32) sqrt(min_vsize),(F32) sqrt(max_vsize)));
+ }
+ else if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_FACE_AREA))
+ {
+ setDebugText(llformat("%.0f:%.0f", (F32) sqrt(min_vsize),(F32) sqrt(max_vsize)));
+ }
+
+ if (mPixelArea == 0)
+ { //flexi phasing issues make this happen
+ mPixelArea = old_area;
+ }
+}
+
+BOOL LLVOVolume::isActive() const
+{
+ return !mStatic || mTextureAnimp || (mVolumeImpl && mVolumeImpl->isActive()) ||
+ (mDrawable.notNull() && mDrawable->isActive());
+}
+
+BOOL LLVOVolume::setMaterial(const U8 material)
+{
+ BOOL res = LLViewerObject::setMaterial(material);
+
+ return res;
+}
+
+void LLVOVolume::setTexture(const S32 face)
+{
+ llassert(face < getNumTEs());
+ gGL.getTexUnit(0)->bind(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_POSITION, TRUE);
+ }
+}
+
+LLFace* LLVOVolume::addFace(S32 f)
+{
+ const LLTextureEntry* te = getTE(f);
+ LLViewerTexture* imagep = getTEImage(f);
+ return mDrawable->addFace(te, imagep);
+}
+
+LLDrawable *LLVOVolume::createDrawable(LLPipeline *pipeline)
+{
+ pipeline->allocDrawable(this);
+
+ mDrawable->setRenderType(LLPipeline::RENDER_TYPE_VOLUME);
+
+ S32 max_tes_to_set = getNumTEs();
+ for (S32 i = 0; i < max_tes_to_set; i++)
+ {
+ addFace(i);
+ }
+ mNumFaces = max_tes_to_set;
+
+ if (isAttachment())
+ {
+ mDrawable->makeActive();
+ }
+
+ if (getIsLight())
+ {
+ // Add it to the pipeline mLightSet
+ gPipeline.setLight(mDrawable, TRUE);
+ }
+
+ updateRadius();
+ bool force_update = true; // avoid non-alpha mDistance update being optimized away
+ mDrawable->updateDistance(*LLViewerCamera::getInstance(), force_update);
+
+ return mDrawable;
+}
+
+BOOL LLVOVolume::setVolume(const LLVolumeParams &params_in, const S32 detail, bool unique_volume)
+{
+ LLVolumeParams volume_params = params_in;
+
+ S32 last_lod = mVolumep.notNull() ? LLVolumeLODGroup::getVolumeDetailFromScale(mVolumep->getDetail()) : -1;
+ S32 lod = mLOD;
+
+ BOOL is404 = FALSE;
+
+ if (isSculpted())
+ {
+ // if it's a mesh
+ if ((volume_params.getSculptType() & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH)
+ { //meshes might not have all LODs, get the force detail to best existing LOD
+ LLUUID mesh_id = volume_params.getSculptID();
+
+ lod = gMeshRepo.getActualMeshLOD(volume_params, lod);
+ if (lod == -1)
+ {
+ is404 = TRUE;
+ lod = 0;
+ }
+ }
+ }
+
+ // 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);
+ }
+ }
+ }
+
+ if (is404)
+ {
+ setIcon(LLViewerTextureManager::getFetchedTextureFromFile("icons/Inv_Mesh.png", TRUE, LLViewerTexture::BOOST_UI));
+ //render prim proxy when mesh loading attempts give up
+ volume_params.setSculptID(LLUUID::null, LL_SCULPT_TYPE_NONE);
+
+ }
+
+ if ((LLPrimitive::setVolume(volume_params, lod, (mVolumeImpl && mVolumeImpl->isVolumeUnique()))) || mSculptChanged)
+ {
+ mFaceMappingChanged = TRUE;
+
+ if (mVolumeImpl)
+ {
+ mVolumeImpl->onSetVolume(volume_params, mLOD);
+ }
+
+ updateSculptTexture();
+
+ if (isSculpted())
+ {
+ updateSculptTexture();
+ // if it's a mesh
+ if ((volume_params.getSculptType() & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH)
+ {
+ if (!getVolume()->isMeshAssetLoaded())
+ {
+ //load request not yet issued, request pipeline load this mesh
+ LLUUID asset_id = volume_params.getSculptID();
+ S32 available_lod = gMeshRepo.loadMesh(this, volume_params, lod, last_lod);
+ if (available_lod != lod)
+ {
+ LLPrimitive::setVolume(volume_params, available_lod);
+ }
+ }
+
+ }
+ else // otherwise is sculptie
+ {
+ if (mSculptTexture.notNull())
+ {
+ sculpt();
+ }
+ }
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void LLVOVolume::updateSculptTexture()
+{
+ LLPointer<LLViewerFetchedTexture> old_sculpt = mSculptTexture;
+
+ if (isSculpted() && !isMesh())
+ {
+ LLSculptParams *sculpt_params = (LLSculptParams *)getParameterEntry(LLNetworkData::PARAMS_SCULPT);
+ LLUUID id = sculpt_params->getSculptTexture();
+ if (id.notNull())
+ {
+ mSculptTexture = LLViewerTextureManager::getFetchedTexture(id, TRUE, LLViewerTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
+ }
+ }
+ else
+ {
+ mSculptTexture = NULL;
+ }
+
+ if (mSculptTexture != old_sculpt)
+ {
+ if (old_sculpt.notNull())
+ {
+ old_sculpt->removeVolume(this);
+ }
+ if (mSculptTexture.notNull())
+ {
+ mSculptTexture->addVolume(this);
+ }
+ }
+
+}
+
+void LLVOVolume::notifyMeshLoaded()
+{
+ mSculptChanged = TRUE;
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY, TRUE);
+}
+
+// sculpt replaces generate() for sculpted surfaces
+void LLVOVolume::sculpt()
+{
+ if (mSculptTexture.notNull())
+ {
+ U16 sculpt_height = 0;
+ U16 sculpt_width = 0;
+ S8 sculpt_components = 0;
+ const U8* sculpt_data = NULL;
+
+ S32 discard_level = mSculptTexture->getDiscardLevel() ;
+ LLImageRaw* raw_image = mSculptTexture->getCachedRawImage() ;
+
+ S32 max_discard = mSculptTexture->getMaxDiscardLevel();
+ if (discard_level > max_discard)
+ discard_level = max_discard; // clamp to the best we can do
+
+ S32 current_discard = getVolume()->getSculptLevel() ;
+ if(current_discard < -2)
+ {
+ llwarns << "WARNING!!: Current discard of sculpty at " << current_discard
+ << " is less than -2." << llendl;
+
+ // corrupted volume... don't update the sculpty
+ return;
+ }
+ else if (current_discard > MAX_DISCARD_LEVEL)
+ {
+ llwarns << "WARNING!!: Current discard of sculpty at " << current_discard
+ << " is more than than allowed max of " << MAX_DISCARD_LEVEL << llendl;
+
+ // corrupted volume... don't update the sculpty
+ return;
+ }
+
+ if (current_discard == discard_level) // no work to do here
+ return;
+
+ if(!raw_image)
+ {
+ llassert(discard_level < 0) ;
+
+ sculpt_width = 0;
+ sculpt_height = 0;
+ sculpt_data = NULL ;
+
+ if(LLViewerTextureManager::sTesterp)
+ {
+ LLViewerTextureManager::sTesterp->updateGrayTextureBinding();
+ }
+ }
+ else
+ {
+ sculpt_height = raw_image->getHeight();
+ sculpt_width = raw_image->getWidth();
+ sculpt_components = raw_image->getComponents();
+
+ sculpt_data = raw_image->getData();
+
+ if(LLViewerTextureManager::sTesterp)
+ {
+ mSculptTexture->updateBindStatsForTester() ;
+ }
+ }
+ getVolume()->sculpt(sculpt_width, sculpt_height, sculpt_components, sculpt_data, discard_level);
+
+ //notify rebuild any other VOVolumes that reference this sculpty volume
+ for (S32 i = 0; i < mSculptTexture->getNumVolumes(); ++i)
+ {
+ LLVOVolume* volume = (*(mSculptTexture->getVolumeList()))[i];
+ if (volume != this && volume->getVolume() == getVolume())
+ {
+ gPipeline.markRebuild(volume->mDrawable, LLDrawable::REBUILD_GEOMETRY, FALSE);
+ }
+ }
+ }
+}
+
+S32 LLVOVolume::computeLODDetail(F32 distance, F32 radius)
+{
+ S32 cur_detail;
+ if (LLPipeline::sDynamicLOD)
+ {
+ // We've got LOD in the profile, and in the twist. Use radius.
+ F32 tan_angle = (LLVOVolume::sLODFactor*radius)/distance;
+ cur_detail = LLVolumeLODGroup::getDetailFromTan(llround(tan_angle, 0.01f));
+ }
+ else
+ {
+ cur_detail = llclamp((S32) (sqrtf(radius)*LLVOVolume::sLODFactor*4.f), 0, 3);
+ }
+ return cur_detail;
+}
+
+BOOL LLVOVolume::calcLOD()
+{
+ if (mDrawable.isNull())
+ {
+ return FALSE;
+ }
+
+ S32 cur_detail = 0;
+
+ F32 radius;
+ F32 distance;
+
+ if (mDrawable->isState(LLDrawable::RIGGED))
+ {
+ LLVOAvatar* avatar = getAvatar();
+ distance = avatar->mDrawable->mDistanceWRTCamera;
+ radius = avatar->getBinRadius();
+ }
+ else
+ {
+ distance = mDrawable->mDistanceWRTCamera;
+ radius = getVolume()->mLODScaleBias.scaledVec(getScale()).length();
+ }
+
+ //hold onto unmodified distance for debugging
+ //F32 debug_distance = distance;
+
+ 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;
+ }
+
+ // DON'T Compensate for field of view changing on FOV zoom.
+ distance *= F_PI/3.f;
+
+ cur_detail = computeLODDetail(llround(distance, 0.01f),
+ llround(radius, 0.01f));
+
+
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_LOD_INFO))
+ {
+ //setDebugText(llformat("%.2f:%.2f, %d", debug_distance, radius, cur_detail));
+
+ setDebugText(llformat("%d", mDrawable->getFace(0)->getTextureIndex()));
+ }
+
+ if (cur_detail != mLOD)
+ {
+ mAppAngle = llround((F32) atan2( mDrawable->getRadius(), mDrawable->mDistanceWRTCamera) * RAD_TO_DEG, 0.01f);
+ mLOD = cur_detail;
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+BOOL LLVOVolume::updateLOD()
+{
+ if (mDrawable.isNull())
+ {
+ return FALSE;
+ }
+
+ BOOL lod_changed = calcLOD();
+
+ if (lod_changed)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, FALSE);
+ mLODChanged = TRUE;
+ }
+ else
+ {
+ F32 new_radius = getBinRadius();
+ F32 old_radius = mDrawable->getBinRadius();
+ if (new_radius < old_radius * 0.9f || new_radius > old_radius*1.1f)
+ {
+ gPipeline.markPartitionMove(mDrawable);
+ }
+ }
+
+ lod_changed = lod_changed || LLViewerObject::updateLOD();
+
+ return lod_changed;
+}
+
+BOOL LLVOVolume::setDrawableParent(LLDrawable* parentp)
+{
+ if (!LLViewerObject::setDrawableParent(parentp))
+ {
+ // no change in drawable parent
+ return FALSE;
+ }
+
+ if (!mDrawable->isRoot())
+ {
+ // rebuild vertices in parent relative space
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, 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);
+ if (!face)
+ {
+ return;
+ }
+
+ 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);
+ }
+ }
+}
+
+BOOL LLVOVolume::setParent(LLViewerObject* parent)
+{
+ BOOL ret = FALSE ;
+ if (parent != getParent())
+ {
+ ret = LLViewerObject::setParent(parent);
+ if (ret && mDrawable)
+ {
+ gPipeline.markMoved(mDrawable);
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
+ }
+ }
+
+ return ret ;
+}
+
+// NOTE: regenFaces() MUST be followed by genTriangles()!
+void LLVOVolume::regenFaces()
+{
+ // remove existing faces
+ BOOL count_changed = mNumFaces != getNumTEs();
+
+ if (count_changed)
+ {
+ deleteFaces();
+ // add new faces
+ mNumFaces = getNumTEs();
+ }
+
+ for (S32 i = 0; i < mNumFaces; i++)
+ {
+ LLFace* facep = count_changed ? addFace(i) : mDrawable->getFace(i);
+ facep->setTEOffset(i);
+ facep->setTexture(getTEImage(i));
+ facep->setViewerObject(this);
+
+ // If the face had media on it, this will have broken the link between the LLViewerMediaTexture and the face.
+ // Re-establish the link.
+ if((int)mMediaImplList.size() > i)
+ {
+ if(mMediaImplList[i])
+ {
+ LLViewerMediaTexture* media_tex = LLViewerTextureManager::findMediaTexture(mMediaImplList[i]->getMediaTextureID()) ;
+ if(media_tex)
+ {
+ media_tex->addMediaToFace(facep) ;
+ }
+ }
+ }
+ }
+
+ if (!count_changed)
+ {
+ updateFaceFlags();
+ }
+}
+
+BOOL LLVOVolume::genBBoxes(BOOL force_global)
+{
+ BOOL res = TRUE;
+
+ LLVector4a min,max;
+
+ min.clear();
+ max.clear();
+
+ BOOL rebuild = mDrawable->isState(LLDrawable::REBUILD_VOLUME | LLDrawable::REBUILD_POSITION | LLDrawable::REBUILD_RIGGED);
+
+// bool rigged = false;
+ LLVolume* volume = mRiggedVolume;
+ if (!volume)
+ {
+ volume = getVolume();
+ }
+
+ for (S32 i = 0; i < getVolume()->getNumVolumeFaces(); i++)
+ {
+ LLFace *face = mDrawable->getFace(i);
+ if (!face)
+ {
+ continue;
+ }
+ res &= face->genVolumeBBoxes(*volume, i,
+ mRelativeXform, mRelativeXformInvTrans,
+ (mVolumeImpl && mVolumeImpl->isVolumeGlobal()) || force_global);
+
+ if (rebuild)
+ {
+ if (i == 0)
+ {
+ min = face->mExtents[0];
+ max = face->mExtents[1];
+ }
+ else
+ {
+ min.setMin(min, face->mExtents[0]);
+ max.setMax(max, face->mExtents[1]);
+ }
+ }
+ }
+
+ if (rebuild)
+ {
+ mDrawable->setSpatialExtents(min,max);
+ min.add(max);
+ min.mul(0.5f);
+ mDrawable->setPositionGroup(min);
+ }
+
+ updateRadius();
+ mDrawable->movePartition();
+
+ return res;
+}
+
+void LLVOVolume::preRebuild()
+{
+ if (mVolumeImpl != NULL)
+ {
+ mVolumeImpl->preRebuild();
+ }
+}
+
+void LLVOVolume::updateRelativeXform()
+{
+ if (mVolumeImpl)
+ {
+ mVolumeImpl->updateRelativeXform();
+ return;
+ }
+
+ LLDrawable* drawable = mDrawable;
+
+ if (drawable->isState(LLDrawable::RIGGED) && mRiggedVolume.notNull())
+ { //rigged volume (which is in agent space) is used for generating bounding boxes etc
+ //inverse of render matrix should go to partition space
+ mRelativeXform = getRenderMatrix();
+
+ F32* dst = (F32*) mRelativeXformInvTrans.mMatrix;
+ F32* src = (F32*) mRelativeXform.mMatrix;
+ dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2];
+ dst[3] = src[4]; dst[4] = src[5]; dst[5] = src[6];
+ dst[6] = src[8]; dst[7] = src[9]; dst[8] = src[10];
+
+ mRelativeXform.invert();
+ mRelativeXformInvTrans.transpose();
+ }
+ else 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));
+
+
+ // compute inverse transpose for normals
+ // mRelativeXformInvTrans.setRows(x_axis, y_axis, z_axis);
+ // mRelativeXformInvTrans.invert();
+ // mRelativeXformInvTrans.setRows(x_axis, y_axis, z_axis);
+ // grumble - invert is NOT a matrix invert, so we do it by hand:
+
+ LLMatrix3 rot_inverse = LLMatrix3(~delta_rot);
+
+ LLMatrix3 scale_inverse;
+ scale_inverse.setRows(LLVector3(1.0, 0.0, 0.0) / delta_scale.mV[VX],
+ LLVector3(0.0, 1.0, 0.0) / delta_scale.mV[VY],
+ LLVector3(0.0, 0.0, 1.0) / delta_scale.mV[VZ]);
+
+
+ mRelativeXformInvTrans = rot_inverse * scale_inverse;
+
+ mRelativeXformInvTrans.transpose();
+ }
+ 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));
+
+ // compute inverse transpose for normals
+ LLMatrix3 rot_inverse = LLMatrix3(~rot);
+
+ LLMatrix3 scale_inverse;
+ scale_inverse.setRows(LLVector3(1.0, 0.0, 0.0) / scale.mV[VX],
+ LLVector3(0.0, 1.0, 0.0) / scale.mV[VY],
+ LLVector3(0.0, 0.0, 1.0) / scale.mV[VZ]);
+
+
+ mRelativeXformInvTrans = rot_inverse * scale_inverse;
+
+ mRelativeXformInvTrans.transpose();
+ }
+}
+
+static LLFastTimer::DeclareTimer FTM_GEN_FLEX("Generate Flexies");
+static LLFastTimer::DeclareTimer FTM_UPDATE_PRIMITIVES("Update Primitives");
+static LLFastTimer::DeclareTimer FTM_UPDATE_RIGGED_VOLUME("Update Rigged");
+
+BOOL LLVOVolume::updateGeometry(LLDrawable *drawable)
+{
+ LLFastTimer t(FTM_UPDATE_PRIMITIVES);
+
+ if (mDrawable->isState(LLDrawable::REBUILD_RIGGED))
+ {
+ {
+ LLFastTimer t(FTM_UPDATE_RIGGED_VOLUME);
+ updateRiggedVolume();
+ }
+ genBBoxes(FALSE);
+ mDrawable->clearState(LLDrawable::REBUILD_RIGGED);
+ }
+
+ if (mVolumeImpl != NULL)
+ {
+ BOOL res;
+ {
+ LLFastTimer t(FTM_GEN_FLEX);
+ res = mVolumeImpl->doUpdateGeometry(drawable);
+ }
+ updateFaceFlags();
+ return res;
+ }
+
+ dirtySpatialGroup(drawable->isState(LLDrawable::IN_REBUILD_Q1));
+
+ BOOL compiled = FALSE;
+
+ updateRelativeXform();
+
+ if (mDrawable.isNull()) // Not sure why this is happening, but it is...
+ {
+ return TRUE; // No update to complete
+ }
+
+ if (mVolumeChanged || mFaceMappingChanged )
+ {
+ compiled = TRUE;
+
+ if (mVolumeChanged)
+ {
+ LLFastTimer ftm(FTM_GEN_VOLUME);
+ LLVolumeParams volume_params = getVolume()->getParams();
+ setVolume(volume_params, 0);
+ drawable->setState(LLDrawable::REBUILD_VOLUME);
+ }
+
+ {
+ LLFastTimer t(FTM_GEN_TRIANGLES);
+ regenFaces();
+ genBBoxes(FALSE);
+ }
+ }
+ else if ((mLODChanged) || (mSculptChanged))
+ {
+ LLVolume *old_volumep, *new_volumep;
+ F32 old_lod, new_lod;
+ S32 old_num_faces, new_num_faces ;
+
+ old_volumep = getVolume();
+ old_lod = old_volumep->getDetail();
+ old_num_faces = old_volumep->getNumFaces() ;
+ old_volumep = NULL ;
+
+ {
+ LLFastTimer ftm(FTM_GEN_VOLUME);
+ LLVolumeParams volume_params = getVolume()->getParams();
+ setVolume(volume_params, 0);
+ }
+
+ new_volumep = getVolume();
+ new_lod = new_volumep->getDetail();
+ new_num_faces = new_volumep->getNumFaces() ;
+ new_volumep = NULL ;
+
+ if ((new_lod != old_lod) || mSculptChanged)
+ {
+ compiled = TRUE;
+ sNumLODChanges += new_num_faces ;
+
+ drawable->setState(LLDrawable::REBUILD_VOLUME); // for face->genVolumeTriangles()
+
+ {
+ LLFastTimer t(FTM_GEN_TRIANGLES);
+ if (new_num_faces != old_num_faces)
+ {
+ regenFaces();
+ }
+ genBBoxes(FALSE);
+
+ if (mSculptChanged)
+ { //changes in sculpt maps can thrash an object bounding box without
+ //triggering a spatial group bounding box update -- force spatial group
+ //to update bounding boxes
+ LLSpatialGroup* group = mDrawable->getSpatialGroup();
+ if (group)
+ {
+ group->unbound();
+ }
+ }
+ }
+ }
+ }
+ // 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(FTM_GEN_TRIANGLES);
+ genBBoxes(FALSE);
+ }
+
+ // Update face flags
+ updateFaceFlags();
+
+ if(compiled)
+ {
+ LLPipeline::sCompiles++;
+ }
+
+ mVolumeChanged = FALSE;
+ mLODChanged = FALSE;
+ mSculptChanged = FALSE;
+ mFaceMappingChanged = FALSE;
+
+ return LLViewerObject::updateGeometry(drawable);
+}
+
+void LLVOVolume::updateFaceSize(S32 idx)
+{
+ LLFace* facep = mDrawable->getFace(idx);
+ if (idx >= getVolume()->getNumVolumeFaces())
+ {
+ facep->setSize(0,0, true);
+ }
+ else
+ {
+ const LLVolumeFace& vol_face = getVolume()->getVolumeFace(idx);
+ facep->setSize(vol_face.mNumVertices, vol_face.mNumIndices,
+ true); // <--- volume faces should be padded for 16-byte alignment
+
+ }
+}
+
+BOOL LLVOVolume::isRootEdit() const
+{
+ if (mParent && !((LLViewerObject*)mParent)->isAvatar())
+ {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+//virtual
+void LLVOVolume::setNumTEs(const U8 num_tes)
+{
+ const U8 old_num_tes = getNumTEs() ;
+
+ if(old_num_tes && old_num_tes < num_tes) //new faces added
+ {
+ LLViewerObject::setNumTEs(num_tes) ;
+
+ if(mMediaImplList.size() >= old_num_tes && mMediaImplList[old_num_tes -1].notNull())//duplicate the last media textures if exists.
+ {
+ mMediaImplList.resize(num_tes) ;
+ const LLTextureEntry* te = getTE(old_num_tes - 1) ;
+ for(U8 i = old_num_tes; i < num_tes ; i++)
+ {
+ setTE(i, *te) ;
+ mMediaImplList[i] = mMediaImplList[old_num_tes -1] ;
+ }
+ mMediaImplList[old_num_tes -1]->setUpdated(TRUE) ;
+ }
+ }
+ else if(old_num_tes > num_tes && mMediaImplList.size() > num_tes) //old faces removed
+ {
+ U8 end = mMediaImplList.size() ;
+ for(U8 i = num_tes; i < end ; i++)
+ {
+ removeMediaImpl(i) ;
+ }
+ mMediaImplList.resize(num_tes) ;
+
+ LLViewerObject::setNumTEs(num_tes) ;
+ }
+ else
+ {
+ LLViewerObject::setNumTEs(num_tes) ;
+ }
+
+ return ;
+}
+
+void LLVOVolume::setTEImage(const U8 te, LLViewerTexture *imagep)
+{
+ BOOL changed = (mTEImages[te] != imagep);
+ LLViewerObject::setTEImage(te, imagep);
+ if (changed)
+ {
+ gPipeline.markTextured(mDrawable);
+ mFaceMappingChanged = TRUE;
+ }
+}
+
+S32 LLVOVolume::setTETexture(const U8 te, const LLUUID &uuid)
+{
+ S32 res = LLViewerObject::setTETexture(te, uuid);
+ if (res)
+ {
+ gPipeline.markTextured(mDrawable);
+ mFaceMappingChanged = TRUE;
+ }
+ return res;
+}
+
+S32 LLVOVolume::setTEColor(const U8 te, const LLColor3& color)
+{
+ return setTEColor(te, LLColor4(color));
+}
+
+S32 LLVOVolume::setTEColor(const U8 te, const LLColor4& color)
+{
+ S32 retval = 0;
+ const LLTextureEntry *tep = getTE(te);
+ if (!tep)
+ {
+ llwarns << "No texture entry for te " << (S32)te << ", object " << mID << llendl;
+ }
+ else if (color != tep->getColor())
+ {
+ if (color.mV[3] != tep->getColor().mV[3])
+ {
+ gPipeline.markTextured(mDrawable);
+ }
+ retval = LLPrimitive::setTEColor(te, color);
+ if (mDrawable.notNull() && retval)
+ {
+ // These should only happen on updates which are not the initial update.
+ mDrawable->setState(LLDrawable::REBUILD_COLOR);
+ dirtyMesh();
+ }
+ }
+
+ return retval;
+}
+
+S32 LLVOVolume::setTEBumpmap(const U8 te, const U8 bumpmap)
+{
+ S32 res = LLViewerObject::setTEBumpmap(te, bumpmap);
+ if (res)
+ {
+ gPipeline.markTextured(mDrawable);
+ mFaceMappingChanged = TRUE;
+ }
+ return res;
+}
+
+S32 LLVOVolume::setTETexGen(const U8 te, const U8 texgen)
+{
+ S32 res = LLViewerObject::setTETexGen(te, texgen);
+ if (res)
+ {
+ gPipeline.markTextured(mDrawable);
+ mFaceMappingChanged = TRUE;
+ }
+ return res;
+}
+
+S32 LLVOVolume::setTEMediaTexGen(const U8 te, const U8 media)
+{
+ S32 res = LLViewerObject::setTEMediaTexGen(te, media);
+ if (res)
+ {
+ gPipeline.markTextured(mDrawable);
+ mFaceMappingChanged = TRUE;
+ }
+ return res;
+}
+
+S32 LLVOVolume::setTEShiny(const U8 te, const U8 shiny)
+{
+ S32 res = LLViewerObject::setTEShiny(te, shiny);
+ if (res)
+ {
+ gPipeline.markTextured(mDrawable);
+ mFaceMappingChanged = TRUE;
+ }
+ return res;
+}
+
+S32 LLVOVolume::setTEFullbright(const U8 te, const U8 fullbright)
+{
+ S32 res = LLViewerObject::setTEFullbright(te, fullbright);
+ if (res)
+ {
+ gPipeline.markTextured(mDrawable);
+ mFaceMappingChanged = TRUE;
+ }
+ return res;
+}
+
+S32 LLVOVolume::setTEBumpShinyFullbright(const U8 te, const U8 bump)
+{
+ S32 res = LLViewerObject::setTEBumpShinyFullbright(te, bump);
+ if (res)
+ {
+ gPipeline.markTextured(mDrawable);
+ mFaceMappingChanged = TRUE;
+ }
+ return res;
+}
+
+S32 LLVOVolume::setTEMediaFlags(const U8 te, const U8 media_flags)
+{
+ S32 res = LLViewerObject::setTEMediaFlags(te, media_flags);
+ if (res)
+ {
+ gPipeline.markTextured(mDrawable);
+ mFaceMappingChanged = TRUE;
+ }
+ return res;
+}
+
+S32 LLVOVolume::setTEGlow(const U8 te, const F32 glow)
+{
+ S32 res = LLViewerObject::setTEGlow(te, glow);
+ if (res)
+ {
+ gPipeline.markTextured(mDrawable);
+ mFaceMappingChanged = TRUE;
+ }
+ return res;
+}
+
+S32 LLVOVolume::setTEScale(const U8 te, const F32 s, const F32 t)
+{
+ S32 res = LLViewerObject::setTEScale(te, s, t);
+ if (res)
+ {
+ gPipeline.markTextured(mDrawable);
+ mFaceMappingChanged = TRUE;
+ }
+ return res;
+}
+
+S32 LLVOVolume::setTEScaleS(const U8 te, const F32 s)
+{
+ S32 res = LLViewerObject::setTEScaleS(te, s);
+ if (res)
+ {
+ gPipeline.markTextured(mDrawable);
+ mFaceMappingChanged = TRUE;
+ }
+ return res;
+}
+
+S32 LLVOVolume::setTEScaleT(const U8 te, const F32 t)
+{
+ S32 res = LLViewerObject::setTEScaleT(te, t);
+ if (res)
+ {
+ gPipeline.markTextured(mDrawable);
+ mFaceMappingChanged = TRUE;
+ }
+ return res;
+}
+
+void LLVOVolume::updateTEData()
+{
+ /*if (mDrawable.notNull())
+ {
+ mFaceMappingChanged = TRUE;
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_MATERIAL, TRUE);
+ }*/
+}
+
+bool LLVOVolume::hasMedia() const
+{
+ bool result = false;
+ const U8 numTEs = getNumTEs();
+ for (U8 i = 0; i < numTEs; i++)
+ {
+ const LLTextureEntry* te = getTE(i);
+ if(te->hasMedia())
+ {
+ result = true;
+ break;
+ }
+ }
+ return result;
+}
+
+LLVector3 LLVOVolume::getApproximateFaceNormal(U8 face_id)
+{
+ LLVolume* volume = getVolume();
+ LLVector4a result;
+ result.clear();
+
+ LLVector3 ret;
+
+ if (volume && face_id < volume->getNumVolumeFaces())
+ {
+ const LLVolumeFace& face = volume->getVolumeFace(face_id);
+ for (S32 i = 0; i < (S32)face.mNumVertices; ++i)
+ {
+ result.add(face.mNormals[i]);
+ }
+
+ LLVector3 ret(result.getF32ptr());
+ ret = volumeDirectionToAgent(ret);
+ ret.normVec();
+ }
+
+ return ret;
+}
+
+void LLVOVolume::requestMediaDataUpdate(bool isNew)
+{
+ if (sObjectMediaClient)
+ sObjectMediaClient->fetchMedia(new LLMediaDataClientObjectImpl(this, isNew));
+}
+
+bool LLVOVolume::isMediaDataBeingFetched() const
+{
+ // I know what I'm doing by const_casting this away: this is just
+ // a wrapper class that is only going to do a lookup.
+ return (sObjectMediaClient) ? sObjectMediaClient->isInQueue(new LLMediaDataClientObjectImpl(const_cast<LLVOVolume*>(this), false)) : false;
+}
+
+void LLVOVolume::cleanUpMediaImpls()
+{
+ // Iterate through our TEs and remove any Impls that are no longer used
+ const U8 numTEs = getNumTEs();
+ for (U8 i = 0; i < numTEs; i++)
+ {
+ const LLTextureEntry* te = getTE(i);
+ if( ! te->hasMedia())
+ {
+ // Delete the media IMPL!
+ removeMediaImpl(i) ;
+ }
+ }
+}
+
+void LLVOVolume::updateObjectMediaData(const LLSD &media_data_array, const std::string &media_version)
+{
+ // media_data_array is an array of media entry maps
+ // media_version is the version string in the response.
+ U32 fetched_version = LLTextureEntry::getVersionFromMediaVersionString(media_version);
+
+ // Only update it if it is newer!
+ if ( (S32)fetched_version > mLastFetchedMediaVersion)
+ {
+ mLastFetchedMediaVersion = fetched_version;
+ //llinfos << "updating:" << this->getID() << " " << ll_pretty_print_sd(media_data_array) << llendl;
+
+ LLSD::array_const_iterator iter = media_data_array.beginArray();
+ LLSD::array_const_iterator end = media_data_array.endArray();
+ U8 texture_index = 0;
+ for (; iter != end; ++iter, ++texture_index)
+ {
+ syncMediaData(texture_index, *iter, false/*merge*/, false/*ignore_agent*/);
+ }
+ }
+}
+
+void LLVOVolume::syncMediaData(S32 texture_index, const LLSD &media_data, bool merge, bool ignore_agent)
+{
+ if(mDead)
+ {
+ // If the object has been marked dead, don't process media updates.
+ return;
+ }
+
+ LLTextureEntry *te = getTE(texture_index);
+ if(!te)
+ {
+ return ;
+ }
+
+ LL_DEBUGS("MediaOnAPrim") << "BEFORE: texture_index = " << texture_index
+ << " hasMedia = " << te->hasMedia() << " : "
+ << ((NULL == te->getMediaData()) ? "NULL MEDIA DATA" : ll_pretty_print_sd(te->getMediaData()->asLLSD())) << llendl;
+
+ std::string previous_url;
+ LLMediaEntry* mep = te->getMediaData();
+ if(mep)
+ {
+ // Save the "current url" from before the update so we can tell if
+ // it changes.
+ previous_url = mep->getCurrentURL();
+ }
+
+ if (merge)
+ {
+ te->mergeIntoMediaData(media_data);
+ }
+ else {
+ // XXX Question: what if the media data is undefined LLSD, but the
+ // update we got above said that we have media flags?? Here we clobber
+ // that, assuming the data from the service is more up-to-date.
+ te->updateMediaData(media_data);
+ }
+
+ mep = te->getMediaData();
+ if(mep)
+ {
+ bool update_from_self = false;
+ if (!ignore_agent)
+ {
+ LLUUID updating_agent = LLTextureEntry::getAgentIDFromMediaVersionString(getMediaURL());
+ update_from_self = (updating_agent == gAgent.getID());
+ }
+ viewer_media_t media_impl = LLViewerMedia::updateMediaImpl(mep, previous_url, update_from_self);
+
+ addMediaImpl(media_impl, texture_index) ;
+ }
+ else
+ {
+ removeMediaImpl(texture_index);
+ }
+
+ LL_DEBUGS("MediaOnAPrim") << "AFTER: texture_index = " << texture_index
+ << " hasMedia = " << te->hasMedia() << " : "
+ << ((NULL == te->getMediaData()) ? "NULL MEDIA DATA" : ll_pretty_print_sd(te->getMediaData()->asLLSD())) << llendl;
+}
+
+void LLVOVolume::mediaNavigateBounceBack(U8 texture_index)
+{
+ // Find the media entry for this navigate
+ const LLMediaEntry* mep = NULL;
+ viewer_media_t impl = getMediaImpl(texture_index);
+ LLTextureEntry *te = getTE(texture_index);
+ if(te)
+ {
+ mep = te->getMediaData();
+ }
+
+ if (mep && impl)
+ {
+ std::string url = mep->getCurrentURL();
+ // Look for a ":", if not there, assume "http://"
+ if (!url.empty() && std::string::npos == url.find(':'))
+ {
+ url = "http://" + url;
+ }
+ // If the url we're trying to "bounce back" to is either empty or not
+ // allowed by the whitelist, try the home url. If *that* doesn't work,
+ // set the media as failed and unload it
+ if (url.empty() || !mep->checkCandidateUrl(url))
+ {
+ url = mep->getHomeURL();
+ // Look for a ":", if not there, assume "http://"
+ if (!url.empty() && std::string::npos == url.find(':'))
+ {
+ url = "http://" + url;
+ }
+ }
+ if (url.empty() || !mep->checkCandidateUrl(url))
+ {
+ // The url to navigate back to is not good, and we have nowhere else
+ // to go.
+ LL_WARNS("MediaOnAPrim") << "FAILED to bounce back URL \"" << url << "\" -- unloading impl" << LL_ENDL;
+ impl->setMediaFailed(true);
+ }
+ else {
+ // Okay, navigate now
+ LL_INFOS("MediaOnAPrim") << "bouncing back to URL: " << url << LL_ENDL;
+ impl->navigateTo(url, "", false, true);
+ }
+ }
+}
+
+bool LLVOVolume::hasMediaPermission(const LLMediaEntry* media_entry, MediaPermType perm_type)
+{
+ // NOTE: This logic ALMOST duplicates the logic in the server (in particular, in llmediaservice.cpp).
+ if (NULL == media_entry ) return false; // XXX should we assert here?
+
+ // The agent has permissions if:
+ // - world permissions are on, or
+ // - group permissions are on, and agent_id is in the group, or
+ // - agent permissions are on, and agent_id is the owner
+
+ // *NOTE: We *used* to check for modify permissions here (i.e. permissions were
+ // granted if permModify() was true). However, this doesn't make sense in the
+ // viewer: we don't want to show controls or allow interaction if the author
+ // has deemed it so. See DEV-42115.
+
+ U8 media_perms = (perm_type == MEDIA_PERM_INTERACT) ? media_entry->getPermsInteract() : media_entry->getPermsControl();
+
+ // World permissions
+ if (0 != (media_perms & LLMediaEntry::PERM_ANYONE))
+ {
+ return true;
+ }
+
+ // Group permissions
+ else if (0 != (media_perms & LLMediaEntry::PERM_GROUP))
+ {
+ LLPermissions* obj_perm = LLSelectMgr::getInstance()->findObjectPermissions(this);
+ if (obj_perm && gAgent.isInGroup(obj_perm->getGroup()))
+ {
+ return true;
+ }
+ }
+
+ // Owner permissions
+ else if (0 != (media_perms & LLMediaEntry::PERM_OWNER) && permYouOwner())
+ {
+ return true;
+ }
+
+ return false;
+
+}
+
+void LLVOVolume::mediaNavigated(LLViewerMediaImpl *impl, LLPluginClassMedia* plugin, std::string new_location)
+{
+ bool block_navigation = false;
+ // FIXME: if/when we allow the same media impl to be used by multiple faces, the logic here will need to be fixed
+ // to deal with multiple face indices.
+ int face_index = getFaceIndexWithMediaImpl(impl, -1);
+
+ // Find the media entry for this navigate
+ LLMediaEntry* mep = NULL;
+ LLTextureEntry *te = getTE(face_index);
+ if(te)
+ {
+ mep = te->getMediaData();
+ }
+
+ if(mep)
+ {
+ if(!mep->checkCandidateUrl(new_location))
+ {
+ block_navigation = true;
+ }
+ if (!block_navigation && !hasMediaPermission(mep, MEDIA_PERM_INTERACT))
+ {
+ block_navigation = true;
+ }
+ }
+ else
+ {
+ LL_WARNS("MediaOnAPrim") << "Couldn't find media entry!" << LL_ENDL;
+ }
+
+ if(block_navigation)
+ {
+ LL_INFOS("MediaOnAPrim") << "blocking navigate to URI " << new_location << LL_ENDL;
+
+ // "bounce back" to the current URL from the media entry
+ mediaNavigateBounceBack(face_index);
+ }
+ else if (sObjectMediaNavigateClient)
+ {
+
+ LL_DEBUGS("MediaOnAPrim") << "broadcasting navigate with URI " << new_location << LL_ENDL;
+
+ sObjectMediaNavigateClient->navigate(new LLMediaDataClientObjectImpl(this, false), face_index, new_location);
+ }
+}
+
+void LLVOVolume::mediaEvent(LLViewerMediaImpl *impl, LLPluginClassMedia* plugin, LLViewerMediaObserver::EMediaEvent event)
+{
+ switch(event)
+ {
+
+ case LLViewerMediaObserver::MEDIA_EVENT_LOCATION_CHANGED:
+ {
+ switch(impl->getNavState())
+ {
+ case LLViewerMediaImpl::MEDIANAVSTATE_FIRST_LOCATION_CHANGED:
+ {
+ // This is the first location changed event after the start of a non-server-directed nav. It may need to be broadcast or bounced back.
+ mediaNavigated(impl, plugin, plugin->getLocation());
+ }
+ break;
+
+ case LLViewerMediaImpl::MEDIANAVSTATE_FIRST_LOCATION_CHANGED_SPURIOUS:
+ // This navigate didn't change the current URL.
+ LL_DEBUGS("MediaOnAPrim") << " NOT broadcasting navigate (spurious)" << LL_ENDL;
+ break;
+
+ case LLViewerMediaImpl::MEDIANAVSTATE_SERVER_FIRST_LOCATION_CHANGED:
+ // This is the first location changed event after the start of a server-directed nav. Don't broadcast it.
+ LL_INFOS("MediaOnAPrim") << " NOT broadcasting navigate (server-directed)" << LL_ENDL;
+ break;
+
+ default:
+ // This is a subsequent location-changed due to a redirect. Don't broadcast.
+ LL_INFOS("MediaOnAPrim") << " NOT broadcasting navigate (redirect)" << LL_ENDL;
+ break;
+ }
+ }
+ break;
+
+ case LLViewerMediaObserver::MEDIA_EVENT_NAVIGATE_COMPLETE:
+ {
+ switch(impl->getNavState())
+ {
+ case LLViewerMediaImpl::MEDIANAVSTATE_COMPLETE_BEFORE_LOCATION_CHANGED:
+ {
+ // This is the first location changed event after the start of a non-server-directed nav. It may need to be broadcast or bounced back.
+ mediaNavigated(impl, plugin, plugin->getNavigateURI());
+ }
+ break;
+
+ case LLViewerMediaImpl::MEDIANAVSTATE_COMPLETE_BEFORE_LOCATION_CHANGED_SPURIOUS:
+ // This navigate didn't change the current URL.
+ LL_DEBUGS("MediaOnAPrim") << " NOT broadcasting navigate (spurious)" << LL_ENDL;
+ break;
+
+ case LLViewerMediaImpl::MEDIANAVSTATE_SERVER_COMPLETE_BEFORE_LOCATION_CHANGED:
+ // This is the the navigate complete event from a server-directed nav. Don't broadcast it.
+ LL_INFOS("MediaOnAPrim") << " NOT broadcasting navigate (server-directed)" << LL_ENDL;
+ break;
+
+ default:
+ // For all other states, the navigate should have been handled by LOCATION_CHANGED events already.
+ break;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+}
+
+void LLVOVolume::sendMediaDataUpdate()
+{
+ if (sObjectMediaClient)
+ sObjectMediaClient->updateMedia(new LLMediaDataClientObjectImpl(this, false));
+}
+
+void LLVOVolume::removeMediaImpl(S32 texture_index)
+{
+ if(mMediaImplList.size() <= (U32)texture_index || mMediaImplList[texture_index].isNull())
+ {
+ return ;
+ }
+
+ //make the face referencing to mMediaImplList[texture_index] to point back to the old texture.
+ if(mDrawable && texture_index < mDrawable->getNumFaces())
+ {
+ LLFace* facep = mDrawable->getFace(texture_index) ;
+ if(facep)
+ {
+ LLViewerMediaTexture* media_tex = LLViewerTextureManager::findMediaTexture(mMediaImplList[texture_index]->getMediaTextureID()) ;
+ if(media_tex)
+ {
+ media_tex->removeMediaFromFace(facep) ;
+ }
+ }
+ }
+
+ //check if some other face(s) of this object reference(s)to this media impl.
+ S32 i ;
+ S32 end = (S32)mMediaImplList.size() ;
+ for(i = 0; i < end ; i++)
+ {
+ if( i != texture_index && mMediaImplList[i] == mMediaImplList[texture_index])
+ {
+ break ;
+ }
+ }
+
+ if(i == end) //this object does not need this media impl.
+ {
+ mMediaImplList[texture_index]->removeObject(this) ;
+ }
+
+ mMediaImplList[texture_index] = NULL ;
+ return ;
+}
+
+void LLVOVolume::addMediaImpl(LLViewerMediaImpl* media_impl, S32 texture_index)
+{
+ if((S32)mMediaImplList.size() < texture_index + 1)
+ {
+ mMediaImplList.resize(texture_index + 1) ;
+ }
+
+ if(mMediaImplList[texture_index].notNull())
+ {
+ if(mMediaImplList[texture_index] == media_impl)
+ {
+ return ;
+ }
+
+ removeMediaImpl(texture_index) ;
+ }
+
+ mMediaImplList[texture_index] = media_impl;
+ media_impl->addObject(this) ;
+
+ //add the face to show the media if it is in playing
+ if(mDrawable)
+ {
+ LLFace* facep = mDrawable->getFace(texture_index) ;
+ if(facep)
+ {
+ LLViewerMediaTexture* media_tex = LLViewerTextureManager::findMediaTexture(mMediaImplList[texture_index]->getMediaTextureID()) ;
+ if(media_tex)
+ {
+ media_tex->addMediaToFace(facep) ;
+ }
+ }
+ else //the face is not available now, start media on this face later.
+ {
+ media_impl->setUpdated(TRUE) ;
+ }
+ }
+ return ;
+}
+
+viewer_media_t LLVOVolume::getMediaImpl(U8 face_id) const
+{
+ if(mMediaImplList.size() > face_id)
+ {
+ return mMediaImplList[face_id];
+ }
+ return NULL;
+}
+
+F64 LLVOVolume::getTotalMediaInterest() const
+{
+ // If this object is currently focused, this object has "high" interest
+ if (LLViewerMediaFocus::getInstance()->getFocusedObjectID() == getID())
+ return F64_MAX;
+
+ F64 interest = (F64)-1.0; // means not interested;
+
+ // If this object is selected, this object has "high" interest, but since
+ // there can be more than one, we still add in calculated impl interest
+ // XXX Sadly, 'contains()' doesn't take a const :(
+ if (LLSelectMgr::getInstance()->getSelection()->contains(const_cast<LLVOVolume*>(this)))
+ interest = F64_MAX / 2.0;
+
+ int i = 0;
+ const int end = getNumTEs();
+ for ( ; i < end; ++i)
+ {
+ const viewer_media_t &impl = getMediaImpl(i);
+ if (!impl.isNull())
+ {
+ if (interest == (F64)-1.0) interest = (F64)0.0;
+ interest += impl->getInterest();
+ }
+ }
+ return interest;
+}
+
+S32 LLVOVolume::getFaceIndexWithMediaImpl(const LLViewerMediaImpl* media_impl, S32 start_face_id)
+{
+ S32 end = (S32)mMediaImplList.size() ;
+ for(S32 face_id = start_face_id + 1; face_id < end; face_id++)
+ {
+ if(mMediaImplList[face_id] == media_impl)
+ {
+ return face_id ;
+ }
+ }
+ return -1 ;
+}
+
+//----------------------------------------------------------------------------
+
+void LLVOVolume::setLightTextureID(LLUUID id)
+{
+ if (id.notNull())
+ {
+ if (!hasLightTexture())
+ {
+ setParameterEntryInUse(LLNetworkData::PARAMS_LIGHT_IMAGE, TRUE, true);
+ }
+ LLLightImageParams* param_block = (LLLightImageParams*) getParameterEntry(LLNetworkData::PARAMS_LIGHT_IMAGE);
+ if (param_block && param_block->getLightTexture() != id)
+ {
+ param_block->setLightTexture(id);
+ parameterChanged(LLNetworkData::PARAMS_LIGHT_IMAGE, true);
+ }
+ }
+ else
+ {
+ if (hasLightTexture())
+ {
+ setParameterEntryInUse(LLNetworkData::PARAMS_LIGHT_IMAGE, FALSE, true);
+ mLightTexture = NULL;
+ }
+ }
+}
+
+void LLVOVolume::setSpotLightParams(LLVector3 params)
+{
+ LLLightImageParams* param_block = (LLLightImageParams*) getParameterEntry(LLNetworkData::PARAMS_LIGHT_IMAGE);
+ if (param_block && param_block->getParams() != params)
+ {
+ param_block->setParams(params);
+ parameterChanged(LLNetworkData::PARAMS_LIGHT_IMAGE, true);
+ }
+}
+
+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);
+ }
+ }
+}
+
+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);
+ gPipeline.markTextured(mDrawable);
+ mFaceMappingChanged = 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);
+ }
+}
+
+LLUUID LLVOVolume::getLightTextureID() const
+{
+ if (getParameterEntryInUse(LLNetworkData::PARAMS_LIGHT_IMAGE))
+ {
+ const LLLightImageParams *param_block = (const LLLightImageParams *)getParameterEntry(LLNetworkData::PARAMS_LIGHT_IMAGE);
+ if (param_block)
+ {
+ return param_block->getLightTexture();
+ }
+ }
+
+ return LLUUID::null;
+}
+
+
+LLVector3 LLVOVolume::getSpotLightParams() const
+{
+ if (getParameterEntryInUse(LLNetworkData::PARAMS_LIGHT_IMAGE))
+ {
+ const LLLightImageParams *param_block = (const LLLightImageParams *)getParameterEntry(LLNetworkData::PARAMS_LIGHT_IMAGE);
+ if (param_block)
+ {
+ return param_block->getParams();
+ }
+ }
+
+ return LLVector3();
+}
+
+F32 LLVOVolume::getSpotLightPriority() const
+{
+ return mSpotLightPriority;
+}
+
+void LLVOVolume::updateSpotLightPriority()
+{
+ LLVector3 pos = mDrawable->getPositionAgent();
+ LLVector3 at(0,0,-1);
+ at *= getRenderRotation();
+
+ F32 r = getLightRadius()*0.5f;
+
+ pos += at * r;
+
+ at = LLViewerCamera::getInstance()->getAtAxis();
+
+ pos -= at * r;
+
+ mSpotLightPriority = gPipeline.calcPixelArea(pos, LLVector3(r,r,r), *LLViewerCamera::getInstance());
+
+ if (mLightTexture.notNull())
+ {
+ mLightTexture->addTextureStats(mSpotLightPriority);
+ mLightTexture->setBoostLevel(LLViewerTexture::BOOST_CLOUDS);
+ }
+}
+
+
+bool LLVOVolume::isLightSpotlight() const
+{
+ LLLightImageParams* params = (LLLightImageParams*) getParameterEntry(LLNetworkData::PARAMS_LIGHT_IMAGE);
+ if (params)
+ {
+ return params->isLightSpotlight();
+ }
+ return false;
+}
+
+
+LLViewerTexture* LLVOVolume::getLightTexture()
+{
+ LLUUID id = getLightTextureID();
+
+ if (id.notNull())
+ {
+ if (mLightTexture.isNull() || id != mLightTexture->getID())
+ {
+ mLightTexture = LLViewerTextureManager::getFetchedTexture(id);
+ }
+ }
+ else
+ {
+ mLightTexture = NULL;
+ }
+
+ return mLightTexture;
+}
+
+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;
+ }
+}
+
+U32 LLVOVolume::getVolumeInterfaceID() const
+{
+ if (mVolumeImpl)
+ {
+ return mVolumeImpl->getID();
+ }
+
+ return 0;
+}
+
+BOOL LLVOVolume::isFlexible() const
+{
+ if (getParameterEntryInUse(LLNetworkData::PARAMS_FLEXIBLE))
+ {
+ LLVolume* volume = getVolume();
+ if (volume && volume->getParams().getPathParams().getCurveType() != LL_PCODE_PATH_FLEXIBLE)
+ {
+ 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::isSculpted() const
+{
+ if (getParameterEntryInUse(LLNetworkData::PARAMS_SCULPT))
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+BOOL LLVOVolume::isMesh() const
+{
+ if (isSculpted())
+ {
+ LLSculptParams *sculpt_params = (LLSculptParams *)getParameterEntry(LLNetworkData::PARAMS_SCULPT);
+ U8 sculpt_type = sculpt_params->getSculptType();
+
+ if ((sculpt_type & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH)
+ // mesh is a mesh
+ {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+BOOL LLVOVolume::hasLightTexture() const
+{
+ if (getParameterEntryInUse(LLNetworkData::PARAMS_LIGHT_IMAGE))
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+BOOL LLVOVolume::isVolumeGlobal() const
+{
+ if (mVolumeImpl)
+ {
+ return mVolumeImpl->isVolumeGlobal() ? TRUE : FALSE;
+ }
+ else if (mRiggedVolume.notNull())
+ {
+ return TRUE;
+ }
+
+ 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;
+
+ //transform view vector into volume space
+ view_vector -= getRenderPosition();
+ mDrawable->mDistanceWRTCamera = view_vector.length();
+ LLQuaternion worldRot = getRenderRotation();
+ view_vector = view_vector * ~worldRot;
+ if (!isVolumeGlobal())
+ {
+ 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();
+ LLMatrix4 trans_mat = mRelativeXform;
+ if (mDrawable->isStatic())
+ {
+ trans_mat.translate(getRegion()->getOriginAgent());
+ }
+
+ volume->generateSilhouetteVertices(nodep->mSilhouetteVertices, nodep->mSilhouetteNormals, view_vector, trans_mat, mRelativeXformInvTrans, nodep->getTESelectMask());
+
+ nodep->mSilhouetteExists = TRUE;
+ }
+}
+
+void LLVOVolume::deleteFaces()
+{
+ S32 face_count = mNumFaces;
+ if (mDrawable.notNull())
+ {
+ mDrawable->deleteFaces(0, face_count);
+ }
+
+ mNumFaces = 0;
+}
+
+void LLVOVolume::updateRadius()
+{
+ if (mDrawable.isNull())
+ {
+ return;
+ }
+
+ mVObjRadius = getScale().length();
+ mDrawable->setRadius(mVObjRadius);
+}
+
+
+BOOL LLVOVolume::isAttachment() const
+{
+ return mState != 0 ;
+}
+
+BOOL LLVOVolume::isHUDAttachment() const
+{
+ // *NOTE: we 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();
+}
+
+// Returns a base cost and adds textures to passed in set.
+// total cost is returned value + 5 * size of the resulting set.
+// Cannot include cost of textures, as they may be re-used in linked
+// children, and cost should only be increased for unique textures -Nyx
+U32 LLVOVolume::getRenderCost(texture_cost_t &textures) const
+{
+ // Get access to params we'll need at various points.
+ // Skip if this is object doesn't have a volume (e.g. is an avatar).
+ BOOL has_volume = (getVolume() != NULL);
+ LLVolumeParams volume_params;
+ LLPathParams path_params;
+ LLProfileParams profile_params;
+
+ U32 num_triangles = 0;
+
+ // per-prim costs
+ static const U32 ARC_PARTICLE_COST = 1; // determined experimentally
+ static const U32 ARC_PARTICLE_MAX = 2048; // default values
+ static const U32 ARC_TEXTURE_COST = 16; // multiplier for texture resolution - performance tested
+
+ // per-prim multipliers
+ static const F32 ARC_GLOW_MULT = 1.5f; // tested based on performance
+ static const F32 ARC_BUMP_MULT = 1.25f; // tested based on performance
+ static const F32 ARC_FLEXI_MULT = 5; // tested based on performance
+ static const F32 ARC_SHINY_MULT = 1.6f; // tested based on performance
+ static const F32 ARC_INVISI_COST = 1.2f; // tested based on performance
+ static const F32 ARC_WEIGHTED_MESH = 1.2f; // tested based on performance
+
+ static const F32 ARC_PLANAR_COST = 1.0f; // tested based on performance to have negligible impact
+ static const F32 ARC_ANIM_TEX_COST = 4.f; // tested based on performance
+ static const F32 ARC_ALPHA_COST = 4.f; // 4x max - based on performance
+
+ F32 shame = 0;
+
+ U32 invisi = 0;
+ U32 shiny = 0;
+ U32 glow = 0;
+ U32 alpha = 0;
+ U32 flexi = 0;
+ U32 animtex = 0;
+ U32 particles = 0;
+ U32 bump = 0;
+ U32 planar = 0;
+ U32 weighted_mesh = 0;
+
+ // these multipliers are variable and can be floating point
+ F32 scale = 0.f;
+
+ const LLDrawable* drawablep = mDrawable;
+ U32 num_faces = drawablep->getNumFaces();
+
+ if (has_volume)
+ {
+ volume_params = getVolume()->getParams();
+ path_params = volume_params.getPathParams();
+ profile_params = volume_params.getProfileParams();
+
+ F32 weighted_triangles = -1.0;
+ getStreamingCost(NULL, NULL, &weighted_triangles);
+
+ if (weighted_triangles > 0.0)
+ {
+ num_triangles = (U32)weighted_triangles;
+ }
+ }
+
+ if (num_triangles == 0)
+ {
+ num_triangles = 4;
+ }
+
+ if (isSculpted())
+ {
+ if (isMesh())
+ {
+ // base cost is dependent on mesh complexity
+ // note that 3 is the highest LOD as of the time of this coding.
+ S32 size = gMeshRepo.getMeshSize(volume_params.getSculptID(),3);
+ if ( size > 0)
+ {
+ if (gMeshRepo.getSkinInfo(volume_params.getSculptID(), this))
+ {
+ // weighted attachment - 1 point for every 3 bytes
+ weighted_mesh = 1;
+ }
+
+ }
+ else
+ {
+ // something went wrong - user should know their content isn't render-free
+ return 0;
+ }
+ }
+ else
+ {
+ const LLSculptParams *sculpt_params = (LLSculptParams *) getParameterEntry(LLNetworkData::PARAMS_SCULPT);
+ LLUUID sculpt_id = sculpt_params->getSculptTexture();
+ if (textures.find(sculpt_id) == textures.end())
+ {
+ LLViewerFetchedTexture *texture = LLViewerTextureManager::getFetchedTexture(sculpt_id);
+ if (texture)
+ {
+ S32 texture_cost = 256 + (S32)(ARC_TEXTURE_COST * (texture->getFullHeight() / 128.f + texture->getFullWidth() / 128.f));
+ textures.insert(texture_cost_t::value_type(sculpt_id, texture_cost));
+ }
+ }
+ }
+ }
+
+ if (isFlexible())
+ {
+ flexi = 1;
+ }
+ if (isParticleSource())
+ {
+ particles = 1;
+ }
+
+ const LLVector3& sc = getScale();
+ scale += (sc.mV[0] + sc.mV[1] + sc.mV[2]) / 4.f; // scale to 1/4 the sum of the size
+ // enforce scale multiplier to be in the range [1,7] (7 was determined to experimentally be a reasonable max)
+ scale = scale > 7.f ? 7.f : scale;
+ scale = scale < 1.f ? 1.f : scale;
+
+ for (S32 i = 0; i < num_faces; ++i)
+ {
+ const LLFace* face = drawablep->getFace(i);
+ const LLTextureEntry* te = face->getTextureEntry();
+ const LLViewerTexture* img = face->getTexture();
+
+ if (img)
+ {
+ if (textures.find(img->getID()) == textures.end())
+ {
+ S32 texture_cost = 256 + (S32)(ARC_TEXTURE_COST * (img->getFullHeight() / 128.f + img->getFullWidth() / 128.f));
+ textures.insert(texture_cost_t::value_type(img->getID(), texture_cost));
+ }
+ }
+
+ if (face->getPoolType() == LLDrawPool::POOL_ALPHA)
+ {
+ alpha = 1;
+ }
+ else if (img && img->getPrimaryFormat() == GL_ALPHA)
+ {
+ invisi = 1;
+ }
+
+ if (te)
+ {
+ if (te->getBumpmap())
+ {
+ // bump is a multiplier, don't add per-face
+ bump = 1;
+ }
+ if (te->getShiny())
+ {
+ // shiny is a multiplier, don't add per-face
+ shiny = 1;
+ }
+ if (te->getGlow() > 0.f)
+ {
+ // glow is a multiplier, don't add per-face
+ glow = 1;
+ }
+ if (face->mTextureMatrix != NULL)
+ {
+ animtex = 1;
+ }
+ if (te->getTexGen())
+ {
+ planar = 1;
+ }
+ }
+ }
+
+ // shame currently has the "base" cost of 1 point per 15 triangles, min 2.
+ shame = num_triangles / 15.f;
+ shame = shame < 2.f ? 2.f : shame;
+
+ // factor in scale
+ shame *= scale;
+
+ // multiply by per-face modifiers
+ if (planar)
+ {
+ shame *= planar * ARC_PLANAR_COST;
+ }
+
+ if (animtex)
+ {
+ shame *= animtex * ARC_ANIM_TEX_COST;
+ }
+
+ if (alpha)
+ {
+ shame *= alpha * ARC_ALPHA_COST;
+ }
+
+ if(invisi)
+ {
+ shame *= invisi * ARC_INVISI_COST;
+ }
+
+ if (glow)
+ {
+ shame *= glow * ARC_GLOW_MULT;
+ }
+
+ if (bump)
+ {
+ shame *= bump * ARC_BUMP_MULT;
+ }
+
+ if (shiny)
+ {
+ shame *= shiny * ARC_SHINY_MULT;
+ }
+
+
+ // multiply shame by multipliers
+ if (weighted_mesh)
+ {
+ shame *= weighted_mesh * ARC_WEIGHTED_MESH;
+ }
+
+ if (flexi)
+ {
+ shame *= flexi * ARC_FLEXI_MULT;
+ }
+
+
+ // add additional costs
+ if (particles)
+ {
+ const LLPartSysData *part_sys_data = &(mPartSourcep->mPartSysData);
+ const LLPartData *part_data = &(part_sys_data->mPartData);
+ U32 num_particles = (U32)(part_sys_data->mBurstPartCount * llceil( part_data->mMaxAge / part_sys_data->mBurstRate));
+ num_particles = num_particles > ARC_PARTICLE_MAX ? ARC_PARTICLE_MAX : num_particles;
+ F32 part_size = (llmax(part_data->mStartScale[0], part_data->mEndScale[0]) + llmax(part_data->mStartScale[1], part_data->mEndScale[1])) / 2.f;
+ shame += num_particles * part_size * ARC_PARTICLE_COST;
+ }
+
+ if (shame > mRenderComplexity_current)
+ {
+ mRenderComplexity_current = (S32)shame;
+ }
+
+ return (U32)shame;
+}
+
+F32 LLVOVolume::getStreamingCost(S32* bytes, S32* visible_bytes, F32* unscaled_value) const
+{
+ F32 radius = getScale().length()*0.5f;
+
+ if (isMesh())
+ {
+ LLSD& header = gMeshRepo.getMeshHeader(getVolume()->getParams().getSculptID());
+
+ return LLMeshRepository::getStreamingCost(header, radius, bytes, visible_bytes, mLOD, unscaled_value);
+ }
+ else
+ {
+ LLVolume* volume = getVolume();
+ S32 counts[4];
+ LLVolume::getLoDTriangleCounts(volume->getParams(), counts);
+
+ LLSD header;
+ header["lowest_lod"]["size"] = counts[0] * 10;
+ header["low_lod"]["size"] = counts[1] * 10;
+ header["medium_lod"]["size"] = counts[2] * 10;
+ header["high_lod"]["size"] = counts[3] * 10;
+
+ return LLMeshRepository::getStreamingCost(header, radius, NULL, NULL, -1, unscaled_value);
+ }
+}
+
+//static
+void LLVOVolume::updateRenderComplexity()
+{
+ mRenderComplexity_last = mRenderComplexity_current;
+ mRenderComplexity_current = 0;
+}
+
+U32 LLVOVolume::getTriangleCount() const
+{
+ U32 count = 0;
+ LLVolume* volume = getVolume();
+ if (volume)
+ {
+ count = volume->getNumTriangles();
+ }
+
+ return count;
+}
+
+U32 LLVOVolume::getHighLODTriangleCount()
+{
+ U32 ret = 0;
+
+ LLVolume* volume = getVolume();
+
+ if (!isSculpted())
+ {
+ LLVolume* ref = LLPrimitive::getVolumeManager()->refVolume(volume->getParams(), 3);
+ ret = ref->getNumTriangles();
+ LLPrimitive::getVolumeManager()->unrefVolume(ref);
+ }
+ else if (isMesh())
+ {
+ LLVolume* ref = LLPrimitive::getVolumeManager()->refVolume(volume->getParams(), 3);
+ if (!ref->isMeshAssetLoaded() || ref->getNumVolumeFaces() == 0)
+ {
+ gMeshRepo.loadMesh(this, volume->getParams(), LLModel::LOD_HIGH);
+ }
+ ret = ref->getNumTriangles();
+ LLPrimitive::getVolumeManager()->unrefVolume(ref);
+ }
+ else
+ { //default sculpts have a constant number of triangles
+ ret = 31*2*31; //31 rows of 31 columns of quads for a 32x32 vertex patch
+ }
+
+ return ret;
+}
+
+//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::setSelected(BOOL sel)
+{
+ LLViewerObject::setSelected(sel);
+ if (mDrawable.notNull())
+ {
+ markForUpdate(TRUE);
+ }
+}
+
+void LLVOVolume::updateSpatialExtents(LLVector4a& newMin, LLVector4a& newMax)
+{
+}
+
+F32 LLVOVolume::getBinRadius()
+{
+ F32 radius;
+
+ F32 scale = 1.f;
+
+ S32 size_factor = llmax(gSavedSettings.getS32("OctreeStaticObjectSizeFactor"), 1);
+ S32 attachment_size_factor = llmax(gSavedSettings.getS32("OctreeAttachmentSizeFactor"), 1);
+ LLVector3 distance_factor = gSavedSettings.getVector3("OctreeDistanceFactor");
+ LLVector3 alpha_distance_factor = gSavedSettings.getVector3("OctreeAlphaDistanceFactor");
+ const LLVector4a* ext = mDrawable->getSpatialExtents();
+
+ BOOL shrink_wrap = mDrawable->isAnimating();
+ BOOL alpha_wrap = FALSE;
+
+ if (!isHUDAttachment())
+ {
+ for (S32 i = 0; i < mDrawable->getNumFaces(); i++)
+ {
+ LLFace* face = mDrawable->getFace(i);
+ if (face->getPoolType() == LLDrawPool::POOL_ALPHA &&
+ !face->canRenderAsMask())
+ {
+ alpha_wrap = TRUE;
+ break;
+ }
+ }
+ }
+ else
+ {
+ shrink_wrap = FALSE;
+ }
+
+ if (alpha_wrap)
+ {
+ LLVector3 bounds = getScale();
+ radius = llmin(bounds.mV[1], bounds.mV[2]);
+ radius = llmin(radius, bounds.mV[0]);
+ radius *= 0.5f;
+ radius *= 1.f+mDrawable->mDistanceWRTCamera*alpha_distance_factor[1];
+ radius += mDrawable->mDistanceWRTCamera*alpha_distance_factor[0];
+ }
+ else if (shrink_wrap)
+ {
+ LLVector4a rad;
+ rad.setSub(ext[1], ext[0]);
+
+ radius = rad.getLength3().getF32()*0.5f;
+ }
+ else if (mDrawable->isStatic())
+ {
+ radius = llmax((S32) mDrawable->getRadius(), 1)*size_factor;
+ radius *= 1.f + mDrawable->mDistanceWRTCamera * distance_factor[1];
+ radius += mDrawable->mDistanceWRTCamera * distance_factor[0];
+ }
+ else if (mDrawable->getVObj()->isAttachment())
+ {
+ radius = llmax((S32) mDrawable->getRadius(),1)*attachment_size_factor;
+ }
+ else
+ {
+ radius = mDrawable->getRadius();
+ radius *= 1.f + mDrawable->mDistanceWRTCamera * distance_factor[1];
+ radius += mDrawable->mDistanceWRTCamera * distance_factor[0];
+ }
+
+ return llclamp(radius*scale, 0.5f, 256.f);
+}
+
+const LLVector3 LLVOVolume::getPivotPositionAgent() const
+{
+ if (mVolumeImpl)
+ {
+ return mVolumeImpl->getPivotPosition();
+ }
+ return LLViewerObject::getPivotPositionAgent();
+}
+
+void LLVOVolume::onShift(const LLVector4a &shift_vector)
+{
+ if (mVolumeImpl)
+ {
+ mVolumeImpl->onShift(shift_vector);
+ }
+
+ updateRelativeXform();
+}
+
+const LLMatrix4& LLVOVolume::getWorldMatrix(LLXformMatrix* xform) const
+{
+ if (mVolumeImpl)
+ {
+ return mVolumeImpl->getWorldMatrix(xform);
+ }
+ return xform->getWorldMatrix();
+}
+
+LLVector3 LLVOVolume::agentPositionToVolume(const LLVector3& pos) const
+{
+ LLVector3 ret = pos - getRenderPosition();
+ ret = ret * ~getRenderRotation();
+ LLVector3 objScale = isVolumeGlobal() ? LLVector3(1,1,1) : 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
+{
+ LLVector3 ret = dir * ~getRenderRotation();
+
+ LLVector3 objScale = isVolumeGlobal() ? LLVector3(1,1,1) : getScale();
+ ret.scaleVec(objScale);
+
+ return ret;
+}
+
+LLVector3 LLVOVolume::volumePositionToAgent(const LLVector3& dir) const
+{
+ LLVector3 ret = dir;
+ LLVector3 objScale = isVolumeGlobal() ? LLVector3(1,1,1) : getScale();
+ ret.scaleVec(objScale);
+ ret = ret * getRenderRotation();
+ ret += getRenderPosition();
+
+ return ret;
+}
+
+LLVector3 LLVOVolume::volumeDirectionToAgent(const LLVector3& dir) const
+{
+ LLVector3 ret = dir;
+ LLVector3 objScale = isVolumeGlobal() ? LLVector3(1,1,1) : getScale();
+ LLVector3 invObjScale(1.f / objScale.mV[VX], 1.f / objScale.mV[VY], 1.f / objScale.mV[VZ]);
+ ret.scaleVec(invObjScale);
+ ret = ret * getRenderRotation();
+
+ return ret;
+}
+
+
+BOOL LLVOVolume::lineSegmentIntersect(const LLVector3& start, const LLVector3& end, S32 face, BOOL pick_transparent, S32 *face_hitp,
+ LLVector3* intersection,LLVector2* tex_coord, LLVector3* normal, LLVector3* bi_normal)
+
+{
+ if (!mbCanSelect
+ || mDrawable->isDead()
+ || !gPipeline.hasRenderType(mDrawable->getRenderType()))
+ {
+ return FALSE;
+ }
+
+ BOOL ret = FALSE;
+
+ LLVolume* volume = getVolume();
+
+ bool transform = true;
+
+ if (mDrawable->isState(LLDrawable::RIGGED))
+ {
+ if (LLFloater::isVisible(gFloaterTools) && getAvatar()->isSelf())
+ {
+ updateRiggedVolume();
+ genBBoxes(FALSE);
+ volume = mRiggedVolume;
+ transform = false;
+ }
+ else
+ { //cannot pick rigged attachments on other avatars or when not in build mode
+ return FALSE;
+ }
+ }
+
+ if (volume)
+ {
+ LLVector3 v_start, v_end, v_dir;
+
+ if (transform)
+ {
+ v_start = agentPositionToVolume(start);
+ v_end = agentPositionToVolume(end);
+ }
+ else
+ {
+ v_start = start;
+ v_end = end;
+ }
+
+ LLVector3 p;
+ LLVector3 n;
+ LLVector2 tc;
+ LLVector3 bn;
+
+ if (intersection != NULL)
+ {
+ p = *intersection;
+ }
+
+ if (tex_coord != NULL)
+ {
+ tc = *tex_coord;
+ }
+
+ if (normal != NULL)
+ {
+ n = *normal;
+ }
+
+ if (bi_normal != NULL)
+ {
+ bn = *bi_normal;
+ }
+
+ S32 face_hit = -1;
+
+ S32 start_face, end_face;
+ if (face == -1)
+ {
+ start_face = 0;
+ end_face = volume->getNumVolumeFaces();
+ }
+ else
+ {
+ start_face = face;
+ end_face = face+1;
+ }
+
+ bool special_cursor = specialHoverCursor();
+ for (S32 i = start_face; i < end_face; ++i)
+ {
+ if (!special_cursor && !pick_transparent && getTE(i)->getColor().mV[3] == 0.f)
+ { //don't attempt to pick completely transparent faces unless
+ //pick_transparent is true
+ continue;
+ }
+
+ face_hit = volume->lineSegmentIntersect(v_start, v_end, i,
+ &p, &tc, &n, &bn);
+
+ if (face_hit >= 0 && mDrawable->getNumFaces() > face_hit)
+ {
+ LLFace* face = mDrawable->getFace(face_hit);
+
+ if (pick_transparent || !face->getTexture() || !face->getTexture()->hasGLTexture() || face->getTexture()->getMask(face->surfaceToTexture(tc, p, n)))
+ {
+ v_end = p;
+ if (face_hitp != NULL)
+ {
+ *face_hitp = face_hit;
+ }
+
+ if (intersection != NULL)
+ {
+ if (transform)
+ {
+ *intersection = volumePositionToAgent(p); // must map back to agent space
+ }
+ else
+ {
+ *intersection = p;
+ }
+ }
+
+ if (normal != NULL)
+ {
+ if (transform)
+ {
+ *normal = volumeDirectionToAgent(n);
+ }
+ else
+ {
+ *normal = n;
+ }
+
+ (*normal).normVec();
+ }
+
+ if (bi_normal != NULL)
+ {
+ if (transform)
+ {
+ *bi_normal = volumeDirectionToAgent(bn);
+ }
+ else
+ {
+ *bi_normal = bn;
+ }
+ (*bi_normal).normVec();
+ }
+
+ if (tex_coord != NULL)
+ {
+ *tex_coord = tc;
+ }
+
+ ret = TRUE;
+ }
+ }
+ }
+ }
+
+ return ret;
+}
+
+bool LLVOVolume::treatAsRigged()
+{
+ return LLFloater::isVisible(gFloaterTools) &&
+ isAttachment() &&
+ getAvatar() &&
+ getAvatar()->isSelf() &&
+ mDrawable.notNull() &&
+ mDrawable->isState(LLDrawable::RIGGED);
+}
+
+LLRiggedVolume* LLVOVolume::getRiggedVolume()
+{
+ return mRiggedVolume;
+}
+
+void LLVOVolume::clearRiggedVolume()
+{
+ if (mRiggedVolume.notNull())
+ {
+ mRiggedVolume = NULL;
+ updateRelativeXform();
+ }
+}
+
+void LLVOVolume::updateRiggedVolume()
+{
+ //Update mRiggedVolume to match current animation frame of avatar.
+ //Also update position/size in octree.
+
+ if (!treatAsRigged())
+ {
+ clearRiggedVolume();
+
+ return;
+ }
+
+ LLVolume* volume = getVolume();
+
+ const LLMeshSkinInfo* skin = gMeshRepo.getSkinInfo(volume->getParams().getSculptID(), this);
+
+ if (!skin)
+ {
+ clearRiggedVolume();
+ return;
+ }
+
+ LLVOAvatar* avatar = getAvatar();
+
+ if (!avatar)
+ {
+ clearRiggedVolume();
+ return;
+ }
+
+ if (!mRiggedVolume)
+ {
+ LLVolumeParams p;
+ mRiggedVolume = new LLRiggedVolume(p);
+ updateRelativeXform();
+ }
+
+ mRiggedVolume->update(skin, avatar, volume);
+
+}
+
+static LLFastTimer::DeclareTimer FTM_SKIN_RIGGED("Skin");
+static LLFastTimer::DeclareTimer FTM_RIGGED_OCTREE("Octree");
+
+void LLRiggedVolume::update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, const LLVolume* volume)
+{
+ bool copy = false;
+ if (volume->getNumVolumeFaces() != getNumVolumeFaces())
+ {
+ copy = true;
+ }
+
+ for (S32 i = 0; i < volume->getNumVolumeFaces() && !copy; ++i)
+ {
+ const LLVolumeFace& src_face = volume->getVolumeFace(i);
+ const LLVolumeFace& dst_face = getVolumeFace(i);
+
+ if (src_face.mNumIndices != dst_face.mNumIndices ||
+ src_face.mNumVertices != dst_face.mNumVertices)
+ {
+ copy = true;
+ }
+ }
+
+ if (copy)
+ {
+ copyVolumeFaces(volume);
+ }
+
+ //build matrix palette
+ LLMatrix4a mp[64];
+ LLMatrix4* mat = (LLMatrix4*) mp;
+
+ for (U32 j = 0; j < skin->mJointNames.size(); ++j)
+ {
+ LLJoint* joint = avatar->getJoint(skin->mJointNames[j]);
+ if (joint)
+ {
+ mat[j] = skin->mInvBindMatrix[j];
+ mat[j] *= joint->getWorldMatrix();
+ }
+ }
+
+ for (S32 i = 0; i < volume->getNumVolumeFaces(); ++i)
+ {
+ const LLVolumeFace& vol_face = volume->getVolumeFace(i);
+
+ LLVolumeFace& dst_face = mVolumeFaces[i];
+
+ LLVector4a* weight = vol_face.mWeights;
+
+ LLMatrix4a bind_shape_matrix;
+ bind_shape_matrix.loadu(skin->mBindShapeMatrix);
+
+ LLVector4a* pos = dst_face.mPositions;
+
+ {
+ LLFastTimer t(FTM_SKIN_RIGGED);
+
+ for (U32 j = 0; j < dst_face.mNumVertices; ++j)
+ {
+ LLMatrix4a final_mat;
+ final_mat.clear();
+
+ S32 idx[4];
+
+ LLVector4 wght;
+
+ F32 scale = 0.f;
+ for (U32 k = 0; k < 4; k++)
+ {
+ F32 w = weight[j][k];
+
+ idx[k] = (S32) floorf(w);
+ wght[k] = w - floorf(w);
+ scale += wght[k];
+ }
+
+ wght *= 1.f/scale;
+
+ for (U32 k = 0; k < 4; k++)
+ {
+ F32 w = wght[k];
+
+ LLMatrix4a src;
+ src.setMul(mp[idx[k]], w);
+
+ final_mat.add(src);
+ }
+
+
+ LLVector4a& v = vol_face.mPositions[j];
+ LLVector4a t;
+ LLVector4a dst;
+ bind_shape_matrix.affineTransform(v, t);
+ final_mat.affineTransform(t, dst);
+ pos[j] = dst;
+ }
+
+ //update bounding box
+ LLVector4a& min = dst_face.mExtents[0];
+ LLVector4a& max = dst_face.mExtents[1];
+
+ min = pos[0];
+ max = pos[1];
+
+ for (U32 j = 1; j < dst_face.mNumVertices; ++j)
+ {
+ min.setMin(min, pos[j]);
+ max.setMax(max, pos[j]);
+ }
+
+ dst_face.mCenter->setAdd(dst_face.mExtents[0], dst_face.mExtents[1]);
+ dst_face.mCenter->mul(0.5f);
+
+ }
+
+ {
+ LLFastTimer t(FTM_RIGGED_OCTREE);
+ delete dst_face.mOctree;
+ dst_face.mOctree = NULL;
+
+ LLVector4a size;
+ size.setSub(dst_face.mExtents[1], dst_face.mExtents[0]);
+ size.splat(size.getLength3().getF32()*0.5f);
+
+ dst_face.createOctree(1.f);
+ }
+ }
+}
+
+U32 LLVOVolume::getPartitionType() const
+{
+ if (isHUDAttachment())
+ {
+ return LLViewerRegion::PARTITION_HUD;
+ }
+
+ return LLViewerRegion::PARTITION_VOLUME;
+}
+
+LLVolumePartition::LLVolumePartition()
+: LLSpatialPartition(LLVOVolume::VERTEX_DATA_MASK, TRUE, GL_DYNAMIC_DRAW_ARB)
+{
+ mLODPeriod = 32;
+ mDepthMask = FALSE;
+ mDrawableType = LLPipeline::RENDER_TYPE_VOLUME;
+ mPartitionType = LLViewerRegion::PARTITION_VOLUME;
+ mSlopRatio = 0.25f;
+ mBufferUsage = GL_DYNAMIC_DRAW_ARB;
+}
+
+LLVolumeBridge::LLVolumeBridge(LLDrawable* drawablep)
+: LLSpatialBridge(drawablep, TRUE, LLVOVolume::VERTEX_DATA_MASK)
+{
+ mDepthMask = FALSE;
+ mLODPeriod = 32;
+ mDrawableType = LLPipeline::RENDER_TYPE_VOLUME;
+ mPartitionType = LLViewerRegion::PARTITION_BRIDGE;
+
+ mBufferUsage = GL_DYNAMIC_DRAW_ARB;
+
+ mSlopRatio = 0.25f;
+}
+
+bool can_batch_texture(LLFace* facep)
+{
+ if (facep->getTextureEntry()->getBumpmap())
+ { //bump maps aren't worked into texture batching yet
+ return false;
+ }
+
+ if (facep->getTexture() && facep->getTexture()->getPrimaryFormat() == GL_ALPHA)
+ { //can't batch invisiprims
+ return false;
+ }
+
+ if (facep->isState(LLFace::TEXTURE_ANIM) && facep->getVirtualSize() > MIN_TEX_ANIM_SIZE)
+ { //texture animation breaks batches
+ return false;
+ }
+
+ return true;
+}
+
+void LLVolumeGeometryManager::registerFace(LLSpatialGroup* group, LLFace* facep, U32 type)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+
+ if (facep->getViewerObject()->isSelected() && LLSelectMgr::getInstance()->mHideSelectedObjects)
+ {
+ return;
+ }
+
+ //add face to drawmap
+ LLSpatialGroup::drawmap_elem_t& draw_vec = group->mDrawMap[type];
+
+ S32 idx = draw_vec.size()-1;
+
+ BOOL fullbright = (type == LLRenderPass::PASS_FULLBRIGHT) ||
+ (type == LLRenderPass::PASS_INVISIBLE) ||
+ (type == LLRenderPass::PASS_ALPHA && facep->isState(LLFace::FULLBRIGHT));
+
+ if (!fullbright && type != LLRenderPass::PASS_GLOW && !facep->getVertexBuffer()->hasDataType(LLVertexBuffer::TYPE_NORMAL))
+ {
+ llwarns << "Non fullbright face has no normals!" << llendl;
+ return;
+ }
+
+ const LLMatrix4* tex_mat = NULL;
+ if (facep->isState(LLFace::TEXTURE_ANIM) && facep->getVirtualSize() > MIN_TEX_ANIM_SIZE)
+ {
+ tex_mat = facep->mTextureMatrix;
+ }
+
+ const LLMatrix4* model_mat = NULL;
+
+ LLDrawable* drawable = facep->getDrawable();
+ if (drawable->isActive())
+ {
+ model_mat = &(drawable->getRenderMatrix());
+ }
+ else
+ {
+ model_mat = &(drawable->getRegion()->mRenderMatrix);
+ if (model_mat->isIdentity())
+ {
+ model_mat = NULL;
+ }
+ }
+
+ U8 bump = (type == LLRenderPass::PASS_BUMP || type == LLRenderPass::PASS_POST_BUMP) ? facep->getTextureEntry()->getBumpmap() : 0;
+
+ LLViewerTexture* tex = facep->getTexture();
+
+ U8 index = facep->getTextureIndex();
+
+ bool batchable = false;
+
+ if (index < 255 && idx >= 0)
+ {
+ if (index < draw_vec[idx]->mTextureList.size())
+ {
+ if (draw_vec[idx]->mTextureList[index].isNull())
+ {
+ batchable = true;
+ draw_vec[idx]->mTextureList[index] = tex;
+ }
+ else if (draw_vec[idx]->mTextureList[index] == tex)
+ { //this face's texture index can be used with this batch
+ batchable = true;
+ }
+ }
+ else
+ { //texture list can be expanded to fit this texture index
+ batchable = true;
+ }
+ }
+
+ U8 glow = (U8) (facep->getTextureEntry()->getGlow() * 255);
+
+ if (idx >= 0 &&
+ draw_vec[idx]->mVertexBuffer == facep->getVertexBuffer() &&
+ draw_vec[idx]->mEnd == facep->getGeomIndex()-1 &&
+ (LLPipeline::sTextureBindTest || draw_vec[idx]->mTexture == tex || batchable) &&
+#if LL_DARWIN
+ draw_vec[idx]->mEnd - draw_vec[idx]->mStart + facep->getGeomCount() <= (U32) gGLManager.mGLMaxVertexRange &&
+ draw_vec[idx]->mCount + facep->getIndicesCount() <= (U32) gGLManager.mGLMaxIndexRange &&
+#endif
+ draw_vec[idx]->mGlowColor.mV[3] == glow &&
+ draw_vec[idx]->mFullbright == fullbright &&
+ draw_vec[idx]->mBump == bump &&
+ draw_vec[idx]->mTextureMatrix == tex_mat &&
+ draw_vec[idx]->mModelMatrix == model_mat)
+ {
+ draw_vec[idx]->mCount += facep->getIndicesCount();
+ draw_vec[idx]->mEnd += facep->getGeomCount();
+ draw_vec[idx]->mVSize = llmax(draw_vec[idx]->mVSize, facep->getVirtualSize());
+
+ if (index >= draw_vec[idx]->mTextureList.size())
+ {
+ draw_vec[idx]->mTextureList.resize(index+1);
+ draw_vec[idx]->mTextureList[index] = tex;
+ }
+ draw_vec[idx]->validate();
+ update_min_max(draw_vec[idx]->mExtents[0], draw_vec[idx]->mExtents[1], facep->mExtents[0]);
+ update_min_max(draw_vec[idx]->mExtents[0], draw_vec[idx]->mExtents[1], facep->mExtents[1]);
+ }
+ else
+ {
+ U32 start = facep->getGeomIndex();
+ U32 end = start + facep->getGeomCount()-1;
+ U32 offset = facep->getIndicesStart();
+ U32 count = facep->getIndicesCount();
+ LLPointer<LLDrawInfo> draw_info = new LLDrawInfo(start,end,count,offset, tex,
+ facep->getVertexBuffer(), fullbright, bump);
+ draw_info->mGroup = group;
+ draw_info->mVSize = facep->getVirtualSize();
+ draw_vec.push_back(draw_info);
+ draw_info->mTextureMatrix = tex_mat;
+ draw_info->mModelMatrix = model_mat;
+ draw_info->mGlowColor.setVec(0,0,0,glow);
+ if (type == LLRenderPass::PASS_ALPHA)
+ { //for alpha sorting
+ facep->setDrawInfo(draw_info);
+ }
+ draw_info->mExtents[0] = facep->mExtents[0];
+ draw_info->mExtents[1] = facep->mExtents[1];
+
+ if (LLPipeline::sUseTriStrips)
+ {
+ draw_info->mDrawMode = LLRender::TRIANGLE_STRIP;
+ }
+
+ if (index < 255)
+ { //initialize texture list for texture batching
+ draw_info->mTextureList.resize(index+1);
+ draw_info->mTextureList[index] = tex;
+ }
+ draw_info->validate();
+ }
+}
+
+void LLVolumeGeometryManager::getGeometry(LLSpatialGroup* group)
+{
+
+}
+
+static LLFastTimer::DeclareTimer FTM_REBUILD_VOLUME_VB("Volume");
+static LLFastTimer::DeclareTimer FTM_REBUILD_VBO("VBO Rebuilt");
+
+static LLDrawPoolAvatar* get_avatar_drawpool(LLViewerObject* vobj)
+{
+ LLVOAvatar* avatar = vobj->getAvatar();
+
+ if (avatar)
+ {
+ LLDrawable* drawable = avatar->mDrawable;
+ if (drawable && drawable->getNumFaces() > 0)
+ {
+ LLFace* face = drawable->getFace(0);
+ if (face)
+ {
+ LLDrawPool* drawpool = face->getPool();
+ if (drawpool)
+ {
+ if (drawpool->getType() == LLDrawPool::POOL_AVATAR)
+ {
+ return (LLDrawPoolAvatar*) drawpool;
+ }
+ }
+ }
+ }
+ }
+
+ return NULL;
+}
+
+void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
+{
+ if (group->changeLOD())
+ {
+ group->mLastUpdateDistance = group->mDistance;
+ }
+
+ group->mLastUpdateViewAngle = group->mViewAngle;
+
+ if (!group->isState(LLSpatialGroup::GEOM_DIRTY | LLSpatialGroup::ALPHA_DIRTY))
+ {
+ if (group->isState(LLSpatialGroup::MESH_DIRTY) && !LLPipeline::sDelayVBUpdate)
+ {
+ LLFastTimer ftm(FTM_REBUILD_VBO);
+ LLFastTimer ftm2(FTM_REBUILD_VOLUME_VB);
+
+ rebuildMesh(group);
+ }
+ return;
+ }
+
+ group->mBuilt = 1.f;
+ LLFastTimer ftm(FTM_REBUILD_VBO);
+
+ LLFastTimer ftm2(FTM_REBUILD_VOLUME_VB);
+
+ group->clearDrawMap();
+
+ mFaceList.clear();
+
+ std::vector<LLFace*> fullbright_faces;
+ std::vector<LLFace*> bump_faces;
+ std::vector<LLFace*> simple_faces;
+
+ std::vector<LLFace*> alpha_faces;
+ U32 useage = group->mSpatialPartition->mBufferUsage;
+
+ U32 max_vertices = (gSavedSettings.getS32("RenderMaxVBOSize")*1024)/LLVertexBuffer::calcVertexSize(group->mSpatialPartition->mVertexDataMask);
+ U32 max_total = (gSavedSettings.getS32("RenderMaxNodeSize")*1024)/LLVertexBuffer::calcVertexSize(group->mSpatialPartition->mVertexDataMask);
+ max_vertices = llmin(max_vertices, (U32) 65535);
+
+ U32 cur_total = 0;
+
+ //get all the faces into a list
+ for (LLSpatialGroup::element_iter drawable_iter = group->getData().begin(); drawable_iter != group->getData().end(); ++drawable_iter)
+ {
+ LLDrawable* drawablep = *drawable_iter;
+
+ if (drawablep->isDead() || drawablep->isState(LLDrawable::FORCE_INVISIBLE) )
+ {
+ continue;
+ }
+
+ if (drawablep->isAnimating())
+ { //fall back to stream draw for animating verts
+ useage = GL_STREAM_DRAW_ARB;
+ }
+
+ LLVOVolume* vobj = drawablep->getVOVolume();
+
+ if (vobj->isMesh() &&
+ (vobj->getVolume() && !vobj->getVolume()->isMeshAssetLoaded() || !gMeshRepo.meshRezEnabled()))
+ {
+ continue;
+ }
+
+ llassert_always(vobj);
+ vobj->updateTextureVirtualSize();
+ vobj->preRebuild();
+
+ drawablep->clearState(LLDrawable::HAS_ALPHA);
+
+ bool rigged = vobj->isAttachment() &&
+ vobj->isMesh() &&
+ gMeshRepo.getSkinInfo(vobj->getVolume()->getParams().getSculptID(), vobj);
+
+ bool bake_sunlight = LLPipeline::sBakeSunlight && drawablep->isStatic();
+
+ bool is_rigged = false;
+
+ //for each face
+ for (S32 i = 0; i < drawablep->getNumFaces(); i++)
+ {
+ LLFace* facep = drawablep->getFace(i);
+
+ //ALWAYS null out vertex buffer on rebuild -- if the face lands in a render
+ // batch, it will recover its vertex buffer reference from the spatial group
+ facep->setVertexBuffer(NULL);
+
+ //sum up face verts and indices
+ drawablep->updateFaceSize(i);
+
+
+
+ if (rigged)
+ {
+ if (!facep->isState(LLFace::RIGGED))
+ { //completely reset vertex buffer
+ facep->clearVertexBuffer();
+ }
+
+ facep->setState(LLFace::RIGGED);
+ is_rigged = true;
+
+ //get drawpool of avatar with rigged face
+ LLDrawPoolAvatar* pool = get_avatar_drawpool(vobj);
+
+ //Determine if we've received skininfo that contains an
+ //alternate bind matrix - if it does then apply the translational component
+ //to the joints of the avatar.
+ LLVOAvatar* pAvatarVO = vobj->getAvatar();
+ bool pelvisGotSet = false;
+
+ if ( pAvatarVO )
+ {
+ LLUUID currentId = vobj->getVolume()->getParams().getSculptID();
+ const LLMeshSkinInfo* pSkinData = gMeshRepo.getSkinInfo( currentId, vobj );
+
+ if ( pSkinData )
+ {
+ const int bindCnt = pSkinData->mAlternateBindMatrix.size();
+ if ( bindCnt > 0 )
+ {
+ const int jointCnt = pSkinData->mJointNames.size();
+ const F32 pelvisZOffset = pSkinData->mPelvisOffset;
+ bool fullRig = (jointCnt>=20) ? true : false;
+ if ( fullRig )
+ {
+ for ( int i=0; i<jointCnt; ++i )
+ {
+ std::string lookingForJoint = pSkinData->mJointNames[i].c_str();
+ //llinfos<<"joint name "<<lookingForJoint.c_str()<<llendl;
+ LLJoint* pJoint = pAvatarVO->getJoint( lookingForJoint );
+ if ( pJoint && pJoint->getId() != currentId )
+ {
+ pJoint->setId( currentId );
+ const LLVector3& jointPos = pSkinData->mAlternateBindMatrix[i].getTranslation();
+ //Set the joint position
+ pJoint->storeCurrentXform( jointPos );
+ //If joint is a pelvis then handle old/new pelvis to foot values
+ if ( lookingForJoint == "mPelvis" )
+ {
+ pJoint->storeCurrentXform( jointPos );
+ if ( !pAvatarVO->hasPelvisOffset() )
+ {
+ pAvatarVO->setPelvisOffset( true, jointPos, pelvisZOffset );
+ //Trigger to rebuild viewer AV
+ pelvisGotSet = true;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ //If we've set the pelvis to a new position we need to also rebuild some information that the
+ //viewer does at launch (e.g. body size etc.)
+ if ( pelvisGotSet )
+ {
+ pAvatarVO->postPelvisSetRecalc();
+ }
+
+ if (pool)
+ {
+ const LLTextureEntry* te = facep->getTextureEntry();
+
+ //remove face from old pool if it exists
+ LLDrawPool* old_pool = facep->getPool();
+ if (old_pool && old_pool->getType() == LLDrawPool::POOL_AVATAR)
+ {
+ ((LLDrawPoolAvatar*) old_pool)->removeRiggedFace(facep);
+ }
+
+ //add face to new pool
+ LLViewerTexture* tex = facep->getTexture();
+ U32 type = gPipeline.getPoolTypeFromTE(te, tex);
+
+ if (type == LLDrawPool::POOL_ALPHA)
+ {
+ if (te->getFullbright())
+ {
+ pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_FULLBRIGHT_ALPHA);
+ }
+ else
+ {
+ pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_ALPHA);
+ }
+ }
+ else if (te->getShiny())
+ {
+ if (te->getFullbright())
+ {
+ pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_FULLBRIGHT_SHINY);
+ }
+ else
+ {
+ if (LLPipeline::sRenderDeferred)
+ {
+ pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_SIMPLE);
+ }
+ else
+ {
+ pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_SHINY);
+ }
+ }
+ }
+ else
+ {
+ if (te->getFullbright())
+ {
+ pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_FULLBRIGHT);
+ }
+ else
+ {
+ pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_SIMPLE);
+ }
+ }
+
+ if (te->getGlow())
+ {
+ pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_GLOW);
+ }
+
+ if (LLPipeline::sRenderDeferred)
+ {
+ if (type != LLDrawPool::POOL_ALPHA && !te->getFullbright())
+ {
+ if (te->getBumpmap())
+ {
+ pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_DEFERRED_BUMP);
+ }
+ else
+ {
+ pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_DEFERRED_SIMPLE);
+ }
+ }
+ }
+ }
+
+ continue;
+ }
+ else
+ {
+ if (facep->isState(LLFace::RIGGED))
+ { //face is not rigged but used to be, remove from rigged face pool
+ LLDrawPoolAvatar* pool = (LLDrawPoolAvatar*) facep->getPool();
+ if (pool)
+ {
+ pool->removeRiggedFace(facep);
+ }
+ facep->clearState(LLFace::RIGGED);
+ }
+ }
+
+ if (cur_total > max_total || facep->getIndicesCount() <= 0 || facep->getGeomCount() <= 0)
+ {
+ facep->clearVertexBuffer();
+ continue;
+ }
+
+ cur_total += facep->getGeomCount();
+
+ if (facep->hasGeometry() && facep->getPixelArea() > FORCE_CULL_AREA)
+ {
+ const LLTextureEntry* te = facep->getTextureEntry();
+ LLViewerTexture* tex = facep->getTexture();
+
+ if (facep->isState(LLFace::TEXTURE_ANIM))
+ {
+ if (!vobj->mTexAnimMode)
+ {
+ facep->clearState(LLFace::TEXTURE_ANIM);
+ }
+ }
+
+ BOOL force_simple = (facep->getPixelArea() < FORCE_SIMPLE_RENDER_AREA);
+ U32 type = gPipeline.getPoolTypeFromTE(te, tex);
+ if (type != LLDrawPool::POOL_ALPHA && force_simple)
+ {
+ type = LLDrawPool::POOL_SIMPLE;
+ }
+ facep->setPoolType(type);
+
+ if (vobj->isHUDAttachment())
+ {
+ facep->setState(LLFace::FULLBRIGHT);
+ }
+
+ if (vobj->mTextureAnimp && vobj->mTexAnimMode)
+ {
+ if (vobj->mTextureAnimp->mFace <= -1)
+ {
+ S32 face;
+ for (face = 0; face < vobj->getNumTEs(); face++)
+ {
+ drawablep->getFace(face)->setState(LLFace::TEXTURE_ANIM);
+ }
+ }
+ else if (vobj->mTextureAnimp->mFace < vobj->getNumTEs())
+ {
+ drawablep->getFace(vobj->mTextureAnimp->mFace)->setState(LLFace::TEXTURE_ANIM);
+ }
+ }
+
+ if (type == LLDrawPool::POOL_ALPHA)
+ {
+ if (facep->canRenderAsMask())
+ { //can be treated as alpha mask
+ simple_faces.push_back(facep);
+ }
+ else
+ {
+ drawablep->setState(LLDrawable::HAS_ALPHA);
+ alpha_faces.push_back(facep);
+ }
+ }
+ else
+ {
+ if (drawablep->isState(LLDrawable::REBUILD_VOLUME))
+ {
+ facep->mLastUpdateTime = gFrameTimeSeconds;
+ }
+
+ if (gPipeline.canUseWindLightShadersOnObjects()
+ && LLPipeline::sRenderBump)
+ {
+ if (te->getBumpmap())
+ { //needs normal + binormal
+ bump_faces.push_back(facep);
+ }
+ else if (te->getShiny() || !te->getFullbright())
+ { //needs normal
+ simple_faces.push_back(facep);
+ }
+ else
+ { //doesn't need normal
+ facep->setState(LLFace::FULLBRIGHT);
+ fullbright_faces.push_back(facep);
+ }
+ }
+ else
+ {
+ if (te->getBumpmap() && LLPipeline::sRenderBump)
+ { //needs normal + binormal
+ bump_faces.push_back(facep);
+ }
+ else if ((te->getShiny() && LLPipeline::sRenderBump) ||
+ !(te->getFullbright() || bake_sunlight))
+ { //needs normal
+ simple_faces.push_back(facep);
+ }
+ else
+ { //doesn't need normal
+ facep->setState(LLFace::FULLBRIGHT);
+ fullbright_faces.push_back(facep);
+ }
+ }
+ }
+ }
+ else
+ { //face has no renderable geometry
+ facep->clearVertexBuffer();
+ }
+ }
+
+ if (is_rigged)
+ {
+ drawablep->setState(LLDrawable::RIGGED);
+ }
+ else
+ {
+ drawablep->clearState(LLDrawable::RIGGED);
+ }
+ }
+
+ group->mBufferUsage = useage;
+
+ //PROCESS NON-ALPHA FACES
+ U32 simple_mask = LLVertexBuffer::MAP_TEXCOORD0 | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_COLOR;
+ U32 alpha_mask = simple_mask | 0x80000000; //hack to give alpha verts their own VBO
+ U32 bump_mask = LLVertexBuffer::MAP_TEXCOORD0 | LLVertexBuffer::MAP_TEXCOORD1 | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_COLOR;
+ U32 fullbright_mask = LLVertexBuffer::MAP_TEXCOORD0 | LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_COLOR;
+
+ bool batch_textures = LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_OBJECT) > 1;
+
+ if (batch_textures)
+ {
+ bump_mask |= LLVertexBuffer::MAP_BINORMAL;
+ genDrawInfo(group, simple_mask | LLVertexBuffer::MAP_TEXTURE_INDEX, simple_faces, FALSE, TRUE);
+ genDrawInfo(group, fullbright_mask | LLVertexBuffer::MAP_TEXTURE_INDEX, fullbright_faces, FALSE, TRUE);
+ genDrawInfo(group, bump_mask | LLVertexBuffer::MAP_TEXTURE_INDEX, bump_faces, FALSE, TRUE);
+ genDrawInfo(group, alpha_mask | LLVertexBuffer::MAP_TEXTURE_INDEX, alpha_faces, TRUE, TRUE);
+ }
+ else
+ {
+ genDrawInfo(group, simple_mask, simple_faces);
+ genDrawInfo(group, fullbright_mask, fullbright_faces);
+ genDrawInfo(group, bump_mask, bump_faces, FALSE, TRUE);
+ genDrawInfo(group, alpha_mask, alpha_faces, TRUE);
+ }
+
+
+ if (!LLPipeline::sDelayVBUpdate)
+ {
+ //drawables have been rebuilt, clear rebuild status
+ for (LLSpatialGroup::element_iter drawable_iter = group->getData().begin(); drawable_iter != group->getData().end(); ++drawable_iter)
+ {
+ LLDrawable* drawablep = *drawable_iter;
+ drawablep->clearState(LLDrawable::REBUILD_ALL);
+ }
+ }
+
+ group->mLastUpdateTime = gFrameTimeSeconds;
+ group->mBuilt = 1.f;
+ group->clearState(LLSpatialGroup::GEOM_DIRTY | LLSpatialGroup::ALPHA_DIRTY);
+
+ if (LLPipeline::sDelayVBUpdate)
+ {
+ group->setState(LLSpatialGroup::MESH_DIRTY | LLSpatialGroup::NEW_DRAWINFO);
+ }
+
+ mFaceList.clear();
+}
+
+static LLFastTimer::DeclareTimer FTM_VOLUME_GEOM("Volume Geometry");
+static LLFastTimer::DeclareTimer FTM_VOLUME_GEOM_PARTIAL("Terse Rebuild");
+
+void LLVolumeGeometryManager::rebuildMesh(LLSpatialGroup* group)
+{
+ llassert(group);
+ if (group && group->isState(LLSpatialGroup::MESH_DIRTY) && !group->isState(LLSpatialGroup::GEOM_DIRTY))
+ {
+ LLFastTimer tm(FTM_VOLUME_GEOM);
+ S32 num_mapped_veretx_buffer = LLVertexBuffer::sMappedCount ;
+
+ group->mBuilt = 1.f;
+
+ std::set<LLVertexBuffer*> mapped_buffers;
+
+ for (LLSpatialGroup::element_iter drawable_iter = group->getData().begin(); drawable_iter != group->getData().end(); ++drawable_iter)
+ {
+ LLFastTimer t(FTM_VOLUME_GEOM_PARTIAL);
+ LLDrawable* drawablep = *drawable_iter;
+
+ if (!drawablep->isDead() && drawablep->isState(LLDrawable::REBUILD_ALL) )
+ {
+ LLVOVolume* vobj = drawablep->getVOVolume();
+ vobj->preRebuild();
+
+ LLVolume* volume = vobj->getVolume();
+ for (S32 i = 0; i < drawablep->getNumFaces(); ++i)
+ {
+ LLFace* face = drawablep->getFace(i);
+ if (face)
+ {
+ LLVertexBuffer* buff = face->getVertexBuffer();
+ if (buff)
+ {
+ face->getGeometryVolume(*volume, face->getTEOffset(),
+ vobj->getRelativeXform(), vobj->getRelativeXformInvTrans(), face->getGeomIndex());
+
+ if (buff->isLocked())
+ {
+ mapped_buffers.insert(buff);
+ }
+ }
+ }
+ }
+
+ drawablep->clearState(LLDrawable::REBUILD_ALL);
+ }
+ }
+
+ for (std::set<LLVertexBuffer*>::iterator iter = mapped_buffers.begin(); iter != mapped_buffers.end(); ++iter)
+ {
+ (*iter)->setBuffer(0);
+ }
+
+ // don't forget alpha
+ if(group != NULL &&
+ !group->mVertexBuffer.isNull() &&
+ group->mVertexBuffer->isLocked())
+ {
+ group->mVertexBuffer->setBuffer(0);
+ }
+
+ //if not all buffers are unmapped
+ if(num_mapped_veretx_buffer != LLVertexBuffer::sMappedCount)
+ {
+ llwarns << "Not all mapped vertex buffers are unmapped!" << llendl ;
+ for (LLSpatialGroup::element_iter drawable_iter = group->getData().begin(); drawable_iter != group->getData().end(); ++drawable_iter)
+ {
+ LLDrawable* drawablep = *drawable_iter;
+ for (S32 i = 0; i < drawablep->getNumFaces(); ++i)
+ {
+ LLFace* face = drawablep->getFace(i);
+ LLVertexBuffer* buff = face->getVertexBuffer();
+ if (face && buff && buff->isLocked())
+ {
+ buff->setBuffer(0) ;
+ }
+ }
+ }
+ }
+
+ group->clearState(LLSpatialGroup::MESH_DIRTY | LLSpatialGroup::NEW_DRAWINFO);
+ }
+
+ llassert(!group || !group->isState(LLSpatialGroup::NEW_DRAWINFO));
+}
+
+struct CompareBatchBreakerModified
+{
+ bool operator()(const LLFace* const& lhs, const LLFace* const& rhs)
+ {
+ const LLTextureEntry* lte = lhs->getTextureEntry();
+ const LLTextureEntry* rte = rhs->getTextureEntry();
+
+ if (lte->getBumpmap() != rte->getBumpmap())
+ {
+ return lte->getBumpmap() < rte->getBumpmap();
+ }
+ else if (lte->getFullbright() != rte->getFullbright())
+ {
+ return lte->getFullbright() < rte->getFullbright();
+ }
+ else if (lte->getGlow() != rte->getGlow())
+ {
+ return lte->getGlow() < rte->getGlow();
+ }
+ else
+ {
+ return lhs->getTexture() < rhs->getTexture();
+ }
+
+ }
+};
+
+void LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, std::vector<LLFace*>& faces, BOOL distance_sort, BOOL batch_textures)
+{
+ //calculate maximum number of vertices to store in a single buffer
+ U32 max_vertices = (gSavedSettings.getS32("RenderMaxVBOSize")*1024)/LLVertexBuffer::calcVertexSize(group->mSpatialPartition->mVertexDataMask);
+ max_vertices = llmin(max_vertices, (U32) 65535);
+
+ if (!distance_sort)
+ {
+ //sort faces by things that break batches
+ std::sort(faces.begin(), faces.end(), CompareBatchBreakerModified());
+ }
+ else
+ {
+ //sort faces by distance
+ std::sort(faces.begin(), faces.end(), LLFace::CompareDistanceGreater());
+ }
+
+ std::vector<LLFace*>::iterator face_iter = faces.begin();
+
+ LLSpatialGroup::buffer_map_t buffer_map;
+
+ LLViewerTexture* last_tex = NULL;
+ S32 buffer_index = 0;
+
+ if (distance_sort)
+ {
+ buffer_index = -1;
+ }
+
+ S32 texture_index_channels = gGLManager.mNumTextureImageUnits-1; //always reserve one for shiny for now just for simplicity
+
+ if (gGLManager.mGLVersion < 3.1f)
+ {
+ texture_index_channels = 1;
+ }
+
+ if (LLPipeline::sRenderDeferred && distance_sort)
+ {
+ texture_index_channels = gDeferredAlphaProgram.mFeatures.mIndexedTextureChannels;
+ }
+
+ texture_index_channels = llmin(texture_index_channels, (S32) gSavedSettings.getU32("RenderMaxTextureIndex"));
+
+
+ while (face_iter != faces.end())
+ {
+ //pull off next face
+ LLFace* facep = *face_iter;
+ LLViewerTexture* tex = facep->getTexture();
+
+ if (distance_sort)
+ {
+ tex = NULL;
+ }
+
+ if (last_tex == tex)
+ {
+ buffer_index++;
+ }
+ else
+ {
+ last_tex = tex;
+ buffer_index = 0;
+ }
+
+ bool bake_sunlight = LLPipeline::sBakeSunlight && facep->getDrawable()->isStatic();
+
+ U32 index_count = facep->getIndicesCount();
+ U32 geom_count = facep->getGeomCount();
+
+ //sum up vertices needed for this render batch
+ std::vector<LLFace*>::iterator i = face_iter;
+ ++i;
+
+ std::vector<LLViewerTexture*> texture_list;
+
+ if (batch_textures)
+ {
+ U8 cur_tex = 0;
+ facep->setTextureIndex(cur_tex);
+ texture_list.push_back(tex);
+
+ //if (can_batch_texture(facep))
+ {
+ while (i != faces.end())
+ {
+ facep = *i;
+ if (facep->getTexture() != tex)
+ {
+ if (distance_sort)
+ { //textures might be out of order, see if texture exists in current batch
+ bool found = false;
+ for (U32 tex_idx = 0; tex_idx < texture_list.size(); ++tex_idx)
+ {
+ if (facep->getTexture() == texture_list[tex_idx])
+ {
+ cur_tex = tex_idx;
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ cur_tex = texture_list.size();
+ }
+ }
+ else
+ {
+ cur_tex++;
+ }
+
+ if (!can_batch_texture(facep))
+ { //face is bump mapped or has an animated texture matrix -- can't
+ //batch more than 1 texture at a time
+ break;
+ }
+
+ if (cur_tex >= texture_index_channels)
+ { //cut batches when index channels are depleted
+ break;
+ }
+
+ tex = facep->getTexture();
+
+ texture_list.push_back(tex);
+ }
+
+ if (geom_count + facep->getGeomCount() > max_vertices)
+ { //cut batches on geom count too big
+ break;
+ }
+
+ ++i;
+ index_count += facep->getIndicesCount();
+ geom_count += facep->getGeomCount();
+
+ facep->setTextureIndex(cur_tex);
+ }
+ }
+
+ tex = texture_list[0];
+ }
+ else
+ {
+ while (i != faces.end() &&
+ (LLPipeline::sTextureBindTest || (distance_sort || (*i)->getTexture() == tex)))
+ {
+ facep = *i;
+
+
+ //face has no texture index
+ facep->mDrawInfo = NULL;
+ facep->setTextureIndex(255);
+
+ if (geom_count + facep->getGeomCount() > max_vertices)
+ { //cut batches on geom count too big
+ break;
+ }
+
+ ++i;
+ index_count += facep->getIndicesCount();
+ geom_count += facep->getGeomCount();
+ }
+ }
+
+ //create/delete/resize vertex buffer if needed
+ LLVertexBuffer* buffer = NULL;
+ LLSpatialGroup::buffer_texture_map_t::iterator found_iter = group->mBufferMap[mask].find(*face_iter);
+
+ if (found_iter != group->mBufferMap[mask].end())
+ {
+ if ((U32) buffer_index < found_iter->second.size())
+ {
+ buffer = found_iter->second[buffer_index];
+ }
+ }
+
+ if (!buffer)
+ { //create new buffer if needed
+ buffer = createVertexBuffer(mask,
+ group->mBufferUsage);
+ buffer->allocateBuffer(geom_count, index_count, TRUE);
+ }
+ else
+ { //resize pre-existing buffer
+ if (LLVertexBuffer::sEnableVBOs && buffer->getUsage() != group->mBufferUsage ||
+ buffer->getTypeMask() != mask)
+ {
+ buffer = createVertexBuffer(mask,
+ group->mBufferUsage);
+ buffer->allocateBuffer(geom_count, index_count, TRUE);
+ }
+ else
+ {
+ buffer->resizeBuffer(geom_count, index_count);
+ }
+ }
+
+ buffer_map[mask][*face_iter].push_back(buffer);
+
+ //add face geometry
+
+ U32 indices_index = 0;
+ U16 index_offset = 0;
+
+ while (face_iter < i)
+ { //update face indices for new buffer
+ facep = *face_iter;
+ facep->setIndicesIndex(indices_index);
+ facep->setGeomIndex(index_offset);
+ facep->setVertexBuffer(buffer);
+
+ if (batch_textures && facep->getTextureIndex() == 255)
+ {
+ llerrs << "Invalid texture index." << llendl;
+ }
+
+ {
+ //for debugging, set last time face was updated vs moved
+ facep->updateRebuildFlags();
+
+ if (!LLPipeline::sDelayVBUpdate)
+ { //copy face geometry into vertex buffer
+ LLDrawable* drawablep = facep->getDrawable();
+ LLVOVolume* vobj = drawablep->getVOVolume();
+ LLVolume* volume = vobj->getVolume();
+
+ U32 te_idx = facep->getTEOffset();
+
+ facep->getGeometryVolume(*volume, te_idx,
+ vobj->getRelativeXform(), vobj->getRelativeXformInvTrans(), index_offset);
+ }
+ }
+
+ index_offset += facep->getGeomCount();
+ indices_index += facep->getIndicesCount();
+
+
+ //append face to appropriate render batch
+
+ BOOL force_simple = facep->getPixelArea() < FORCE_SIMPLE_RENDER_AREA;
+ BOOL fullbright = facep->isState(LLFace::FULLBRIGHT);
+ if ((mask & LLVertexBuffer::MAP_NORMAL) == 0)
+ { //paranoia check to make sure GL doesn't try to read non-existant normals
+ fullbright = TRUE;
+ }
+
+ const LLTextureEntry* te = facep->getTextureEntry();
+ tex = facep->getTexture();
+
+ BOOL is_alpha = (facep->getPoolType() == LLDrawPool::POOL_ALPHA) ? TRUE : FALSE;
+
+ if (is_alpha)
+ {
+ // can we safely treat this as an alpha mask?
+ if (facep->canRenderAsMask())
+ {
+ if (te->getFullbright() || LLPipeline::sNoAlpha)
+ {
+ registerFace(group, facep, LLRenderPass::PASS_FULLBRIGHT_ALPHA_MASK);
+ }
+ else
+ {
+ registerFace(group, facep, LLRenderPass::PASS_ALPHA_MASK);
+ }
+ }
+ else
+ {
+ registerFace(group, facep, LLRenderPass::PASS_ALPHA);
+ }
+
+ if (LLPipeline::sRenderDeferred)
+ {
+ registerFace(group, facep, LLRenderPass::PASS_ALPHA_SHADOW);
+ }
+ }
+ else if (gPipeline.canUseVertexShaders()
+ && group->mSpatialPartition->mPartitionType != LLViewerRegion::PARTITION_HUD
+ && LLPipeline::sRenderBump
+ && te->getShiny())
+ { //shiny
+ if (tex->getPrimaryFormat() == GL_ALPHA)
+ { //invisiprim+shiny
+ registerFace(group, facep, LLRenderPass::PASS_INVISI_SHINY);
+ registerFace(group, facep, LLRenderPass::PASS_INVISIBLE);
+ }
+ else if (LLPipeline::sRenderDeferred)
+ { //deferred rendering
+ if (te->getFullbright())
+ { //register in post deferred fullbright shiny pass
+ registerFace(group, facep, LLRenderPass::PASS_FULLBRIGHT_SHINY);
+ if (te->getBumpmap())
+ { //register in post deferred bump pass
+ registerFace(group, facep, LLRenderPass::PASS_POST_BUMP);
+ }
+ }
+ else if (te->getBumpmap())
+ { //register in deferred bump pass
+ registerFace(group, facep, LLRenderPass::PASS_BUMP);
+ }
+ else
+ { //register in deferred simple pass (deferred simple includes shiny)
+ llassert(mask & LLVertexBuffer::MAP_NORMAL);
+ registerFace(group, facep, LLRenderPass::PASS_SIMPLE);
+ }
+ }
+ else if (fullbright)
+ { //not deferred, register in standard fullbright shiny pass
+ registerFace(group, facep, LLRenderPass::PASS_FULLBRIGHT_SHINY);
+ }
+ else
+ { //not deferred or fullbright, register in standard shiny pass
+ registerFace(group, facep, LLRenderPass::PASS_SHINY);
+ }
+ }
+ else
+ { //not alpha and not shiny
+ if (!is_alpha && tex->getPrimaryFormat() == GL_ALPHA)
+ { //invisiprim
+ registerFace(group, facep, LLRenderPass::PASS_INVISIBLE);
+ }
+ else if (fullbright || bake_sunlight)
+ { //fullbright
+ registerFace(group, facep, LLRenderPass::PASS_FULLBRIGHT);
+ if (LLPipeline::sRenderDeferred && LLPipeline::sRenderBump && te->getBumpmap())
+ { //if this is the deferred render and a bump map is present, register in post deferred bump
+ registerFace(group, facep, LLRenderPass::PASS_POST_BUMP);
+ }
+ }
+ else
+ {
+ if (LLPipeline::sRenderDeferred && LLPipeline::sRenderBump && te->getBumpmap())
+ { //non-shiny or fullbright deferred bump
+ registerFace(group, facep, LLRenderPass::PASS_BUMP);
+ }
+ else
+ { //all around simple
+ llassert(mask & LLVertexBuffer::MAP_NORMAL);
+ registerFace(group, facep, LLRenderPass::PASS_SIMPLE);
+ }
+ }
+
+ //not sure why this is here -- shiny HUD attachments maybe? -- davep 5/11/2010
+ if (!is_alpha && te->getShiny() && LLPipeline::sRenderBump)
+ {
+ registerFace(group, facep, LLRenderPass::PASS_SHINY);
+ }
+ }
+
+ //not sure why this is here, and looks like it might cause bump mapped objects to get rendered redundantly -- davep 5/11/2010
+ if (!is_alpha && !LLPipeline::sRenderDeferred)
+ {
+ llassert((mask & LLVertexBuffer::MAP_NORMAL) || fullbright);
+ facep->setPoolType((fullbright) ? LLDrawPool::POOL_FULLBRIGHT : LLDrawPool::POOL_SIMPLE);
+
+ if (!force_simple && te->getBumpmap() && LLPipeline::sRenderBump)
+ {
+ registerFace(group, facep, LLRenderPass::PASS_BUMP);
+ }
+ }
+
+ if (!is_alpha && LLPipeline::sRenderGlow && te->getGlow() > 0.f)
+ {
+ registerFace(group, facep, LLRenderPass::PASS_GLOW);
+ }
+
+ ++face_iter;
+ }
+
+ buffer->setBuffer(0);
+ }
+
+ group->mBufferMap[mask].clear();
+ for (LLSpatialGroup::buffer_texture_map_t::iterator i = buffer_map[mask].begin(); i != buffer_map[mask].end(); ++i)
+ {
+ group->mBufferMap[mask][i->first] = i->second;
+ }
+}
+
+void LLGeometryManager::addGeometryCount(LLSpatialGroup* group, U32 &vertex_count, U32 &index_count)
+{
+ //initialize to default usage for this partition
+ U32 usage = group->mSpatialPartition->mBufferUsage;
+
+ //clear off any old faces
+ mFaceList.clear();
+
+ //for each drawable
+ for (LLSpatialGroup::element_iter drawable_iter = group->getData().begin(); drawable_iter != group->getData().end(); ++drawable_iter)
+ {
+ LLDrawable* drawablep = *drawable_iter;
+
+ if (drawablep->isDead())
+ {
+ continue;
+ }
+
+ if (drawablep->isAnimating())
+ { //fall back to stream draw for animating verts
+ usage = GL_STREAM_DRAW_ARB;
+ }
+
+ //for each face
+ for (S32 i = 0; i < drawablep->getNumFaces(); i++)
+ {
+ //sum up face verts and indices
+ drawablep->updateFaceSize(i);
+ LLFace* facep = drawablep->getFace(i);
+ if (facep->hasGeometry() && facep->getPixelArea() > FORCE_CULL_AREA)
+ {
+ vertex_count += facep->getGeomCount();
+ index_count += facep->getIndicesCount();
+ llassert(facep->getIndicesCount() < 65536);
+ //remember face (for sorting)
+ mFaceList.push_back(facep);
+ }
+ else
+ {
+ facep->clearVertexBuffer();
+ }
+ }
+ }
+
+ group->mBufferUsage = usage;
+}
+
+LLHUDPartition::LLHUDPartition()
+{
+ mPartitionType = LLViewerRegion::PARTITION_HUD;
+ mDrawableType = LLPipeline::RENDER_TYPE_HUD;
+ mSlopRatio = 0.f;
+ mLODPeriod = 1;
+}
+
+void LLHUDPartition::shift(const LLVector4a &offset)
+{
+ //HUD objects don't shift with region crossing. That would be silly.
+}
+
+
diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h
index fc00f0c0d0..556497f0fa 100644
--- a/indra/newview/llvovolume.h
+++ b/indra/newview/llvovolume.h
@@ -129,9 +129,11 @@ public:
const LLMatrix4& getRelativeXform() const { return mRelativeXform; }
const LLMatrix3& getRelativeXformInvTrans() const { return mRelativeXformInvTrans; }
/*virtual*/ const LLMatrix4 getRenderMatrix() const;
- U32 getRenderCost(std::set<LLUUID> &textures) const;
- /*virtual*/ F32 getStreamingCost(S32* bytes = NULL, S32* visible_bytes = NULL);
- /*virtual*/ U32 getTriangleCount();
+ typedef std::map<LLUUID, S32> texture_cost_t;
+ U32 getRenderCost(texture_cost_t &textures) const;
+ /*virtual*/ F32 getStreamingCost(S32* bytes = NULL, S32* visible_bytes = NULL, F32* unscaled_value = NULL) const;
+
+ /*virtual*/ U32 getTriangleCount() const;
/*virtual*/ U32 getHighLODTriangleCount();
/*virtual*/ BOOL lineSegmentIntersect(const LLVector3& start, const LLVector3& end,
S32 face = -1, // which face to check, -1 = ALL_SIDES
@@ -320,11 +322,19 @@ protected:
LLFace* addFace(S32 face_index);
void updateTEData();
+ // stats tracking for render complexity
+ static S32 mRenderComplexity_last;
+ static S32 mRenderComplexity_current;
+
void requestMediaDataUpdate(bool isNew);
void cleanUpMediaImpls();
void addMediaImpl(LLViewerMediaImpl* media_impl, S32 texture_index) ;
void removeMediaImpl(S32 texture_index) ;
public:
+
+ static S32 getRenderComplexityMax() {return mRenderComplexity_last;}
+ static void updateRenderComplexity();
+
LLViewerTextureAnim *mTextureAnimp;
U8 mTexAnimMode;
private:
@@ -359,8 +369,6 @@ public:
static LLPointer<LLObjectMediaDataClient> sObjectMediaClient;
static LLPointer<LLObjectMediaNavigateClient> sObjectMediaNavigateClient;
- static const U32 ARC_TEXTURE_COST = 5;
-
protected:
static S32 sNumLODChanges;
diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h
index 28e6526acd..671e0f66b0 100644
--- a/indra/newview/pipeline.h
+++ b/indra/newview/pipeline.h
@@ -459,6 +459,7 @@ public:
RENDER_DEBUG_PHYSICS_SHAPES = 0x1000000,
RENDER_DEBUG_NORMALS = 0x2000000,
RENDER_DEBUG_LOD_INFO = 0x4000000,
+ RENDER_DEBUG_RENDER_COMPLEXITY = 0x8000000
};
public:
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index 01699955a6..994ab0a4e5 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -1287,7 +1287,7 @@
parameter="stats" />
</menu_item_check>
<menu_item_check
- label="Show Avatar Rendering Cost"
+ label="Show Draw Weight for Avatars"
name="Avatar Rendering Cost">
<menu_item_check.on_check
function="Advanced.CheckInfoDisplay"
@@ -2435,6 +2435,16 @@
function="Advanced.ToggleInfoDisplay"
parameter="raycast" />
</menu_item_check>
+ <menu_item_check
+ label="Render Complexity"
+ name="rendercomplexity">
+ <menu_item_check.on_check
+ function="Advanced.CheckInfoDisplay"
+ parameter="rendercomplexity" />
+ <menu_item_check.on_click
+ function="Advanced.ToggleInfoDisplay"
+ parameter="rendercomplexity" />
+ </menu_item_check>
<menu_item_check
label="Sculpt"
name="Sculpt">