summaryrefslogtreecommitdiff
path: root/indra
diff options
context:
space:
mode:
Diffstat (limited to 'indra')
-rw-r--r--indra/llcommon/CMakeLists.txt1
-rw-r--r--indra/llcommon/llaccountingquota.h86
-rw-r--r--indra/llcommon/llsdserialize.cpp4
-rw-r--r--indra/llcommon/llstat.cpp2
-rw-r--r--indra/llinventory/llparcel.cpp9
-rw-r--r--indra/llinventory/llparcel.h13
-rw-r--r--indra/llmath/lloctree.h11
-rw-r--r--indra/llmath/llvolume.cpp11
-rw-r--r--indra/llprimitive/llmodel.cpp75
-rw-r--r--indra/llprimitive/llmodel.h1
-rw-r--r--indra/llrender/llgl.cpp68
-rw-r--r--indra/llrender/llgl.h10
-rw-r--r--indra/llrender/llglheaders.h10
-rw-r--r--indra/llrender/llglslshader.cpp3
-rw-r--r--indra/llrender/llimagegl.cpp9
-rw-r--r--indra/llrender/llimagegl.h2
-rw-r--r--indra/llrender/llrender.cpp37
-rw-r--r--indra/llrender/llrender.h1
-rw-r--r--indra/llrender/llrendertarget.cpp411
-rw-r--r--indra/llrender/llrendertarget.h26
-rw-r--r--indra/llrender/llshadermgr.cpp6
-rw-r--r--indra/llrender/llshadermgr.h3
-rw-r--r--indra/newview/CMakeLists.txt2
-rw-r--r--indra/newview/app_settings/settings.xml75
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/alphaSkinnedV.glsl25
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/alphaV.glsl25
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/avatarAlphaV.glsl25
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/avatarShadowF.glsl3
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/avatarShadowV.glsl7
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/blurLightF.glsl4
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/blurLightMSF.glsl113
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/cloudsF.glsl79
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/cloudsV.glsl165
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/multiPointLightF.glsl63
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/multiPointLightMSF.glsl137
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/multiSpotLightMSF.glsl232
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/pointLightF.glsl2
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/pointLightMSF.glsl108
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/pointLightV.glsl7
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/postDeferredF.glsl2
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/postDeferredMSF.glsl133
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/postDeferredNoDoFMSF.glsl37
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/skyF.glsl44
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/skyV.glsl140
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl2
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/softenLightMSF.glsl318
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/spotLightMSF.glsl234
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/starsF.glsl19
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/starsV.glsl17
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/sunLightMSF.glsl17
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/sunLightSSAOF.glsl2
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/sunLightSSAOMSF.glsl123
-rw-r--r--indra/newview/app_settings/shaders/class1/effects/glowExtractMSF.glsl38
-rw-r--r--indra/newview/app_settings/shaders/class2/deferred/alphaSkinnedV.glsl25
-rw-r--r--indra/newview/app_settings/shaders/class2/deferred/alphaV.glsl25
-rw-r--r--indra/newview/app_settings/shaders/class2/deferred/avatarAlphaV.glsl25
-rw-r--r--indra/newview/app_settings/shaders/class2/deferred/edgeF.glsl2
-rw-r--r--indra/newview/app_settings/shaders/class2/deferred/edgeMSF.glsl74
-rw-r--r--indra/newview/app_settings/shaders/class2/deferred/multiSpotLightF.glsl2
-rw-r--r--indra/newview/app_settings/shaders/class2/deferred/multiSpotLightMSF.glsl244
-rw-r--r--indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl52
-rw-r--r--indra/newview/app_settings/shaders/class2/deferred/softenLightMSF.glsl307
-rw-r--r--indra/newview/app_settings/shaders/class2/deferred/spotLightMSF.glsl245
-rw-r--r--indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl2
-rw-r--r--indra/newview/app_settings/shaders/class2/deferred/sunLightMSF.glsl202
-rw-r--r--indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl2
-rw-r--r--indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOMSF.glsl241
-rw-r--r--indra/newview/featuretable.txt1
-rw-r--r--indra/newview/featuretable_mac.txt30
-rw-r--r--indra/newview/featuretable_xp.txt1
-rw-r--r--indra/newview/llaccountingquotamanager.cpp284
-rwxr-xr-xindra/newview/llaccountingquotamanager.h60
-rw-r--r--indra/newview/llagentcamera.cpp4
-rw-r--r--indra/newview/lldrawable.cpp12
-rw-r--r--indra/newview/lldrawable.h1
-rw-r--r--indra/newview/lldrawpoolwlsky.cpp91
-rw-r--r--indra/newview/lldrawpoolwlsky.h8
-rw-r--r--indra/newview/llface.cpp9
-rw-r--r--indra/newview/llfeaturemanager.cpp1614
-rw-r--r--indra/newview/llfirstuse.cpp2
-rw-r--r--indra/newview/llfloatermodelpreview.cpp247
-rw-r--r--indra/newview/llfloatermodelpreview.h15
-rw-r--r--indra/newview/llfloatermodelwizard.cpp25
-rw-r--r--indra/newview/llfloaterregioninfo.cpp5
-rw-r--r--indra/newview/llfloatertools.cpp1
-rw-r--r--indra/newview/llinventorymodel.cpp2
-rwxr-xr-x[-rw-r--r--]indra/newview/llmeshrepository.cpp7770
-rw-r--r--indra/newview/llmeshrepository.h13
-rw-r--r--indra/newview/llpanelobject.cpp96
-rw-r--r--indra/newview/llpanelobject.h5
-rw-r--r--indra/newview/llpanelvolume.cpp94
-rw-r--r--indra/newview/llpanelvolume.h6
-rw-r--r--indra/newview/llselectmgr.cpp10
-rw-r--r--indra/newview/llspatialpartition.cpp88
-rw-r--r--indra/newview/llviewercontrol.cpp33
-rw-r--r--indra/newview/llviewerdisplay.cpp3
-rw-r--r--indra/newview/llviewerobject.cpp27
-rw-r--r--indra/newview/llviewerobject.h11
-rw-r--r--indra/newview/llviewerobjectlist.cpp27
-rw-r--r--indra/newview/llviewerobjectlist.h4
-rw-r--r--indra/newview/llviewerregion.cpp5
-rw-r--r--indra/newview/llviewerregion.h1
-rw-r--r--indra/newview/llviewershadermgr.cpp199
-rw-r--r--indra/newview/llviewershadermgr.h4
-rw-r--r--indra/newview/llvoavatar.cpp23
-rw-r--r--indra/newview/llvotree.cpp5
-rw-r--r--indra/newview/llvovolume.cpp58
-rw-r--r--indra/newview/pipeline.cpp176
-rw-r--r--indra/newview/pipeline.h8
-rw-r--r--indra/newview/skins/default/xui/en/floater_model_preview.xml4
-rw-r--r--indra/newview/skins/default/xui/en/floater_model_wizard.xml4
-rw-r--r--indra/newview/skins/default/xui/en/floater_tools.xml80
-rw-r--r--indra/newview/skins/default/xui/en/menu_inventory_add.xml14
-rw-r--r--indra/newview/skins/default/xui/en/panel_region_general.xml1
114 files changed, 9906 insertions, 5720 deletions
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt
index 80df91a5c1..9910281b64 100644
--- a/indra/llcommon/CMakeLists.txt
+++ b/indra/llcommon/CMakeLists.txt
@@ -115,6 +115,7 @@ set(llcommon_HEADER_FILES
indra_constants.h
linden_common.h
linked_lists.h
+ llaccountingquota.h
llallocator.h
llallocator_heap_profile.h
llagentconstants.h
diff --git a/indra/llcommon/llaccountingquota.h b/indra/llcommon/llaccountingquota.h
new file mode 100644
index 0000000000..567d02dac1
--- /dev/null
+++ b/indra/llcommon/llaccountingquota.h
@@ -0,0 +1,86 @@
+/**
+ * @file llaccountingquota.h
+ * @
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ *
+ * Copyright (c) 2001-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_ACCOUNTINGQUOTA_H
+#define LL_ACCOUNTINGQUOTA_H
+
+struct ParcelQuota
+{
+ ParcelQuota( F32 ownerRenderCost, F32 ownerPhysicsCost, F32 ownerNetworkCost, F32 ownerSimulationCost,
+ F32 groupRenderCost, F32 groupPhysicsCost, F32 groupNetworkCost, F32 groupSimulationCost,
+ F32 otherRenderCost, F32 otherPhysicsCost, F32 otherNetworkCost, F32 otherSimulationCost,
+ F32 tempRenderCost, F32 tempPhysicsCost, F32 tempNetworkCost, F32 tempSimulationCost,
+ F32 selectedRenderCost, F32 selectedPhysicsCost, F32 selectedNetworkCost, F32 selectedSimulationCost,
+ F32 parcelCapacity )
+ : mOwnerRenderCost( ownerRenderCost ), mOwnerPhysicsCost( ownerPhysicsCost )
+ , mOwnerNetworkCost( ownerNetworkCost ), mOwnerSimulationCost( ownerSimulationCost )
+ , mGroupRenderCost( groupRenderCost ), mGroupPhysicsCost( groupPhysicsCost )
+ , mGroupNetworkCost( groupNetworkCost ), mGroupSimulationCost( groupSimulationCost )
+ , mOtherRenderCost( otherRenderCost ), mOtherPhysicsCost( otherPhysicsCost )
+ , mOtherNetworkCost( otherNetworkCost ), mOtherSimulationCost( otherSimulationCost )
+ , mTempRenderCost( tempRenderCost ), mTempPhysicsCost( tempPhysicsCost )
+ , mTempNetworkCost( tempNetworkCost ), mTempSimulationCost( tempSimulationCost )
+ , mSelectedRenderCost( tempRenderCost ), mSelectedPhysicsCost( tempPhysicsCost )
+ , mSelectedNetworkCost( tempNetworkCost ), mSelectedSimulationCost( selectedSimulationCost )
+ , mParcelCapacity( parcelCapacity )
+ {
+ }
+
+ ParcelQuota(){}
+ F32 mOwnerRenderCost, mOwnerPhysicsCost, mOwnerNetworkCost, mOwnerSimulationCost;
+ F32 mGroupRenderCost, mGroupPhysicsCost, mGroupNetworkCost, mGroupSimulationCost;
+ F32 mOtherRenderCost, mOtherPhysicsCost, mOtherNetworkCost, mOtherSimulationCost;
+ F32 mTempRenderCost, mTempPhysicsCost, mTempNetworkCost, mTempSimulationCost;
+ F32 mSelectedRenderCost, mSelectedPhysicsCost, mSelectedNetworkCost, mSelectedSimulationCost;
+ F32 mParcelCapacity;
+};
+
+struct SelectionQuota
+{
+ SelectionQuota( LLUUID localId, F32 renderCost, F32 physicsCost, F32 networkCost, F32 simulationCost )
+ : mLocalId( localId)
+ , mRenderCost( renderCost )
+ , mPhysicsCost( physicsCost )
+ , mNetworkCost( networkCost )
+ , mSimulationCost( simulationCost )
+ {
+ }
+ SelectionQuota() {}
+
+ F32 mRenderCost, mPhysicsCost, mNetworkCost, mSimulationCost;
+ LLUUID mLocalId;
+};
+
+#endif
+
+
+
diff --git a/indra/llcommon/llsdserialize.cpp b/indra/llcommon/llsdserialize.cpp
index 5be5ecc492..bf62600514 100644
--- a/indra/llcommon/llsdserialize.cpp
+++ b/indra/llcommon/llsdserialize.cpp
@@ -2036,7 +2036,9 @@ std::string zip_llsd(LLSD& data)
{ //copy result into output
if (strm.avail_out >= CHUNK)
{
- llerrs << "WTF?" << llendl;
+ free(output);
+ llwarns << "Failed to compress LLSD block." << llendl;
+ return std::string();
}
have = CHUNK-strm.avail_out;
diff --git a/indra/llcommon/llstat.cpp b/indra/llcommon/llstat.cpp
index 8ba97d7730..b2c495d093 100644
--- a/indra/llcommon/llstat.cpp
+++ b/indra/llcommon/llstat.cpp
@@ -737,7 +737,7 @@ void LLPerfBlock::addStatsToLLSDandReset( LLSD & stats,
}
}
else
- { // WTF? Shouldn't have a NULL pointer in the map.
+ { // Shouldn't have a NULL pointer in the map.
llwarns << "Unexpected NULL dynamic stat at '" << stats_full_path << "'" << llendl;
}
}
diff --git a/indra/llinventory/llparcel.cpp b/indra/llinventory/llparcel.cpp
index 0a4cd51ea0..e8cd871157 100644
--- a/indra/llinventory/llparcel.cpp
+++ b/indra/llinventory/llparcel.cpp
@@ -1348,3 +1348,12 @@ LLParcel::ECategory category_ui_string_to_category(const std::string& s)
// is a distinct option from "None" and "Other"
return LLParcel::C_ANY;
}
+
+void LLParcel::updateQuota( const LLUUID& objectId, const ParcelQuota& quota )
+{
+ if ( mID == objectId )
+ {
+ mQuota = quota;
+ }
+}
+
diff --git a/indra/llinventory/llparcel.h b/indra/llinventory/llparcel.h
index 71b65d99ce..4893337967 100644
--- a/indra/llinventory/llparcel.h
+++ b/indra/llinventory/llparcel.h
@@ -34,7 +34,7 @@
#include "llpermissions.h"
#include "lltimer.h"
#include "v3math.h"
-
+#include "llaccountingquota.h"
// Grid out of which parcels taken is stepped every 4 meters.
const F32 PARCEL_GRID_STEP_METERS = 4.f;
@@ -586,7 +586,11 @@ public:
LLUUID getPreviousOwnerID() const { return mPreviousOwnerID; }
BOOL getPreviouslyGroupOwned() const { return mPreviouslyGroupOwned; }
BOOL getSellWithObjects() const { return (mParcelFlags & PF_SELL_PARCEL_OBJECTS) ? TRUE : FALSE; }
-
+
+
+ void updateQuota( const LLUUID& objectId, const ParcelQuota& quota );
+ const ParcelQuota& getQuota( void ) { return mQuota; }
+
protected:
LLUUID mID;
LLUUID mOwnerID;
@@ -657,8 +661,9 @@ protected:
BOOL mRegionPushOverride;
BOOL mRegionDenyAnonymousOverride;
BOOL mRegionDenyAgeUnverifiedOverride;
-
-
+
+ ParcelQuota mQuota;
+
public:
// HACK, make private
S32 mLocalID;
diff --git a/indra/llmath/lloctree.h b/indra/llmath/lloctree.h
index fdfc24f8b7..e5ca47da69 100644
--- a/indra/llmath/lloctree.h
+++ b/indra/llmath/lloctree.h
@@ -35,12 +35,14 @@
#define OCT_ERRS LL_WARNS("OctreeErrors")
-#define LL_OCTREE_PARANOIA_CHECK 0
+
+extern U32 gOctreeMaxCapacity;
+/*#define LL_OCTREE_PARANOIA_CHECK 0
#if LL_DARWIN
#define LL_OCTREE_MAX_CAPACITY 32
#else
#define LL_OCTREE_MAX_CAPACITY 128
-#endif
+#endif*/
template <class T> class LLOctreeNode;
@@ -74,6 +76,7 @@ template <class T>
class LLOctreeNode : public LLTreeNode<T>
{
public:
+
typedef LLOctreeTraveler<T> oct_traveler;
typedef LLTreeTraveler<T> tree_traveler;
typedef typename std::set<LLPointer<T> > element_list;
@@ -294,8 +297,8 @@ public:
//is it here?
if (isInside(data->getPositionGroup()))
{
- if ((getElementCount() < LL_OCTREE_MAX_CAPACITY && contains(data->getBinRadius()) ||
- (data->getBinRadius() > getSize()[0] && parent && parent->getElementCount() >= LL_OCTREE_MAX_CAPACITY)))
+ if ((getElementCount() < gOctreeMaxCapacity && contains(data->getBinRadius()) ||
+ (data->getBinRadius() > getSize()[0] && parent && parent->getElementCount() >= gOctreeMaxCapacity)))
{ //it belongs here
#if LL_OCTREE_PARANOIA_CHECK
//if this is a redundant insertion, error out (should never happen)
diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp
index c504215ee5..fbda824179 100644
--- a/indra/llmath/llvolume.cpp
+++ b/indra/llmath/llvolume.cpp
@@ -100,7 +100,7 @@ void assert_aligned(void* ptr, uintptr_t alignment)
uintptr_t t = (uintptr_t) ptr;
if (t%alignment != 0)
{
- llerrs << "WTF?" << llendl;
+ llerrs << "Alignment check failed." << llendl;
}
#endif
}
@@ -361,7 +361,7 @@ public:
}
else
{
- llerrs << "WTF? Empty leaf" << llendl;
+ llerrs << "Empty leaf" << llendl;
}
for (S32 i = 0; i < branch->getChildCount(); ++i)
@@ -2159,8 +2159,9 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size)
U32 face_count = mdl.size();
if (face_count == 0)
- {
- llerrs << "WTF?" << llendl;
+ { //no faces unpacked, treat as failed decode
+ llwarns << "found no faces!" << llendl;
+ return false;
}
mVolumeFaces.resize(face_count);
@@ -2179,7 +2180,7 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size)
if (idx.empty() || face.mNumIndices < 3)
{ //why is there an empty index list?
- llerrs <<"WTF?" << llendl;
+ llwarns <<"Empty face present!" << llendl;
continue;
}
diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp
index 794cdb83d5..3439cf3310 100644
--- a/indra/llprimitive/llmodel.cpp
+++ b/indra/llprimitive/llmodel.cpp
@@ -84,7 +84,7 @@ void load_face_from_dom_inputs(LLVolumeFace& face, const domInputLocalOffset_Arr
domInputLocal_Array& v_inp = vertices->getInput_array();
if (inputs[j]->getOffset() != 0)
{
- llerrs << "WTF?" << llendl;
+ llerrs << "Vertex array offset MUST be zero." << llendl;
}
for (U32 k = 0; k < v_inp.getCount(); ++k)
@@ -98,7 +98,7 @@ void load_face_from_dom_inputs(LLVolumeFace& face, const domInputLocalOffset_Arr
if (src->getTechnique_common()->getAccessor()->getStride() != 3)
{
- llerrs << "WTF?" << llendl;
+ llerrs << "Vertex array stride MUST be three." << llendl;
}
domListOfFloats& v = src->getFloat_array()->getValue();
@@ -667,11 +667,6 @@ LLModel::EModelStatus load_face_from_dom_polygons(std::vector<LLVolumeFace>& fac
}
}
- if (cur_idx != vert_idx.size())
- {
- llerrs << "WTF?" << llendl;
- }
-
//build vertex array from map
std::vector<LLVolumeFace::VertexData> new_verts;
new_verts.resize(vert_idx.size());
@@ -926,11 +921,6 @@ void LLModel::normalizeVolumeFaces()
{
LLVector4a min, max;
- if (mVolumeFaces[0].mNumVertices <= 0)
- {
- llerrs << "WTF?" << llendl;
- }
-
// For all of the volume faces
// in the model, loop over
// them and see what the extents
@@ -942,11 +932,6 @@ void LLModel::normalizeVolumeFaces()
{
LLVolumeFace& face = mVolumeFaces[i];
- if (face.mNumVertices <= 0)
- {
- llerrs << "WTF?" << llendl;
- }
-
update_min_max(min, max, face.mExtents[0]);
update_min_max(min, max, face.mExtents[1]);
}
@@ -991,6 +976,9 @@ void LLModel::normalizeVolumeFaces()
scale.splat(1.f);
scale.div(size);
+ LLVector4a inv_scale(1.f);
+ inv_scale.div(scale);
+
for (U32 i = 0; i < mVolumeFaces.size(); ++i)
{
LLVolumeFace& face = mVolumeFaces[i];
@@ -1007,10 +995,14 @@ void LLModel::normalizeVolumeFaces()
// For all the positions, we scale
// the positions to fit within the unit cube.
LLVector4a* pos = (LLVector4a*) face.mPositions;
+ LLVector4a* norm = (LLVector4a*) face.mNormals;
+
for (U32 j = 0; j < face.mNumVertices; ++j)
{
pos[j].add(trans);
pos[j].mul(scale);
+ norm[j].mul(inv_scale);
+ norm[j].normalize3();
}
}
@@ -1282,11 +1274,6 @@ void LLModel::generateNormals(F32 angle_cutoff)
{
LLVector4a& n = iter->second[k].getNormal();
- if (!iter->second[k].getPosition().equals3(new_face.mPositions[i]))
- {
- llerrs << "WTF?" << llendl;
- }
-
F32 cur = n.dot3(ref_norm).getF32();
if (cur > best)
@@ -1532,11 +1519,6 @@ LLSD LLModel::writeModel(
weight_list& weights = high->getJointInfluences(pos);
- if (weights.size() > 4)
- {
- llerrs << "WTF?" << llendl;
- }
-
S32 count = 0;
for (weight_list::iterator iter = weights.begin(); iter != weights.end(); ++iter)
{
@@ -1600,10 +1582,6 @@ LLSD LLModel::writeModelToStream(std::ostream& ostr, LLSD& mdl, BOOL nowrite)
cur_offset += size;
bytes += size;
}
- else
- {
- llerrs << "WTF?" << llendl;
- }
}
std::string decomposition;
@@ -1678,7 +1656,7 @@ LLModel::weight_list& LLModel::getJointInfluences(const LLVector3& pos)
{
if ((iter->first - pos).magVec() > 0.1f)
{
- llerrs << "WTF?" << llendl;
+ llerrs << "Couldn't find weight list." << llendl;
}
return iter->second;
@@ -1771,7 +1749,7 @@ void LLModel::updateHullCenters()
if (mHullPoints > 0)
{
mCenterOfHullCenters *= 1.f / mHullPoints;
- llassert(mPhysics.asLLSD().has("HullList"));
+ llassert(mPhysics.hasHullList());
}
}
@@ -2120,6 +2098,11 @@ void LLModel::Decomposition::fromLLSD(LLSD& decomp)
}
}
+bool LLModel::Decomposition::hasHullList() const
+{
+ return !mHull.empty() ;
+}
+
LLSD LLModel::Decomposition::asLLSD() const
{
LLSD ret;
@@ -2201,14 +2184,17 @@ LLSD LLModel::Decomposition::asLLSD() const
//convert to 16-bit normalized across domain
U16 val = (U16) (((mHull[i][j].mV[k]-min.mV[k])/range.mV[k])*65535);
- switch (k)
+ if(valid.size() < 3)
{
- case 0: test = test | (U64) val; break;
- case 1: test = test | ((U64) val << 16); break;
- case 2: test = test | ((U64) val << 32); break;
- };
+ switch (k)
+ {
+ case 0: test = test | (U64) val; break;
+ case 1: test = test | ((U64) val << 16); break;
+ case 2: test = test | ((U64) val << 32); break;
+ };
- valid.insert(test);
+ valid.insert(test);
+ }
U8* buff = (U8*) &val;
//write to binary buffer
@@ -2220,8 +2206,8 @@ LLSD LLModel::Decomposition::asLLSD() const
}
}
- //must have at least 4 unique points
- llassert(valid.size() > 3);
+ //must have at least 3 unique points
+ llassert(valid.size() > 2);
}
ret["Position"] = p;
@@ -2248,7 +2234,7 @@ LLSD LLModel::Decomposition::asLLSD() const
if (vert_idx > p.size())
{
- llerrs << "WTF?" << llendl;
+ llerrs << "Index out of bounds" << llendl;
}
}
}
@@ -2283,10 +2269,5 @@ void LLModel::Decomposition::merge(const LLModel::Decomposition* rhs)
{ //take physics shape mesh from rhs
mPhysicsShapeMesh = rhs->mPhysicsShapeMesh;
}
-
- if (!mHull.empty())
- { //verify
- llassert(asLLSD().has("HullList"));
- }
}
diff --git a/indra/llprimitive/llmodel.h b/indra/llprimitive/llmodel.h
index 23f4b5cb42..ebca1f9d9d 100644
--- a/indra/llprimitive/llmodel.h
+++ b/indra/llprimitive/llmodel.h
@@ -106,6 +106,7 @@ public:
Decomposition(LLSD& data);
void fromLLSD(LLSD& data);
LLSD asLLSD() const;
+ bool hasHullList() const;
void merge(const Decomposition* rhs);
diff --git a/indra/llrender/llgl.cpp b/indra/llrender/llgl.cpp
index f29ee0e57e..96b1838158 100644
--- a/indra/llrender/llgl.cpp
+++ b/indra/llrender/llgl.cpp
@@ -178,6 +178,12 @@ PFNGLBLITFRAMEBUFFERPROC glBlitFramebuffer = NULL;
PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glRenderbufferStorageMultisample = NULL;
PFNGLFRAMEBUFFERTEXTURELAYERPROC glFramebufferTextureLayer = NULL;
+//GL_ARB_texture_multisample
+PFNGLTEXIMAGE2DMULTISAMPLEPROC glTexImage2DMultisample;
+PFNGLTEXIMAGE3DMULTISAMPLEPROC glTexImage3DMultisample;
+PFNGLGETMULTISAMPLEFVPROC glGetMultisamplefv;
+PFNGLSAMPLEMASKIPROC glSampleMaski;
+
// GL_EXT_blend_func_separate
PFNGLBLENDFUNCSEPARATEEXTPROC glBlendFuncSeparateEXT = NULL;
@@ -321,6 +327,7 @@ LLGLManager::LLGLManager() :
mHasMipMapGeneration(FALSE),
mHasCompressedTextures(FALSE),
mHasFramebufferObject(FALSE),
+ mMaxSamples(0),
mHasBlendFuncSeparate(FALSE),
mHasVertexBufferObject(FALSE),
@@ -328,11 +335,17 @@ LLGLManager::LLGLManager() :
mHasShaderObjects(FALSE),
mHasVertexShader(FALSE),
mHasFragmentShader(FALSE),
+ mNumTextureImageUnits(0),
mHasOcclusionQuery(FALSE),
mHasOcclusionQuery2(FALSE),
mHasPointParameters(FALSE),
mHasDrawBuffers(FALSE),
mHasTextureRectangle(FALSE),
+ mHasTextureMultisample(FALSE),
+ mMaxSampleMaskWords(0),
+ mMaxColorTextureSamples(0),
+ mMaxDepthTextureSamples(0),
+ mMaxIntegerSamples(0),
mHasAnisotropic(FALSE),
mHasARBEnvCombine(FALSE),
@@ -534,6 +547,26 @@ bool LLGLManager::initGL()
return false;
}
+ if (mHasFragmentShader)
+ {
+ GLint num_tex_image_units;
+ glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS_ARB, &num_tex_image_units);
+ mNumTextureImageUnits = num_tex_image_units;
+ }
+
+ if (mHasTextureMultisample)
+ {
+ glGetIntegerv(GL_MAX_COLOR_TEXTURE_SAMPLES, &mMaxColorTextureSamples);
+ glGetIntegerv(GL_MAX_DEPTH_TEXTURE_SAMPLES, &mMaxDepthTextureSamples);
+ glGetIntegerv(GL_MAX_INTEGER_SAMPLES, &mMaxIntegerSamples);
+ glGetIntegerv(GL_MAX_SAMPLE_MASK_WORDS, &mMaxSampleMaskWords);
+ }
+
+ if (mHasFramebufferObject)
+ {
+ glGetIntegerv(GL_MAX_SAMPLES, &mMaxSamples);
+ }
+
setToDebugGPU();
initGLStates();
@@ -640,6 +673,14 @@ std::string LLGLManager::getRawGLString()
return gl_string;
}
+U32 LLGLManager::getNumFBOFSAASamples(U32 samples)
+{
+ samples = llmin(samples, (U32) mMaxColorTextureSamples);
+ samples = llmin(samples, (U32) mMaxDepthTextureSamples);
+ samples = llmin(samples, (U32) 4);
+ return samples;
+}
+
void LLGLManager::shutdownGL()
{
if (mInited)
@@ -734,6 +775,7 @@ void LLGLManager::initExtensions()
mHasDrawBuffers = ExtensionExists("GL_ARB_draw_buffers", gGLHExts.mSysExts);
mHasBlendFuncSeparate = ExtensionExists("GL_EXT_blend_func_separate", gGLHExts.mSysExts);
mHasTextureRectangle = ExtensionExists("GL_ARB_texture_rectangle", gGLHExts.mSysExts);
+ mHasTextureMultisample = ExtensionExists("GL_ARB_texture_multisample", gGLHExts.mSysExts);
#if !LL_DARWIN
mHasPointParameters = !mIsATI && ExtensionExists("GL_ARB_point_parameters", gGLHExts.mSysExts);
#endif
@@ -878,11 +920,13 @@ void LLGLManager::initExtensions()
LL_INFOS("RenderInit") << "Disabling mip-map generation for Intel GPUs" << LL_ENDL;
mHasMipMapGeneration = FALSE;
}
+#if !LL_DARWIN
if (mIsATI && mHasMipMapGeneration)
{
LL_INFOS("RenderInit") << "Disabling mip-map generation for ATI GPUs (performance opt)" << LL_ENDL;
mHasMipMapGeneration = FALSE;
}
+#endif
// Misc
glGetIntegerv(GL_MAX_ELEMENTS_VERTICES, (GLint*) &mGLMaxVertexRange);
@@ -943,6 +987,13 @@ void LLGLManager::initExtensions()
{
glBlendFuncSeparateEXT = (PFNGLBLENDFUNCSEPARATEEXTPROC) GLH_EXT_GET_PROC_ADDRESS("glBlendFuncSeparateEXT");
}
+ if (mHasTextureMultisample)
+ {
+ glTexImage2DMultisample = (PFNGLTEXIMAGE2DMULTISAMPLEPROC) GLH_EXT_GET_PROC_ADDRESS("glTexImage2DMultisample");
+ glTexImage3DMultisample = (PFNGLTEXIMAGE3DMULTISAMPLEPROC) GLH_EXT_GET_PROC_ADDRESS("glTexImage3DMultisample");
+ glGetMultisamplefv = (PFNGLGETMULTISAMPLEFVPROC) GLH_EXT_GET_PROC_ADDRESS("glGetMultisamplefv");
+ glSampleMaski = (PFNGLSAMPLEMASKIPROC) GLH_EXT_GET_PROC_ADDRESS("glSampleMaski");
+ }
#if (!LL_LINUX && !LL_SOLARIS) || LL_LINUX_NV_GL_HEADERS
// This is expected to be a static symbol on Linux GL implementations, except if we use the nvidia headers - bah
glDrawRangeElements = (PFNGLDRAWRANGEELEMENTSPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawRangeElements");
@@ -1374,7 +1425,8 @@ void LLGLState::checkTextureChannels(const std::string& msg)
"GL_TEXTURE_GEN_T",
"GL_TEXTURE_GEN_Q",
"GL_TEXTURE_GEN_R",
- "GL_TEXTURE_RECTANGLE_ARB"
+ "GL_TEXTURE_RECTANGLE_ARB",
+ "GL_TEXTURE_2D_MULTISAMPLE"
};
static GLint value[] =
@@ -1387,7 +1439,8 @@ void LLGLState::checkTextureChannels(const std::string& msg)
GL_TEXTURE_GEN_T,
GL_TEXTURE_GEN_Q,
GL_TEXTURE_GEN_R,
- GL_TEXTURE_RECTANGLE_ARB
+ GL_TEXTURE_RECTANGLE_ARB,
+ GL_TEXTURE_2D_MULTISAMPLE
};
GLint stackDepth = 0;
@@ -1427,11 +1480,16 @@ void LLGLState::checkTextureChannels(const std::string& msg)
gFailLog << "Texture matrix in channel " << i << " corrupt." << std::endl;
}
}
-
-
+
for (S32 j = (i == 0 ? 1 : 0);
- j < (gGLManager.mHasTextureRectangle ? 9 : 8); j++)
+ j < 9; j++)
{
+ if (j == 8 && !gGLManager.mHasTextureRectangle ||
+ j == 9 && !gGLManager.mHasTextureMultisample)
+ {
+ continue;
+ }
+
if (glIsEnabled(value[j]))
{
error = TRUE;
diff --git a/indra/llrender/llgl.h b/indra/llrender/llgl.h
index 3d002fd8c4..420922cf06 100644
--- a/indra/llrender/llgl.h
+++ b/indra/llrender/llgl.h
@@ -83,20 +83,27 @@ public:
BOOL mHasMipMapGeneration;
BOOL mHasCompressedTextures;
BOOL mHasFramebufferObject;
+ S32 mMaxSamples;
BOOL mHasBlendFuncSeparate;
-
+
// ARB Extensions
BOOL mHasVertexBufferObject;
BOOL mHasPBuffer;
BOOL mHasShaderObjects;
BOOL mHasVertexShader;
BOOL mHasFragmentShader;
+ S32 mNumTextureImageUnits;
BOOL mHasOcclusionQuery;
BOOL mHasOcclusionQuery2;
BOOL mHasPointParameters;
BOOL mHasDrawBuffers;
BOOL mHasDepthClamp;
BOOL mHasTextureRectangle;
+ BOOL mHasTextureMultisample;
+ S32 mMaxSampleMaskWords;
+ S32 mMaxColorTextureSamples;
+ S32 mMaxDepthTextureSamples;
+ S32 mMaxIntegerSamples;
// Other extensions.
BOOL mHasAnisotropic;
@@ -138,6 +145,7 @@ public:
void printGLInfoString();
void getGLInfo(LLSD& info);
+ U32 getNumFBOFSAASamples(U32 desired_samples = 32);
// In ALL CAPS
std::string mGLVendor;
std::string mGLVendorShort;
diff --git a/indra/llrender/llglheaders.h b/indra/llrender/llglheaders.h
index d8140a124d..825d304d35 100644
--- a/indra/llrender/llglheaders.h
+++ b/indra/llrender/llglheaders.h
@@ -474,6 +474,11 @@ extern PFNGLFRAMEBUFFERTEXTURELAYERPROC glFramebufferTextureLayer;
//GL_ARB_draw_buffers
extern PFNGLDRAWBUFFERSARBPROC glDrawBuffersARB;
+//GL_ARB_texture_multisample
+extern PFNGLTEXIMAGE2DMULTISAMPLEPROC glTexImage2DMultisample;
+extern PFNGLTEXIMAGE3DMULTISAMPLEPROC glTexImage3DMultisample;
+extern PFNGLGETMULTISAMPLEFVPROC glGetMultisamplefv;
+extern PFNGLSAMPLEMASKIPROC glSampleMaski;
#elif LL_WINDOWS
//----------------------------------------------------------------------------
@@ -673,6 +678,11 @@ extern PFNGLFRAMEBUFFERTEXTURELAYERPROC glFramebufferTextureLayer;
//GL_ARB_draw_buffers
extern PFNGLDRAWBUFFERSARBPROC glDrawBuffersARB;
+//GL_ARB_texture_multisample
+extern PFNGLTEXIMAGE2DMULTISAMPLEPROC glTexImage2DMultisample;
+extern PFNGLTEXIMAGE3DMULTISAMPLEPROC glTexImage3DMultisample;
+extern PFNGLGETMULTISAMPLEFVPROC glGetMultisamplefv;
+extern PFNGLSAMPLEMASKIPROC glSampleMaski;
#elif LL_DARWIN
//----------------------------------------------------------------------------
diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp
index 257bcd9380..2e7147a3b4 100644
--- a/indra/llrender/llglslshader.cpp
+++ b/indra/llrender/llglslshader.cpp
@@ -293,7 +293,8 @@ void LLGLSLShader::mapUniform(GLint index, const vector<string> * uniforms)
GLint LLGLSLShader::mapUniformTextureChannel(GLint location, GLenum type)
{
- if (type >= GL_SAMPLER_1D_ARB && type <= GL_SAMPLER_2D_RECT_SHADOW_ARB)
+ if (type >= GL_SAMPLER_1D_ARB && type <= GL_SAMPLER_2D_RECT_SHADOW_ARB ||
+ type == GL_SAMPLER_2D_MULTISAMPLE)
{ //this here is a texture
glUniform1iARB(location, mActiveTextureChannels);
LL_DEBUGS("ShaderLoading") << "Assigned to texture channel " << mActiveTextureChannels << LL_ENDL;
diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp
index d408077c68..3a4139bace 100644
--- a/indra/llrender/llimagegl.cpp
+++ b/indra/llrender/llimagegl.cpp
@@ -1083,12 +1083,17 @@ void LLImageGL::generateTextures(S32 numTextures, U32 *textures)
}
// static
-void LLImageGL::deleteTextures(S32 numTextures, U32 *textures)
+void LLImageGL::deleteTextures(S32 numTextures, U32 *textures, bool immediate)
{
for (S32 i = 0; i < numTextures; i++)
{
sDeadTextureList.push_back(textures[i]);
}
+
+ if (immediate)
+ {
+ LLImageGL::deleteDeadTextures();
+ }
}
// static
@@ -1417,7 +1422,7 @@ void LLImageGL::deleteDeadTextures()
{
if (sCurrentBoundTextures[i] == tex)
{
- gGL.getTexUnit(i)->unbind(LLTexUnit::TT_TEXTURE);
+ gGL.getTexUnit(i)->unbind(gGL.getTexUnit(i)->getCurrType());
stop_glerror();
}
}
diff --git a/indra/llrender/llimagegl.h b/indra/llrender/llimagegl.h
index 6c980984c0..2cfb15b0d9 100644
--- a/indra/llrender/llimagegl.h
+++ b/indra/llrender/llimagegl.h
@@ -98,7 +98,7 @@ public:
// These 3 functions currently wrap glGenTextures(), glDeleteTextures(), and glTexImage2D()
// for tracking purposes and will be deprecated in the future
static void generateTextures(S32 numTextures, U32 *textures);
- static void deleteTextures(S32 numTextures, U32 *textures);
+ static void deleteTextures(S32 numTextures, U32 *textures, bool immediate = false);
static void setManualImage(U32 target, S32 miplevel, S32 intformat, S32 width, S32 height, U32 pixformat, U32 pixtype, const void *pixels);
BOOL createGLTexture() ;
diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp
index 49e10c4790..049dd4346b 100644
--- a/indra/llrender/llrender.cpp
+++ b/indra/llrender/llrender.cpp
@@ -53,7 +53,8 @@ static GLenum sGLTextureType[] =
{
GL_TEXTURE_2D,
GL_TEXTURE_RECTANGLE_ARB,
- GL_TEXTURE_CUBE_MAP_ARB
+ GL_TEXTURE_CUBE_MAP_ARB,
+ GL_TEXTURE_2D_MULTISAMPLE
};
static GLint sGLAddressMode[] =
@@ -119,14 +120,29 @@ void LLTexUnit::refreshState(void)
gGL.flush();
glActiveTextureARB(GL_TEXTURE0_ARB + mIndex);
+
+ //
+ // Per apple spec, don't call glEnable/glDisable when index exceeds max texture units
+ // http://www.mailinglistarchive.com/html/mac-opengl@lists.apple.com/2008-07/msg00653.html
+ //
+ bool enableDisable = (mIndex < gGLManager.mNumTextureUnits) && mCurrTexType != LLTexUnit::TT_MULTISAMPLE_TEXTURE;
+
if (mCurrTexType != TT_NONE)
{
- glEnable(sGLTextureType[mCurrTexType]);
+ if (enableDisable)
+ {
+ glEnable(sGLTextureType[mCurrTexType]);
+ }
+
glBindTexture(sGLTextureType[mCurrTexType], mCurrTexture);
}
else
{
- glDisable(GL_TEXTURE_2D);
+ if (enableDisable)
+ {
+ glDisable(GL_TEXTURE_2D);
+ }
+
glBindTexture(GL_TEXTURE_2D, 0);
}
@@ -167,7 +183,11 @@ void LLTexUnit::enable(eTextureType type)
mCurrTexType = type;
gGL.flush();
- glEnable(sGLTextureType[type]);
+ if (type != LLTexUnit::TT_MULTISAMPLE_TEXTURE &&
+ mIndex < gGLManager.mNumTextureUnits)
+ {
+ glEnable(sGLTextureType[type]);
+ }
}
}
@@ -180,7 +200,12 @@ void LLTexUnit::disable(void)
activate();
unbind(mCurrTexType);
gGL.flush();
- glDisable(sGLTextureType[mCurrTexType]);
+ if (mCurrTexType != LLTexUnit::TT_MULTISAMPLE_TEXTURE &&
+ mIndex < gGLManager.mNumTextureUnits)
+ {
+ glDisable(sGLTextureType[mCurrTexType]);
+ }
+
mCurrTexType = TT_NONE;
}
}
@@ -399,7 +424,7 @@ void LLTexUnit::setTextureAddressMode(eTextureAddressMode mode)
void LLTexUnit::setTextureFilteringOption(LLTexUnit::eTextureFilterOptions option)
{
- if (mIndex < 0 || mCurrTexture == 0) return;
+ if (mIndex < 0 || mCurrTexture == 0 || mCurrTexType == LLTexUnit::TT_MULTISAMPLE_TEXTURE) return;
gGL.flush();
diff --git a/indra/llrender/llrender.h b/indra/llrender/llrender.h
index 7ba14f7b40..41e7b35341 100644
--- a/indra/llrender/llrender.h
+++ b/indra/llrender/llrender.h
@@ -57,6 +57,7 @@ public:
TT_TEXTURE = 0, // Standard 2D Texture
TT_RECT_TEXTURE, // Non power of 2 texture
TT_CUBE_MAP, // 6-sided cube map texture
+ TT_MULTISAMPLE_TEXTURE, // see GL_ARB_texture_multisample
TT_NONE // No texture type is currently enabled
} eTextureType;
diff --git a/indra/llrender/llrendertarget.cpp b/indra/llrender/llrendertarget.cpp
index cd2556d435..715f466314 100644
--- a/indra/llrender/llrendertarget.cpp
+++ b/indra/llrender/llrendertarget.cpp
@@ -44,6 +44,7 @@ void check_framebuffer_status()
case GL_FRAMEBUFFER_COMPLETE:
break;
default:
+ llwarns << "check_framebuffer_status failed -- " << std::hex << status << llendl;
ll_fail("check_framebuffer_status failed");
break;
}
@@ -62,8 +63,7 @@ LLRenderTarget::LLRenderTarget() :
mUseDepth(false),
mRenderDepth(false),
mUsage(LLTexUnit::TT_TEXTURE),
- mSamples(0),
- mSampleBuffer(NULL)
+ mSamples(0)
{
}
@@ -72,13 +72,7 @@ LLRenderTarget::~LLRenderTarget()
release();
}
-
-void LLRenderTarget::setSampleBuffer(LLMultisampleBuffer* buffer)
-{
- mSampleBuffer = buffer;
-}
-
-void LLRenderTarget::allocate(U32 resx, U32 resy, U32 color_fmt, bool depth, bool stencil, LLTexUnit::eTextureType usage, bool use_fbo)
+void LLRenderTarget::allocate(U32 resx, U32 resy, U32 color_fmt, bool depth, bool stencil, LLTexUnit::eTextureType usage, bool use_fbo, S32 samples)
{
stop_glerror();
mResX = resx;
@@ -87,6 +81,20 @@ void LLRenderTarget::allocate(U32 resx, U32 resy, U32 color_fmt, bool depth, boo
mStencil = stencil;
mUsage = usage;
mUseDepth = depth;
+ mSamples = samples;
+
+ mSamples = gGLManager.getNumFBOFSAASamples(mSamples);
+
+ if (mSamples > 1 && gGLManager.mHasTextureMultisample)
+ {
+ mUsage = LLTexUnit::TT_MULTISAMPLE_TEXTURE;
+ //no support for multisampled stencil targets yet
+ mStencil = false;
+ }
+ else
+ {
+ mSamples = 0;
+ }
release();
@@ -145,29 +153,47 @@ void LLRenderTarget::addColorAttachment(U32 color_fmt)
stop_glerror();
- LLImageGL::setManualImage(LLTexUnit::getInternalType(mUsage), 0, color_fmt, mResX, mResY, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
- stop_glerror();
-
- if (offset == 0)
+ if (mSamples > 1)
{
- gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR);
+ glTexImage2DMultisample(LLTexUnit::getInternalType(mUsage), mSamples, color_fmt, mResX, mResY, GL_TRUE);
}
else
- { //don't filter data attachments
- gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_POINT);
- }
- if (mUsage != LLTexUnit::TT_RECT_TEXTURE)
{
- gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_MIRROR);
+ LLImageGL::setManualImage(LLTexUnit::getInternalType(mUsage), 0, color_fmt, mResX, mResY, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
}
- else
- {
- // ATI doesn't support mirrored repeat for rectangular textures.
- gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_CLAMP);
+
+ stop_glerror();
+
+ if (mSamples == 0)
+ {
+ if (offset == 0)
+ { //use bilinear filtering on single texture render targets that aren't multisampled
+ gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR);
+ stop_glerror();
+ }
+ else
+ { //don't filter data attachments
+ gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_POINT);
+ stop_glerror();
+ }
+
+ if (mUsage != LLTexUnit::TT_RECT_TEXTURE)
+ {
+ gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_MIRROR);
+ stop_glerror();
+ }
+ else
+ {
+ // ATI doesn't support mirrored repeat for rectangular textures.
+ gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_CLAMP);
+ stop_glerror();
+ }
}
+
if (mFBO)
{
+ stop_glerror();
glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0+offset,
LLTexUnit::getInternalType(mUsage), tex, 0);
@@ -180,6 +206,12 @@ void LLRenderTarget::addColorAttachment(U32 color_fmt)
mTex.push_back(tex);
+ if (gDebugGL)
+ { //bind and unbind to validate target
+ bindTarget();
+ flush();
+ }
+
}
void LLRenderTarget::allocateDepth()
@@ -196,9 +228,16 @@ void LLRenderTarget::allocateDepth()
{
LLImageGL::generateTextures(1, &mDepth);
gGL.getTexUnit(0)->bindManual(mUsage, mDepth);
- U32 internal_type = LLTexUnit::getInternalType(mUsage);
- gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_POINT);
- LLImageGL::setManualImage(internal_type, 0, GL_DEPTH_COMPONENT32, mResX, mResY, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL);
+ if (mSamples == 0)
+ {
+ U32 internal_type = LLTexUnit::getInternalType(mUsage);
+ gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_POINT);
+ LLImageGL::setManualImage(internal_type, 0, GL_DEPTH_COMPONENT32, mResX, mResY, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL);
+ }
+ else
+ {
+ glTexImage2DMultisample(LLTexUnit::getInternalType(mUsage), mSamples, GL_DEPTH_COMPONENT32, mResX, mResY, GL_TRUE);
+ }
}
}
@@ -238,6 +277,9 @@ void LLRenderTarget::shareDepthBuffer(LLRenderTarget& target)
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, LLTexUnit::getInternalType(mUsage), mDepth, 0);
stop_glerror();
}
+
+ check_framebuffer_status();
+
glBindFramebuffer(GL_FRAMEBUFFER, 0);
target.mUseDepth = true;
@@ -255,7 +297,7 @@ void LLRenderTarget::release()
}
else
{
- LLImageGL::deleteTextures(1, &mDepth);
+ LLImageGL::deleteTextures(1, &mDepth, true);
stop_glerror();
}
mDepth = 0;
@@ -284,11 +326,10 @@ void LLRenderTarget::release()
if (mTex.size() > 0)
{
- LLImageGL::deleteTextures(mTex.size(), &mTex[0]);
+ LLImageGL::deleteTextures(mTex.size(), &mTex[0], true);
mTex.clear();
}
- mSampleBuffer = NULL;
sBoundTarget = NULL;
}
@@ -297,34 +338,27 @@ void LLRenderTarget::bindTarget()
if (mFBO)
{
stop_glerror();
- if (mSampleBuffer)
- {
- mSampleBuffer->bindTarget(this);
- stop_glerror();
+
+ glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
+ stop_glerror();
+ if (gGLManager.mHasDrawBuffers)
+ { //setup multiple render targets
+ GLenum drawbuffers[] = {GL_COLOR_ATTACHMENT0,
+ GL_COLOR_ATTACHMENT1,
+ GL_COLOR_ATTACHMENT2,
+ GL_COLOR_ATTACHMENT3};
+ glDrawBuffersARB(mTex.size(), drawbuffers);
}
- else
- {
- glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
- stop_glerror();
- if (gGLManager.mHasDrawBuffers)
- { //setup multiple render targets
- GLenum drawbuffers[] = {GL_COLOR_ATTACHMENT0,
- GL_COLOR_ATTACHMENT1,
- GL_COLOR_ATTACHMENT2,
- GL_COLOR_ATTACHMENT3};
- glDrawBuffersARB(mTex.size(), drawbuffers);
- }
- if (mTex.empty())
- { //no color buffer to draw to
- glDrawBuffer(GL_NONE);
- glReadBuffer(GL_NONE);
- }
+ if (mTex.empty())
+ { //no color buffer to draw to
+ glDrawBuffer(GL_NONE);
+ glReadBuffer(GL_NONE);
+ }
- check_framebuffer_status();
+ check_framebuffer_status();
- stop_glerror();
- }
+ stop_glerror();
}
glViewport(0, 0, mResX, mResY);
@@ -406,50 +440,8 @@ void LLRenderTarget::flush(bool fetch_depth)
else
{
stop_glerror();
-
glBindFramebuffer(GL_FRAMEBUFFER, 0);
-
stop_glerror();
-
- if (mSampleBuffer)
- {
- LLGLEnable multisample(GL_MULTISAMPLE);
- stop_glerror();
- glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
- stop_glerror();
- check_framebuffer_status();
- glBindFramebuffer(GL_READ_FRAMEBUFFER, mSampleBuffer->mFBO);
- check_framebuffer_status();
-
- stop_glerror();
- glBlitFramebuffer(0, 0, mResX, mResY, 0, 0, mResX, mResY, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST);
- stop_glerror();
-
- if (mTex.size() > 1)
- {
- for (U32 i = 1; i < mTex.size(); ++i)
- {
- glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
- LLTexUnit::getInternalType(mUsage), mTex[i], 0);
- stop_glerror();
- glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, mSampleBuffer->mTex[i]);
- stop_glerror();
- glBlitFramebuffer(0, 0, mResX, mResY, 0, 0, mResX, mResY, GL_COLOR_BUFFER_BIT, GL_NEAREST);
- stop_glerror();
- }
-
- for (U32 i = 0; i < mTex.size(); ++i)
- {
- glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0+i,
- LLTexUnit::getInternalType(mUsage), mTex[i], 0);
- stop_glerror();
- glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0+i, GL_RENDERBUFFER, mSampleBuffer->mTex[i]);
- stop_glerror();
- }
- }
- }
-
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
}
@@ -466,37 +458,36 @@ void LLRenderTarget::copyContents(LLRenderTarget& source, S32 srcX0, S32 srcY0,
llerrs << "Cannot copy framebuffer contents for non FBO render targets." << llendl;
}
- if (mSampleBuffer)
+
+ if (mask == GL_DEPTH_BUFFER_BIT && source.mStencil != mStencil)
{
- mSampleBuffer->copyContents(source, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
+ stop_glerror();
+
+ glBindFramebuffer(GL_FRAMEBUFFER, source.mFBO);
+ check_framebuffer_status();
+ gGL.getTexUnit(0)->bind(this, true);
+ stop_glerror();
+ glCopyTexSubImage2D(LLTexUnit::getInternalType(mUsage), 0, srcX0, srcY0, dstX0, dstY0, dstX1, dstY1);
+ stop_glerror();
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ stop_glerror();
}
else
{
- if (mask == GL_DEPTH_BUFFER_BIT && source.mStencil != mStencil)
- {
- stop_glerror();
-
- glBindFramebuffer(GL_FRAMEBUFFER, source.mFBO);
- gGL.getTexUnit(0)->bind(this, true);
- stop_glerror();
- glCopyTexSubImage2D(LLTexUnit::getInternalType(mUsage), 0, srcX0, srcY0, dstX0, dstY0, dstX1, dstY1);
- stop_glerror();
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
- stop_glerror();
- }
- else
- {
- glBindFramebuffer(GL_READ_FRAMEBUFFER, source.mFBO);
- stop_glerror();
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mFBO);
- stop_glerror();
- check_framebuffer_status();
- stop_glerror();
- glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
- stop_glerror();
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
- stop_glerror();
- }
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, source.mFBO);
+ stop_glerror();
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mFBO);
+ stop_glerror();
+ check_framebuffer_status();
+ stop_glerror();
+ glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
+ stop_glerror();
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
+ stop_glerror();
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+ stop_glerror();
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ stop_glerror();
}
}
@@ -539,179 +530,3 @@ void LLRenderTarget::getViewport(S32* viewport)
viewport[3] = mResY;
}
-//==================================================
-// LLMultisampleBuffer implementation
-//==================================================
-LLMultisampleBuffer::LLMultisampleBuffer()
-{
-
-}
-
-LLMultisampleBuffer::~LLMultisampleBuffer()
-{
- release();
-}
-
-void LLMultisampleBuffer::release()
-{
- if (mFBO)
- {
- glDeleteFramebuffers(1, (GLuint *) &mFBO);
- mFBO = 0;
- }
-
- if (mTex.size() > 0)
- {
- glDeleteRenderbuffers(mTex.size(), (GLuint *) &mTex[0]);
- mTex.clear();
- }
-
- if (mDepth)
- {
- glDeleteRenderbuffers(1, (GLuint *) &mDepth);
- mDepth = 0;
- }
-}
-
-void LLMultisampleBuffer::bindTarget()
-{
- bindTarget(this);
-}
-
-void LLMultisampleBuffer::bindTarget(LLRenderTarget* ref)
-{
- if (!ref)
- {
- ref = this;
- }
-
- glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
- if (gGLManager.mHasDrawBuffers)
- { //setup multiple render targets
- GLenum drawbuffers[] = {GL_COLOR_ATTACHMENT0,
- GL_COLOR_ATTACHMENT1,
- GL_COLOR_ATTACHMENT2,
- GL_COLOR_ATTACHMENT3};
- glDrawBuffersARB(ref->mTex.size(), drawbuffers);
- }
-
- check_framebuffer_status();
-
- glViewport(0, 0, mResX, mResY);
-
- sBoundTarget = this;
-}
-
-void LLMultisampleBuffer::allocate(U32 resx, U32 resy, U32 color_fmt, bool depth, bool stencil, LLTexUnit::eTextureType usage, bool use_fbo )
-{
- allocate(resx,resy,color_fmt,depth,stencil,usage,use_fbo,2);
-}
-
-void LLMultisampleBuffer::allocate(U32 resx, U32 resy, U32 color_fmt, bool depth, bool stencil, LLTexUnit::eTextureType usage, bool use_fbo, U32 samples )
-{
- stop_glerror();
- mResX = resx;
- mResY = resy;
-
- mUsage = usage;
- mUseDepth = depth;
- mStencil = stencil;
-
- release();
-
- mSamples = samples;
-
- if (mSamples <= 1)
- {
- llerrs << "Cannot create a multisample buffer with less than 2 samples." << llendl;
- }
-
- stop_glerror();
-
- if ((sUseFBO || use_fbo) && gGLManager.mHasFramebufferObject)
- {
-
- if (depth)
- {
- stop_glerror();
- allocateDepth();
- stop_glerror();
- }
-
- glGenFramebuffers(1, (GLuint *) &mFBO);
-
- glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
-
- if (mDepth)
- {
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, mDepth);
- if (mStencil)
- {
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, mDepth);
- }
- }
-
- stop_glerror();
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
- stop_glerror();
- }
-
- addColorAttachment(color_fmt);
-}
-
-void LLMultisampleBuffer::addColorAttachment(U32 color_fmt)
-{
- if (color_fmt == 0)
- {
- return;
- }
-
- U32 offset = mTex.size();
- if (offset >= 4 ||
- (offset > 0 && (mFBO == 0 || !gGLManager.mHasDrawBuffers)))
- {
- llerrs << "Too many color attachments!" << llendl;
- }
-
- U32 tex;
- glGenRenderbuffers(1, &tex);
-
- glBindRenderbuffer(GL_RENDERBUFFER, tex);
- glRenderbufferStorageMultisample(GL_RENDERBUFFER, mSamples, color_fmt, mResX, mResY);
- stop_glerror();
-
- if (mFBO)
- {
- glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0+offset, GL_RENDERBUFFER, tex);
- stop_glerror();
- GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
- switch (status)
- {
- case GL_FRAMEBUFFER_COMPLETE:
- break;
- default:
- llerrs << "WTF? " << std::hex << status << llendl;
- break;
- }
-
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
- }
-
- mTex.push_back(tex);
-}
-
-void LLMultisampleBuffer::allocateDepth()
-{
- glGenRenderbuffers(1, (GLuint* ) &mDepth);
- glBindRenderbuffer(GL_RENDERBUFFER, mDepth);
- if (mStencil)
- {
- glRenderbufferStorageMultisample(GL_RENDERBUFFER, mSamples, GL_DEPTH24_STENCIL8, mResX, mResY);
- }
- else
- {
- glRenderbufferStorageMultisample(GL_RENDERBUFFER, mSamples, GL_DEPTH_COMPONENT16, mResX, mResY);
- }
-}
-
diff --git a/indra/llrender/llrendertarget.h b/indra/llrender/llrendertarget.h
index 12dd1c8b90..094b58b562 100644
--- a/indra/llrender/llrendertarget.h
+++ b/indra/llrender/llrendertarget.h
@@ -71,10 +71,7 @@ public:
//allocate resources for rendering
//must be called before use
//multiple calls will release previously allocated resources
- void allocate(U32 resx, U32 resy, U32 color_fmt, bool depth, bool stencil, LLTexUnit::eTextureType usage = LLTexUnit::TT_TEXTURE, bool use_fbo = FALSE);
-
- //provide this render target with a multisample resource.
- void setSampleBuffer(LLMultisampleBuffer* buffer);
+ void allocate(U32 resx, U32 resy, U32 color_fmt, bool depth, bool stencil, LLTexUnit::eTextureType usage = LLTexUnit::TT_TEXTURE, bool use_fbo = false, S32 samples = 0);
//add color buffer attachment
//limit of 4 color attachments per render target
@@ -141,7 +138,6 @@ public:
static LLRenderTarget* getCurrentBoundTarget() { return sBoundTarget; }
protected:
- friend class LLMultisampleBuffer;
U32 mResX;
U32 mResY;
std::vector<U32> mTex;
@@ -152,26 +148,8 @@ protected:
bool mRenderDepth;
LLTexUnit::eTextureType mUsage;
U32 mSamples;
- LLMultisampleBuffer* mSampleBuffer;
-
- static LLRenderTarget* sBoundTarget;
-};
-
-class LLMultisampleBuffer : public LLRenderTarget
-{
-public:
- LLMultisampleBuffer();
- virtual ~LLMultisampleBuffer();
-
- virtual void release();
-
- virtual void bindTarget();
- void bindTarget(LLRenderTarget* ref);
- virtual void allocate(U32 resx, U32 resy, U32 color_fmt, bool depth, bool stencil, LLTexUnit::eTextureType usage, bool use_fbo);
- void allocate(U32 resx, U32 resy, U32 color_fmt, bool depth, bool stencil, LLTexUnit::eTextureType usage, bool use_fbo, U32 samples);
- virtual void addColorAttachment(U32 color_fmt);
- virtual void allocateDepth();
+ static LLRenderTarget* sBoundTarget;
};
#endif //!LL_MESA_HEADLESS
diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp
index 98a0a93084..b8f9c60ca9 100644
--- a/indra/llrender/llshadermgr.cpp
+++ b/indra/llrender/llshadermgr.cpp
@@ -374,6 +374,12 @@ GLhandleARB LLShaderMgr::loadShaderFile(const std::string& filename, S32 & shade
GLcharARB* text[1024];
GLuint count = 0;
+ //copy preprocessor definitions into buffer
+ for (std::map<std::string,std::string>::iterator iter = mDefinitions.begin(); iter != mDefinitions.end(); ++iter)
+ {
+ std::string define = "#define " + iter->first + " " + iter->second + "\n";
+ text[count++] = (GLcharARB *) strdup(define.c_str());
+ }
//copy file into memory
while( fgets((char *)buff, 1024, file) != NULL && count < LL_ARRAY_SIZE(buff) )
diff --git a/indra/llrender/llshadermgr.h b/indra/llrender/llshadermgr.h
index c54c4608d7..9ee11b98c8 100644
--- a/indra/llrender/llshadermgr.h
+++ b/indra/llrender/llshadermgr.h
@@ -60,6 +60,9 @@ public:
std::vector<std::string> mReservedUniforms;
+ //preprocessor definitions (name/value)
+ std::map<std::string, std::string> mDefinitions;
+
protected:
// our parameter manager singleton instance
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index cbf22b75e8..769dcf8457 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -77,6 +77,7 @@ include_directories(
set(viewer_SOURCE_FILES
groupchatlistener.cpp
+ llaccountingquotamanager.cpp
llagent.cpp
llagentaccess.cpp
llagentcamera.cpp
@@ -626,6 +627,7 @@ set(viewer_HEADER_FILES
CMakeLists.txt
ViewerInstall.cmake
groupchatlistener.h
+ llaccountingquotamanager.h
llagent.h
llagentaccess.h
llagentcamera.h
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 78db307d64..2c6d75e3c9 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -7091,7 +7091,76 @@
<key>Value</key>
<integer>1</integer>
</map>
- <key>RenderAnisotropic</key>
+
+ <key>OctreeMaxNodeCapacity</key>
+ <map>
+ <key>Comment</key>
+ <string>Maximum number of elements to store in a single octree node</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>U32</string>
+ <key>Value</key>
+ <integer>128</integer>
+ </map>
+
+ <key>OctreeStaticObjectSizeFactor</key>
+ <map>
+ <key>Comment</key>
+ <string>Multiplier on static object size for determining octree node size </string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>S32</string>
+ <key>Value</key>
+ <integer>1</integer>
+ </map>
+
+ <key>OctreeAlphaDistanceFactor</key>
+ <map>
+ <key>Comment</key>
+ <string>Multiplier on alpha object distance for determining octree node size </string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Vector3</string>
+ <key>Value</key>
+ <array>
+ <real>0.05</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ </array>
+ </map>
+
+ <key>OctreeAttachmentSizeFactor</key>
+ <map>
+ <key>Comment</key>
+ <string>Multiplier on attachment size for determining octree node size </string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>S32</string>
+ <key>Value</key>
+ <integer>4</integer>
+ </map>
+
+ <key>OctreeDistanceFactor</key>
+ <map>
+ <key>Comment</key>
+ <string>Multiplier on distance for determining octree node size </string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Vector3</string>
+ <key>Value</key>
+ <array>
+ <real>0.05</real>
+ <real>0.0</real>
+ <real>0.0</real>
+ </array>
+ </map>
+
+ <key>RenderAnisotropic</key>
<map>
<key>Comment</key>
<string>Render textures using anisotropic filtering</string>
@@ -7188,7 +7257,7 @@
<key>Type</key>
<string>F32</string>
<key>Value</key>
- <integer>1.0</integer>
+ <real>1.0</real>
</map>
<key>RenderAvatarVP</key>
<map>
@@ -8589,7 +8658,7 @@
<key>Type</key>
<string>S32</string>
<key>Value</key>
- <integer>8192</integer>
+ <integer>65536</integer>
</map>
<key>RenderMaxVBOSize</key>
<map>
diff --git a/indra/newview/app_settings/shaders/class1/deferred/alphaSkinnedV.glsl b/indra/newview/app_settings/shaders/class1/deferred/alphaSkinnedV.glsl
index 5addbbb176..65d9209983 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/alphaSkinnedV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/alphaSkinnedV.glsl
@@ -35,19 +35,24 @@ float calcPointLightOrSpotLight(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, floa
//get distance
float d = length(lv);
- //normalize light vector
- lv *= 1.0/d;
+ float da = 0.0;
+
+ if (d > 0.0 && la > 0.0 && fa > 0.0)
+ {
+ //normalize light vector
+ lv *= 1.0/d;
- //distance attenuation
- float dist2 = d*d/(la*la);
- float da = clamp(1.0-(dist2-1.0*(1.0-fa))/fa, 0.0, 1.0);
+ //distance attenuation
+ float dist2 = d*d/(la*la);
+ da = clamp(1.0-(dist2-1.0*(1.0-fa))/fa, 0.0, 1.0);
- // spotlight coefficient.
- float spot = max(dot(-ln, lv), is_pointlight);
- da *= spot*spot; // GL_SPOT_EXPONENT=2
+ // spotlight coefficient.
+ float spot = max(dot(-ln, lv), is_pointlight);
+ da *= spot*spot; // GL_SPOT_EXPONENT=2
- //angular attenuation
- da *= calcDirectionalLight(n, lv);
+ //angular attenuation
+ da *= calcDirectionalLight(n, lv);
+ }
return da;
}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/alphaV.glsl b/indra/newview/app_settings/shaders/class1/deferred/alphaV.glsl
index 525b68c437..2691fc8ded 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/alphaV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/alphaV.glsl
@@ -36,19 +36,24 @@ float calcPointLightOrSpotLight(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, floa
//get distance
float d = length(lv);
- //normalize light vector
- lv *= 1.0/d;
+ float da = 0.0;
+
+ if (d > 0.0 && la > 0.0 && fa > 0.0)
+ {
+ //normalize light vector
+ lv *= 1.0/d;
- //distance attenuation
- float dist2 = d*d/(la*la);
- float da = clamp(1.0-(dist2-1.0*(1.0-fa))/fa, 0.0, 1.0);
+ //distance attenuation
+ float dist2 = d*d/(la*la);
+ da = clamp(1.0-(dist2-1.0*(1.0-fa))/fa, 0.0, 1.0);
- // spotlight coefficient.
- float spot = max(dot(-ln, lv), is_pointlight);
- da *= spot*spot; // GL_SPOT_EXPONENT=2
+ // spotlight coefficient.
+ float spot = max(dot(-ln, lv), is_pointlight);
+ da *= spot*spot; // GL_SPOT_EXPONENT=2
- //angular attenuation
- da *= calcDirectionalLight(n, lv);
+ //angular attenuation
+ da *= calcDirectionalLight(n, lv);
+ }
return da;
}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/avatarAlphaV.glsl b/indra/newview/app_settings/shaders/class1/deferred/avatarAlphaV.glsl
index a2a7dea20d..a012cb5030 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/avatarAlphaV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/avatarAlphaV.glsl
@@ -35,19 +35,24 @@ float calcPointLightOrSpotLight(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, floa
//get distance
float d = length(lv);
- //normalize light vector
- lv *= 1.0/d;
+ float da = 0.0;
+
+ if (d > 0.0 && la > 0.0 && fa > 0.0)
+ {
+ //normalize light vector
+ lv *= 1.0/d;
- //distance attenuation
- float dist2 = d*d/(la*la);
- float da = clamp(1.0-(dist2-1.0*(1.0-fa))/fa, 0.0, 1.0);
+ //distance attenuation
+ float dist2 = d*d/(la*la);
+ da = clamp(1.0-(dist2-1.0*(1.0-fa))/fa, 0.0, 1.0);
- // spotlight coefficient.
- float spot = max(dot(-ln, lv), is_pointlight);
- da *= spot*spot; // GL_SPOT_EXPONENT=2
+ // spotlight coefficient.
+ float spot = max(dot(-ln, lv), is_pointlight);
+ da *= spot*spot; // GL_SPOT_EXPONENT=2
- //angular attenuation
- da *= calcDirectionalLight(n, lv);
+ //angular attenuation
+ da *= calcDirectionalLight(n, lv);
+ }
return da;
}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/avatarShadowF.glsl b/indra/newview/app_settings/shaders/class1/deferred/avatarShadowF.glsl
index 1b7ae06888..3d0884bbe8 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/avatarShadowF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/avatarShadowF.glsl
@@ -9,10 +9,13 @@
uniform sampler2D diffuseMap;
+varying vec4 post_pos;
void main()
{
//gl_FragColor = vec4(1,1,1,gl_Color.a * texture2D(diffuseMap, gl_TexCoord[0].xy).a);
gl_FragColor = vec4(1,1,1,1);
+
+ gl_FragDepth = max(post_pos.z/post_pos.w*0.5+0.5, 0.0);
}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/avatarShadowV.glsl b/indra/newview/app_settings/shaders/class1/deferred/avatarShadowV.glsl
index cf6579a40d..3460f40657 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/avatarShadowV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/avatarShadowV.glsl
@@ -11,6 +11,8 @@ mat4 getSkinnedTransform();
attribute vec4 weight;
+varying vec4 post_pos;
+
void main()
{
gl_TexCoord[0] = gl_MultiTexCoord0;
@@ -30,8 +32,9 @@ void main()
norm = normalize(norm);
pos = gl_ProjectionMatrix * pos;
- pos.z = max(pos.z, -pos.w+0.01);
- gl_Position = pos;
+ post_pos = pos;
+
+ gl_Position = vec4(pos.x, pos.y, pos.w*0.5, pos.w);
gl_FrontColor = gl_Color;
}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/blurLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/blurLightF.glsl
index d9f021b114..98d034c2b5 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/blurLightF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/blurLightF.glsl
@@ -26,7 +26,7 @@ uniform vec2 screen_res;
vec4 getPosition(vec2 pos_screen)
{
- float depth = texture2DRect(depthMap, pos_screen.xy).a;
+ float depth = texture2DRect(depthMap, pos_screen.xy).r;
vec2 sc = pos_screen.xy*2.0;
sc /= screen_res;
sc -= vec2(1.0,1.0);
@@ -39,7 +39,7 @@ vec4 getPosition(vec2 pos_screen)
void main()
{
- vec2 tc = vary_fragcoord.xy;
+ vec2 tc = vary_fragcoord.xy;
vec3 norm = texture2DRect(normalMap, tc).xyz;
norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm
vec3 pos = getPosition(tc).xyz;
diff --git a/indra/newview/app_settings/shaders/class1/deferred/blurLightMSF.glsl b/indra/newview/app_settings/shaders/class1/deferred/blurLightMSF.glsl
new file mode 100644
index 0000000000..edec83ea07
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/deferred/blurLightMSF.glsl
@@ -0,0 +1,113 @@
+/**
+ * @file blurLightF.glsl
+ *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
+ * $/LicenseInfo$
+ */
+
+#version 120
+
+#extension GL_ARB_texture_rectangle : enable
+#extension GL_ARB_texture_multisample : enable
+
+uniform sampler2DMS depthMap;
+uniform sampler2DMS normalMap;
+uniform sampler2DRect lightMap;
+
+uniform float dist_factor;
+uniform float blur_size;
+uniform vec2 delta;
+uniform vec3 kern[4];
+uniform float kern_scale;
+
+varying vec2 vary_fragcoord;
+
+uniform mat4 inv_proj;
+uniform vec2 screen_res;
+
+vec3 texture2DMS3(sampler2DMS tex, ivec2 tc)
+{
+ vec3 ret = vec3(0,0,0);
+ for (int i = 0; i < samples; i++)
+ {
+ ret += texelFetch(tex, tc, i).rgb;
+ }
+
+ return ret/samples;
+}
+
+float texture2DMS1(sampler2DMS tex, ivec2 tc)
+{
+ float ret = 0;
+ for (int i = 0; i < samples; i++)
+ {
+ ret += texelFetch(tex, tc, i).r;
+ }
+
+ return ret/samples;
+}
+
+vec4 getPosition(ivec2 pos_screen)
+{
+ float depth = texture2DMS1(depthMap, pos_screen.xy);
+ vec2 sc = pos_screen.xy*2.0;
+ sc /= screen_res;
+ sc -= vec2(1.0,1.0);
+ vec4 ndc = vec4(sc.x, sc.y, 2.0*depth-1.0, 1.0);
+ vec4 pos = inv_proj * ndc;
+ pos /= pos.w;
+ pos.w = 1.0;
+ return pos;
+}
+
+void main()
+{
+ vec2 tc = vary_fragcoord.xy;
+ ivec2 itc = ivec2(tc);
+
+ vec3 norm = texture2DMS3(normalMap, itc).xyz;
+ norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm
+ vec3 pos = getPosition(itc).xyz;
+ vec4 ccol = texture2DRect(lightMap, tc).rgba;
+
+ vec2 dlt = kern_scale * delta / (1.0+norm.xy*norm.xy);
+ dlt /= max(-pos.z*dist_factor, 1.0);
+
+ vec2 defined_weight = kern[0].xy; // special case the first (centre) sample's weight in the blur; we have to sample it anyway so we get it for 'free'
+ vec4 col = defined_weight.xyxx * ccol;
+
+ // relax tolerance according to distance to avoid speckling artifacts, as angles and distances are a lot more abrupt within a small screen area at larger distances
+ float pointplanedist_tolerance_pow2 = pos.z*pos.z*0.00005;
+
+ // perturb sampling origin slightly in screen-space to hide edge-ghosting artifacts where smoothing radius is quite large
+ tc += ( (mod(tc.x+tc.y,2) - 0.5) * kern[1].z * dlt * 0.5 );
+
+ for (int i = 1; i < 4; i++)
+ {
+ vec2 samptc = tc + kern[i].z*dlt;
+ vec3 samppos = getPosition(ivec2(samptc)).xyz;
+ float d = dot(norm.xyz, samppos.xyz-pos.xyz);// dist from plane
+ if (d*d <= pointplanedist_tolerance_pow2)
+ {
+ col += texture2DRect(lightMap, samptc)*kern[i].xyxx;
+ defined_weight += kern[i].xy;
+ }
+ }
+ for (int i = 1; i < 4; i++)
+ {
+ vec2 samptc = vec2(tc - kern[i].z*dlt);
+ vec3 samppos = getPosition(ivec2(samptc)).xyz;
+ float d = dot(norm.xyz, samppos.xyz-pos.xyz);// dist from plane
+ if (d*d <= pointplanedist_tolerance_pow2)
+ {
+ col += texture2DRect(lightMap, samptc)*kern[i].xyxx;
+ defined_weight += kern[i].xy;
+ }
+ }
+
+ col /= defined_weight.xyxx;
+ col.y *= col.y;
+
+ gl_FragColor = col;
+}
+
diff --git a/indra/newview/app_settings/shaders/class1/deferred/cloudsF.glsl b/indra/newview/app_settings/shaders/class1/deferred/cloudsF.glsl
new file mode 100644
index 0000000000..9e551fa976
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/deferred/cloudsF.glsl
@@ -0,0 +1,79 @@
+/**
+ * @file WLCloudsF.glsl
+ *
+ * $LicenseInfo:firstyear=2005&license=viewerlgpl$
+ * $/LicenseInfo$
+ */
+
+#version 120
+
+/////////////////////////////////////////////////////////////////////////
+// The fragment shader for the sky
+/////////////////////////////////////////////////////////////////////////
+
+varying vec4 vary_CloudColorSun;
+varying vec4 vary_CloudColorAmbient;
+varying float vary_CloudDensity;
+
+uniform sampler2D cloud_noise_texture;
+uniform vec4 cloud_pos_density1;
+uniform vec4 cloud_pos_density2;
+uniform vec4 gamma;
+
+/// Soft clips the light with a gamma correction
+vec3 scaleSoftClip(vec3 light) {
+ //soft clip effect:
+ light = 1. - clamp(light, vec3(0.), vec3(1.));
+ light = 1. - pow(light, gamma.xxx);
+
+ return light;
+}
+
+void main()
+{
+ // Set variables
+ vec2 uv1 = gl_TexCoord[0].xy;
+ vec2 uv2 = gl_TexCoord[1].xy;
+
+ vec4 cloudColorSun = vary_CloudColorSun;
+ vec4 cloudColorAmbient = vary_CloudColorAmbient;
+ float cloudDensity = vary_CloudDensity;
+ vec2 uv3 = gl_TexCoord[2].xy;
+ vec2 uv4 = gl_TexCoord[3].xy;
+
+ // Offset texture coords
+ uv1 += cloud_pos_density1.xy; //large texture, visible density
+ uv2 += cloud_pos_density1.xy; //large texture, self shadow
+ uv3 += cloud_pos_density2.xy; //small texture, visible density
+ uv4 += cloud_pos_density2.xy; //small texture, self shadow
+
+
+ // Compute alpha1, the main cloud opacity
+ float alpha1 = (texture2D(cloud_noise_texture, uv1).x - 0.5) + (texture2D(cloud_noise_texture, uv3).x - 0.5) * cloud_pos_density2.z;
+ alpha1 = min(max(alpha1 + cloudDensity, 0.) * 10. * cloud_pos_density1.z, 1.);
+
+ // And smooth
+ alpha1 = 1. - alpha1 * alpha1;
+ alpha1 = 1. - alpha1 * alpha1;
+
+
+ // Compute alpha2, for self shadowing effect
+ // (1 - alpha2) will later be used as percentage of incoming sunlight
+ float alpha2 = (texture2D(cloud_noise_texture, uv2).x - 0.5);
+ alpha2 = min(max(alpha2 + cloudDensity, 0.) * 2.5 * cloud_pos_density1.z, 1.);
+
+ // And smooth
+ alpha2 = 1. - alpha2;
+ alpha2 = 1. - alpha2 * alpha2;
+
+ // Combine
+ vec4 color;
+ color = (cloudColorSun*(1.-alpha2) + cloudColorAmbient);
+ color *= 2.;
+
+ /// Gamma correct for WL (soft clip effect).
+ gl_FragData[0] = vec4(scaleSoftClip(color.rgb), alpha1);
+ gl_FragData[1] = vec4(0.0,0.0,0.0,0.0);
+ gl_FragData[2] = vec4(0,0,1,0);
+}
+
diff --git a/indra/newview/app_settings/shaders/class1/deferred/cloudsV.glsl b/indra/newview/app_settings/shaders/class1/deferred/cloudsV.glsl
new file mode 100644
index 0000000000..267ef36d4d
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/deferred/cloudsV.glsl
@@ -0,0 +1,165 @@
+/**
+ * @file WLCloudsV.glsl
+ *
+ * $LicenseInfo:firstyear=2005&license=viewerlgpl$
+ * $/LicenseInfo$
+ */
+
+#version 120
+
+//////////////////////////////////////////////////////////////////////////
+// The vertex shader for creating the atmospheric sky
+///////////////////////////////////////////////////////////////////////////////
+
+// Output parameters
+varying vec4 vary_CloudColorSun;
+varying vec4 vary_CloudColorAmbient;
+varying float vary_CloudDensity;
+
+// Inputs
+uniform vec3 camPosLocal;
+
+uniform vec4 lightnorm;
+uniform vec4 sunlight_color;
+uniform vec4 ambient;
+uniform vec4 blue_horizon;
+uniform vec4 blue_density;
+uniform vec4 haze_horizon;
+uniform vec4 haze_density;
+
+uniform vec4 cloud_shadow;
+uniform vec4 density_multiplier;
+uniform vec4 max_y;
+
+uniform vec4 glow;
+
+uniform vec4 cloud_color;
+
+uniform vec4 cloud_scale;
+
+void main()
+{
+
+ // World / view / projection
+ gl_Position = ftransform();
+
+ gl_TexCoord[0] = gl_MultiTexCoord0;
+
+ // Get relative position
+ vec3 P = gl_Vertex.xyz - camPosLocal.xyz + vec3(0,50,0);
+
+ // Set altitude
+ if (P.y > 0.)
+ {
+ P *= (max_y.x / P.y);
+ }
+ else
+ {
+ P *= (-32000. / P.y);
+ }
+
+ // Can normalize then
+ vec3 Pn = normalize(P);
+ float Plen = length(P);
+
+ // Initialize temp variables
+ vec4 temp1 = vec4(0.);
+ vec4 temp2 = vec4(0.);
+ vec4 blue_weight;
+ vec4 haze_weight;
+ vec4 sunlight = sunlight_color;
+ vec4 light_atten;
+
+
+ // Sunlight attenuation effect (hue and brightness) due to atmosphere
+ // this is used later for sunlight modulation at various altitudes
+ light_atten = (blue_density * 1.0 + haze_density.x * 0.25) * (density_multiplier.x * max_y.x);
+
+ // Calculate relative weights
+ temp1 = blue_density + haze_density.x;
+ blue_weight = blue_density / temp1;
+ haze_weight = haze_density.x / temp1;
+
+ // Compute sunlight from P & lightnorm (for long rays like sky)
+ temp2.y = max(0., max(0., Pn.y) * 1.0 + lightnorm.y );
+ temp2.y = 1. / temp2.y;
+ sunlight *= exp( - light_atten * temp2.y);
+
+ // Distance
+ temp2.z = Plen * density_multiplier.x;
+
+ // Transparency (-> temp1)
+ // ATI Bugfix -- can't store temp1*temp2.z in a variable because the ati
+ // compiler gets confused.
+ temp1 = exp(-temp1 * temp2.z);
+
+
+ // Compute haze glow
+ temp2.x = dot(Pn, lightnorm.xyz);
+ temp2.x = 1. - temp2.x;
+ // temp2.x is 0 at the sun and increases away from sun
+ temp2.x = max(temp2.x, .001);
+ // Set a minimum "angle" (smaller glow.y allows tighter, brighter hotspot)
+ temp2.x *= glow.x;
+ // Higher glow.x gives dimmer glow (because next step is 1 / "angle")
+ temp2.x = pow(temp2.x, glow.z);
+ // glow.z should be negative, so we're doing a sort of (1 / "angle") function
+
+ // Add "minimum anti-solar illumination"
+ temp2.x += .25;
+
+ // Increase ambient when there are more clouds
+ vec4 tmpAmbient = ambient;
+ tmpAmbient += (1. - tmpAmbient) * cloud_shadow.x * 0.5;
+
+ // Dim sunlight by cloud shadow percentage
+ sunlight *= (1. - cloud_shadow.x);
+
+ // Haze color below cloud
+ vec4 additiveColorBelowCloud = ( blue_horizon * blue_weight * (sunlight + tmpAmbient)
+ + (haze_horizon.r * haze_weight) * (sunlight * temp2.x + tmpAmbient)
+ );
+
+ // CLOUDS
+
+ sunlight = sunlight_color;
+ temp2.y = max(0., lightnorm.y * 2.);
+ temp2.y = 1. / temp2.y;
+ sunlight *= exp( - light_atten * temp2.y);
+
+ // Cloud color out
+ vary_CloudColorSun = (sunlight * temp2.x) * cloud_color;
+ vary_CloudColorAmbient = tmpAmbient * cloud_color;
+
+ // Attenuate cloud color by atmosphere
+ temp1 = sqrt(temp1); //less atmos opacity (more transparency) below clouds
+ vary_CloudColorSun *= temp1;
+ vary_CloudColorAmbient *= temp1;
+ vec4 oHazeColorBelowCloud = additiveColorBelowCloud * (1. - temp1);
+
+ // Make a nice cloud density based on the cloud_shadow value that was passed in.
+ vary_CloudDensity = 2. * (cloud_shadow.x - 0.25);
+
+
+ // Texture coords
+ gl_TexCoord[0] = gl_MultiTexCoord0;
+ gl_TexCoord[0].xy -= 0.5;
+ gl_TexCoord[0].xy /= cloud_scale.x;
+ gl_TexCoord[0].xy += 0.5;
+
+ gl_TexCoord[1] = gl_TexCoord[0];
+ gl_TexCoord[1].x += lightnorm.x * 0.0125;
+ gl_TexCoord[1].y += lightnorm.z * 0.0125;
+
+ gl_TexCoord[2] = gl_TexCoord[0] * 16.;
+ gl_TexCoord[3] = gl_TexCoord[1] * 16.;
+
+ // Combine these to minimize register use
+ vary_CloudColorAmbient += oHazeColorBelowCloud;
+
+ // needs this to compile on mac
+ //vary_AtmosAttenuation = vec3(0.0,0.0,0.0);
+
+ // END CLOUDS
+}
+
diff --git a/indra/newview/app_settings/shaders/class1/deferred/multiPointLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/multiPointLightF.glsl
index c5ddf31ac0..e10fb105e8 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/multiPointLightF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/multiPointLightF.glsl
@@ -23,8 +23,9 @@ uniform float sun_wash;
uniform int light_count;
-uniform vec4 light[16];
-uniform vec4 light_col[16];
+#define MAX_LIGHT_COUNT 16
+uniform vec4 light[MAX_LIGHT_COUNT];
+uniform vec4 light_col[MAX_LIGHT_COUNT];
varying vec4 vary_fragcoord;
uniform vec2 screen_res;
@@ -35,7 +36,7 @@ uniform mat4 inv_proj;
vec4 getPosition(vec2 pos_screen)
{
- float depth = texture2DRect(depthMap, pos_screen.xy).a;
+ float depth = texture2DRect(depthMap, pos_screen.xy).r;
vec2 sc = pos_screen.xy*2.0;
sc /= screen_res;
sc -= vec2(1.0,1.0);
@@ -63,50 +64,56 @@ void main()
float noise = texture2D(noiseMap, frag.xy/128.0).b;
vec3 out_col = vec3(0,0,0);
vec3 npos = normalize(-pos);
-
- for (int i = 0; i < light_count; ++i)
+
+ // As of OSX 10.6.7 ATI Apple's crash when using a variable size loop
+ for (int i = 0; i < MAX_LIGHT_COUNT; ++i)
{
+ bool light_contrib = (i < light_count);
+
vec3 lv = light[i].xyz-pos;
float dist2 = dot(lv,lv);
dist2 /= light[i].w;
if (dist2 > 1.0)
{
- continue;
+ light_contrib = false;
}
float da = dot(norm, lv);
if (da < 0.0)
{
- continue;
+ light_contrib = false;
}
-
- lv = normalize(lv);
- da = dot(norm, lv);
-
- float fa = light_col[i].a+1.0;
- float dist_atten = clamp(1.0-(dist2-1.0*(1.0-fa))/fa, 0.0, 1.0);
- dist_atten *= noise;
-
- float lit = da * dist_atten;
- vec3 col = light_col[i].rgb*lit*diff;
- //vec3 col = vec3(dist2, light_col[i].a, lit);
-
- if (spec.a > 0.0)
+ if (light_contrib)
{
- //vec3 ref = dot(pos+lv, norm);
+ lv = normalize(lv);
+ da = dot(norm, lv);
+
+ float fa = light_col[i].a+1.0;
+ float dist_atten = clamp(1.0-(dist2-1.0*(1.0-fa))/fa, 0.0, 1.0);
+ dist_atten *= noise;
+
+ float lit = da * dist_atten;
- float sa = dot(normalize(lv+npos),norm);
+ vec3 col = light_col[i].rgb*lit*diff;
+ //vec3 col = vec3(dist2, light_col[i].a, lit);
- if (sa > 0.0)
+ if (spec.a > 0.0)
{
- sa = texture2D(lightFunc,vec2(sa, spec.a)).a * min(dist_atten*4.0, 1.0);
- sa *= noise;
- col += da*sa*light_col[i].rgb*spec.rgb;
+ //vec3 ref = dot(pos+lv, norm);
+
+ float sa = dot(normalize(lv+npos),norm);
+
+ if (sa > 0.0)
+ {
+ sa = texture2D(lightFunc,vec2(sa, spec.a)).a * min(dist_atten*4.0, 1.0);
+ sa *= noise;
+ col += da*sa*light_col[i].rgb*spec.rgb;
+ }
}
+
+ out_col += col;
}
-
- out_col += col;
}
if (dot(out_col, out_col) <= 0.0)
diff --git a/indra/newview/app_settings/shaders/class1/deferred/multiPointLightMSF.glsl b/indra/newview/app_settings/shaders/class1/deferred/multiPointLightMSF.glsl
new file mode 100644
index 0000000000..66c7a5cb4a
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/deferred/multiPointLightMSF.glsl
@@ -0,0 +1,137 @@
+/**
+ * @file multiPointLightF.glsl
+ *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
+ * $/LicenseInfo$
+ */
+
+#version 120
+
+#extension GL_ARB_texture_rectangle : enable
+#extension GL_ARB_texture_multisample : enable
+
+uniform sampler2DMS depthMap;
+uniform sampler2DMS diffuseRect;
+uniform sampler2DMS specularRect;
+uniform sampler2DMS normalMap;
+uniform sampler2D noiseMap;
+uniform sampler2D lightFunc;
+
+
+uniform vec3 env_mat[3];
+uniform float sun_wash;
+
+uniform int light_count;
+
+#define MAX_LIGHT_COUNT 16
+uniform vec4 light[MAX_LIGHT_COUNT];
+uniform vec4 light_col[MAX_LIGHT_COUNT];
+
+varying vec4 vary_fragcoord;
+uniform vec2 screen_res;
+
+uniform float far_z;
+
+uniform mat4 inv_proj;
+
+vec4 getPosition(ivec2 pos_screen, int sample)
+{
+ float depth = texelFetch(depthMap, pos_screen, sample).r;
+ vec2 sc = vec2(pos_screen.xy)*2.0;
+ sc /= screen_res;
+ sc -= vec2(1.0,1.0);
+ vec4 ndc = vec4(sc.x, sc.y, 2.0*depth-1.0, 1.0);
+ vec4 pos = inv_proj * ndc;
+ pos /= pos.w;
+ pos.w = 1.0;
+ return pos;
+}
+
+void main()
+{
+ vec2 frag = (vary_fragcoord.xy*0.5+0.5)*screen_res;
+ ivec2 itc = ivec2(frag);
+
+ int wght = 0;
+ vec3 fcol = vec3(0,0,0);
+
+ for (int s = 0; s < samples; ++s)
+ {
+ vec3 pos = getPosition(itc, s).xyz;
+ if (pos.z >= far_z)
+ {
+ vec3 norm = texelFetch(normalMap, itc, s).xyz;
+ norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm
+ norm = normalize(norm);
+ vec4 spec = texelFetch(specularRect, itc, s);
+ vec3 diff = texelFetch(diffuseRect, itc, s).rgb;
+ float noise = texture2D(noiseMap, frag.xy/128.0).b;
+ vec3 out_col = vec3(0,0,0);
+ vec3 npos = normalize(-pos);
+
+ // As of OSX 10.6.7 ATI Apple's crash when using a variable size loop
+ for (int i = 0; i < MAX_LIGHT_COUNT; ++i)
+ {
+ bool light_contrib = (i < light_count);
+
+ vec3 lv = light[i].xyz-pos;
+ float dist2 = dot(lv,lv);
+ dist2 /= light[i].w;
+ if (dist2 > 1.0)
+ {
+ light_contrib = false;
+ }
+
+ float da = dot(norm, lv);
+ if (da < 0.0)
+ {
+ light_contrib = false;
+ }
+
+ if (light_contrib)
+ {
+ lv = normalize(lv);
+ da = dot(norm, lv);
+
+ float fa = light_col[i].a+1.0;
+ float dist_atten = clamp(1.0-(dist2-1.0*(1.0-fa))/fa, 0.0, 1.0);
+ dist_atten *= noise;
+
+ float lit = da * dist_atten;
+
+ vec3 col = light_col[i].rgb*lit*diff;
+ //vec3 col = vec3(dist2, light_col[i].a, lit);
+
+ if (spec.a > 0.0)
+ {
+ //vec3 ref = dot(pos+lv, norm);
+
+ float sa = dot(normalize(lv+npos),norm);
+
+ if (sa > 0.0)
+ {
+ sa = texture2D(lightFunc,vec2(sa, spec.a)).a * min(dist_atten*4.0, 1.0);
+ sa *= noise;
+ col += da*sa*light_col[i].rgb*spec.rgb;
+ }
+ }
+
+ out_col += col;
+ }
+ }
+
+ fcol += out_col;
+ ++wght;
+ }
+ }
+
+ if (wght <= 0)
+ {
+ discard;
+ }
+
+ gl_FragColor.rgb = fcol/samples;
+ gl_FragColor.a = 0.0;
+
+
+}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/multiSpotLightMSF.glsl b/indra/newview/app_settings/shaders/class1/deferred/multiSpotLightMSF.glsl
new file mode 100644
index 0000000000..b450ff1ca2
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/deferred/multiSpotLightMSF.glsl
@@ -0,0 +1,232 @@
+/**
+ * @file multiSpotLightF.glsl
+ *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
+ * $/LicenseInfo$
+ */
+
+#version 120
+
+//class 1 -- no shadows
+
+#extension GL_ARB_texture_rectangle : enable
+#extension GL_ARB_texture_multisample : enable
+
+uniform sampler2DMS diffuseRect;
+uniform sampler2DMS specularRect;
+uniform sampler2DMS depthMap;
+uniform sampler2DMS normalMap;
+uniform sampler2D noiseMap;
+uniform sampler2D lightFunc;
+uniform sampler2D projectionMap;
+
+uniform mat4 proj_mat; //screen space to light space
+uniform float proj_near; //near clip for projection
+uniform vec3 proj_p; //plane projection is emitting from (in screen space)
+uniform vec3 proj_n;
+uniform float proj_focus; //distance from plane to begin blurring
+uniform float proj_lod; //(number of mips in proj map)
+uniform float proj_range; //range between near clip and far clip plane of projection
+uniform float proj_ambient_lod;
+uniform float proj_ambiance;
+uniform float near_clip;
+uniform float far_clip;
+
+uniform vec3 proj_origin; //origin of projection to be used for angular attenuation
+uniform float sun_wash;
+uniform float shadow_fade;
+
+varying vec4 vary_light;
+
+varying vec4 vary_fragcoord;
+uniform vec2 screen_res;
+
+uniform mat4 inv_proj;
+
+vec4 texture2DLodSpecular(sampler2D projectionMap, vec2 tc, float lod)
+{
+ vec4 ret = texture2DLod(projectionMap, tc, lod);
+
+ vec2 dist = tc-vec2(0.5);
+
+ float det = max(1.0-lod/(proj_lod*0.5), 0.0);
+
+ float d = dot(dist,dist);
+
+ ret *= min(clamp((0.25-d)/0.25, 0.0, 1.0)+det, 1.0);
+
+ return ret;
+}
+
+vec4 texture2DLodDiffuse(sampler2D projectionMap, vec2 tc, float lod)
+{
+ vec4 ret = texture2DLod(projectionMap, tc, lod);
+
+ vec2 dist = vec2(0.5) - abs(tc-vec2(0.5));
+
+ float det = min(lod/(proj_lod*0.5), 1.0);
+
+ float d = min(dist.x, dist.y);
+
+ float edge = 0.25*det;
+
+ ret *= clamp(d/edge, 0.0, 1.0);
+
+ return ret;
+}
+
+vec4 texture2DLodAmbient(sampler2D projectionMap, vec2 tc, float lod)
+{
+ vec4 ret = texture2DLod(projectionMap, tc, lod);
+
+ vec2 dist = tc-vec2(0.5);
+
+ float d = dot(dist,dist);
+
+ ret *= min(clamp((0.25-d)/0.25, 0.0, 1.0), 1.0);
+
+ return ret;
+}
+
+
+vec4 getPosition(ivec2 pos_screen, int sample)
+{
+ float depth = texelFetch(depthMap, pos_screen, sample).r;
+ vec2 sc = vec2(pos_screen.xy)*2.0;
+ sc /= screen_res;
+ sc -= vec2(1.0,1.0);
+ vec4 ndc = vec4(sc.x, sc.y, 2.0*depth-1.0, 1.0);
+ vec4 pos = inv_proj * ndc;
+ pos /= pos.w;
+ pos.w = 1.0;
+ return pos;
+}
+
+void main()
+{
+ int wght = 0;
+
+ vec3 fcol = vec3(0,0,0);
+
+ vec2 frag = (vary_fragcoord.xy*0.5+0.5)*screen_res;
+
+ ivec2 itc = ivec2(frag.xy);
+
+ for (int i = 0; i < samples; ++i)
+ {
+ vec3 pos = getPosition(itc, i).xyz;
+ vec3 lv = vary_light.xyz-pos.xyz;
+ float dist2 = dot(lv,lv);
+ dist2 /= vary_light.w;
+ if (dist2 <= 1.0)
+ {
+ vec3 norm = texelFetch(normalMap, itc, i).xyz*2.0-1.0;
+
+ norm = normalize(norm);
+ float l_dist = -dot(lv, proj_n);
+
+ vec4 proj_tc = (proj_mat * vec4(pos.xyz, 1.0));
+ if (proj_tc.z >= 0.0)
+ {
+ proj_tc.xyz /= proj_tc.w;
+
+ float fa = gl_Color.a+1.0;
+ float dist_atten = min(1.0-(dist2-1.0*(1.0-fa))/fa, 1.0);
+ if (dist_atten > 0.0)
+ {
+ lv = proj_origin-pos.xyz;
+ lv = normalize(lv);
+ float da = dot(norm, lv);
+
+ vec3 col = vec3(0,0,0);
+
+ vec3 diff_tex = texelFetch(diffuseRect, itc, i).rgb;
+
+ float noise = texture2D(noiseMap, frag.xy/128.0).b;
+ if (proj_tc.z > 0.0 &&
+ proj_tc.x < 1.0 &&
+ proj_tc.y < 1.0 &&
+ proj_tc.x > 0.0 &&
+ proj_tc.y > 0.0)
+ {
+ float lit = 0.0;
+ float amb_da = proj_ambiance;
+
+ if (da > 0.0)
+ {
+ float diff = clamp((l_dist-proj_focus)/proj_range, 0.0, 1.0);
+ float lod = diff * proj_lod;
+
+ vec4 plcol = texture2DLodDiffuse(projectionMap, proj_tc.xy, lod);
+
+ vec3 lcol = gl_Color.rgb * plcol.rgb * plcol.a;
+
+ lit = da * dist_atten * noise;
+
+ col = lcol*lit*diff_tex;
+ amb_da += (da*0.5)*proj_ambiance;
+ }
+
+ //float diff = clamp((proj_range-proj_focus)/proj_range, 0.0, 1.0);
+ vec4 amb_plcol = texture2DLodAmbient(projectionMap, proj_tc.xy, proj_lod);
+
+ amb_da += (da*da*0.5+0.5)*proj_ambiance;
+
+ amb_da *= dist_atten * noise;
+
+ amb_da = min(amb_da, 1.0-lit);
+
+ col += amb_da*gl_Color.rgb*diff_tex.rgb*amb_plcol.rgb*amb_plcol.a;
+ }
+
+
+ vec4 spec = texelFetch(specularRect, itc, i);
+ if (spec.a > 0.0)
+ {
+ vec3 ref = reflect(normalize(pos), norm);
+
+ //project from point pos in direction ref to plane proj_p, proj_n
+ vec3 pdelta = proj_p-pos;
+ float ds = dot(ref, proj_n);
+
+ if (ds < 0.0)
+ {
+ vec3 pfinal = pos + ref * dot(pdelta, proj_n)/ds;
+
+ vec4 stc = (proj_mat * vec4(pfinal.xyz, 1.0));
+
+ if (stc.z > 0.0)
+ {
+ stc.xy /= stc.w;
+
+ float fatten = clamp(spec.a*spec.a+spec.a*0.5, 0.25, 1.0);
+
+ stc.xy = (stc.xy - vec2(0.5)) * fatten + vec2(0.5);
+
+ if (stc.x < 1.0 &&
+ stc.y < 1.0 &&
+ stc.x > 0.0 &&
+ stc.y > 0.0)
+ {
+ vec4 scol = texture2DLodSpecular(projectionMap, stc.xy, proj_lod-spec.a*proj_lod);
+ col += dist_atten*scol.rgb*gl_Color.rgb*scol.a*spec.rgb;
+ }
+ }
+ }
+ }
+
+ fcol += col;
+ ++wght;
+ }
+ }
+ }
+ }
+
+ if (wght <= 0)
+ {
+ discard;
+ }
+
+ gl_FragColor.rgb = fcol/samples;
+ gl_FragColor.a = 0.0;
+}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/pointLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/pointLightF.glsl
index 22ed9dcd40..0d771109fb 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/pointLightF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/pointLightF.glsl
@@ -30,7 +30,7 @@ uniform vec4 viewport;
vec4 getPosition(vec2 pos_screen)
{
- float depth = texture2DRect(depthMap, pos_screen.xy).a;
+ float depth = texture2DRect(depthMap, pos_screen.xy).r;
vec2 sc = (pos_screen.xy-viewport.xy)*2.0;
sc /= viewport.zw;
sc -= vec2(1.0,1.0);
diff --git a/indra/newview/app_settings/shaders/class1/deferred/pointLightMSF.glsl b/indra/newview/app_settings/shaders/class1/deferred/pointLightMSF.glsl
new file mode 100644
index 0000000000..7521c3310c
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/deferred/pointLightMSF.glsl
@@ -0,0 +1,108 @@
+/**
+ * @file pointLightF.glsl
+ *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
+ * $/LicenseInfo$
+ */
+
+ #version 120
+
+#extension GL_ARB_texture_rectangle : enable
+#extension GL_ARB_texture_multisample : enable
+
+uniform sampler2DMS depthMap;
+uniform sampler2DMS diffuseRect;
+uniform sampler2DMS specularRect;
+uniform sampler2DMS normalMap;
+uniform sampler2D noiseMap;
+uniform sampler2D lightFunc;
+
+
+uniform vec3 env_mat[3];
+uniform float sun_wash;
+
+varying vec4 vary_light;
+
+varying vec4 vary_fragcoord;
+uniform vec2 screen_res;
+
+uniform mat4 inv_proj;
+uniform vec4 viewport;
+
+vec4 getPosition(ivec2 pos_screen, int sample)
+{
+ float depth = texelFetch(depthMap, pos_screen, sample).r;
+ vec2 sc = (vec2(pos_screen.xy)-viewport.xy)*2.0;
+ sc /= viewport.zw;
+ sc -= vec2(1.0,1.0);
+ vec4 ndc = vec4(sc.x, sc.y, 2.0*depth-1.0, 1.0);
+ vec4 pos = inv_proj * ndc;
+ pos /= pos.w;
+ pos.w = 1.0;
+ return pos;
+}
+
+void main()
+{
+ vec4 frag = vary_fragcoord;
+ frag.xyz /= frag.w;
+ frag.xyz = frag.xyz*0.5+0.5;
+ frag.xy *= screen_res;
+
+ ivec2 itc = ivec2(frag.xy);
+
+ int wght = 0;
+ vec3 fcol = vec3(0,0,0);
+
+ for (int s = 0; s < samples; ++s)
+ {
+ vec3 pos = getPosition(itc, s).xyz;
+ vec3 lv = vary_light.xyz-pos;
+ float dist2 = dot(lv,lv);
+ dist2 /= vary_light.w;
+ if (dist2 <= 1.0)
+ {
+ vec3 norm = texelFetch(normalMap, itc, s).xyz;
+ norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm
+ float da = dot(norm, lv);
+ if (da >= 0.0)
+ {
+ norm = normalize(norm);
+ lv = normalize(lv);
+ da = dot(norm, lv);
+
+ float noise = texture2D(noiseMap, frag.xy/128.0).b;
+
+ vec3 col = texelFetch(diffuseRect, itc, s).rgb;
+ float fa = gl_Color.a+1.0;
+ float dist_atten = clamp(1.0-(dist2-1.0*(1.0-fa))/fa, 0.0, 1.0);
+ float lit = da * dist_atten * noise;
+
+ col = gl_Color.rgb*lit*col;
+
+ vec4 spec = texelFetch(specularRect, itc, s);
+ if (spec.a > 0.0)
+ {
+ float sa = dot(normalize(lv-normalize(pos)),norm);
+ if (sa > 0.0)
+ {
+ sa = texture2D(lightFunc, vec2(sa, spec.a)).a * min(dist_atten*4.0, 1.0);
+ sa *= noise;
+ col += da*sa*gl_Color.rgb*spec.rgb;
+ }
+ }
+
+ fcol += col;
+ ++wght;
+ }
+ }
+ }
+
+ if (wght <= 0)
+ {
+ discard;
+ }
+
+ gl_FragColor.rgb = fcol/samples;
+ gl_FragColor.a = 0.0;
+}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/pointLightV.glsl b/indra/newview/app_settings/shaders/class1/deferred/pointLightV.glsl
index 8e74feb615..2377947e7f 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/pointLightV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/pointLightV.glsl
@@ -10,14 +10,9 @@
varying vec4 vary_light;
varying vec4 vary_fragcoord;
-uniform vec2 screen_res;
-uniform float near_clip;
-
void main()
{
//transform vertex
- gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
-
vec4 pos = gl_ModelViewProjectionMatrix * gl_Vertex;
vary_fragcoord = pos;
@@ -25,6 +20,8 @@ void main()
tex.w = 1.0;
vary_light = gl_MultiTexCoord0;
+
+ gl_Position = pos;
gl_FrontColor = gl_Color;
}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/postDeferredF.glsl b/indra/newview/app_settings/shaders/class1/deferred/postDeferredF.glsl
index 77f1b2224c..d389add03b 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/postDeferredF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/postDeferredF.glsl
@@ -29,7 +29,7 @@ varying vec2 vary_fragcoord;
float getDepth(vec2 pos_screen)
{
- float z = texture2DRect(depthMap, pos_screen.xy).a;
+ float z = texture2DRect(depthMap, pos_screen.xy).r;
z = z*2.0-1.0;
vec4 ndc = vec4(0.0, 0.0, z, 1.0);
vec4 p = inv_proj*ndc;
diff --git a/indra/newview/app_settings/shaders/class1/deferred/postDeferredMSF.glsl b/indra/newview/app_settings/shaders/class1/deferred/postDeferredMSF.glsl
new file mode 100644
index 0000000000..711c6dc96d
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/deferred/postDeferredMSF.glsl
@@ -0,0 +1,133 @@
+/**
+ * @file postDeferredF.glsl
+ *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
+ * $/LicenseInfo$
+ */
+
+#version 120
+
+#extension GL_ARB_texture_rectangle : enable
+#extension GL_ARB_texture_multisample : enable
+
+uniform sampler2DMS diffuseRect;
+uniform sampler2DMS edgeMap;
+uniform sampler2DMS depthMap;
+uniform sampler2DMS normalMap;
+uniform sampler2D bloomMap;
+
+uniform float depth_cutoff;
+uniform float norm_cutoff;
+uniform float focal_distance;
+uniform float blur_constant;
+uniform float tan_pixel_angle;
+uniform float magnification;
+
+uniform mat4 inv_proj;
+uniform vec2 screen_res;
+
+varying vec2 vary_fragcoord;
+
+vec4 texture2DMS(sampler2DMS tex, ivec2 tc)
+{
+ vec4 ret = vec4(0,0,0,0);
+ for (int i = 0; i < samples; ++i)
+ {
+ ret += texelFetch(tex, tc, i);
+ }
+
+ return ret/samples;
+}
+
+float getDepth(ivec2 pos_screen)
+{
+ float z = texture2DMS(depthMap, pos_screen.xy).r;
+ z = z*2.0-1.0;
+ vec4 ndc = vec4(0.0, 0.0, z, 1.0);
+ vec4 p = inv_proj*ndc;
+ return p.z/p.w;
+}
+
+float calc_cof(float depth)
+{
+ float sc = abs(depth-focal_distance)/-depth*blur_constant;
+
+ sc /= magnification;
+
+ // tan_pixel_angle = pixel_length/-depth;
+ float pixel_length = tan_pixel_angle*-focal_distance;
+
+ sc = sc/pixel_length;
+ sc *= 1.414;
+
+ return sc;
+}
+
+void dofSample(inout vec4 diff, inout float w, float min_sc, float cur_depth, ivec2 tc)
+{
+ float d = getDepth(tc);
+
+ float sc = calc_cof(d);
+
+ if (sc > min_sc //sampled pixel is more "out of focus" than current sample radius
+ || d < cur_depth) //sampled pixel is further away than current pixel
+ {
+ float wg = 0.25;
+
+ vec4 s = texture2DMS(diffuseRect, tc);
+ // de-weight dull areas to make highlights 'pop'
+ wg += s.r+s.g+s.b;
+
+ diff += wg*s;
+
+ w += wg;
+ }
+}
+
+
+void main()
+{
+ ivec2 itc = ivec2(vary_fragcoord.xy);
+
+ vec3 norm = texture2DMS(normalMap, itc).xyz;
+ norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm
+
+ float depth = getDepth(itc);
+
+ vec4 diff = texture2DMS(diffuseRect, itc);
+
+ {
+ float w = 1.0;
+
+ float sc = calc_cof(depth);
+ sc = min(abs(sc), 10.0);
+
+ float fd = depth*0.5f;
+
+ float PI = 3.14159265358979323846264;
+
+ int isc = int(sc);
+
+ // sample quite uniformly spaced points within a circle, for a circular 'bokeh'
+ //if (depth < focal_distance)
+ {
+ for (int x = -isc; x <= isc; x+=2)
+ {
+ for (int y = -isc; y <= isc; y+=2)
+ {
+ ivec2 cur_samp = ivec2(x,y);
+ float cur_sc = length(vec2(cur_samp));
+ if (cur_sc < sc)
+ {
+ dofSample(diff, w, cur_sc, depth, itc+cur_samp);
+ }
+ }
+ }
+ }
+
+ diff /= w;
+ }
+
+ vec4 bloom = texture2D(bloomMap, vary_fragcoord.xy/screen_res);
+ gl_FragColor = diff + bloom;
+}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/postDeferredNoDoFMSF.glsl b/indra/newview/app_settings/shaders/class1/deferred/postDeferredNoDoFMSF.glsl
new file mode 100644
index 0000000000..359b26b7d4
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/deferred/postDeferredNoDoFMSF.glsl
@@ -0,0 +1,37 @@
+/**
+ * @file postDeferredF.glsl
+ *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
+ * $/LicenseInfo$
+ */
+
+#version 120
+
+#extension GL_ARB_texture_rectangle : enable
+#extension GL_ARB_texture_multisample : enable
+
+uniform sampler2DMS diffuseRect;
+uniform sampler2D bloomMap;
+
+uniform vec2 screen_res;
+varying vec2 vary_fragcoord;
+
+vec4 texture2DMS(sampler2DMS tex, ivec2 tc)
+{
+ vec4 ret = vec4(0,0,0,0);
+
+ for (int i = 0; i < samples; ++i)
+ {
+ ret += texelFetch(tex,tc,i);
+ }
+
+ return ret/samples;
+}
+
+void main()
+{
+ vec4 diff = texture2DMS(diffuseRect, ivec2(vary_fragcoord.xy));
+
+ vec4 bloom = texture2D(bloomMap, vary_fragcoord.xy/screen_res);
+ gl_FragColor = diff + bloom;
+}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/skyF.glsl b/indra/newview/app_settings/shaders/class1/deferred/skyF.glsl
new file mode 100644
index 0000000000..91143943b6
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/deferred/skyF.glsl
@@ -0,0 +1,44 @@
+/**
+ * @file WLSkyF.glsl
+ *
+ * $LicenseInfo:firstyear=2005&license=viewerlgpl$
+ * $/LicenseInfo$
+ */
+
+#version 120
+
+/////////////////////////////////////////////////////////////////////////
+// The fragment shader for the sky
+/////////////////////////////////////////////////////////////////////////
+
+varying vec4 vary_HazeColor;
+
+uniform sampler2D cloud_noise_texture;
+uniform vec4 gamma;
+
+/// Soft clips the light with a gamma correction
+vec3 scaleSoftClip(vec3 light) {
+ //soft clip effect:
+ light = 1. - clamp(light, vec3(0.), vec3(1.));
+ light = 1. - pow(light, gamma.xxx);
+
+ return light;
+}
+
+void main()
+{
+ // Potential Fill-rate optimization. Add cloud calculation
+ // back in and output alpha of 0 (so that alpha culling kills
+ // the fragment) if the sky wouldn't show up because the clouds
+ // are fully opaque.
+
+ vec4 color;
+ color = vary_HazeColor;
+ color *= 2.;
+
+ /// Gamma correct for WL (soft clip effect).
+ gl_FragData[0] = vec4(scaleSoftClip(color.rgb), 1.0);
+ gl_FragData[1] = vec4(0.0,0.0,0.0,0.0);
+ gl_FragData[2] = vec4(0,0,1,0);
+}
+
diff --git a/indra/newview/app_settings/shaders/class1/deferred/skyV.glsl b/indra/newview/app_settings/shaders/class1/deferred/skyV.glsl
new file mode 100644
index 0000000000..03bca8f27e
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/deferred/skyV.glsl
@@ -0,0 +1,140 @@
+/**
+ * @file WLSkyV.glsl
+ *
+ * $LicenseInfo:firstyear=2005&license=viewerlgpl$
+ * $/LicenseInfo$
+ */
+
+#version 120
+
+// SKY ////////////////////////////////////////////////////////////////////////
+// The vertex shader for creating the atmospheric sky
+///////////////////////////////////////////////////////////////////////////////
+
+// Output parameters
+varying vec4 vary_HazeColor;
+
+// Inputs
+uniform vec3 camPosLocal;
+
+uniform vec4 lightnorm;
+uniform vec4 sunlight_color;
+uniform vec4 ambient;
+uniform vec4 blue_horizon;
+uniform vec4 blue_density;
+uniform vec4 haze_horizon;
+uniform vec4 haze_density;
+
+uniform vec4 cloud_shadow;
+uniform vec4 density_multiplier;
+uniform vec4 max_y;
+
+uniform vec4 glow;
+
+uniform vec4 cloud_color;
+
+uniform vec4 cloud_scale;
+
+void main()
+{
+
+ // World / view / projection
+ gl_Position = ftransform();
+ gl_TexCoord[0] = gl_MultiTexCoord0;
+
+ // Get relative position
+ vec3 P = gl_Vertex.xyz - camPosLocal.xyz + vec3(0,50,0);
+ //vec3 P = gl_Vertex.xyz + vec3(0,50,0);
+
+ // Set altitude
+ if (P.y > 0.)
+ {
+ P *= (max_y.x / P.y);
+ }
+ else
+ {
+ P *= (-32000. / P.y);
+ }
+
+ // Can normalize then
+ vec3 Pn = normalize(P);
+ float Plen = length(P);
+
+ // Initialize temp variables
+ vec4 temp1 = vec4(0.);
+ vec4 temp2 = vec4(0.);
+ vec4 blue_weight;
+ vec4 haze_weight;
+ vec4 sunlight = sunlight_color;
+ vec4 light_atten;
+
+
+ // Sunlight attenuation effect (hue and brightness) due to atmosphere
+ // this is used later for sunlight modulation at various altitudes
+ light_atten = (blue_density * 1.0 + haze_density.x * 0.25) * (density_multiplier.x * max_y.x);
+
+ // Calculate relative weights
+ temp1 = blue_density + haze_density.x;
+ blue_weight = blue_density / temp1;
+ haze_weight = haze_density.x / temp1;
+
+ // Compute sunlight from P & lightnorm (for long rays like sky)
+ temp2.y = max(0., max(0., Pn.y) * 1.0 + lightnorm.y );
+ temp2.y = 1. / temp2.y;
+ sunlight *= exp( - light_atten * temp2.y);
+
+ // Distance
+ temp2.z = Plen * density_multiplier.x;
+
+ // Transparency (-> temp1)
+ // ATI Bugfix -- can't store temp1*temp2.z in a variable because the ati
+ // compiler gets confused.
+ temp1 = exp(-temp1 * temp2.z);
+
+
+ // Compute haze glow
+ temp2.x = dot(Pn, lightnorm.xyz);
+ temp2.x = 1. - temp2.x;
+ // temp2.x is 0 at the sun and increases away from sun
+ temp2.x = max(temp2.x, .001);
+ // Set a minimum "angle" (smaller glow.y allows tighter, brighter hotspot)
+ temp2.x *= glow.x;
+ // Higher glow.x gives dimmer glow (because next step is 1 / "angle")
+ temp2.x = pow(temp2.x, glow.z);
+ // glow.z should be negative, so we're doing a sort of (1 / "angle") function
+
+ // Add "minimum anti-solar illumination"
+ temp2.x += .25;
+
+
+ // Haze color above cloud
+ vary_HazeColor = ( blue_horizon * blue_weight * (sunlight + ambient)
+ + (haze_horizon.r * haze_weight) * (sunlight * temp2.x + ambient)
+ );
+
+
+ // Increase ambient when there are more clouds
+ vec4 tmpAmbient = ambient;
+ tmpAmbient += (1. - tmpAmbient) * cloud_shadow.x * 0.5;
+
+ // Dim sunlight by cloud shadow percentage
+ sunlight *= (1. - cloud_shadow.x);
+
+ // Haze color below cloud
+ vec4 additiveColorBelowCloud = ( blue_horizon * blue_weight * (sunlight + tmpAmbient)
+ + (haze_horizon.r * haze_weight) * (sunlight * temp2.x + tmpAmbient)
+ );
+
+ // Final atmosphere additive
+ vary_HazeColor *= (1. - temp1);
+
+ // Attenuate cloud color by atmosphere
+ temp1 = sqrt(temp1); //less atmos opacity (more transparency) below clouds
+
+ // At horizon, blend high altitude sky color towards the darker color below the clouds
+ vary_HazeColor += (additiveColorBelowCloud - vary_HazeColor) * (1. - sqrt(temp1));
+
+ // won't compile on mac without this being set
+ //vary_AtmosAttenuation = vec3(0.0,0.0,0.0);
+}
+
diff --git a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl
index 29340c7e9f..c1147feff6 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl
@@ -259,7 +259,7 @@ vec3 scaleSoftClip(vec3 light)
void main()
{
vec2 tc = vary_fragcoord.xy;
- float depth = texture2DRect(depthMap, tc.xy).a;
+ float depth = texture2DRect(depthMap, tc.xy).r;
vec3 pos = getPosition_d(tc, depth).xyz;
vec3 norm = texture2DRect(normalMap, tc).xyz;
norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm
diff --git a/indra/newview/app_settings/shaders/class1/deferred/softenLightMSF.glsl b/indra/newview/app_settings/shaders/class1/deferred/softenLightMSF.glsl
new file mode 100644
index 0000000000..fcba4f57e6
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/deferred/softenLightMSF.glsl
@@ -0,0 +1,318 @@
+/**
+ * @file softenLightF.glsl
+ *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
+ * $/LicenseInfo$
+ */
+
+#version 120
+
+#extension GL_ARB_texture_rectangle : enable
+#extension GL_ARB_texture_multisample : enable
+
+uniform sampler2DMS diffuseRect;
+uniform sampler2DMS specularRect;
+uniform sampler2DMS normalMap;
+uniform sampler2DMS depthMap;
+uniform sampler2D noiseMap;
+uniform samplerCube environmentMap;
+uniform sampler2D lightFunc;
+
+uniform float blur_size;
+uniform float blur_fidelity;
+
+// Inputs
+uniform vec4 morphFactor;
+uniform vec3 camPosLocal;
+//uniform vec4 camPosWorld;
+uniform vec4 gamma;
+uniform vec4 lightnorm;
+uniform vec4 sunlight_color;
+uniform vec4 ambient;
+uniform vec4 blue_horizon;
+uniform vec4 blue_density;
+uniform vec4 haze_horizon;
+uniform vec4 haze_density;
+uniform vec4 cloud_shadow;
+uniform vec4 density_multiplier;
+uniform vec4 distance_multiplier;
+uniform vec4 max_y;
+uniform vec4 glow;
+uniform float scene_light_strength;
+uniform vec3 env_mat[3];
+//uniform mat4 shadow_matrix[3];
+//uniform vec4 shadow_clip;
+uniform mat3 ssao_effect_mat;
+
+varying vec4 vary_light;
+varying vec2 vary_fragcoord;
+
+vec3 vary_PositionEye;
+
+vec3 vary_SunlitColor;
+vec3 vary_AmblitColor;
+vec3 vary_AdditiveColor;
+vec3 vary_AtmosAttenuation;
+
+uniform mat4 inv_proj;
+uniform vec2 screen_res;
+
+vec4 getPosition_d(vec2 pos_screen, float depth)
+{
+ vec2 sc = pos_screen.xy*2.0;
+ sc /= screen_res;
+ sc -= vec2(1.0,1.0);
+ vec4 ndc = vec4(sc.x, sc.y, 2.0*depth-1.0, 1.0);
+ vec4 pos = inv_proj * ndc;
+ pos /= pos.w;
+ pos.w = 1.0;
+ return pos;
+}
+
+vec3 getPositionEye()
+{
+ return vary_PositionEye;
+}
+vec3 getSunlitColor()
+{
+ return vary_SunlitColor;
+}
+vec3 getAmblitColor()
+{
+ return vary_AmblitColor;
+}
+vec3 getAdditiveColor()
+{
+ return vary_AdditiveColor;
+}
+vec3 getAtmosAttenuation()
+{
+ return vary_AtmosAttenuation;
+}
+
+
+void setPositionEye(vec3 v)
+{
+ vary_PositionEye = v;
+}
+
+void setSunlitColor(vec3 v)
+{
+ vary_SunlitColor = v;
+}
+
+void setAmblitColor(vec3 v)
+{
+ vary_AmblitColor = v;
+}
+
+void setAdditiveColor(vec3 v)
+{
+ vary_AdditiveColor = v;
+}
+
+void setAtmosAttenuation(vec3 v)
+{
+ vary_AtmosAttenuation = v;
+}
+
+void calcAtmospherics(vec3 inPositionEye, float ambFactor) {
+
+ vec3 P = inPositionEye;
+ setPositionEye(P);
+
+ //(TERRAIN) limit altitude
+ if (P.y > max_y.x) P *= (max_y.x / P.y);
+ if (P.y < -max_y.x) P *= (-max_y.x / P.y);
+
+ vec3 tmpLightnorm = lightnorm.xyz;
+
+ vec3 Pn = normalize(P);
+ float Plen = length(P);
+
+ vec4 temp1 = vec4(0);
+ vec3 temp2 = vec3(0);
+ vec4 blue_weight;
+ vec4 haze_weight;
+ vec4 sunlight = sunlight_color;
+ vec4 light_atten;
+
+ //sunlight attenuation effect (hue and brightness) due to atmosphere
+ //this is used later for sunlight modulation at various altitudes
+ light_atten = (blue_density * 1.0 + vec4(haze_density.r) * 0.25) * (density_multiplier.x * max_y.x);
+ //I had thought blue_density and haze_density should have equal weighting,
+ //but attenuation due to haze_density tends to seem too strong
+
+ temp1 = blue_density + vec4(haze_density.r);
+ blue_weight = blue_density / temp1;
+ haze_weight = vec4(haze_density.r) / temp1;
+
+ //(TERRAIN) compute sunlight from lightnorm only (for short rays like terrain)
+ temp2.y = max(0.0, tmpLightnorm.y);
+ temp2.y = 1. / temp2.y;
+ sunlight *= exp( - light_atten * temp2.y);
+
+ // main atmospheric scattering line integral
+ temp2.z = Plen * density_multiplier.x;
+
+ // Transparency (-> temp1)
+ // ATI Bugfix -- can't store temp1*temp2.z*distance_multiplier.x in a variable because the ati
+ // compiler gets confused.
+ temp1 = exp(-temp1 * temp2.z * distance_multiplier.x);
+
+ //final atmosphere attenuation factor
+ setAtmosAttenuation(temp1.rgb);
+
+ //compute haze glow
+ //(can use temp2.x as temp because we haven't used it yet)
+ temp2.x = dot(Pn, tmpLightnorm.xyz);
+ temp2.x = 1. - temp2.x;
+ //temp2.x is 0 at the sun and increases away from sun
+ temp2.x = max(temp2.x, .03); //was glow.y
+ //set a minimum "angle" (smaller glow.y allows tighter, brighter hotspot)
+ temp2.x *= glow.x;
+ //higher glow.x gives dimmer glow (because next step is 1 / "angle")
+ temp2.x = pow(temp2.x, glow.z);
+ //glow.z should be negative, so we're doing a sort of (1 / "angle") function
+
+ //add "minimum anti-solar illumination"
+ temp2.x += .25;
+
+ //increase ambient when there are more clouds
+ vec4 tmpAmbient = ambient + (vec4(1.) - ambient) * cloud_shadow.x * 0.5;
+
+ /* decrease value and saturation (that in HSV, not HSL) for occluded areas
+ * // for HSV color/geometry used here, see http://gimp-savvy.com/BOOK/index.html?node52.html
+ * // The following line of code performs the equivalent of:
+ * float ambAlpha = tmpAmbient.a;
+ * float ambValue = dot(vec3(tmpAmbient), vec3(0.577)); // projection onto <1/rt(3), 1/rt(3), 1/rt(3)>, the neutral white-black axis
+ * vec3 ambHueSat = vec3(tmpAmbient) - vec3(ambValue);
+ * tmpAmbient = vec4(RenderSSAOEffect.valueFactor * vec3(ambValue) + RenderSSAOEffect.saturationFactor *(1.0 - ambFactor) * ambHueSat, ambAlpha);
+ */
+ tmpAmbient = vec4(mix(ssao_effect_mat * tmpAmbient.rgb, tmpAmbient.rgb, ambFactor), tmpAmbient.a);
+
+ //haze color
+ setAdditiveColor(
+ vec3(blue_horizon * blue_weight * (sunlight*(1.-cloud_shadow.x) + tmpAmbient)
+ + (haze_horizon.r * haze_weight) * (sunlight*(1.-cloud_shadow.x) * temp2.x
+ + tmpAmbient)));
+
+ //brightness of surface both sunlight and ambient
+ setSunlitColor(vec3(sunlight * .5));
+ setAmblitColor(vec3(tmpAmbient * .25));
+ setAdditiveColor(getAdditiveColor() * vec3(1.0 - temp1));
+}
+
+vec3 atmosLighting(vec3 light)
+{
+ light *= getAtmosAttenuation().r;
+ light += getAdditiveColor();
+ return (2.0 * light);
+}
+
+vec3 atmosTransport(vec3 light) {
+ light *= getAtmosAttenuation().r;
+ light += getAdditiveColor() * 2.0;
+ return light;
+}
+vec3 atmosGetDiffuseSunlightColor()
+{
+ return getSunlitColor();
+}
+
+vec3 scaleDownLight(vec3 light)
+{
+ return (light / scene_light_strength );
+}
+
+vec3 scaleUpLight(vec3 light)
+{
+ return (light * scene_light_strength);
+}
+
+vec3 atmosAmbient(vec3 light)
+{
+ return getAmblitColor() + light / 2.0;
+}
+
+vec3 atmosAffectDirectionalLight(float lightIntensity)
+{
+ return getSunlitColor() * lightIntensity;
+}
+
+vec3 scaleSoftClip(vec3 light)
+{
+ //soft clip effect:
+ light = 1. - clamp(light, vec3(0.), vec3(1.));
+ light = 1. - pow(light, gamma.xxx);
+
+ return light;
+}
+
+vec4 texture2DMS(sampler2DMS tex, ivec2 tc)
+{
+ vec4 ret = vec4(0,0,0,0);
+
+ for (int i = 0; i < samples; ++i)
+ {
+ ret += texelFetch(tex,tc,i);
+ }
+
+ return ret/samples;
+}
+
+void main()
+{
+ vec2 tc = vary_fragcoord.xy;
+ ivec2 itc = ivec2(tc);
+
+ vec3 fcol = vec3(0,0,0);
+
+ for (int i = 0; i < samples; ++i)
+ {
+ float depth = texelFetch(depthMap, itc, i).r;
+ vec3 pos = getPosition_d(tc, depth).xyz;
+ vec3 norm = texelFetch(normalMap, itc, i).xyz;
+
+ norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm
+ //vec3 nz = texture2D(noiseMap, vary_fragcoord.xy/128.0).xyz;
+
+ float da = max(dot(norm.xyz, vary_light.xyz), 0.0);
+
+ vec4 diffuse = texelFetch(diffuseRect, itc, i);
+ if (diffuse.a >= 1.0)
+ {
+ fcol += diffuse.rgb;
+ }
+ else
+ {
+ vec4 spec = texelFetch(specularRect, itc, i);
+
+ calcAtmospherics(pos.xyz, 1.0);
+
+ vec3 col = atmosAmbient(vec3(0));
+ col += atmosAffectDirectionalLight(max(min(da, 1.0), diffuse.a));
+
+ col *= diffuse.rgb;
+
+ if (spec.a > 0.0) // specular reflection
+ {
+ // the old infinite-sky shiny reflection
+ //
+ vec3 refnormpersp = normalize(reflect(pos.xyz, norm.xyz));
+ float sa = dot(refnormpersp, vary_light.xyz);
+ vec3 dumbshiny = vary_SunlitColor*texture2D(lightFunc, vec2(sa, spec.a)).a;
+
+ // add the two types of shiny together
+ col += dumbshiny * spec.rgb;
+ }
+
+ col = atmosLighting(col);
+ col = scaleSoftClip(col);
+ fcol += col;
+ }
+ }
+
+ gl_FragColor.rgb = fcol.rgb/samples;
+ gl_FragColor.a = 0.0;
+}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/spotLightMSF.glsl b/indra/newview/app_settings/shaders/class1/deferred/spotLightMSF.glsl
new file mode 100644
index 0000000000..6702bd5014
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/deferred/spotLightMSF.glsl
@@ -0,0 +1,234 @@
+/**
+ * @file multiSpotLightF.glsl
+ *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
+ * $/LicenseInfo$
+ */
+
+#version 120
+
+//class 1 -- no shadows
+
+#extension GL_ARB_texture_rectangle : enable
+#extension GL_ARB_texture_multisample : enable
+
+uniform sampler2DMS diffuseRect;
+uniform sampler2DMS specularRect;
+uniform sampler2DMS depthMap;
+uniform sampler2DMS normalMap;
+uniform sampler2D noiseMap;
+uniform sampler2D lightFunc;
+uniform sampler2D projectionMap;
+
+uniform mat4 proj_mat; //screen space to light space
+uniform float proj_near; //near clip for projection
+uniform vec3 proj_p; //plane projection is emitting from (in screen space)
+uniform vec3 proj_n;
+uniform float proj_focus; //distance from plane to begin blurring
+uniform float proj_lod; //(number of mips in proj map)
+uniform float proj_range; //range between near clip and far clip plane of projection
+uniform float proj_ambient_lod;
+uniform float proj_ambiance;
+uniform float near_clip;
+uniform float far_clip;
+
+uniform vec3 proj_origin; //origin of projection to be used for angular attenuation
+uniform float sun_wash;
+uniform int proj_shadow_idx;
+uniform float shadow_fade;
+
+varying vec4 vary_light;
+
+varying vec4 vary_fragcoord;
+uniform vec2 screen_res;
+
+uniform mat4 inv_proj;
+
+vec4 texture2DLodSpecular(sampler2D projectionMap, vec2 tc, float lod)
+{
+ vec4 ret = texture2DLod(projectionMap, tc, lod);
+
+ vec2 dist = tc-vec2(0.5);
+
+ float det = max(1.0-lod/(proj_lod*0.5), 0.0);
+
+ float d = dot(dist,dist);
+
+ ret *= min(clamp((0.25-d)/0.25, 0.0, 1.0)+det, 1.0);
+
+ return ret;
+}
+
+vec4 texture2DLodDiffuse(sampler2D projectionMap, vec2 tc, float lod)
+{
+ vec4 ret = texture2DLod(projectionMap, tc, lod);
+
+ vec2 dist = vec2(0.5) - abs(tc-vec2(0.5));
+
+ float det = min(lod/(proj_lod*0.5), 1.0);
+
+ float d = min(dist.x, dist.y);
+
+ float edge = 0.25*det;
+
+ ret *= clamp(d/edge, 0.0, 1.0);
+
+ return ret;
+}
+
+vec4 texture2DLodAmbient(sampler2D projectionMap, vec2 tc, float lod)
+{
+ vec4 ret = texture2DLod(projectionMap, tc, lod);
+
+ vec2 dist = tc-vec2(0.5);
+
+ float d = dot(dist,dist);
+
+ ret *= min(clamp((0.25-d)/0.25, 0.0, 1.0), 1.0);
+
+ return ret;
+}
+
+
+vec4 getPosition(ivec2 pos_screen, int sample)
+{
+ float depth = texelFetch(depthMap, pos_screen, sample).r;
+ vec2 sc = vec2(pos_screen.xy)*2.0;
+ sc /= screen_res;
+ sc -= vec2(1.0,1.0);
+ vec4 ndc = vec4(sc.x, sc.y, 2.0*depth-1.0, 1.0);
+ vec4 pos = inv_proj * ndc;
+ pos /= pos.w;
+ pos.w = 1.0;
+ return pos;
+}
+
+void main()
+{
+ vec4 frag = vary_fragcoord;
+ frag.xyz /= frag.w;
+ frag.xyz = frag.xyz*0.5+0.5;
+ frag.xy *= screen_res;
+ ivec2 itc = ivec2(frag.xy);
+
+ vec3 fcol = vec3(0,0,0);
+ int wght = 0;
+
+ for (int i = 0; i < samples; ++i)
+ {
+ vec3 pos = getPosition(itc, i).xyz;
+ vec3 lv = vary_light.xyz-pos.xyz;
+ float dist2 = dot(lv,lv);
+ dist2 /= vary_light.w;
+ if (dist2 <= 1.0)
+ {
+ vec3 norm = texelFetch(normalMap, itc, i).xyz*2.0-1.0;
+
+ norm = normalize(norm);
+ float l_dist = -dot(lv, proj_n);
+
+ vec4 proj_tc = (proj_mat * vec4(pos.xyz, 1.0));
+ if (proj_tc.z >= 0.0)
+ {
+ proj_tc.xyz /= proj_tc.w;
+
+ float fa = gl_Color.a+1.0;
+ float dist_atten = min(1.0-(dist2-1.0*(1.0-fa))/fa, 1.0);
+ if (dist_atten > 0.0)
+ {
+ lv = proj_origin-pos.xyz;
+ lv = normalize(lv);
+ float da = dot(norm, lv);
+
+ vec3 col = vec3(0,0,0);
+
+ vec3 diff_tex = texelFetch(diffuseRect, itc, i).rgb;
+
+ float noise = texture2D(noiseMap, frag.xy/128.0).b;
+ if (proj_tc.z > 0.0 &&
+ proj_tc.x < 1.0 &&
+ proj_tc.y < 1.0 &&
+ proj_tc.x > 0.0 &&
+ proj_tc.y > 0.0)
+ {
+ float lit = 0.0;
+ float amb_da = proj_ambiance;
+
+ if (da > 0.0)
+ {
+ float diff = clamp((l_dist-proj_focus)/proj_range, 0.0, 1.0);
+ float lod = diff * proj_lod;
+
+ vec4 plcol = texture2DLodDiffuse(projectionMap, proj_tc.xy, lod);
+
+ vec3 lcol = gl_Color.rgb * plcol.rgb * plcol.a;
+
+ lit = da * dist_atten * noise;
+
+ col = lcol*lit*diff_tex;
+ amb_da += (da*0.5)*proj_ambiance;
+ }
+
+ //float diff = clamp((proj_range-proj_focus)/proj_range, 0.0, 1.0);
+ vec4 amb_plcol = texture2DLodAmbient(projectionMap, proj_tc.xy, proj_lod);
+
+ amb_da += (da*da*0.5+0.5)*proj_ambiance;
+
+ amb_da *= dist_atten * noise;
+
+ amb_da = min(amb_da, 1.0-lit);
+
+ col += amb_da*gl_Color.rgb*diff_tex.rgb*amb_plcol.rgb*amb_plcol.a;
+ }
+
+
+ vec4 spec = texelFetch(specularRect, itc, i);
+ if (spec.a > 0.0)
+ {
+ vec3 ref = reflect(normalize(pos), norm);
+
+ //project from point pos in direction ref to plane proj_p, proj_n
+ vec3 pdelta = proj_p-pos;
+ float ds = dot(ref, proj_n);
+
+ if (ds < 0.0)
+ {
+ vec3 pfinal = pos + ref * dot(pdelta, proj_n)/ds;
+
+ vec4 stc = (proj_mat * vec4(pfinal.xyz, 1.0));
+
+ if (stc.z > 0.0)
+ {
+ stc.xy /= stc.w;
+
+ float fatten = clamp(spec.a*spec.a+spec.a*0.5, 0.25, 1.0);
+
+ stc.xy = (stc.xy - vec2(0.5)) * fatten + vec2(0.5);
+
+ if (stc.x < 1.0 &&
+ stc.y < 1.0 &&
+ stc.x > 0.0 &&
+ stc.y > 0.0)
+ {
+ vec4 scol = texture2DLodSpecular(projectionMap, stc.xy, proj_lod-spec.a*proj_lod);
+ col += dist_atten*scol.rgb*gl_Color.rgb*scol.a*spec.rgb;
+ }
+ }
+ }
+ }
+
+ fcol += col;
+ ++wght;
+ }
+ }
+ }
+ }
+
+ if (wght <= 0)
+ {
+ discard;
+ }
+
+ gl_FragColor.rgb = fcol/samples;
+ gl_FragColor.a = 0.0;
+}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/starsF.glsl b/indra/newview/app_settings/shaders/class1/deferred/starsF.glsl
new file mode 100644
index 0000000000..b1c25b7fd3
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/deferred/starsF.glsl
@@ -0,0 +1,19 @@
+/**
+ * @file starsF.glsl
+ *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
+ * $/LicenseInfo$
+ */
+
+#version 120
+
+uniform sampler2D diffuseMap;
+
+void main()
+{
+ vec4 col = gl_Color * texture2D(diffuseMap, gl_TexCoord[0].xy);
+
+ gl_FragData[0] = col;
+ gl_FragData[1] = vec4(0,0,0,0);
+ gl_FragData[2] = vec4(0,0,1,0);
+}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/starsV.glsl b/indra/newview/app_settings/shaders/class1/deferred/starsV.glsl
new file mode 100644
index 0000000000..1f0376ce6b
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/deferred/starsV.glsl
@@ -0,0 +1,17 @@
+/**
+ * @file starsV.glsl
+ *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
+ * $/LicenseInfo$
+ */
+
+#version 120
+
+
+void main()
+{
+ //transform vertex
+ gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
+ gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
+ gl_FrontColor = gl_Color;
+}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/sunLightMSF.glsl b/indra/newview/app_settings/shaders/class1/deferred/sunLightMSF.glsl
new file mode 100644
index 0000000000..00093836a2
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/deferred/sunLightMSF.glsl
@@ -0,0 +1,17 @@
+/**
+ * @file sunLightF.glsl
+ *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
+ * $/LicenseInfo$
+ */
+
+#version 120
+
+//class 1, no shadow, no SSAO, should never be called
+
+#extension GL_ARB_texture_rectangle : enable
+
+void main()
+{
+ gl_FragColor = vec4(0,0,0,0);
+}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/sunLightSSAOF.glsl b/indra/newview/app_settings/shaders/class1/deferred/sunLightSSAOF.glsl
index cd91351ad4..d1e8359742 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/sunLightSSAOF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/sunLightSSAOF.glsl
@@ -35,7 +35,7 @@ uniform float shadow_offset;
vec4 getPosition(vec2 pos_screen)
{
- float depth = texture2DRect(depthMap, pos_screen.xy).a;
+ float depth = texture2DRect(depthMap, pos_screen.xy).r;
vec2 sc = pos_screen.xy*2.0;
sc /= screen_res;
sc -= vec2(1.0,1.0);
diff --git a/indra/newview/app_settings/shaders/class1/deferred/sunLightSSAOMSF.glsl b/indra/newview/app_settings/shaders/class1/deferred/sunLightSSAOMSF.glsl
new file mode 100644
index 0000000000..ce0ebc54f8
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/deferred/sunLightSSAOMSF.glsl
@@ -0,0 +1,123 @@
+/**
+ * @file sunLightSSAOF.glsl
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#version 120
+
+#extension GL_ARB_texture_rectangle : enable
+#extension GL_ARB_texture_multisample : enable
+
+//class 1 -- no shadow, SSAO only
+
+uniform sampler2DMS depthMap;
+uniform sampler2DMS normalMap;
+uniform sampler2D noiseMap;
+
+
+// Inputs
+uniform mat4 shadow_matrix[6];
+uniform vec4 shadow_clip;
+uniform float ssao_radius;
+uniform float ssao_max_radius;
+uniform float ssao_factor;
+uniform float ssao_factor_inv;
+
+varying vec2 vary_fragcoord;
+varying vec4 vary_light;
+
+uniform mat4 inv_proj;
+uniform vec2 screen_res;
+
+uniform float shadow_bias;
+uniform float shadow_offset;
+
+vec4 getPosition(ivec2 pos_screen, int sample)
+{
+ float depth = texelFetch(depthMap, pos_screen, sample).r;
+ vec2 sc = pos_screen.xy*2.0;
+ sc /= screen_res;
+ sc -= vec2(1.0,1.0);
+ vec4 ndc = vec4(sc.x, sc.y, 2.0*depth-1.0, 1.0);
+ vec4 pos = inv_proj * ndc;
+ pos /= pos.w;
+ pos.w = 1.0;
+ return pos;
+}
+
+//calculate decreases in ambient lighting when crowded out (SSAO)
+float calcAmbientOcclusion(vec4 pos, vec3 norm, int sample)
+{
+ float ret = 1.0;
+
+ vec2 kern[8];
+ // exponentially (^2) distant occlusion samples spread around origin
+ kern[0] = vec2(-1.0, 0.0) * 0.125*0.125;
+ kern[1] = vec2(1.0, 0.0) * 0.250*0.250;
+ kern[2] = vec2(0.0, 1.0) * 0.375*0.375;
+ kern[3] = vec2(0.0, -1.0) * 0.500*0.500;
+ kern[4] = vec2(0.7071, 0.7071) * 0.625*0.625;
+ kern[5] = vec2(-0.7071, -0.7071) * 0.750*0.750;
+ kern[6] = vec2(-0.7071, 0.7071) * 0.875*0.875;
+ kern[7] = vec2(0.7071, -0.7071) * 1.000*1.000;
+
+ vec2 pos_screen = vary_fragcoord.xy;
+ vec3 pos_world = pos.xyz;
+ vec2 noise_reflect = texture2D(noiseMap, vary_fragcoord.xy/128.0).xy;
+
+ float angle_hidden = 0.0;
+ int points = 0;
+
+ float scale = min(ssao_radius / -pos_world.z, ssao_max_radius);
+
+ // it was found that keeping # of samples a constant was the fastest, probably due to compiler optimizations unrolling?)
+ for (int i = 0; i < 8; i++)
+ {
+ ivec2 samppos_screen = ivec2(pos_screen + scale * reflect(kern[i], noise_reflect));
+ vec3 samppos_world = getPosition(samppos_screen, sample).xyz;
+
+ vec3 diff = pos_world - samppos_world;
+ float dist2 = dot(diff, diff);
+
+ // assume each sample corresponds to an occluding sphere with constant radius, constant x-sectional area
+ // --> solid angle shrinking by the square of distance
+ //radius is somewhat arbitrary, can approx with just some constant k * 1 / dist^2
+ //(k should vary inversely with # of samples, but this is taken care of later)
+
+ angle_hidden = angle_hidden + float(dot((samppos_world - 0.05*norm - pos_world), norm) > 0.0) * min(1.0/dist2, ssao_factor_inv);
+
+ // 'blocked' samples (significantly closer to camera relative to pos_world) are "no data", not "no occlusion"
+ points = points + int(diff.z > -1.0);
+ }
+
+ angle_hidden = min(ssao_factor*angle_hidden/float(points), 1.0);
+
+ ret = (1.0 - (float(points != 0) * angle_hidden));
+
+ return min(ret, 1.0);
+}
+
+void main()
+{
+ vec2 pos_screen = vary_fragcoord.xy;
+ ivec2 itc = ivec2(pos_screen);
+
+ float col = 0;
+
+ for (int i = 0; i < samples; i++)
+ {
+ vec4 pos = getPosition(itc, i);
+ vec3 norm = texelFetch(normalMap, itc, i).xyz;
+ norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm
+ col += calcAmbientOcclusion(pos,norm,i);
+ }
+
+ col /= samples;
+
+ gl_FragColor[0] = 1.0;
+ gl_FragColor[1] = col;
+ gl_FragColor[2] = 1.0;
+ gl_FragColor[3] = 1.0;
+}
diff --git a/indra/newview/app_settings/shaders/class1/effects/glowExtractMSF.glsl b/indra/newview/app_settings/shaders/class1/effects/glowExtractMSF.glsl
new file mode 100644
index 0000000000..c03a0c61bd
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/effects/glowExtractMSF.glsl
@@ -0,0 +1,38 @@
+/**
+ * @file glowExtractF.glsl
+ *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
+ * $/LicenseInfo$
+ */
+
+#version 120
+
+#extension GL_ARB_texture_rectangle : enable
+#extension GL_ARB_texture_multisample : enable
+
+uniform sampler2DMS diffuseMap;
+uniform float minLuminance;
+uniform float maxExtractAlpha;
+uniform vec3 lumWeights;
+uniform vec3 warmthWeights;
+uniform float warmthAmount;
+
+void main()
+{
+ ivec2 itc = ivec2(gl_TexCoord[0].xy);
+ vec4 fcol = vec4(0,0,0,0);
+
+ for (int i = 0; i < samples; i++)
+ {
+ vec4 col = texelFetch(diffuseMap, itc, i);
+
+ /// CALCULATING LUMINANCE (Using NTSC lum weights)
+ /// http://en.wikipedia.org/wiki/Luma_%28video%29
+ float lum = smoothstep(minLuminance, minLuminance+1.0, dot(col.rgb, lumWeights ) );
+ float warmth = smoothstep(minLuminance, minLuminance+1.0, max(col.r * warmthWeights.r, max(col.g * warmthWeights.g, col.b * warmthWeights.b)) );
+
+ fcol += vec4(col.rgb, max(col.a, mix(lum, warmth, warmthAmount) * maxExtractAlpha));
+ }
+
+ gl_FragColor = fcol/samples;
+}
diff --git a/indra/newview/app_settings/shaders/class2/deferred/alphaSkinnedV.glsl b/indra/newview/app_settings/shaders/class2/deferred/alphaSkinnedV.glsl
index d227346163..dfb36980b0 100644
--- a/indra/newview/app_settings/shaders/class2/deferred/alphaSkinnedV.glsl
+++ b/indra/newview/app_settings/shaders/class2/deferred/alphaSkinnedV.glsl
@@ -35,19 +35,24 @@ float calcPointLightOrSpotLight(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, floa
//get distance
float d = length(lv);
- //normalize light vector
- lv *= 1.0/d;
+ float da = 0.0;
+
+ if (d > 0.0 && la > 0.0 && fa > 0.0)
+ {
+ //normalize light vector
+ lv *= 1.0/d;
- //distance attenuation
- float dist2 = d*d/(la*la);
- float da = clamp(1.0-(dist2-1.0*(1.0-fa))/fa, 0.0, 1.0);
+ //distance attenuation
+ float dist2 = d*d/(la*la);
+ da = clamp(1.0-(dist2-1.0*(1.0-fa))/fa, 0.0, 1.0);
- // spotlight coefficient.
- float spot = max(dot(-ln, lv), is_pointlight);
- da *= spot*spot; // GL_SPOT_EXPONENT=2
+ // spotlight coefficient.
+ float spot = max(dot(-ln, lv), is_pointlight);
+ da *= spot*spot; // GL_SPOT_EXPONENT=2
- //angular attenuation
- da *= calcDirectionalLight(n, lv);
+ //angular attenuation
+ da *= calcDirectionalLight(n, lv);
+ }
return da;
}
diff --git a/indra/newview/app_settings/shaders/class2/deferred/alphaV.glsl b/indra/newview/app_settings/shaders/class2/deferred/alphaV.glsl
index 86f014df35..f6160815eb 100644
--- a/indra/newview/app_settings/shaders/class2/deferred/alphaV.glsl
+++ b/indra/newview/app_settings/shaders/class2/deferred/alphaV.glsl
@@ -35,19 +35,24 @@ float calcPointLightOrSpotLight(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, floa
//get distance
float d = length(lv);
- //normalize light vector
- lv *= 1.0/d;
+ float da = 0.0;
+
+ if (d > 0.0 && la > 0.0 && fa > 0.0)
+ {
+ //normalize light vector
+ lv *= 1.0/d;
- //distance attenuation
- float dist2 = d*d/(la*la);
- float da = clamp(1.0-(dist2-1.0*(1.0-fa))/fa, 0.0, 1.0);
+ //distance attenuation
+ float dist2 = d*d/(la*la);
+ da = clamp(1.0-(dist2-1.0*(1.0-fa))/fa, 0.0, 1.0);
- // spotlight coefficient.
- float spot = max(dot(-ln, lv), is_pointlight);
- da *= spot*spot; // GL_SPOT_EXPONENT=2
+ // spotlight coefficient.
+ float spot = max(dot(-ln, lv), is_pointlight);
+ da *= spot*spot; // GL_SPOT_EXPONENT=2
- //angular attenuation
- da *= calcDirectionalLight(n, lv);
+ //angular attenuation
+ da *= calcDirectionalLight(n, lv);
+ }
return da;
}
diff --git a/indra/newview/app_settings/shaders/class2/deferred/avatarAlphaV.glsl b/indra/newview/app_settings/shaders/class2/deferred/avatarAlphaV.glsl
index 495e86c8db..0ae09df0c6 100644
--- a/indra/newview/app_settings/shaders/class2/deferred/avatarAlphaV.glsl
+++ b/indra/newview/app_settings/shaders/class2/deferred/avatarAlphaV.glsl
@@ -37,19 +37,24 @@ float calcPointLightOrSpotLight(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, floa
//get distance
float d = length(lv);
- //normalize light vector
- lv *= 1.0/d;
+ float da = 0.0;
+
+ if (d > 0.0 && la > 0.0 && fa > 0.0)
+ {
+ //normalize light vector
+ lv *= 1.0/d;
- //distance attenuation
- float dist2 = d*d/(la*la);
- float da = clamp(1.0-(dist2-1.0*(1.0-fa))/fa, 0.0, 1.0);
+ //distance attenuation
+ float dist2 = d*d/(la*la);
+ da = clamp(1.0-(dist2-1.0*(1.0-fa))/fa, 0.0, 1.0);
- // spotlight coefficient.
- float spot = max(dot(-ln, lv), is_pointlight);
- da *= spot*spot; // GL_SPOT_EXPONENT=2
+ // spotlight coefficient.
+ float spot = max(dot(-ln, lv), is_pointlight);
+ da *= spot*spot; // GL_SPOT_EXPONENT=2
- //angular attenuation
- da *= calcDirectionalLight(n, lv);
+ //angular attenuation
+ da *= calcDirectionalLight(n, lv);
+ }
return da;
}
diff --git a/indra/newview/app_settings/shaders/class2/deferred/edgeF.glsl b/indra/newview/app_settings/shaders/class2/deferred/edgeF.glsl
index 3155f3f929..30e1702e9f 100644
--- a/indra/newview/app_settings/shaders/class2/deferred/edgeF.glsl
+++ b/indra/newview/app_settings/shaders/class2/deferred/edgeF.glsl
@@ -22,7 +22,7 @@ uniform vec2 screen_res;
float getDepth(vec2 pos_screen)
{
- float z = texture2DRect(depthMap, pos_screen.xy).a;
+ float z = texture2DRect(depthMap, pos_screen.xy).r;
z = z*2.0-1.0;
vec4 ndc = vec4(0.0, 0.0, z, 1.0);
vec4 p = inv_proj*ndc;
diff --git a/indra/newview/app_settings/shaders/class2/deferred/edgeMSF.glsl b/indra/newview/app_settings/shaders/class2/deferred/edgeMSF.glsl
new file mode 100644
index 0000000000..38100d1523
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class2/deferred/edgeMSF.glsl
@@ -0,0 +1,74 @@
+/**
+ * @file edgeF.glsl
+ *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
+ * $/LicenseInfo$
+ */
+
+#version 120
+
+#extension GL_ARB_texture_rectangle : enable
+#extension GL_ARB_texture_multisample : enable
+
+uniform sampler2DMS depthMap;
+uniform sampler2DMS normalMap;
+
+varying vec2 vary_fragcoord;
+
+uniform float depth_cutoff;
+uniform float norm_cutoff;
+
+uniform mat4 inv_proj;
+uniform vec2 screen_res;
+
+float getDepth(ivec2 pos_screen, int sample)
+{
+ float z = texelFetch(depthMap, pos_screen, sample).r;
+ z = z*2.0-1.0;
+ vec4 ndc = vec4(0.0, 0.0, z, 1.0);
+ vec4 p = inv_proj*ndc;
+ return p.z/p.w;
+}
+
+void main()
+{
+ float e = 0;
+
+ ivec2 itc = ivec2(vary_fragcoord.xy);
+
+ for (int i = 0; i < samples; i++)
+ {
+ vec3 norm = texelFetch(normalMap, itc, i).xyz;
+ norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm
+ float depth = getDepth(itc, i);
+
+ vec2 tc = vary_fragcoord.xy;
+
+ int sc = 1;
+
+ vec2 de;
+ de.x = (depth-getDepth(itc+ivec2(sc, sc),i)) + (depth-getDepth(itc+ivec2(-sc, -sc), i));
+ de.y = (depth-getDepth(itc+ivec2(-sc, sc),i)) + (depth-getDepth(itc+ivec2(sc, -sc), i));
+ de /= depth;
+ de *= de;
+ de = step(depth_cutoff, de);
+
+ vec2 ne;
+ vec3 nexnorm = texelFetch(normalMap, itc+ivec2(-sc,-sc), i).rgb;
+ nexnorm = vec3((nexnorm.xy-0.5)*2.0,nexnorm.z); // unpack norm
+ ne.x = dot(nexnorm, norm);
+ vec3 neynorm = texelFetch(normalMap, itc+ivec2(sc,sc), i).rgb;
+ neynorm = vec3((neynorm.xy-0.5)*2.0,neynorm.z); // unpack norm
+ ne.y = dot(neynorm, norm);
+
+ ne = 1.0-ne;
+
+ ne = step(norm_cutoff, ne);
+
+ e += dot(de,de)+dot(ne,ne);
+ }
+
+ e /= samples;
+
+ gl_FragColor.a = e;
+}
diff --git a/indra/newview/app_settings/shaders/class2/deferred/multiSpotLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/multiSpotLightF.glsl
index d6cd984ebe..de987b1233 100644
--- a/indra/newview/app_settings/shaders/class2/deferred/multiSpotLightF.glsl
+++ b/indra/newview/app_settings/shaders/class2/deferred/multiSpotLightF.glsl
@@ -91,7 +91,7 @@ vec4 texture2DLodAmbient(sampler2D projectionMap, vec2 tc, float lod)
vec4 getPosition(vec2 pos_screen)
{
- float depth = texture2DRect(depthMap, pos_screen.xy).a;
+ float depth = texture2DRect(depthMap, pos_screen.xy).r;
vec2 sc = pos_screen.xy*2.0;
sc /= screen_res;
sc -= vec2(1.0,1.0);
diff --git a/indra/newview/app_settings/shaders/class2/deferred/multiSpotLightMSF.glsl b/indra/newview/app_settings/shaders/class2/deferred/multiSpotLightMSF.glsl
new file mode 100644
index 0000000000..d0f9101774
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class2/deferred/multiSpotLightMSF.glsl
@@ -0,0 +1,244 @@
+/**
+ * @file multiSpotLightF.glsl
+ *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
+ * $/LicenseInfo$
+ */
+
+#version 120
+
+#extension GL_ARB_texture_rectangle : enable
+#extension GL_ARB_texture_multisample : enable
+
+uniform sampler2DMS diffuseRect;
+uniform sampler2DMS specularRect;
+uniform sampler2DMS depthMap;
+uniform sampler2DMS normalMap;
+uniform sampler2DRect lightMap;
+uniform sampler2D noiseMap;
+uniform sampler2D lightFunc;
+uniform sampler2D projectionMap;
+
+uniform mat4 proj_mat; //screen space to light space
+uniform float proj_near; //near clip for projection
+uniform vec3 proj_p; //plane projection is emitting from (in screen space)
+uniform vec3 proj_n;
+uniform float proj_focus; //distance from plane to begin blurring
+uniform float proj_lod; //(number of mips in proj map)
+uniform float proj_range; //range between near clip and far clip plane of projection
+uniform float proj_ambient_lod;
+uniform float proj_ambiance;
+uniform float near_clip;
+uniform float far_clip;
+
+uniform vec3 proj_origin; //origin of projection to be used for angular attenuation
+uniform float sun_wash;
+uniform int proj_shadow_idx;
+uniform float shadow_fade;
+
+varying vec4 vary_light;
+
+varying vec4 vary_fragcoord;
+uniform vec2 screen_res;
+
+uniform mat4 inv_proj;
+
+vec4 texture2DLodSpecular(sampler2D projectionMap, vec2 tc, float lod)
+{
+ vec4 ret = texture2DLod(projectionMap, tc, lod);
+
+ vec2 dist = tc-vec2(0.5);
+
+ float det = max(1.0-lod/(proj_lod*0.5), 0.0);
+
+ float d = dot(dist,dist);
+
+ ret *= min(clamp((0.25-d)/0.25, 0.0, 1.0)+det, 1.0);
+
+ return ret;
+}
+
+vec4 texture2DLodDiffuse(sampler2D projectionMap, vec2 tc, float lod)
+{
+ vec4 ret = texture2DLod(projectionMap, tc, lod);
+
+ vec2 dist = vec2(0.5) - abs(tc-vec2(0.5));
+
+ float det = min(lod/(proj_lod*0.5), 1.0);
+
+ float d = min(dist.x, dist.y);
+
+ float edge = 0.25*det;
+
+ ret *= clamp(d/edge, 0.0, 1.0);
+
+ return ret;
+}
+
+vec4 texture2DLodAmbient(sampler2D projectionMap, vec2 tc, float lod)
+{
+ vec4 ret = texture2DLod(projectionMap, tc, lod);
+
+ vec2 dist = tc-vec2(0.5);
+
+ float d = dot(dist,dist);
+
+ ret *= min(clamp((0.25-d)/0.25, 0.0, 1.0), 1.0);
+
+ return ret;
+}
+
+
+vec4 getPosition(ivec2 pos_screen, int sample)
+{
+ float depth = texelFetch(depthMap, pos_screen, sample).r;
+ vec2 sc = vec2(pos_screen.xy)*2.0;
+ sc /= screen_res;
+ sc -= vec2(1.0,1.0);
+ vec4 ndc = vec4(sc.x, sc.y, 2.0*depth-1.0, 1.0);
+ vec4 pos = inv_proj * ndc;
+ pos /= pos.w;
+ pos.w = 1.0;
+ return pos;
+}
+
+void main()
+{
+ int wght = 0;
+
+ vec3 fcol = vec3(0,0,0);
+
+ vec2 frag = (vary_fragcoord.xy*0.5+0.5)*screen_res;
+
+ ivec2 itc = ivec2(frag.xy);
+
+ float shadow = 1.0;
+
+ if (proj_shadow_idx >= 0)
+ {
+ vec4 shd = texture2DRect(lightMap, frag);
+ float sh[2];
+ sh[0] = shd.b;
+ sh[1] = shd.a;
+ shadow = min(sh[proj_shadow_idx]+shadow_fade, 1.0);
+ }
+
+ for (int i = 0; i < samples; i++)
+ {
+ vec3 pos = getPosition(itc, i).xyz;
+ vec3 lv = vary_light.xyz-pos.xyz;
+ float dist2 = dot(lv,lv);
+ dist2 /= vary_light.w;
+ if (dist2 <= 1.0)
+ {
+ vec3 norm = texelFetch(normalMap, itc, i).xyz;
+ norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm
+
+ norm = normalize(norm);
+ float l_dist = -dot(lv, proj_n);
+
+ vec4 proj_tc = (proj_mat * vec4(pos.xyz, 1.0));
+ if (proj_tc.z >= 0.0)
+ {
+ proj_tc.xyz /= proj_tc.w;
+
+ float fa = gl_Color.a+1.0;
+ float dist_atten = min(1.0-(dist2-1.0*(1.0-fa))/fa, 1.0);
+ if (dist_atten > 0.0)
+ {
+ lv = proj_origin-pos.xyz;
+ lv = normalize(lv);
+ float da = dot(norm, lv);
+
+ vec3 col = vec3(0,0,0);
+
+ vec3 diff_tex = texelFetch(diffuseRect, itc, i).rgb;
+
+ float noise = texture2D(noiseMap, frag.xy/128.0).b;
+ if (proj_tc.z > 0.0 &&
+ proj_tc.x < 1.0 &&
+ proj_tc.y < 1.0 &&
+ proj_tc.x > 0.0 &&
+ proj_tc.y > 0.0)
+ {
+ float lit = 0.0;
+ float amb_da = proj_ambiance;
+
+ if (da > 0.0)
+ {
+ float diff = clamp((l_dist-proj_focus)/proj_range, 0.0, 1.0);
+ float lod = diff * proj_lod;
+
+ vec4 plcol = texture2DLodDiffuse(projectionMap, proj_tc.xy, lod);
+
+ vec3 lcol = gl_Color.rgb * plcol.rgb * plcol.a;
+
+ lit = da * dist_atten * noise;
+
+ col = lcol*lit*diff_tex*shadow;
+ amb_da += (da*0.5)*(1.0-shadow)*proj_ambiance;
+ }
+
+ //float diff = clamp((proj_range-proj_focus)/proj_range, 0.0, 1.0);
+ vec4 amb_plcol = texture2DLodAmbient(projectionMap, proj_tc.xy, proj_lod);
+
+ amb_da += (da*da*0.5+0.5)*proj_ambiance;
+
+ amb_da *= dist_atten * noise;
+
+ amb_da = min(amb_da, 1.0-lit);
+
+ col += amb_da*gl_Color.rgb*diff_tex.rgb*amb_plcol.rgb*amb_plcol.a;
+ }
+
+
+ vec4 spec = texelFetch(specularRect, itc, i);
+ if (spec.a > 0.0)
+ {
+ vec3 ref = reflect(normalize(pos), norm);
+
+ //project from point pos in direction ref to plane proj_p, proj_n
+ vec3 pdelta = proj_p-pos;
+ float ds = dot(ref, proj_n);
+
+ if (ds < 0.0)
+ {
+ vec3 pfinal = pos + ref * dot(pdelta, proj_n)/ds;
+
+ vec4 stc = (proj_mat * vec4(pfinal.xyz, 1.0));
+
+ if (stc.z > 0.0)
+ {
+ stc.xy /= stc.w;
+
+ float fatten = clamp(spec.a*spec.a+spec.a*0.5, 0.25, 1.0);
+
+ stc.xy = (stc.xy - vec2(0.5)) * fatten + vec2(0.5);
+
+ if (stc.x < 1.0 &&
+ stc.y < 1.0 &&
+ stc.x > 0.0 &&
+ stc.y > 0.0)
+ {
+ vec4 scol = texture2DLodSpecular(projectionMap, stc.xy, proj_lod-spec.a*proj_lod);
+ col += dist_atten*scol.rgb*gl_Color.rgb*scol.a*spec.rgb*shadow;
+ }
+ }
+ }
+ }
+
+ fcol += col;
+ wght++;
+ }
+ }
+ }
+ }
+
+ if (wght <= 0)
+ {
+ discard;
+ }
+
+ gl_FragColor.rgb = fcol/samples;
+ gl_FragColor.a = 0.0;
+}
diff --git a/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl
index 0160e84278..dfa1964142 100644
--- a/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl
+++ b/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl
@@ -71,7 +71,7 @@ vec4 getPosition_d(vec2 pos_screen, float depth)
vec4 getPosition(vec2 pos_screen)
{ //get position in screen space (world units) given window coordinate and depth map
- float depth = texture2DRect(depthMap, pos_screen.xy).a;
+ float depth = texture2DRect(depthMap, pos_screen.xy).r;
return getPosition_d(pos_screen, depth);
}
@@ -258,7 +258,7 @@ vec3 scaleSoftClip(vec3 light)
void main()
{
vec2 tc = vary_fragcoord.xy;
- float depth = texture2DRect(depthMap, tc.xy).a;
+ float depth = texture2DRect(depthMap, tc.xy).r;
vec3 pos = getPosition_d(tc, depth).xyz;
vec3 norm = texture2DRect(normalMap, tc).xyz;
norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm
@@ -288,54 +288,8 @@ void main()
float sa = dot(refnormpersp, vary_light.xyz);
vec3 dumbshiny = vary_SunlitColor*scol_ambocc.r*texture2D(lightFunc, vec2(sa, spec.a)).a;
- /*
- // screen-space cheap fakey reflection map
- //
- vec3 refnorm = normalize(reflect(vec3(0,0,-1), norm.xyz));
- depth -= 0.5; // unbias depth
- // first figure out where we'll make our 2D guess from
- vec2 ref2d = (0.25 * screen_res.y) * (refnorm.xy) * abs(refnorm.z) / depth;
- // Offset the guess source a little according to a trivial
- // checkerboard dither function and spec.a.
- // This is meant to be similar to sampling a blurred version
- // of the diffuse map. LOD would be better in that regard.
- // The goal of the blur is to soften reflections in surfaces
- // with low shinyness, and also to disguise our lameness.
- float checkerboard = floor(mod(tc.x+tc.y, 2.0)); // 0.0, 1.0
- float checkoffset = (3.0 + (7.0*(1.0-spec.a)))*(checkerboard-0.5);
- ref2d += vec2(checkoffset, checkoffset);
- ref2d += tc.xy; // use as offset from destination
- // Get attributes from the 2D guess point.
- // We average two samples of diffuse (not of anything else) per
- // pixel to try to reduce aliasing some more.
- vec3 refcol = 0.5 * (texture2DRect(diffuseRect, ref2d + vec2(0.0, -checkoffset)).rgb +
- texture2DRect(diffuseRect, ref2d + vec2(-checkoffset, 0.0)).rgb);
- float refdepth = texture2DRect(depthMap, ref2d).a;
- vec3 refpos = getPosition_d(ref2d, refdepth).xyz;
- float refshad = texture2DRect(lightMap, ref2d).r;
- vec3 refn = texture2DRect(normalMap, ref2d).rgb;
- refn = vec3((refn.xy-0.5)*2.0,refn.z); // unpack norm
- refn = normalize(refn);
- // figure out how appropriate our guess actually was
- float refapprop = max(0.0, dot(-refnorm, normalize(pos - refpos)));
- // darken reflections from points which face away from the reflected ray - our guess was a back-face
- //refapprop *= step(dot(refnorm, refn), 0.0);
- refapprop = min(refapprop, max(0.0, -dot(refnorm, refn))); // more conservative variant
- // get appropriate light strength for guess-point
- // reflect light direction to increase the illusion that
- // these are reflections.
- vec3 reflight = reflect(lightnorm.xyz, norm.xyz);
- float reflit = min(max(dot(refn, reflight.xyz), 0.0), refshad);
- // apply sun color to guess-point, dampen according to inappropriateness of guess
- float refmod = min(refapprop, reflit);
- vec3 refprod = vary_SunlitColor * refcol.rgb * refmod;
- vec3 ssshiny = (refprod * spec.a);
- ssshiny *= 0.3; // dampen it even more
- */
- vec3 ssshiny = vec3(0,0,0);
-
// add the two types of shiny together
- col += (ssshiny + dumbshiny) * spec.rgb;
+ col += dumbshiny * spec.rgb;
}
col = atmosLighting(col);
diff --git a/indra/newview/app_settings/shaders/class2/deferred/softenLightMSF.glsl b/indra/newview/app_settings/shaders/class2/deferred/softenLightMSF.glsl
new file mode 100644
index 0000000000..cb09fe9895
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class2/deferred/softenLightMSF.glsl
@@ -0,0 +1,307 @@
+/**
+ * @file softenLightMSF.glsl
+ *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
+ * $/LicenseInfo$
+ */
+
+#version 120
+
+#extension GL_ARB_texture_rectangle : enable
+#extension GL_ARB_texture_multisample : enable
+
+uniform sampler2DMS diffuseRect;
+uniform sampler2DMS specularRect;
+uniform sampler2DMS normalMap;
+uniform sampler2DRect lightMap;
+uniform sampler2DMS depthMap;
+uniform sampler2D noiseMap;
+uniform samplerCube environmentMap;
+uniform sampler2D lightFunc;
+uniform vec3 gi_quad;
+
+uniform float blur_size;
+uniform float blur_fidelity;
+
+// Inputs
+uniform vec4 morphFactor;
+uniform vec3 camPosLocal;
+//uniform vec4 camPosWorld;
+uniform vec4 gamma;
+uniform vec4 lightnorm;
+uniform vec4 sunlight_color;
+uniform vec4 ambient;
+uniform vec4 blue_horizon;
+uniform vec4 blue_density;
+uniform vec4 haze_horizon;
+uniform vec4 haze_density;
+uniform vec4 cloud_shadow;
+uniform vec4 density_multiplier;
+uniform vec4 distance_multiplier;
+uniform vec4 max_y;
+uniform vec4 glow;
+uniform float scene_light_strength;
+uniform vec3 env_mat[3];
+uniform vec4 shadow_clip;
+uniform mat3 ssao_effect_mat;
+
+uniform mat4 inv_proj;
+uniform vec2 screen_res;
+
+varying vec4 vary_light;
+varying vec2 vary_fragcoord;
+
+vec3 vary_PositionEye;
+
+vec3 vary_SunlitColor;
+vec3 vary_AmblitColor;
+vec3 vary_AdditiveColor;
+vec3 vary_AtmosAttenuation;
+
+vec4 getPosition_d(vec2 pos_screen, float depth)
+{
+ vec2 sc = pos_screen.xy*2.0;
+ sc /= screen_res;
+ sc -= vec2(1.0,1.0);
+ vec4 ndc = vec4(sc.x, sc.y, 2.0*depth-1.0, 1.0);
+ vec4 pos = inv_proj * ndc;
+ pos /= pos.w;
+ pos.w = 1.0;
+ return pos;
+}
+
+vec3 getPositionEye()
+{
+ return vary_PositionEye;
+}
+vec3 getSunlitColor()
+{
+ return vary_SunlitColor;
+}
+vec3 getAmblitColor()
+{
+ return vary_AmblitColor;
+}
+vec3 getAdditiveColor()
+{
+ return vary_AdditiveColor;
+}
+vec3 getAtmosAttenuation()
+{
+ return vary_AtmosAttenuation;
+}
+
+
+void setPositionEye(vec3 v)
+{
+ vary_PositionEye = v;
+}
+
+void setSunlitColor(vec3 v)
+{
+ vary_SunlitColor = v;
+}
+
+void setAmblitColor(vec3 v)
+{
+ vary_AmblitColor = v;
+}
+
+void setAdditiveColor(vec3 v)
+{
+ vary_AdditiveColor = v;
+}
+
+void setAtmosAttenuation(vec3 v)
+{
+ vary_AtmosAttenuation = v;
+}
+
+void calcAtmospherics(vec3 inPositionEye, float ambFactor) {
+
+ vec3 P = inPositionEye;
+ setPositionEye(P);
+
+ //(TERRAIN) limit altitude
+ if (P.y > max_y.x) P *= (max_y.x / P.y);
+ if (P.y < -max_y.x) P *= (-max_y.x / P.y);
+
+ vec3 tmpLightnorm = lightnorm.xyz;
+
+ vec3 Pn = normalize(P);
+ float Plen = length(P);
+
+ vec4 temp1 = vec4(0);
+ vec3 temp2 = vec3(0);
+ vec4 blue_weight;
+ vec4 haze_weight;
+ vec4 sunlight = sunlight_color;
+ vec4 light_atten;
+
+ //sunlight attenuation effect (hue and brightness) due to atmosphere
+ //this is used later for sunlight modulation at various altitudes
+ light_atten = (blue_density * 1.0 + vec4(haze_density.r) * 0.25) * (density_multiplier.x * max_y.x);
+ //I had thought blue_density and haze_density should have equal weighting,
+ //but attenuation due to haze_density tends to seem too strong
+
+ temp1 = blue_density + vec4(haze_density.r);
+ blue_weight = blue_density / temp1;
+ haze_weight = vec4(haze_density.r) / temp1;
+
+ //(TERRAIN) compute sunlight from lightnorm only (for short rays like terrain)
+ temp2.y = max(0.0, tmpLightnorm.y);
+ temp2.y = 1. / temp2.y;
+ sunlight *= exp( - light_atten * temp2.y);
+
+ // main atmospheric scattering line integral
+ temp2.z = Plen * density_multiplier.x;
+
+ // Transparency (-> temp1)
+ // ATI Bugfix -- can't store temp1*temp2.z*distance_multiplier.x in a variable because the ati
+ // compiler gets confused.
+ temp1 = exp(-temp1 * temp2.z * distance_multiplier.x);
+
+ //final atmosphere attenuation factor
+ setAtmosAttenuation(temp1.rgb);
+
+ //compute haze glow
+ //(can use temp2.x as temp because we haven't used it yet)
+ temp2.x = dot(Pn, tmpLightnorm.xyz);
+ temp2.x = 1. - temp2.x;
+ //temp2.x is 0 at the sun and increases away from sun
+ temp2.x = max(temp2.x, .03); //was glow.y
+ //set a minimum "angle" (smaller glow.y allows tighter, brighter hotspot)
+ temp2.x *= glow.x;
+ //higher glow.x gives dimmer glow (because next step is 1 / "angle")
+ temp2.x = pow(temp2.x, glow.z);
+ //glow.z should be negative, so we're doing a sort of (1 / "angle") function
+
+ //add "minimum anti-solar illumination"
+ temp2.x += .25;
+
+ //increase ambient when there are more clouds
+ vec4 tmpAmbient = ambient + (vec4(1.) - ambient) * cloud_shadow.x * 0.5;
+
+ /* decrease value and saturation (that in HSV, not HSL) for occluded areas
+ * // for HSV color/geometry used here, see http://gimp-savvy.com/BOOK/index.html?node52.html
+ * // The following line of code performs the equivalent of:
+ * float ambAlpha = tmpAmbient.a;
+ * float ambValue = dot(vec3(tmpAmbient), vec3(0.577)); // projection onto <1/rt(3), 1/rt(3), 1/rt(3)>, the neutral white-black axis
+ * vec3 ambHueSat = vec3(tmpAmbient) - vec3(ambValue);
+ * tmpAmbient = vec4(RenderSSAOEffect.valueFactor * vec3(ambValue) + RenderSSAOEffect.saturationFactor *(1.0 - ambFactor) * ambHueSat, ambAlpha);
+ */
+ tmpAmbient = vec4(mix(ssao_effect_mat * tmpAmbient.rgb, tmpAmbient.rgb, ambFactor), tmpAmbient.a);
+
+ //haze color
+ setAdditiveColor(
+ vec3(blue_horizon * blue_weight * (sunlight*(1.-cloud_shadow.x) + tmpAmbient)
+ + (haze_horizon.r * haze_weight) * (sunlight*(1.-cloud_shadow.x) * temp2.x
+ + tmpAmbient)));
+
+ //brightness of surface both sunlight and ambient
+ setSunlitColor(vec3(sunlight * .5));
+ setAmblitColor(vec3(tmpAmbient * .25));
+ setAdditiveColor(getAdditiveColor() * vec3(1.0 - temp1));
+}
+
+vec3 atmosLighting(vec3 light)
+{
+ light *= getAtmosAttenuation().r;
+ light += getAdditiveColor();
+ return (2.0 * light);
+}
+
+vec3 atmosTransport(vec3 light) {
+ light *= getAtmosAttenuation().r;
+ light += getAdditiveColor() * 2.0;
+ return light;
+}
+vec3 atmosGetDiffuseSunlightColor()
+{
+ return getSunlitColor();
+}
+
+vec3 scaleDownLight(vec3 light)
+{
+ return (light / scene_light_strength );
+}
+
+vec3 scaleUpLight(vec3 light)
+{
+ return (light * scene_light_strength);
+}
+
+vec3 atmosAmbient(vec3 light)
+{
+ return getAmblitColor() + light / 2.0;
+}
+
+vec3 atmosAffectDirectionalLight(float lightIntensity)
+{
+ return getSunlitColor() * lightIntensity;
+}
+
+vec3 scaleSoftClip(vec3 light)
+{
+ //soft clip effect:
+ light = 1. - clamp(light, vec3(0.), vec3(1.));
+ light = 1. - pow(light, gamma.xxx);
+
+ return light;
+}
+
+void main()
+{
+ vec2 tc = vary_fragcoord.xy;
+ ivec2 itc = ivec2(tc);
+
+ vec3 fcol = vec3(0,0,0);
+
+ vec2 scol_ambocc = texture2DRect(lightMap, tc).rg;
+ float ambocc = scol_ambocc.g;
+
+ for (int i = 0; i < samples; ++i)
+ {
+ float depth = texelFetch(depthMap, itc.xy, i).r;
+ vec3 pos = getPosition_d(tc, depth).xyz;
+ vec3 norm = texelFetch(normalMap, itc, i).xyz;
+ norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm
+
+ float da = max(dot(norm.xyz, vary_light.xyz), 0.0);
+
+ vec4 diffuse = texelFetch(diffuseRect, itc, i);
+ vec4 spec = texelFetch(specularRect, itc, i);
+
+ float amb = 0;
+
+ float scol = max(scol_ambocc.r, diffuse.a);
+ amb += ambocc;
+
+ calcAtmospherics(pos.xyz, ambocc);
+
+ vec3 col = atmosAmbient(vec3(0));
+ col += atmosAffectDirectionalLight(max(min(da, scol), diffuse.a));
+
+ col *= diffuse.rgb;
+
+ if (spec.a > 0.0) // specular reflection
+ {
+ // the old infinite-sky shiny reflection
+ //
+ vec3 refnormpersp = normalize(reflect(pos.xyz, norm.xyz));
+ float sa = dot(refnormpersp, vary_light.xyz);
+ vec3 dumbshiny = vary_SunlitColor*scol_ambocc.r*texture2D(lightFunc, vec2(sa, spec.a)).a;
+
+ // add the two types of shiny together
+ col += dumbshiny * spec.rgb;
+ }
+
+ col = atmosLighting(col);
+ col = scaleSoftClip(col);
+
+ fcol += col;
+ }
+
+ gl_FragColor.rgb = fcol/samples;
+ gl_FragColor.a = 0.0;
+}
diff --git a/indra/newview/app_settings/shaders/class2/deferred/spotLightMSF.glsl b/indra/newview/app_settings/shaders/class2/deferred/spotLightMSF.glsl
new file mode 100644
index 0000000000..8d6ffd79c5
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class2/deferred/spotLightMSF.glsl
@@ -0,0 +1,245 @@
+/**
+ * @file multiSpotLightF.glsl
+ *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
+ * $/LicenseInfo$
+ */
+
+#version 120
+
+#extension GL_ARB_texture_rectangle : enable
+#extension GL_ARB_texture_multisample : enable
+
+uniform sampler2DMS diffuseRect;
+uniform sampler2DMS specularRect;
+uniform sampler2DMS depthMap;
+uniform sampler2DMS normalMap;
+uniform sampler2DRect lightMap;
+uniform sampler2D noiseMap;
+uniform sampler2D lightFunc;
+uniform sampler2D projectionMap;
+
+uniform mat4 proj_mat; //screen space to light space
+uniform float proj_near; //near clip for projection
+uniform vec3 proj_p; //plane projection is emitting from (in screen space)
+uniform vec3 proj_n;
+uniform float proj_focus; //distance from plane to begin blurring
+uniform float proj_lod; //(number of mips in proj map)
+uniform float proj_range; //range between near clip and far clip plane of projection
+uniform float proj_ambient_lod;
+uniform float proj_ambiance;
+uniform float near_clip;
+uniform float far_clip;
+
+uniform vec3 proj_origin; //origin of projection to be used for angular attenuation
+uniform float sun_wash;
+uniform int proj_shadow_idx;
+uniform float shadow_fade;
+
+varying vec4 vary_light;
+
+varying vec4 vary_fragcoord;
+uniform vec2 screen_res;
+
+uniform mat4 inv_proj;
+
+vec4 texture2DLodSpecular(sampler2D projectionMap, vec2 tc, float lod)
+{
+ vec4 ret = texture2DLod(projectionMap, tc, lod);
+
+ vec2 dist = tc-vec2(0.5);
+
+ float det = max(1.0-lod/(proj_lod*0.5), 0.0);
+
+ float d = dot(dist,dist);
+
+ ret *= min(clamp((0.25-d)/0.25, 0.0, 1.0)+det, 1.0);
+
+ return ret;
+}
+
+vec4 texture2DLodDiffuse(sampler2D projectionMap, vec2 tc, float lod)
+{
+ vec4 ret = texture2DLod(projectionMap, tc, lod);
+
+ vec2 dist = vec2(0.5) - abs(tc-vec2(0.5));
+
+ float det = min(lod/(proj_lod*0.5), 1.0);
+
+ float d = min(dist.x, dist.y);
+
+ float edge = 0.25*det;
+
+ ret *= clamp(d/edge, 0.0, 1.0);
+
+ return ret;
+}
+
+vec4 texture2DLodAmbient(sampler2D projectionMap, vec2 tc, float lod)
+{
+ vec4 ret = texture2DLod(projectionMap, tc, lod);
+
+ vec2 dist = tc-vec2(0.5);
+
+ float d = dot(dist,dist);
+
+ ret *= min(clamp((0.25-d)/0.25, 0.0, 1.0), 1.0);
+
+ return ret;
+}
+
+
+vec4 getPosition(ivec2 pos_screen, int sample)
+{
+ float depth = texelFetch(depthMap, pos_screen, sample).r;
+ vec2 sc = vec2(pos_screen.xy)*2.0;
+ sc /= screen_res;
+ sc -= vec2(1.0,1.0);
+ vec4 ndc = vec4(sc.x, sc.y, 2.0*depth-1.0, 1.0);
+ vec4 pos = inv_proj * ndc;
+ pos /= pos.w;
+ pos.w = 1.0;
+ return pos;
+}
+
+void main()
+{
+ vec4 frag = vary_fragcoord;
+ frag.xyz /= frag.w;
+ frag.xyz = frag.xyz*0.5+0.5;
+ frag.xy *= screen_res;
+ ivec2 itc = ivec2(frag.xy);
+
+ vec3 fcol = vec3(0,0,0);
+ int wght = 0;
+
+ float shadow = 1.0;
+
+ if (proj_shadow_idx >= 0)
+ {
+ vec4 shd = texture2DRect(lightMap, frag.xy);
+ float sh[2];
+ sh[0] = shd.b;
+ sh[1] = shd.a;
+ shadow = min(sh[proj_shadow_idx]+shadow_fade, 1.0);
+ }
+
+ for (int i = 0; i < samples; i++)
+ {
+ vec3 pos = getPosition(itc, i).xyz;
+ vec3 lv = vary_light.xyz-pos.xyz;
+ float dist2 = dot(lv,lv);
+ dist2 /= vary_light.w;
+ if (dist2 <= 1.0)
+ {
+ vec3 norm = texelFetch(normalMap, itc, i).xyz;
+ norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm
+
+ norm = normalize(norm);
+ float l_dist = -dot(lv, proj_n);
+
+ vec4 proj_tc = (proj_mat * vec4(pos.xyz, 1.0));
+ if (proj_tc.z >= 0.0)
+ {
+ proj_tc.xyz /= proj_tc.w;
+
+ float fa = gl_Color.a+1.0;
+ float dist_atten = min(1.0-(dist2-1.0*(1.0-fa))/fa, 1.0);
+ if (dist_atten > 0.0)
+ {
+ lv = proj_origin-pos.xyz;
+ lv = normalize(lv);
+ float da = dot(norm, lv);
+
+ vec3 col = vec3(0,0,0);
+
+ vec3 diff_tex = texelFetch(diffuseRect, itc, i).rgb;
+
+ float noise = texture2D(noiseMap, frag.xy/128.0).b;
+ if (proj_tc.z > 0.0 &&
+ proj_tc.x < 1.0 &&
+ proj_tc.y < 1.0 &&
+ proj_tc.x > 0.0 &&
+ proj_tc.y > 0.0)
+ {
+ float lit = 0.0;
+ float amb_da = proj_ambiance;
+
+ if (da > 0.0)
+ {
+ float diff = clamp((l_dist-proj_focus)/proj_range, 0.0, 1.0);
+ float lod = diff * proj_lod;
+
+ vec4 plcol = texture2DLodDiffuse(projectionMap, proj_tc.xy, lod);
+
+ vec3 lcol = gl_Color.rgb * plcol.rgb * plcol.a;
+
+ lit = da * dist_atten * noise;
+
+ col = lcol*lit*diff_tex*shadow;
+ amb_da += (da*0.5)*(1.0-shadow)*proj_ambiance;
+ }
+
+ //float diff = clamp((proj_range-proj_focus)/proj_range, 0.0, 1.0);
+ vec4 amb_plcol = texture2DLodAmbient(projectionMap, proj_tc.xy, proj_lod);
+
+ amb_da += (da*da*0.5+0.5)*proj_ambiance;
+
+ amb_da *= dist_atten * noise;
+
+ amb_da = min(amb_da, 1.0-lit);
+
+ col += amb_da*gl_Color.rgb*diff_tex.rgb*amb_plcol.rgb*amb_plcol.a;
+ }
+
+
+ vec4 spec = texelFetch(specularRect, itc, i);
+ if (spec.a > 0.0)
+ {
+ vec3 ref = reflect(normalize(pos), norm);
+
+ //project from point pos in direction ref to plane proj_p, proj_n
+ vec3 pdelta = proj_p-pos;
+ float ds = dot(ref, proj_n);
+
+ if (ds < 0.0)
+ {
+ vec3 pfinal = pos + ref * dot(pdelta, proj_n)/ds;
+
+ vec4 stc = (proj_mat * vec4(pfinal.xyz, 1.0));
+
+ if (stc.z > 0.0)
+ {
+ stc.xy /= stc.w;
+
+ float fatten = clamp(spec.a*spec.a+spec.a*0.5, 0.25, 1.0);
+
+ stc.xy = (stc.xy - vec2(0.5)) * fatten + vec2(0.5);
+
+ if (stc.x < 1.0 &&
+ stc.y < 1.0 &&
+ stc.x > 0.0 &&
+ stc.y > 0.0)
+ {
+ vec4 scol = texture2DLodSpecular(projectionMap, stc.xy, proj_lod-spec.a*proj_lod);
+ col += dist_atten*scol.rgb*gl_Color.rgb*scol.a*spec.rgb*shadow;
+ }
+ }
+ }
+ }
+
+ fcol += col;
+ wght++;
+ }
+ }
+ }
+ }
+
+ if (wght <= 0)
+ {
+ discard;
+ }
+
+ gl_FragColor.rgb = fcol/wght;
+ gl_FragColor.a = 0.0;
+}
diff --git a/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl
index 4369b3b34f..b724c134b9 100644
--- a/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl
+++ b/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl
@@ -45,7 +45,7 @@ uniform float spot_shadow_offset;
vec4 getPosition(vec2 pos_screen)
{
- float depth = texture2DRect(depthMap, pos_screen.xy).a;
+ float depth = texture2DRect(depthMap, pos_screen.xy).r;
vec2 sc = pos_screen.xy*2.0;
sc /= screen_res;
sc -= vec2(1.0,1.0);
diff --git a/indra/newview/app_settings/shaders/class2/deferred/sunLightMSF.glsl b/indra/newview/app_settings/shaders/class2/deferred/sunLightMSF.glsl
new file mode 100644
index 0000000000..dd6fa958c9
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class2/deferred/sunLightMSF.glsl
@@ -0,0 +1,202 @@
+/**
+ * @file sunLightMSF.glsl
+ *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
+ * $/LicenseInfo$
+ */
+
+#version 120
+
+#extension GL_ARB_texture_rectangle : enable
+#extension GL_ARB_texture_multisample : enable
+
+//class 2, shadows, no SSAO
+
+uniform sampler2DMS depthMap;
+uniform sampler2DMS normalMap;
+uniform sampler2DRectShadow shadowMap0;
+uniform sampler2DRectShadow shadowMap1;
+uniform sampler2DRectShadow shadowMap2;
+uniform sampler2DRectShadow shadowMap3;
+uniform sampler2DShadow shadowMap4;
+uniform sampler2DShadow shadowMap5;
+
+
+// Inputs
+uniform mat4 shadow_matrix[6];
+uniform vec4 shadow_clip;
+uniform float ssao_radius;
+uniform float ssao_max_radius;
+uniform float ssao_factor;
+uniform float ssao_factor_inv;
+
+varying vec2 vary_fragcoord;
+varying vec4 vary_light;
+
+uniform mat4 inv_proj;
+uniform vec2 screen_res;
+uniform vec2 shadow_res;
+uniform vec2 proj_shadow_res;
+
+uniform float shadow_bias;
+uniform float shadow_offset;
+
+uniform float spot_shadow_bias;
+uniform float spot_shadow_offset;
+
+vec4 getPosition(ivec2 pos_screen, int sample)
+{
+ float depth = texelFetch(depthMap, pos_screen.xy, sample).r;
+ vec2 sc = vec2(pos_screen.xy)*2.0;
+ sc /= screen_res;
+ sc -= vec2(1.0,1.0);
+ vec4 ndc = vec4(sc.x, sc.y, 2.0*depth-1.0, 1.0);
+ vec4 pos = inv_proj * ndc;
+ pos /= pos.w;
+ pos.w = 1.0;
+ return pos;
+}
+
+float pcfShadow(sampler2DRectShadow shadowMap, vec4 stc, float scl)
+{
+ stc.xyz /= stc.w;
+ stc.z += shadow_bias*scl;
+
+ float cs = shadow2DRect(shadowMap, stc.xyz).x;
+ float shadow = cs;
+
+ shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(1.5, 1.5, 0.0)).x, cs);
+ shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(1.5, -1.5, 0.0)).x, cs);
+ shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(-1.5, 1.5, 0.0)).x, cs);
+ shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(-1.5, -1.5, 0.0)).x, cs);
+
+ return shadow/5.0;
+
+ //return shadow;
+}
+
+float pcfShadow(sampler2DShadow shadowMap, vec4 stc, float scl)
+{
+ stc.xyz /= stc.w;
+ stc.z += spot_shadow_bias*scl;
+
+ float cs = shadow2D(shadowMap, stc.xyz).x;
+ float shadow = cs;
+
+ vec2 off = 1.5/proj_shadow_res;
+
+ shadow += max(shadow2D(shadowMap, stc.xyz+vec3(off.x, off.y, 0.0)).x, cs);
+ shadow += max(shadow2D(shadowMap, stc.xyz+vec3(off.x, -off.y, 0.0)).x, cs);
+ shadow += max(shadow2D(shadowMap, stc.xyz+vec3(-off.x, off.y, 0.0)).x, cs);
+ shadow += max(shadow2D(shadowMap, stc.xyz+vec3(-off.x, -off.y, 0.0)).x, cs);
+
+ return shadow/5.0;
+
+ //return shadow;
+}
+
+void main()
+{
+ vec2 pos_screen = vary_fragcoord.xy;
+ ivec2 itc = ivec2(pos_screen);
+
+ //try doing an unproject here
+
+ vec4 fcol = vec4(0,0,0,0);
+
+ for (int i = 0; i < samples; i++)
+ {
+ vec4 pos = getPosition(itc, i);
+
+ vec4 nmap4 = texelFetch(normalMap, itc, i);
+ nmap4 = vec4((nmap4.xy-0.5)*2.0,nmap4.z,nmap4.w); // unpack norm
+ float displace = nmap4.w;
+ vec3 norm = nmap4.xyz;
+
+ /*if (pos.z == 0.0) // do nothing for sky *FIX: REMOVE THIS IF/WHEN THE POSITION MAP IS BEING USED AS A STENCIL
+ {
+ gl_FragColor = vec4(0.0); // doesn't matter
+ return;
+ }*/
+
+ float shadow = 1.0;
+ float dp_directional_light = max(0.0, dot(norm, vary_light.xyz));
+
+ vec3 shadow_pos = pos.xyz + displace*norm;
+ vec3 offset = vary_light.xyz * (1.0-dp_directional_light);
+
+ vec4 spos = vec4(shadow_pos+offset*shadow_offset, 1.0);
+
+ if (spos.z > -shadow_clip.w)
+ {
+ if (dp_directional_light == 0.0)
+ {
+ // if we know this point is facing away from the sun then we know it's in shadow without having to do a squirrelly shadow-map lookup
+ shadow = 0.0;
+ }
+ else
+ {
+ vec4 lpos;
+
+ if (spos.z < -shadow_clip.z)
+ {
+ lpos = shadow_matrix[3]*spos;
+ lpos.xy *= shadow_res;
+ shadow = pcfShadow(shadowMap3, lpos, 0.25);
+ shadow += max((pos.z+shadow_clip.z)/(shadow_clip.z-shadow_clip.w)*2.0-1.0, 0.0);
+ }
+ else if (spos.z < -shadow_clip.y)
+ {
+ lpos = shadow_matrix[2]*spos;
+ lpos.xy *= shadow_res;
+ shadow = pcfShadow(shadowMap2, lpos, 0.5);
+ }
+ else if (spos.z < -shadow_clip.x)
+ {
+ lpos = shadow_matrix[1]*spos;
+ lpos.xy *= shadow_res;
+ shadow = pcfShadow(shadowMap1, lpos, 0.75);
+ }
+ else
+ {
+ lpos = shadow_matrix[0]*spos;
+ lpos.xy *= shadow_res;
+ shadow = pcfShadow(shadowMap0, lpos, 1.0);
+ }
+
+ // take the most-shadowed value out of these two:
+ // * the blurred sun shadow in the light (shadow) map
+ // * an unblurred dot product between the sun and this norm
+ // the goal is to err on the side of most-shadow to fill-in shadow holes and reduce artifacting
+ shadow = min(shadow, dp_directional_light);
+
+ //lpos.xy /= lpos.w*32.0;
+ //if (fract(lpos.x) < 0.1 || fract(lpos.y) < 0.1)
+ //{
+ // shadow = 0.0;
+ //}
+
+ }
+ }
+ else
+ {
+ // more distant than the shadow map covers
+ shadow = 1.0;
+ }
+
+ fcol[0] += shadow;
+ fcol[1] += 1.0;
+
+ spos = vec4(shadow_pos+norm*spot_shadow_offset, 1.0);
+
+ //spotlight shadow 1
+ vec4 lpos = shadow_matrix[4]*spos;
+ fcol[2] += pcfShadow(shadowMap4, lpos, 0.8);
+
+ //spotlight shadow 2
+ lpos = shadow_matrix[5]*spos;
+ fcol[3] += pcfShadow(shadowMap5, lpos, 0.8);
+ }
+
+ gl_FragColor = fcol/samples;
+}
diff --git a/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl b/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl
index 847b36b1ac..68e18162f6 100644
--- a/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl
+++ b/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl
@@ -45,7 +45,7 @@ uniform float spot_shadow_offset;
vec4 getPosition(vec2 pos_screen)
{
- float depth = texture2DRect(depthMap, pos_screen.xy).a;
+ float depth = texture2DRect(depthMap, pos_screen.xy).r;
vec2 sc = pos_screen.xy*2.0;
sc /= screen_res;
sc -= vec2(1.0,1.0);
diff --git a/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOMSF.glsl b/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOMSF.glsl
new file mode 100644
index 0000000000..d28741f945
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOMSF.glsl
@@ -0,0 +1,241 @@
+/**
+ * @file sunLightSSAOF.glsl
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#version 120
+
+#extension GL_ARB_texture_rectangle : enable
+#extension GL_ARB_texture_multisample : enable
+
+//class 2 -- shadows and SSAO
+
+uniform sampler2DMS depthMap;
+uniform sampler2DMS normalMap;
+uniform sampler2DRectShadow shadowMap0;
+uniform sampler2DRectShadow shadowMap1;
+uniform sampler2DRectShadow shadowMap2;
+uniform sampler2DRectShadow shadowMap3;
+uniform sampler2DShadow shadowMap4;
+uniform sampler2DShadow shadowMap5;
+uniform sampler2D noiseMap;
+
+// Inputs
+uniform mat4 shadow_matrix[6];
+uniform vec4 shadow_clip;
+uniform float ssao_radius;
+uniform float ssao_max_radius;
+uniform float ssao_factor;
+uniform float ssao_factor_inv;
+
+varying vec2 vary_fragcoord;
+varying vec4 vary_light;
+
+uniform mat4 inv_proj;
+uniform vec2 screen_res;
+uniform vec2 shadow_res;
+uniform vec2 proj_shadow_res;
+
+uniform float shadow_bias;
+uniform float shadow_offset;
+
+uniform float spot_shadow_bias;
+uniform float spot_shadow_offset;
+
+vec4 getPosition(ivec2 pos_screen, int sample)
+{
+ float depth = texelFetch(depthMap, pos_screen, sample).r;
+ vec2 sc = vec2(pos_screen.xy)*2.0;
+ sc /= screen_res;
+ sc -= vec2(1.0,1.0);
+ vec4 ndc = vec4(sc.x, sc.y, 2.0*depth-1.0, 1.0);
+ vec4 pos = inv_proj * ndc;
+ pos /= pos.w;
+ pos.w = 1.0;
+ return pos;
+}
+
+//calculate decreases in ambient lighting when crowded out (SSAO)
+float calcAmbientOcclusion(vec4 pos, vec3 norm, int sample)
+{
+ float ret = 1.0;
+
+ vec2 kern[8];
+ // exponentially (^2) distant occlusion samples spread around origin
+ kern[0] = vec2(-1.0, 0.0) * 0.125*0.125;
+ kern[1] = vec2(1.0, 0.0) * 0.250*0.250;
+ kern[2] = vec2(0.0, 1.0) * 0.375*0.375;
+ kern[3] = vec2(0.0, -1.0) * 0.500*0.500;
+ kern[4] = vec2(0.7071, 0.7071) * 0.625*0.625;
+ kern[5] = vec2(-0.7071, -0.7071) * 0.750*0.750;
+ kern[6] = vec2(-0.7071, 0.7071) * 0.875*0.875;
+ kern[7] = vec2(0.7071, -0.7071) * 1.000*1.000;
+
+ vec2 pos_screen = vary_fragcoord.xy;
+ vec3 pos_world = pos.xyz;
+ vec2 noise_reflect = texture2D(noiseMap, vary_fragcoord.xy/128.0).xy;
+
+ float angle_hidden = 0.0;
+ int points = 0;
+
+ float scale = min(ssao_radius / -pos_world.z, ssao_max_radius);
+
+ // it was found that keeping # of samples a constant was the fastest, probably due to compiler optimizations (unrolling?)
+ for (int i = 0; i < 8; i++)
+ {
+ ivec2 samppos_screen = ivec2(pos_screen + scale * reflect(kern[i], noise_reflect));
+ vec3 samppos_world = getPosition(samppos_screen, sample).xyz;
+
+ vec3 diff = pos_world - samppos_world;
+ float dist2 = dot(diff, diff);
+
+ // assume each sample corresponds to an occluding sphere with constant radius, constant x-sectional area
+ // --> solid angle shrinking by the square of distance
+ //radius is somewhat arbitrary, can approx with just some constant k * 1 / dist^2
+ //(k should vary inversely with # of samples, but this is taken care of later)
+
+ angle_hidden = angle_hidden + float(dot((samppos_world - 0.05*norm - pos_world), norm) > 0.0) * min(1.0/dist2, ssao_factor_inv);
+
+ // 'blocked' samples (significantly closer to camera relative to pos_world) are "no data", not "no occlusion"
+ points = points + int(diff.z > -1.0);
+ }
+
+ angle_hidden = min(ssao_factor*angle_hidden/float(points), 1.0);
+
+ ret = (1.0 - (float(points != 0) * angle_hidden));
+
+ return min(ret, 1.0);
+}
+
+float pcfShadow(sampler2DRectShadow shadowMap, vec4 stc, float scl)
+{
+ stc.xyz /= stc.w;
+ stc.z += shadow_bias*scl;
+
+ float cs = shadow2DRect(shadowMap, stc.xyz).x;
+ float shadow = cs;
+
+ shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(1.5, 1.5, 0.0)).x, cs);
+ shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(1.5, -1.5, 0.0)).x, cs);
+ shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(-1.5, 1.5, 0.0)).x, cs);
+ shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(-1.5, -1.5, 0.0)).x, cs);
+
+ return shadow/5.0;
+
+ //return shadow;
+}
+
+float pcfShadow(sampler2DShadow shadowMap, vec4 stc, float scl)
+{
+ stc.xyz /= stc.w;
+ stc.z += spot_shadow_bias*scl;
+
+ float cs = shadow2D(shadowMap, stc.xyz).x;
+ float shadow = cs;
+
+ vec2 off = 1.5/proj_shadow_res;
+
+ shadow += max(shadow2D(shadowMap, stc.xyz+vec3(off.x, off.y, 0.0)).x, cs);
+ shadow += max(shadow2D(shadowMap, stc.xyz+vec3(off.x, -off.y, 0.0)).x, cs);
+ shadow += max(shadow2D(shadowMap, stc.xyz+vec3(-off.x, off.y, 0.0)).x, cs);
+ shadow += max(shadow2D(shadowMap, stc.xyz+vec3(-off.x, -off.y, 0.0)).x, cs);
+
+
+ return shadow/5.0;
+
+ //return shadow;
+}
+
+void main()
+{
+ vec2 pos_screen = vary_fragcoord.xy;
+ ivec2 itc = ivec2(pos_screen);
+ vec4 fcol = vec4(0,0,0,0);
+
+ for (int i = 0; i < samples; i++)
+ {
+ vec4 pos = getPosition(itc, i);
+
+ vec4 nmap4 = texelFetch(normalMap, itc, i);
+ nmap4 = vec4((nmap4.xy-0.5)*2.0,nmap4.z,nmap4.w); // unpack norm
+ float displace = nmap4.w;
+ vec3 norm = nmap4.xyz;
+
+ float shadow = 1.0;
+ float dp_directional_light = max(0.0, dot(norm, vary_light.xyz));
+
+ vec3 shadow_pos = pos.xyz + displace*norm;
+ vec3 offset = vary_light.xyz * (1.0-dp_directional_light);
+
+ vec4 spos = vec4(shadow_pos+offset*shadow_offset, 1.0);
+
+ if (spos.z > -shadow_clip.w)
+ {
+ if (dp_directional_light == 0.0)
+ {
+ // if we know this point is facing away from the sun then we know it's in shadow without having to do a squirrelly shadow-map lookup
+ shadow = 0.0;
+ }
+ else
+ {
+ vec4 lpos;
+
+ if (spos.z < -shadow_clip.z)
+ {
+ lpos = shadow_matrix[3]*spos;
+ lpos.xy *= shadow_res;
+ shadow = pcfShadow(shadowMap3, lpos, 0.25);
+ shadow += max((pos.z+shadow_clip.z)/(shadow_clip.z-shadow_clip.w)*2.0-1.0, 0.0);
+ }
+ else if (spos.z < -shadow_clip.y)
+ {
+ lpos = shadow_matrix[2]*spos;
+ lpos.xy *= shadow_res;
+ shadow = pcfShadow(shadowMap2, lpos, 0.5);
+ }
+ else if (spos.z < -shadow_clip.x)
+ {
+ lpos = shadow_matrix[1]*spos;
+ lpos.xy *= shadow_res;
+ shadow = pcfShadow(shadowMap1, lpos, 0.75);
+ }
+ else
+ {
+ lpos = shadow_matrix[0]*spos;
+ lpos.xy *= shadow_res;
+ shadow = pcfShadow(shadowMap0, lpos, 1.0);
+ }
+
+ // take the most-shadowed value out of these two:
+ // * the blurred sun shadow in the light (shadow) map
+ // * an unblurred dot product between the sun and this norm
+ // the goal is to err on the side of most-shadow to fill-in shadow holes and reduce artifacting
+ shadow = min(shadow, dp_directional_light);
+
+ }
+ }
+ else
+ {
+ // more distant than the shadow map covers
+ shadow = 1.0;
+ }
+
+
+ fcol[0] += shadow;
+ fcol[1] += calcAmbientOcclusion(pos, norm, i);
+
+ spos.xyz = shadow_pos+offset*spot_shadow_offset;
+
+ //spotlight shadow 1
+ vec4 lpos = shadow_matrix[4]*spos;
+ fcol[2] += pcfShadow(shadowMap4, lpos, 0.8);
+
+ //spotlight shadow 2
+ lpos = shadow_matrix[5]*spos;
+ fcol[3] += pcfShadow(shadowMap5, lpos, 0.8);
+ }
+
+ gl_FragColor = fcol / samples;
+}
diff --git a/indra/newview/featuretable.txt b/indra/newview/featuretable.txt
index dd8a88e558..25dd2ffe78 100644
--- a/indra/newview/featuretable.txt
+++ b/indra/newview/featuretable.txt
@@ -467,7 +467,6 @@ RenderAvatarCloth 0 0
list ATI
RenderUseStreamVBO 1 0
-RenderAvatarVP 1 0
// Disable vertex buffer objects by default for ATI cards with little video memory
list ATIVramLT256
diff --git a/indra/newview/featuretable_mac.txt b/indra/newview/featuretable_mac.txt
index c075c660f3..8b8340bd71 100644
--- a/indra/newview/featuretable_mac.txt
+++ b/indra/newview/featuretable_mac.txt
@@ -1,4 +1,4 @@
-version 23
+version 24
// NOTE: This is mostly identical to featuretable_mac.txt with a few differences
// Should be combined into one table
@@ -281,6 +281,9 @@ RenderVBOEnable 1 0
list TexUnit8orLess
RenderDeferredSSAO 0 0
+list ATI
+RenderDeferredSSAO 0 0
+
list Intel
RenderAnisotropic 1 0
RenderLocalLights 1 0
@@ -409,31 +412,6 @@ Disregard128DefaultDrawDistance 1 0
list ATI_Mobility_Radeon_X1xxx
Disregard128DefaultDrawDistance 1 0
-
-
-
-// Avatar hardware skinning causes
-// invisible avatars on HD 2600... so I masked
-// out other possible bad ones till it's fixed
-
-list ATI_Radeon_HD_2300
-RenderAvatarVP 0 0
-RenderAvatarCloth 0 0
-Disregard128DefaultDrawDistance 1 0
-list ATI_Radeon_HD_2400
-RenderAvatarVP 0 0
-RenderAvatarCloth 0 0
-Disregard128DefaultDrawDistance 1 0
-list ATI_Radeon_HD_2600
-RenderAvatarVP 0 0
-RenderAvatarCloth 0 0
-list ATI_Radeon_HD_2900
-RenderAvatarVP 0 0
-RenderAvatarCloth 0 0
-list ATI_Radeon_HD_3800
-RenderAvatarVP 0 0
-RenderAvatarCloth 0 0
-
/// Tweaked NVIDIA
list NVIDIA_GeForce_FX_5100
diff --git a/indra/newview/featuretable_xp.txt b/indra/newview/featuretable_xp.txt
index 3339172a1a..65a6f59bbe 100644
--- a/indra/newview/featuretable_xp.txt
+++ b/indra/newview/featuretable_xp.txt
@@ -466,7 +466,6 @@ RenderAvatarCloth 0 0
list ATI
RenderUseStreamVBO 1 0
-RenderAvatarVP 1 0
// Disable vertex buffer objects by default for ATI cards with little video memory
list ATIVramLT256
diff --git a/indra/newview/llaccountingquotamanager.cpp b/indra/newview/llaccountingquotamanager.cpp
new file mode 100644
index 0000000000..9b77ada8b7
--- /dev/null
+++ b/indra/newview/llaccountingquotamanager.cpp
@@ -0,0 +1,284 @@
+/**
+ * @file LLAccountingQuotaManager.cpp
+ * @ Handles the setting and accessing for costs associated with mesh
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ *
+ * Copyright (c) 2001-2010, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llaccountingquotamanager.h"
+#include "llagent.h"
+#include "llviewerregion.h"
+#include "llviewerobject.h"
+#include "llviewerobjectlist.h"
+#include "llviewerparcelmgr.h"
+#include "llparcel.h"
+
+//===============================================================================
+LLAccountingQuotaManager::LLAccountingQuotaManager()
+{
+}
+//===============================================================================
+class LLAccountingQuotaResponder : public LLCurl::Responder
+{
+public:
+ LLAccountingQuotaResponder( const LLSD& objectIDs )
+ : mObjectIDs( objectIDs )
+ {
+ }
+
+ void clearPendingRequests ( void )
+ {
+ for ( LLSD::array_iterator iter = mObjectIDs.beginArray(); iter != mObjectIDs.endArray(); ++iter )
+ {
+ LLAccountingQuotaManager::getInstance()->removePendingObjectQuota( iter->asUUID() );
+ }
+ }
+
+ void error( U32 statusNum, const std::string& reason )
+ {
+ llwarns << "Transport error "<<reason<<llendl;
+ //prep#do we really want to remove all because of one failure - verify
+ clearPendingRequests();
+ }
+
+ void result( const LLSD& content )
+ {
+ if ( !content.isMap() || content.has("error") )
+ {
+ llwarns << "Error on fetched data"<< llendl;
+ //prep#do we really want to remove all because of one failure - verify
+ clearPendingRequests();
+ return;
+ }
+
+ //Differentiate what the incoming caps could be from the data
+ bool containsParcel = content.has("parcel");
+ bool containsSelection = content.has("selected");
+
+ //Loop over the stored object ids checking against the incoming data
+ for ( LLSD::array_iterator iter = mObjectIDs.beginArray(); iter != mObjectIDs.endArray(); ++iter )
+ {
+ LLUUID objectID = iter->asUUID();
+
+ LLAccountingQuotaManager::getInstance()->removePendingObjectQuota( objectID );
+
+ if ( containsParcel )
+ {
+ //Typically should be one
+ S32 dataCount = content["parcel"].size();
+ for(S32 i = 0; i < dataCount; i++)
+ {
+ //prep#todo verify that this is safe, otherwise just add a bool
+ LLUUID parcelId;
+ //S32 parcelOwner = 0;
+ if ( content["parcel"][i].has("parcel_id") )
+ {
+ parcelId = content["parcel"][i]["parcel_id"].asUUID();
+ }
+
+ //if ( content["parcel"][i].has("parcel_owner") )
+ //{
+ // parcelOwner = content["parcel"][i]["parcel_owner"].asInteger();
+ //}
+
+ F32 ownerRenderCost = 0;
+ F32 ownerPhysicsCost = 0;
+ F32 ownerNetworkCost = 0;
+ F32 ownerSimulationCost = 0;
+
+ F32 groupRenderCost = 0;
+ F32 groupPhysicsCost = 0;
+ F32 groupNetworkCost = 0;
+ F32 groupSimulationCost = 0;
+
+ F32 otherRenderCost = 0;
+ F32 otherPhysicsCost = 0;
+ F32 otherNetworkCost = 0;
+ F32 otherSimulationCost = 0;
+
+ F32 tempRenderCost = 0;
+ F32 tempPhysicsCost = 0;
+ F32 tempNetworkCost = 0;
+ F32 tempSimulationCost = 0;
+
+ F32 selectedRenderCost = 0;
+ F32 selectedPhysicsCost = 0;
+ F32 selectedNetworkCost = 0;
+ F32 selectedSimulationCost = 0;
+
+ F32 parcelCapacity = 0;
+
+ if ( content["parcel"][i].has("capacity") )
+ {
+ parcelCapacity = content["parcel"][i].has("capacity");
+ }
+
+ if ( content["parcel"][i].has("owner") )
+ {
+ ownerRenderCost = content["parcel"][i]["owner"]["rendering"].asReal();
+ ownerPhysicsCost = content["parcel"][i]["owner"]["physics"].asReal();
+ ownerNetworkCost = content["parcel"][i]["owner"]["streaming"].asReal();
+ ownerSimulationCost = content["parcel"][i]["owner"]["simulation"].asReal();
+ }
+
+ if ( content["parcel"][i].has("group") )
+ {
+ groupRenderCost = content["parcel"][i]["group"]["rendering"].asReal();
+ groupPhysicsCost = content["parcel"][i]["group"]["physics"].asReal();
+ groupNetworkCost = content["parcel"][i]["group"]["streaming"].asReal();
+ groupSimulationCost = content["parcel"][i]["group"]["simulation"].asReal();
+
+ }
+ if ( content["parcel"][i].has("other") )
+ {
+ otherRenderCost = content["parcel"][i]["other"]["rendering"].asReal();
+ otherPhysicsCost = content["parcel"][i]["other"]["physics"].asReal();
+ otherNetworkCost = content["parcel"][i]["other"]["streaming"].asReal();
+ otherSimulationCost = content["parcel"][i]["other"]["simulation"].asReal();
+ }
+
+ if ( content["parcel"][i].has("temp") )
+ {
+ tempRenderCost = content["parcel"][i]["total"]["rendering"].asReal();
+ tempPhysicsCost = content["parcel"][i]["total"]["physics"].asReal();
+ tempNetworkCost = content["parcel"][i]["total"]["streaming"].asReal();
+ tempSimulationCost = content["parcel"][i]["total"]["simulation"].asReal();
+ }
+
+ if ( content["parcel"][i].has("selected") )
+ {
+ selectedRenderCost = content["parcel"][i]["total"]["rendering"].asReal();
+ selectedPhysicsCost = content["parcel"][i]["total"]["physics"].asReal();
+ selectedNetworkCost = content["parcel"][i]["total"]["streaming"].asReal();
+ selectedSimulationCost = content["parcel"][i]["total"]["simulation"].asReal();
+ }
+
+ ParcelQuota parcelQuota( ownerRenderCost, ownerPhysicsCost, ownerNetworkCost, ownerSimulationCost,
+ groupRenderCost, groupPhysicsCost, groupNetworkCost, groupSimulationCost,
+ otherRenderCost, otherPhysicsCost, otherNetworkCost, otherSimulationCost,
+ tempRenderCost, tempPhysicsCost, tempNetworkCost, tempSimulationCost,
+ selectedRenderCost, selectedPhysicsCost, selectedNetworkCost, selectedSimulationCost,
+ parcelCapacity );
+ //Update the Parcel
+ LLParcel* pParcel = LLViewerParcelMgr::getInstance()->getParcelSelection()->getParcel();
+ if ( pParcel )
+ {
+ pParcel->updateQuota( objectID, parcelQuota );
+ }
+ }
+ }
+ else
+ if ( containsSelection )
+ {
+ S32 dataCount = content["selected"].size();
+ for(S32 i = 0; i < dataCount; i++)
+ {
+
+ F32 renderCost = 0;
+ F32 physicsCost = 0;
+ F32 networkCost = 0;
+ F32 simulationCost = 0;
+
+ LLUUID objectId;
+
+ objectId = content["selected"][i]["local_id"].asUUID();
+ renderCost = content["selected"][i]["rendering"].asReal();
+ physicsCost = content["selected"][i]["physics"].asReal();
+ networkCost = content["selected"][i]["streaming"].asReal();
+ simulationCost = content["selected"][i]["simulation"].asReal();
+
+ SelectionQuota selectionQuota( objectId, renderCost, physicsCost, networkCost, simulationCost );
+
+ //Update the objects
+ gObjectList.updateQuota( objectId, selectionQuota );
+
+ }
+ }
+ else
+ {
+ //Nothing in string
+ LLAccountingQuotaManager::getInstance()->removePendingObjectQuota( objectID );
+ }
+ }
+ }
+
+private:
+ //List of posted objects
+ LLSD mObjectIDs;
+};
+//===============================================================================
+void LLAccountingQuotaManager::fetchQuotas( const std::string& url )
+{
+ // Invoking system must have already determined capability availability
+ if ( !url.empty() )
+ {
+ LLSD objectList;
+ U32 objectIndex = 0;
+ IDIt IDIter = mUpdateObjectQuota.begin();
+ IDIt IDIterEnd = mUpdateObjectQuota.end();
+
+ for ( ; IDIter != IDIterEnd; ++IDIter )
+ {
+ // Check to see if a request for this object has already been made.
+ if ( mPendingObjectQuota.find( *IDIter ) == mPendingObjectQuota.end() )
+ {
+ mPendingObjectQuota.insert( *IDIter );
+ objectList[objectIndex++] = *IDIter;
+ }
+ }
+
+ mUpdateObjectQuota.clear();
+
+ //Post results
+ if ( objectList.size() > 0 )
+ {
+ LLSD dataToPost = LLSD::emptyMap();
+ dataToPost["object_ids"] = objectList;
+ LLHTTPClient::post( url, dataToPost, new LLAccountingQuotaResponder( objectList ));
+ }
+ }
+ else
+ {
+ //url was empty - warn & continue
+ llwarns<<"Supplied url is empty "<<llendl;
+ mUpdateObjectQuota.clear();
+ mPendingObjectQuota.clear();
+ }
+}
+//===============================================================================
+void LLAccountingQuotaManager::updateObjectCost( const LLUUID& objectID )
+{
+ mUpdateObjectQuota.insert( objectID );
+}
+//===============================================================================
+void LLAccountingQuotaManager::removePendingObjectQuota( const LLUUID& objectID )
+{
+ mPendingObjectQuota.erase( objectID );
+}
+//===============================================================================
diff --git a/indra/newview/llaccountingquotamanager.h b/indra/newview/llaccountingquotamanager.h
new file mode 100755
index 0000000000..f605d1e6b2
--- /dev/null
+++ b/indra/newview/llaccountingquotamanager.h
@@ -0,0 +1,60 @@
+/**
+ * @file lllAccountingQuotaManager.h
+ * @
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ *
+ * Copyright (c) 2001-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_ACCOUNTINGQUOTAMANAGER_H
+#define LL_ACCOUNTINGQUOTAMANAGER_H
+//===============================================================================
+#include "llaccountingquota.h"
+//===============================================================================
+class LLAccountingQuotaManager : public LLSingleton<LLAccountingQuotaManager>
+{
+public:
+ //Ctor
+ LLAccountingQuotaManager();
+ //Store an object that will be eventually fetched
+ void updateObjectCost( const LLUUID& objectID );
+ //Request quotas for object list
+ void fetchQuotas( const std::string& url );
+ //Delete a specific object from the pending list
+ void removePendingObjectQuota( const LLUUID& objectID );
+
+private:
+ //Set of objects that need to update their cost
+ std::set<LLUUID> mUpdateObjectQuota;
+ //During fetchQuota we move object into a the pending set to signify that
+ //a fetch has been instigated.
+ std::set<LLUUID> mPendingObjectQuota;
+ typedef std::set<LLUUID>::iterator IDIt;
+};
+//===============================================================================
+
+#endif
diff --git a/indra/newview/llagentcamera.cpp b/indra/newview/llagentcamera.cpp
index c6b5a0113f..80085dad9d 100644
--- a/indra/newview/llagentcamera.cpp
+++ b/indra/newview/llagentcamera.cpp
@@ -394,7 +394,9 @@ LLVector3 LLAgentCamera::calcFocusOffset(LLViewerObject *object, LLVector3 origi
LLQuaternion inv_obj_rot = ~obj_rot; // get inverse of rotation
LLVector3 object_extents;
const LLVector4a* oe4 = object->mDrawable->getSpatialExtents();
- object_extents.set( oe4[1][0], oe4[1][1], oe4[1][2] );
+ LLVector4a size;
+ size.setSub(oe4[1], oe4[0]);
+ object_extents.set( size[0], size[1], size[2] );
// make sure they object extents are non-zero
object_extents.clamp(0.001f, F32_MAX);
diff --git a/indra/newview/lldrawable.cpp b/indra/newview/lldrawable.cpp
index bdc12ec0e3..ad3710843c 100644
--- a/indra/newview/lldrawable.cpp
+++ b/indra/newview/lldrawable.cpp
@@ -94,7 +94,9 @@ void LLDrawable::init()
mRenderType = 0;
mCurrentScale = LLVector3(1,1,1);
mDistanceWRTCamera = 0.0f;
-
+ mPositionGroup.clear();
+ mExtents[0].clear();
+ mExtents[1].clear();
mQuietCount = 0;
mState = 0;
@@ -587,7 +589,10 @@ void LLDrawable::setRadius(F32 radius)
void LLDrawable::moveUpdatePipeline(BOOL moved)
{
- makeActive();
+ if (moved)
+ {
+ makeActive();
+ }
// Update the face centers.
for (S32 i = 0; i < getNumFaces(); i++)
@@ -695,7 +700,8 @@ void LLDrawable::updateDistance(LLCamera& camera, bool force_update)
{
if (LLViewerCamera::sCurCameraID != LLViewerCamera::CAMERA_WORLD)
{
- llerrs << "WTF?" << llendl;
+ llwarns << "Attempted to update distance for non-world camera." << llendl;
+ return;
}
//switch LOD with the spatial group to avoid artifacts
diff --git a/indra/newview/lldrawable.h b/indra/newview/lldrawable.h
index 9ebe1a45b4..e268640a21 100644
--- a/indra/newview/lldrawable.h
+++ b/indra/newview/lldrawable.h
@@ -276,6 +276,7 @@ public:
REBUILD_SHADOW = 0x02000000,
HAS_ALPHA = 0x04000000,
RIGGED = 0x08000000,
+ PARTITION_MOVE = 0x10000000,
} EDrawableFlags;
private: //aligned members
diff --git a/indra/newview/lldrawpoolwlsky.cpp b/indra/newview/lldrawpoolwlsky.cpp
index 696c2d1abd..409b18d522 100644
--- a/indra/newview/lldrawpoolwlsky.cpp
+++ b/indra/newview/lldrawpoolwlsky.cpp
@@ -44,6 +44,8 @@ LLPointer<LLViewerTexture> LLDrawPoolWLSky::sCloudNoiseTexture = NULL;
LLPointer<LLImageRaw> LLDrawPoolWLSky::sCloudNoiseRawImage = NULL;
+static LLGLSLShader* cloud_shader = NULL;
+static LLGLSLShader* sky_shader = NULL;
LLDrawPoolWLSky::LLDrawPoolWLSky(void) :
@@ -83,12 +85,32 @@ LLViewerTexture *LLDrawPoolWLSky::getDebugTexture()
void LLDrawPoolWLSky::beginRenderPass( S32 pass )
{
+ sky_shader =
+ LLPipeline::sUnderWaterRender ?
+ &gObjectSimpleWaterProgram :
+ &gWLSkyProgram;
+
+ cloud_shader =
+ LLPipeline::sUnderWaterRender ?
+ &gObjectSimpleWaterProgram :
+ &gWLCloudProgram;
}
void LLDrawPoolWLSky::endRenderPass( S32 pass )
{
}
+void LLDrawPoolWLSky::beginDeferredPass(S32 pass)
+{
+ sky_shader = &gDeferredWLSkyProgram;
+ cloud_shader = &gDeferredWLCloudProgram;
+}
+
+void LLDrawPoolWLSky::endDeferredPass(S32 pass)
+{
+
+}
+
void LLDrawPoolWLSky::renderDome(F32 camHeightLocal, LLGLSLShader * shader) const
{
LLVector3 const & origin = LLViewerCamera::getInstance()->getOrigin();
@@ -128,19 +150,14 @@ void LLDrawPoolWLSky::renderSkyHaze(F32 camHeightLocal) const
{
if (gPipeline.canUseWindLightShaders() && gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_SKY))
{
- LLGLSLShader* shader =
- LLPipeline::sUnderWaterRender ?
- &gObjectSimpleWaterProgram :
- &gWLSkyProgram;
-
LLGLDisable blend(GL_BLEND);
- shader->bind();
+ sky_shader->bind();
/// Render the skydome
- renderDome(camHeightLocal, shader);
+ renderDome(camHeightLocal, sky_shader);
- shader->unbind();
+ sky_shader->unbind();
}
}
@@ -186,23 +203,18 @@ void LLDrawPoolWLSky::renderSkyClouds(F32 camHeightLocal) const
{
if (gPipeline.canUseWindLightShaders() && gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_CLOUDS))
{
- LLGLSLShader* shader =
- LLPipeline::sUnderWaterRender ?
- &gObjectSimpleWaterProgram :
- &gWLCloudProgram;
-
LLGLEnable blend(GL_BLEND);
gGL.setSceneBlendType(LLRender::BT_ALPHA);
gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT);
gGL.getTexUnit(0)->bind(sCloudNoiseTexture);
- shader->bind();
+ cloud_shader->bind();
/// Render the skydome
- renderDome(camHeightLocal, shader);
+ renderDome(camHeightLocal, cloud_shader);
- shader->unbind();
+ cloud_shader->unbind();
}
}
@@ -246,6 +258,53 @@ void LLDrawPoolWLSky::renderHeavenlyBodies()
}
}
+void LLDrawPoolWLSky::renderDeferred(S32 pass)
+{
+ if (!gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_SKY))
+ {
+ return;
+ }
+ LLFastTimer ftm(FTM_RENDER_WL_SKY);
+
+ const F32 camHeightLocal = LLWLParamManager::instance()->getDomeOffset() * LLWLParamManager::instance()->getDomeRadius();
+
+ LLGLSNoFog disableFog;
+ LLGLDepthTest depth(GL_TRUE, GL_FALSE);
+ LLGLDisable clip(GL_CLIP_PLANE0);
+
+ gGL.setColorMask(true, false);
+
+ LLGLSquashToFarClip far_clip(glh_get_current_projection());
+
+ renderSkyHaze(camHeightLocal);
+
+ LLVector3 const & origin = LLViewerCamera::getInstance()->getOrigin();
+ glPushMatrix();
+
+
+ glTranslatef(origin.mV[0], origin.mV[1], origin.mV[2]);
+
+ gDeferredStarProgram.bind();
+ // *NOTE: have to bind a texture here since register combiners blending in
+ // renderStars() requires something to be bound and we might as well only
+ // bind the moon's texture once.
+ gGL.getTexUnit(0)->bind(gSky.mVOSkyp->mFace[LLVOSky::FACE_MOON]->getTexture());
+
+ renderHeavenlyBodies();
+
+ renderStars();
+
+ gDeferredStarProgram.unbind();
+
+ glPopMatrix();
+
+ renderSkyClouds(camHeightLocal);
+
+ gGL.setColorMask(true, true);
+ //gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+
+}
+
void LLDrawPoolWLSky::render(S32 pass)
{
if (!gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_SKY))
diff --git a/indra/newview/lldrawpoolwlsky.h b/indra/newview/lldrawpoolwlsky.h
index 8ca1ebb942..cd15c991ee 100644
--- a/indra/newview/lldrawpoolwlsky.h
+++ b/indra/newview/lldrawpoolwlsky.h
@@ -44,10 +44,10 @@ public:
/*virtual*/ BOOL isDead() { return FALSE; }
- /*virtual*/ S32 getNumPostDeferredPasses() { return getNumPasses(); }
- /*virtual*/ void beginPostDeferredPass(S32 pass) { beginRenderPass(pass); }
- /*virtual*/ void endPostDeferredPass(S32 pass) { endRenderPass(pass); }
- /*virtual*/ void renderPostDeferred(S32 pass) { render(pass); }
+ /*virtual*/ S32 getNumDeferredPasses() { return 1; }
+ /*virtual*/ void beginDeferredPass(S32 pass);
+ /*virtual*/ void endDeferredPass(S32 pass);
+ /*virtual*/ void renderDeferred(S32 pass);
/*virtual*/ LLViewerTexture *getDebugTexture();
/*virtual*/ void beginRenderPass( S32 pass );
diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp
index 5398c13c44..9f9e50ad0a 100644
--- a/indra/newview/llface.cpp
+++ b/indra/newview/llface.cpp
@@ -364,14 +364,7 @@ void LLFace::setSize(S32 num_vertices, S32 num_indices, bool align)
//allocate vertices in blocks of 4 for alignment
num_vertices = (num_vertices + 0x3) & ~0x3;
}
- else
- {
- if (mDrawablep->getVOVolume())
- {
- llerrs << "WTF?" << llendl;
- }
- }
-
+
if (mGeomCount != num_vertices ||
mIndicesCount != num_indices)
{
diff --git a/indra/newview/llfeaturemanager.cpp b/indra/newview/llfeaturemanager.cpp
index 3bdab75acf..d1bff9f423 100644
--- a/indra/newview/llfeaturemanager.cpp
+++ b/indra/newview/llfeaturemanager.cpp
@@ -1,807 +1,807 @@
-/**
- * @file llfeaturemanager.cpp
- * @brief LLFeatureManager class implementation
- *
- * $LicenseInfo:firstyear=2003&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$
- */
-
-#include "llviewerprecompiledheaders.h"
-
-#include <iostream>
-#include <fstream>
-
-#include <boost/regex.hpp>
-
-#include "llfeaturemanager.h"
-#include "lldir.h"
-
-#include "llsys.h"
-#include "llgl.h"
-#include "llsecondlifeurls.h"
-
-#include "llappviewer.h"
-#include "llhttpclient.h"
-#include "llnotificationsutil.h"
-#include "llviewercontrol.h"
-#include "llworld.h"
-#include "lldrawpoolterrain.h"
-#include "llviewertexturelist.h"
-#include "llversioninfo.h"
-#include "llwindow.h"
-#include "llui.h"
-#include "llcontrol.h"
-#include "llboost.h"
-#include "llweb.h"
-
-#if LL_WINDOWS
-#include "lldxhardware.h"
-#endif
-
-
-#if LL_DARWIN
-const char FEATURE_TABLE_FILENAME[] = "featuretable_mac.txt";
-const char FEATURE_TABLE_VER_FILENAME[] = "featuretable_mac.%s.txt";
-#elif LL_LINUX
-const char FEATURE_TABLE_FILENAME[] = "featuretable_linux.txt";
-const char FEATURE_TABLE_VER_FILENAME[] = "featuretable_linux.%s.txt";
-#elif LL_SOLARIS
-const char FEATURE_TABLE_FILENAME[] = "featuretable_solaris.txt";
-const char FEATURE_TABLE_VER_FILENAME[] = "featuretable_solaris.%s.txt";
-#else
-const char FEATURE_TABLE_FILENAME[] = "featuretable%s.txt";
-const char FEATURE_TABLE_VER_FILENAME[] = "featuretable%s.%s.txt";
-#endif
-
-const char GPU_TABLE_FILENAME[] = "gpu_table.txt";
-const char GPU_TABLE_VER_FILENAME[] = "gpu_table.%s.txt";
-
-LLFeatureInfo::LLFeatureInfo(const std::string& name, const BOOL available, const F32 level)
- : mValid(TRUE), mName(name), mAvailable(available), mRecommendedLevel(level)
-{
-}
-
-LLFeatureList::LLFeatureList(const std::string& name)
- : mName(name)
-{
-}
-
-LLFeatureList::~LLFeatureList()
-{
-}
-
-void LLFeatureList::addFeature(const std::string& name, const BOOL available, const F32 level)
-{
- if (mFeatures.count(name))
- {
- LL_WARNS("RenderInit") << "LLFeatureList::Attempting to add preexisting feature " << name << LL_ENDL;
- }
-
- LLFeatureInfo fi(name, available, level);
- mFeatures[name] = fi;
-}
-
-BOOL LLFeatureList::isFeatureAvailable(const std::string& name)
-{
- if (mFeatures.count(name))
- {
- return mFeatures[name].mAvailable;
- }
-
- LL_WARNS("RenderInit") << "Feature " << name << " not on feature list!" << LL_ENDL;
-
- // changing this to TRUE so you have to explicitly disable
- // something for it to be disabled
- return TRUE;
-}
-
-F32 LLFeatureList::getRecommendedValue(const std::string& name)
-{
- if (mFeatures.count(name) && isFeatureAvailable(name))
- {
- return mFeatures[name].mRecommendedLevel;
- }
-
- LL_WARNS("RenderInit") << "Feature " << name << " not on feature list or not available!" << LL_ENDL;
- return 0;
-}
-
-BOOL LLFeatureList::maskList(LLFeatureList &mask)
-{
- //llinfos << "Masking with " << mask.mName << llendl;
- //
- // Lookup the specified feature mask, and overlay it on top of the
- // current feature mask.
- //
-
- LLFeatureInfo mask_fi;
-
- feature_map_t::iterator feature_it;
- for (feature_it = mask.mFeatures.begin(); feature_it != mask.mFeatures.end(); ++feature_it)
- {
- mask_fi = feature_it->second;
- //
- // Look for the corresponding feature
- //
- if (!mFeatures.count(mask_fi.mName))
- {
- LL_WARNS("RenderInit") << "Feature " << mask_fi.mName << " in mask not in top level!" << LL_ENDL;
- continue;
- }
-
- LLFeatureInfo &cur_fi = mFeatures[mask_fi.mName];
- if (mask_fi.mAvailable && !cur_fi.mAvailable)
- {
- LL_WARNS("RenderInit") << "Mask attempting to reenabling disabled feature, ignoring " << cur_fi.mName << LL_ENDL;
- continue;
- }
- cur_fi.mAvailable = mask_fi.mAvailable;
- cur_fi.mRecommendedLevel = llmin(cur_fi.mRecommendedLevel, mask_fi.mRecommendedLevel);
- LL_DEBUGS("RenderInit") << "Feature mask " << mask.mName
- << " Feature " << mask_fi.mName
- << " Mask: " << mask_fi.mRecommendedLevel
- << " Now: " << cur_fi.mRecommendedLevel << LL_ENDL;
- }
-
- LL_DEBUGS("RenderInit") << "After applying mask " << mask.mName << std::endl;
- // Will conditionally call dump only if the above message will be logged, thanks
- // to it being wrapped by the LL_DEBUGS and LL_ENDL macros.
- dump();
- LL_CONT << LL_ENDL;
-
- return TRUE;
-}
-
-void LLFeatureList::dump()
-{
- LL_DEBUGS("RenderInit") << "Feature list: " << mName << LL_ENDL;
- LL_DEBUGS("RenderInit") << "--------------" << LL_ENDL;
-
- LLFeatureInfo fi;
- feature_map_t::iterator feature_it;
- for (feature_it = mFeatures.begin(); feature_it != mFeatures.end(); ++feature_it)
- {
- fi = feature_it->second;
- LL_DEBUGS("RenderInit") << fi.mName << "\t\t" << fi.mAvailable << ":" << fi.mRecommendedLevel << LL_ENDL;
- }
- LL_DEBUGS("RenderInit") << LL_ENDL;
-}
-
-LLFeatureList *LLFeatureManager::findMask(const std::string& name)
-{
- if (mMaskList.count(name))
- {
- return mMaskList[name];
- }
-
- return NULL;
-}
-
-BOOL LLFeatureManager::maskFeatures(const std::string& name)
-{
- LLFeatureList *maskp = findMask(name);
- if (!maskp)
- {
- LL_DEBUGS("RenderInit") << "Unknown feature mask " << name << LL_ENDL;
- return FALSE;
- }
- LL_INFOS("RenderInit") << "Applying GPU Feature list: " << name << LL_ENDL;
- return maskList(*maskp);
-}
-
-BOOL LLFeatureManager::loadFeatureTables()
-{
- // *TODO - if I or anyone else adds something else to the skipped list
- // make this data driven. Put it in the feature table and parse it
- // correctly
- mSkippedFeatures.insert("RenderAnisotropic");
- mSkippedFeatures.insert("RenderGamma");
- mSkippedFeatures.insert("RenderVBOEnable");
- mSkippedFeatures.insert("RenderFogRatio");
-
- // first table is install with app
- std::string app_path = gDirUtilp->getAppRODataDir();
- app_path += gDirUtilp->getDirDelimiter();
-
- std::string filename;
- std::string http_filename;
-#if LL_WINDOWS
- std::string os_string = LLAppViewer::instance()->getOSInfo().getOSStringSimple();
- if (os_string.find("Microsoft Windows XP") == 0)
- {
- filename = llformat(FEATURE_TABLE_FILENAME, "_xp");
- http_filename = llformat(FEATURE_TABLE_VER_FILENAME, "_xp", LLVersionInfo::getVersion().c_str());
- }
- else
- {
- filename = llformat(FEATURE_TABLE_FILENAME, "");
- http_filename = llformat(FEATURE_TABLE_VER_FILENAME, "", LLVersionInfo::getVersion().c_str());
- }
-#else
- filename = FEATURE_TABLE_FILENAME;
- http_filename = llformat(FEATURE_TABLE_VER_FILENAME, LLVersionInfo::getVersion().c_str());
-#endif
-
- app_path += filename;
-
-
- // second table is downloaded with HTTP
- std::string http_path = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, http_filename);
-
- // use HTTP table if it exists
- std::string path;
- if (gDirUtilp->fileExists(http_path))
- {
- path = http_path;
- }
- else
- {
- path = app_path;
- }
-
-
- return parseFeatureTable(path);
-}
-
-
-BOOL LLFeatureManager::parseFeatureTable(std::string filename)
-{
- llinfos << "Looking for feature table in " << filename << llendl;
-
- llifstream file;
- std::string name;
- U32 version;
-
- file.open(filename); /*Flawfinder: ignore*/
-
- if (!file)
- {
- LL_WARNS("RenderInit") << "Unable to open feature table " << filename << LL_ENDL;
- return FALSE;
- }
-
- // Check file version
- file >> name;
- file >> version;
- if (name != "version")
- {
- LL_WARNS("RenderInit") << filename << " does not appear to be a valid feature table!" << LL_ENDL;
- return FALSE;
- }
-
- mTableVersion = version;
-
- LLFeatureList *flp = NULL;
- while (file >> name)
- {
- char buffer[MAX_STRING]; /*Flawfinder: ignore*/
-
- if (name.substr(0,2) == "//")
- {
- // This is a comment.
- file.getline(buffer, MAX_STRING);
- continue;
- }
-
- if (name == "list")
- {
- if (flp)
- {
- //flp->dump();
- }
- // It's a new mask, create it.
- file >> name;
- if (mMaskList.count(name))
- {
- LL_ERRS("RenderInit") << "Overriding mask " << name << ", this is invalid!" << LL_ENDL;
- }
-
- flp = new LLFeatureList(name);
- mMaskList[name] = flp;
- }
- else
- {
- if (!flp)
- {
- LL_ERRS("RenderInit") << "Specified parameter before <list> keyword!" << LL_ENDL;
- return FALSE;
- }
- S32 available;
- F32 recommended;
- file >> available >> recommended;
- flp->addFeature(name, available, recommended);
- }
- }
- file.close();
-
- return TRUE;
-}
-
-void LLFeatureManager::loadGPUClass()
-{
- // defaults
- mGPUClass = GPU_CLASS_UNKNOWN;
- mGPUString = gGLManager.getRawGLString();
- mGPUSupported = FALSE;
-
- // first table is in the app dir
- std::string app_path = gDirUtilp->getAppRODataDir();
- app_path += gDirUtilp->getDirDelimiter();
- app_path += GPU_TABLE_FILENAME;
-
- // second table is downloaded with HTTP
- std::string http_filename = llformat(GPU_TABLE_VER_FILENAME, LLVersionInfo::getVersion().c_str());
- std::string http_path = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, http_filename);
-
- // use HTTP table if it exists
- std::string path;
- if (gDirUtilp->fileExists(http_path))
- {
- path = http_path;
- }
- else
- {
- path = app_path;
- }
-
- parseGPUTable(path);
-}
-
-
-void LLFeatureManager::parseGPUTable(std::string filename)
-{
- llifstream file;
-
- file.open(filename);
-
- if (!file)
- {
- LL_WARNS("RenderInit") << "Unable to open GPU table: " << filename << "!" << LL_ENDL;
- return;
- }
-
- std::string rawRenderer = gGLManager.getRawGLString();
- std::string renderer = rawRenderer;
- for (std::string::iterator i = renderer.begin(); i != renderer.end(); ++i)
- {
- *i = tolower(*i);
- }
-
- bool gpuFound;
- U32 lineNumber;
- for (gpuFound = false, lineNumber = 0; !gpuFound && !file.eof(); lineNumber++)
- {
- char buffer[MAX_STRING]; /*Flawfinder: ignore*/
- buffer[0] = 0;
-
- file.getline(buffer, MAX_STRING);
-
- if (strlen(buffer) >= 2 && /*Flawfinder: ignore*/
- buffer[0] == '/' &&
- buffer[1] == '/')
- {
- // This is a comment.
- continue;
- }
-
- if (strlen(buffer) == 0) /*Flawfinder: ignore*/
- {
- // This is a blank line
- continue;
- }
-
- // setup the tokenizer
- std::string buf(buffer);
- std::string cls, label, expr, supported;
- boost_tokenizer tokens(buf, boost::char_separator<char>("\t\n"));
- boost_tokenizer::iterator token_iter = tokens.begin();
-
- // grab the label, pseudo regular expression, and class
- if(token_iter != tokens.end())
- {
- label = *token_iter++;
- }
- if(token_iter != tokens.end())
- {
- expr = *token_iter++;
- }
- if(token_iter != tokens.end())
- {
- cls = *token_iter++;
- }
- if(token_iter != tokens.end())
- {
- supported = *token_iter++;
- }
-
- if (label.empty() || expr.empty() || cls.empty() || supported.empty())
- {
- LL_WARNS("RenderInit") << "invald gpu_table.txt:" << lineNumber << ": '" << buffer << "'" << LL_ENDL;
- continue;
- }
-
- for (U32 i = 0; i < expr.length(); i++) /*Flawfinder: ignore*/
- {
- expr[i] = tolower(expr[i]);
- }
-
- // run the regular expression against the renderer
- boost::regex re(expr.c_str());
- if(boost::regex_search(renderer, re))
- {
- // if we found it, stop!
- gpuFound = true;
- mGPUString = label;
- mGPUClass = (EGPUClass) strtol(cls.c_str(), NULL, 10);
- mGPUSupported = (BOOL) strtol(supported.c_str(), NULL, 10);
- }
- }
- file.close();
-
- if ( gpuFound )
- {
- LL_INFOS("RenderInit") << "GPU '" << rawRenderer << "' recognized as '" << mGPUString << "'" << LL_ENDL;
- if (!mGPUSupported)
- {
- LL_INFOS("RenderInit") << "GPU '" << mGPUString << "' is not supported." << LL_ENDL;
- }
- }
- else
- {
- LL_WARNS("RenderInit") << "GPU '" << rawRenderer << "' not recognized" << LL_ENDL;
- }
-}
-
-// responder saves table into file
-class LLHTTPFeatureTableResponder : public LLHTTPClient::Responder
-{
-public:
-
- LLHTTPFeatureTableResponder(std::string filename) :
- mFilename(filename)
- {
- }
-
-
- virtual void completedRaw(U32 status, const std::string& reason,
- const LLChannelDescriptors& channels,
- const LLIOPipe::buffer_ptr_t& buffer)
- {
- if (isGoodStatus(status))
- {
- // write to file
-
- llinfos << "writing feature table to " << mFilename << llendl;
-
- S32 file_size = buffer->countAfter(channels.in(), NULL);
- if (file_size > 0)
- {
- // read from buffer
- U8* copy_buffer = new U8[file_size];
- buffer->readAfter(channels.in(), NULL, copy_buffer, file_size);
-
- // write to file
- LLAPRFile out(mFilename, LL_APR_WB);
- out.write(copy_buffer, file_size);
- out.close();
- }
- }
-
- }
-
-private:
- std::string mFilename;
-};
-
-void fetch_feature_table(std::string table)
-{
- const std::string base = gSavedSettings.getString("FeatureManagerHTTPTable");
-
-#if LL_WINDOWS
- std::string os_string = LLAppViewer::instance()->getOSInfo().getOSStringSimple();
- std::string filename;
- if (os_string.find("Microsoft Windows XP") == 0)
- {
- filename = llformat(table.c_str(), "_xp", LLVersionInfo::getVersion().c_str());
- }
- else
- {
- filename = llformat(table.c_str(), "", LLVersionInfo::getVersion().c_str());
- }
-#else
- const std::string filename = llformat(table.c_str(), LLVersionInfo::getVersion().c_str());
-#endif
-
- const std::string url = base + "/" + filename;
-
- const std::string path = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename);
-
- llinfos << "LLFeatureManager fetching " << url << " into " << path << llendl;
-
- LLHTTPClient::get(url, new LLHTTPFeatureTableResponder(path));
-}
-
-void fetch_gpu_table(std::string table)
-{
- const std::string base = gSavedSettings.getString("FeatureManagerHTTPTable");
-
- const std::string filename = llformat(table.c_str(), LLVersionInfo::getVersion().c_str());
-
- const std::string url = base + "/" + filename;
-
- const std::string path = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename);
-
- llinfos << "LLFeatureManager fetching " << url << " into " << path << llendl;
-
- LLHTTPClient::get(url, new LLHTTPFeatureTableResponder(path));
-}
-
-// fetch table(s) from a website (S3)
-void LLFeatureManager::fetchHTTPTables()
-{
- fetch_feature_table(FEATURE_TABLE_VER_FILENAME);
- fetch_gpu_table(GPU_TABLE_VER_FILENAME);
-}
-
-
-void LLFeatureManager::cleanupFeatureTables()
-{
- std::for_each(mMaskList.begin(), mMaskList.end(), DeletePairedPointer());
- mMaskList.clear();
-}
-
-void LLFeatureManager::init()
-{
- // load the tables
- loadFeatureTables();
-
- // get the gpu class
- loadGPUClass();
-
- // apply the base masks, so we know if anything is disabled
- applyBaseMasks();
-}
-
-void LLFeatureManager::applyRecommendedSettings()
-{
- // apply saved settings
- // cap the level at 2 (high)
- S32 level = llmax(GPU_CLASS_0, llmin(mGPUClass, GPU_CLASS_2));
-
- llinfos << "Applying Recommended Features" << llendl;
-
- setGraphicsLevel(level, false);
- gSavedSettings.setU32("RenderQualityPerformance", level);
-
- // now apply the tweaks to draw distance
- // these are double negatives, because feature masks only work by
- // downgrading values, so i needed to make a true value go to false
- // for certain cards, thus the awkward name, "Disregard..."
- if(!gSavedSettings.getBOOL("Disregard96DefaultDrawDistance"))
- {
- gSavedSettings.setF32("RenderFarClip", 96.0f);
- }
- else if(!gSavedSettings.getBOOL("Disregard128DefaultDrawDistance"))
- {
- gSavedSettings.setF32("RenderFarClip", 128.0f);
- }
-}
-
-void LLFeatureManager::applyFeatures(bool skipFeatures)
-{
- // see featuretable.txt / featuretable_linux.txt / featuretable_mac.txt
-
-#ifndef LL_RELEASE_FOR_DOWNLOAD
- dump();
-#endif
-
- // scroll through all of these and set their corresponding control value
- for(feature_map_t::iterator mIt = mFeatures.begin();
- mIt != mFeatures.end();
- ++mIt)
- {
- // skip features you want to skip
- // do this for when you don't want to change certain settings
- if(skipFeatures)
- {
- if(mSkippedFeatures.find(mIt->first) != mSkippedFeatures.end())
- {
- continue;
- }
- }
-
- // get the control setting
- LLControlVariable* ctrl = gSavedSettings.getControl(mIt->first);
- if(ctrl == NULL)
- {
- llwarns << "AHHH! Control setting " << mIt->first << " does not exist!" << llendl;
- continue;
- }
-
- // handle all the different types
- if(ctrl->isType(TYPE_BOOLEAN))
- {
- gSavedSettings.setBOOL(mIt->first, (BOOL)getRecommendedValue(mIt->first));
- }
- else if (ctrl->isType(TYPE_S32))
- {
- gSavedSettings.setS32(mIt->first, (S32)getRecommendedValue(mIt->first));
- }
- else if (ctrl->isType(TYPE_U32))
- {
- gSavedSettings.setU32(mIt->first, (U32)getRecommendedValue(mIt->first));
- }
- else if (ctrl->isType(TYPE_F32))
- {
- gSavedSettings.setF32(mIt->first, (F32)getRecommendedValue(mIt->first));
- }
- else
- {
- llwarns << "AHHH! Control variable is not a numeric type!" << llendl;
- }
- }
-}
-
-void LLFeatureManager::setGraphicsLevel(S32 level, bool skipFeatures)
-{
- applyBaseMasks();
-
- switch (level)
- {
- case 0:
- maskFeatures("Low");
- break;
- case 1:
- maskFeatures("Mid");
- break;
- case 2:
- maskFeatures("High");
- break;
- case 3:
- maskFeatures("Ultra");
- break;
- default:
- maskFeatures("Low");
- break;
- }
-
- applyFeatures(skipFeatures);
-}
-
-void LLFeatureManager::applyBaseMasks()
-{
- // reapply masks
- mFeatures.clear();
-
- LLFeatureList* maskp = findMask("all");
- if(maskp == NULL)
- {
- LL_WARNS("RenderInit") << "AHH! No \"all\" in feature table!" << LL_ENDL;
- return;
- }
-
- mFeatures = maskp->getFeatures();
-
- // mask class
- if (mGPUClass >= 0 && mGPUClass < 4)
- {
- const char* class_table[] =
- {
- "Class0",
- "Class1",
- "Class2",
- "Class3"
- };
-
- LL_INFOS("RenderInit") << "Setting GPU Class to " << class_table[mGPUClass] << LL_ENDL;
- maskFeatures(class_table[mGPUClass]);
- }
- else
- {
- LL_INFOS("RenderInit") << "Setting GPU Class to Unknown" << LL_ENDL;
- maskFeatures("Unknown");
- }
-
- // now all those wacky ones
- if (!gGLManager.mHasFragmentShader)
- {
- maskFeatures("NoPixelShaders");
- }
- if (!gGLManager.mHasVertexShader)
- {
- maskFeatures("NoVertexShaders");
- }
- if (gGLManager.mIsNVIDIA)
- {
- maskFeatures("NVIDIA");
- }
- if (gGLManager.mIsGF2or4MX)
- {
- maskFeatures("GeForce2");
- }
- if (gGLManager.mIsATI)
- {
- maskFeatures("ATI");
- }
- if (gGLManager.mHasATIMemInfo && gGLManager.mVRAM < 256)
- {
- maskFeatures("ATIVramLT256");
- }
- if (gGLManager.mATIOldDriver)
- {
- maskFeatures("ATIOldDriver");
- }
- if (gGLManager.mIsGFFX)
- {
- maskFeatures("GeForceFX");
- }
- if (gGLManager.mIsIntel)
- {
- maskFeatures("Intel");
- }
- if (gGLManager.mGLVersion < 1.5f)
- {
- maskFeatures("OpenGLPre15");
- }
- if (gGLManager.mGLVersion < 3.f)
- {
- maskFeatures("OpenGLPre30");
- }
- if (gGLManager.mNumTextureUnits <= 8)
- {
- maskFeatures("TexUnit8orLess");
- }
-
- // now mask by gpu string
- // Replaces ' ' with '_' in mGPUString to deal with inability for parser to handle spaces
- std::string gpustr = mGPUString;
- for (std::string::iterator iter = gpustr.begin(); iter != gpustr.end(); ++iter)
- {
- if (*iter == ' ')
- {
- *iter = '_';
- }
- }
-
- //llinfos << "Masking features from gpu table match: " << gpustr << llendl;
- maskFeatures(gpustr);
-
- // now mask cpu type ones
- if (gSysMemory.getPhysicalMemoryClamped() <= 256*1024*1024)
- {
- maskFeatures("RAM256MB");
- }
-
-#if LL_SOLARIS && defined(__sparc) // even low MHz SPARCs are fast
-#error The 800 is hinky. Would something like a LL_MIN_MHZ make more sense here?
- if (gSysCPU.getMHz() < 800)
-#else
- if (gSysCPU.getMHz() < 1100)
-#endif
- {
- maskFeatures("CPUSlow");
- }
-
- if (isSafe())
- {
- maskFeatures("safe");
- }
-}
+/**
+ * @file llfeaturemanager.cpp
+ * @brief LLFeatureManager class implementation
+ *
+ * $LicenseInfo:firstyear=2003&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$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include <iostream>
+#include <fstream>
+
+#include <boost/regex.hpp>
+
+#include "llfeaturemanager.h"
+#include "lldir.h"
+
+#include "llsys.h"
+#include "llgl.h"
+#include "llsecondlifeurls.h"
+
+#include "llappviewer.h"
+#include "llhttpclient.h"
+#include "llnotificationsutil.h"
+#include "llviewercontrol.h"
+#include "llworld.h"
+#include "lldrawpoolterrain.h"
+#include "llviewertexturelist.h"
+#include "llversioninfo.h"
+#include "llwindow.h"
+#include "llui.h"
+#include "llcontrol.h"
+#include "llboost.h"
+#include "llweb.h"
+
+#if LL_WINDOWS
+#include "lldxhardware.h"
+#endif
+
+
+#if LL_DARWIN
+const char FEATURE_TABLE_FILENAME[] = "featuretable_mac.txt";
+const char FEATURE_TABLE_VER_FILENAME[] = "featuretable_mac.%s.txt";
+#elif LL_LINUX
+const char FEATURE_TABLE_FILENAME[] = "featuretable_linux.txt";
+const char FEATURE_TABLE_VER_FILENAME[] = "featuretable_linux.%s.txt";
+#elif LL_SOLARIS
+const char FEATURE_TABLE_FILENAME[] = "featuretable_solaris.txt";
+const char FEATURE_TABLE_VER_FILENAME[] = "featuretable_solaris.%s.txt";
+#else
+const char FEATURE_TABLE_FILENAME[] = "featuretable%s.txt";
+const char FEATURE_TABLE_VER_FILENAME[] = "featuretable%s.%s.txt";
+#endif
+
+const char GPU_TABLE_FILENAME[] = "gpu_table.txt";
+const char GPU_TABLE_VER_FILENAME[] = "gpu_table.%s.txt";
+
+LLFeatureInfo::LLFeatureInfo(const std::string& name, const BOOL available, const F32 level)
+ : mValid(TRUE), mName(name), mAvailable(available), mRecommendedLevel(level)
+{
+}
+
+LLFeatureList::LLFeatureList(const std::string& name)
+ : mName(name)
+{
+}
+
+LLFeatureList::~LLFeatureList()
+{
+}
+
+void LLFeatureList::addFeature(const std::string& name, const BOOL available, const F32 level)
+{
+ if (mFeatures.count(name))
+ {
+ LL_WARNS("RenderInit") << "LLFeatureList::Attempting to add preexisting feature " << name << LL_ENDL;
+ }
+
+ LLFeatureInfo fi(name, available, level);
+ mFeatures[name] = fi;
+}
+
+BOOL LLFeatureList::isFeatureAvailable(const std::string& name)
+{
+ if (mFeatures.count(name))
+ {
+ return mFeatures[name].mAvailable;
+ }
+
+ LL_WARNS("RenderInit") << "Feature " << name << " not on feature list!" << LL_ENDL;
+
+ // changing this to TRUE so you have to explicitly disable
+ // something for it to be disabled
+ return TRUE;
+}
+
+F32 LLFeatureList::getRecommendedValue(const std::string& name)
+{
+ if (mFeatures.count(name) && isFeatureAvailable(name))
+ {
+ return mFeatures[name].mRecommendedLevel;
+ }
+
+ LL_WARNS("RenderInit") << "Feature " << name << " not on feature list or not available!" << LL_ENDL;
+ return 0;
+}
+
+BOOL LLFeatureList::maskList(LLFeatureList &mask)
+{
+ //llinfos << "Masking with " << mask.mName << llendl;
+ //
+ // Lookup the specified feature mask, and overlay it on top of the
+ // current feature mask.
+ //
+
+ LLFeatureInfo mask_fi;
+
+ feature_map_t::iterator feature_it;
+ for (feature_it = mask.mFeatures.begin(); feature_it != mask.mFeatures.end(); ++feature_it)
+ {
+ mask_fi = feature_it->second;
+ //
+ // Look for the corresponding feature
+ //
+ if (!mFeatures.count(mask_fi.mName))
+ {
+ LL_WARNS("RenderInit") << "Feature " << mask_fi.mName << " in mask not in top level!" << LL_ENDL;
+ continue;
+ }
+
+ LLFeatureInfo &cur_fi = mFeatures[mask_fi.mName];
+ if (mask_fi.mAvailable && !cur_fi.mAvailable)
+ {
+ LL_WARNS("RenderInit") << "Mask attempting to reenabling disabled feature, ignoring " << cur_fi.mName << LL_ENDL;
+ continue;
+ }
+ cur_fi.mAvailable = mask_fi.mAvailable;
+ cur_fi.mRecommendedLevel = llmin(cur_fi.mRecommendedLevel, mask_fi.mRecommendedLevel);
+ LL_DEBUGS("RenderInit") << "Feature mask " << mask.mName
+ << " Feature " << mask_fi.mName
+ << " Mask: " << mask_fi.mRecommendedLevel
+ << " Now: " << cur_fi.mRecommendedLevel << LL_ENDL;
+ }
+
+ LL_DEBUGS("RenderInit") << "After applying mask " << mask.mName << std::endl;
+ // Will conditionally call dump only if the above message will be logged, thanks
+ // to it being wrapped by the LL_DEBUGS and LL_ENDL macros.
+ dump();
+ LL_CONT << LL_ENDL;
+
+ return TRUE;
+}
+
+void LLFeatureList::dump()
+{
+ LL_DEBUGS("RenderInit") << "Feature list: " << mName << LL_ENDL;
+ LL_DEBUGS("RenderInit") << "--------------" << LL_ENDL;
+
+ LLFeatureInfo fi;
+ feature_map_t::iterator feature_it;
+ for (feature_it = mFeatures.begin(); feature_it != mFeatures.end(); ++feature_it)
+ {
+ fi = feature_it->second;
+ LL_DEBUGS("RenderInit") << fi.mName << "\t\t" << fi.mAvailable << ":" << fi.mRecommendedLevel << LL_ENDL;
+ }
+ LL_DEBUGS("RenderInit") << LL_ENDL;
+}
+
+LLFeatureList *LLFeatureManager::findMask(const std::string& name)
+{
+ if (mMaskList.count(name))
+ {
+ return mMaskList[name];
+ }
+
+ return NULL;
+}
+
+BOOL LLFeatureManager::maskFeatures(const std::string& name)
+{
+ LLFeatureList *maskp = findMask(name);
+ if (!maskp)
+ {
+ LL_DEBUGS("RenderInit") << "Unknown feature mask " << name << LL_ENDL;
+ return FALSE;
+ }
+ LL_INFOS("RenderInit") << "Applying GPU Feature list: " << name << LL_ENDL;
+ return maskList(*maskp);
+}
+
+BOOL LLFeatureManager::loadFeatureTables()
+{
+ // *TODO - if I or anyone else adds something else to the skipped list
+ // make this data driven. Put it in the feature table and parse it
+ // correctly
+ mSkippedFeatures.insert("RenderAnisotropic");
+ mSkippedFeatures.insert("RenderGamma");
+ mSkippedFeatures.insert("RenderVBOEnable");
+ mSkippedFeatures.insert("RenderFogRatio");
+
+ // first table is install with app
+ std::string app_path = gDirUtilp->getAppRODataDir();
+ app_path += gDirUtilp->getDirDelimiter();
+
+ std::string filename;
+ std::string http_filename;
+#if LL_WINDOWS
+ std::string os_string = LLAppViewer::instance()->getOSInfo().getOSStringSimple();
+ if (os_string.find("Microsoft Windows XP") == 0)
+ {
+ filename = llformat(FEATURE_TABLE_FILENAME, "_xp");
+ http_filename = llformat(FEATURE_TABLE_VER_FILENAME, "_xp", LLVersionInfo::getVersion().c_str());
+ }
+ else
+ {
+ filename = llformat(FEATURE_TABLE_FILENAME, "");
+ http_filename = llformat(FEATURE_TABLE_VER_FILENAME, "", LLVersionInfo::getVersion().c_str());
+ }
+#else
+ filename = FEATURE_TABLE_FILENAME;
+ http_filename = llformat(FEATURE_TABLE_VER_FILENAME, LLVersionInfo::getVersion().c_str());
+#endif
+
+ app_path += filename;
+
+
+ // second table is downloaded with HTTP
+ std::string http_path = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, http_filename);
+
+ // use HTTP table if it exists
+ std::string path;
+ if (gDirUtilp->fileExists(http_path))
+ {
+ path = http_path;
+ }
+ else
+ {
+ path = app_path;
+ }
+
+
+ return parseFeatureTable(path);
+}
+
+
+BOOL LLFeatureManager::parseFeatureTable(std::string filename)
+{
+ llinfos << "Looking for feature table in " << filename << llendl;
+
+ llifstream file;
+ std::string name;
+ U32 version;
+
+ file.open(filename); /*Flawfinder: ignore*/
+
+ if (!file)
+ {
+ LL_WARNS("RenderInit") << "Unable to open feature table " << filename << LL_ENDL;
+ return FALSE;
+ }
+
+ // Check file version
+ file >> name;
+ file >> version;
+ if (name != "version")
+ {
+ LL_WARNS("RenderInit") << filename << " does not appear to be a valid feature table!" << LL_ENDL;
+ return FALSE;
+ }
+
+ mTableVersion = version;
+
+ LLFeatureList *flp = NULL;
+ while (file >> name)
+ {
+ char buffer[MAX_STRING]; /*Flawfinder: ignore*/
+
+ if (name.substr(0,2) == "//")
+ {
+ // This is a comment.
+ file.getline(buffer, MAX_STRING);
+ continue;
+ }
+
+ if (name == "list")
+ {
+ if (flp)
+ {
+ //flp->dump();
+ }
+ // It's a new mask, create it.
+ file >> name;
+ if (mMaskList.count(name))
+ {
+ LL_ERRS("RenderInit") << "Overriding mask " << name << ", this is invalid!" << LL_ENDL;
+ }
+
+ flp = new LLFeatureList(name);
+ mMaskList[name] = flp;
+ }
+ else
+ {
+ if (!flp)
+ {
+ LL_ERRS("RenderInit") << "Specified parameter before <list> keyword!" << LL_ENDL;
+ return FALSE;
+ }
+ S32 available;
+ F32 recommended;
+ file >> available >> recommended;
+ flp->addFeature(name, available, recommended);
+ }
+ }
+ file.close();
+
+ return TRUE;
+}
+
+void LLFeatureManager::loadGPUClass()
+{
+ // defaults
+ mGPUClass = GPU_CLASS_UNKNOWN;
+ mGPUString = gGLManager.getRawGLString();
+ mGPUSupported = FALSE;
+
+ // first table is in the app dir
+ std::string app_path = gDirUtilp->getAppRODataDir();
+ app_path += gDirUtilp->getDirDelimiter();
+ app_path += GPU_TABLE_FILENAME;
+
+ // second table is downloaded with HTTP
+ std::string http_filename = llformat(GPU_TABLE_VER_FILENAME, LLVersionInfo::getVersion().c_str());
+ std::string http_path = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, http_filename);
+
+ // use HTTP table if it exists
+ std::string path;
+ if (gDirUtilp->fileExists(http_path))
+ {
+ path = http_path;
+ }
+ else
+ {
+ path = app_path;
+ }
+
+ parseGPUTable(path);
+}
+
+
+void LLFeatureManager::parseGPUTable(std::string filename)
+{
+ llifstream file;
+
+ file.open(filename);
+
+ if (!file)
+ {
+ LL_WARNS("RenderInit") << "Unable to open GPU table: " << filename << "!" << LL_ENDL;
+ return;
+ }
+
+ std::string rawRenderer = gGLManager.getRawGLString();
+ std::string renderer = rawRenderer;
+ for (std::string::iterator i = renderer.begin(); i != renderer.end(); ++i)
+ {
+ *i = tolower(*i);
+ }
+
+ bool gpuFound;
+ U32 lineNumber;
+ for (gpuFound = false, lineNumber = 0; !gpuFound && !file.eof(); lineNumber++)
+ {
+ char buffer[MAX_STRING]; /*Flawfinder: ignore*/
+ buffer[0] = 0;
+
+ file.getline(buffer, MAX_STRING);
+
+ if (strlen(buffer) >= 2 && /*Flawfinder: ignore*/
+ buffer[0] == '/' &&
+ buffer[1] == '/')
+ {
+ // This is a comment.
+ continue;
+ }
+
+ if (strlen(buffer) == 0) /*Flawfinder: ignore*/
+ {
+ // This is a blank line
+ continue;
+ }
+
+ // setup the tokenizer
+ std::string buf(buffer);
+ std::string cls, label, expr, supported;
+ boost_tokenizer tokens(buf, boost::char_separator<char>("\t\n"));
+ boost_tokenizer::iterator token_iter = tokens.begin();
+
+ // grab the label, pseudo regular expression, and class
+ if(token_iter != tokens.end())
+ {
+ label = *token_iter++;
+ }
+ if(token_iter != tokens.end())
+ {
+ expr = *token_iter++;
+ }
+ if(token_iter != tokens.end())
+ {
+ cls = *token_iter++;
+ }
+ if(token_iter != tokens.end())
+ {
+ supported = *token_iter++;
+ }
+
+ if (label.empty() || expr.empty() || cls.empty() || supported.empty())
+ {
+ LL_WARNS("RenderInit") << "invald gpu_table.txt:" << lineNumber << ": '" << buffer << "'" << LL_ENDL;
+ continue;
+ }
+
+ for (U32 i = 0; i < expr.length(); i++) /*Flawfinder: ignore*/
+ {
+ expr[i] = tolower(expr[i]);
+ }
+
+ // run the regular expression against the renderer
+ boost::regex re(expr.c_str());
+ if(boost::regex_search(renderer, re))
+ {
+ // if we found it, stop!
+ gpuFound = true;
+ mGPUString = label;
+ mGPUClass = (EGPUClass) strtol(cls.c_str(), NULL, 10);
+ mGPUSupported = (BOOL) strtol(supported.c_str(), NULL, 10);
+ }
+ }
+ file.close();
+
+ if ( gpuFound )
+ {
+ LL_INFOS("RenderInit") << "GPU '" << rawRenderer << "' recognized as '" << mGPUString << "'" << LL_ENDL;
+ if (!mGPUSupported)
+ {
+ LL_INFOS("RenderInit") << "GPU '" << mGPUString << "' is not supported." << LL_ENDL;
+ }
+ }
+ else
+ {
+ LL_WARNS("RenderInit") << "GPU '" << rawRenderer << "' not recognized" << LL_ENDL;
+ }
+}
+
+// responder saves table into file
+class LLHTTPFeatureTableResponder : public LLHTTPClient::Responder
+{
+public:
+
+ LLHTTPFeatureTableResponder(std::string filename) :
+ mFilename(filename)
+ {
+ }
+
+
+ virtual void completedRaw(U32 status, const std::string& reason,
+ const LLChannelDescriptors& channels,
+ const LLIOPipe::buffer_ptr_t& buffer)
+ {
+ if (isGoodStatus(status))
+ {
+ // write to file
+
+ llinfos << "writing feature table to " << mFilename << llendl;
+
+ S32 file_size = buffer->countAfter(channels.in(), NULL);
+ if (file_size > 0)
+ {
+ // read from buffer
+ U8* copy_buffer = new U8[file_size];
+ buffer->readAfter(channels.in(), NULL, copy_buffer, file_size);
+
+ // write to file
+ LLAPRFile out(mFilename, LL_APR_WB);
+ out.write(copy_buffer, file_size);
+ out.close();
+ }
+ }
+
+ }
+
+private:
+ std::string mFilename;
+};
+
+void fetch_feature_table(std::string table)
+{
+ const std::string base = gSavedSettings.getString("FeatureManagerHTTPTable");
+
+#if LL_WINDOWS
+ std::string os_string = LLAppViewer::instance()->getOSInfo().getOSStringSimple();
+ std::string filename;
+ if (os_string.find("Microsoft Windows XP") == 0)
+ {
+ filename = llformat(table.c_str(), "_xp", LLVersionInfo::getVersion().c_str());
+ }
+ else
+ {
+ filename = llformat(table.c_str(), "", LLVersionInfo::getVersion().c_str());
+ }
+#else
+ const std::string filename = llformat(table.c_str(), LLVersionInfo::getVersion().c_str());
+#endif
+
+ const std::string url = base + "/" + filename;
+
+ const std::string path = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename);
+
+ llinfos << "LLFeatureManager fetching " << url << " into " << path << llendl;
+
+ LLHTTPClient::get(url, new LLHTTPFeatureTableResponder(path));
+}
+
+void fetch_gpu_table(std::string table)
+{
+ const std::string base = gSavedSettings.getString("FeatureManagerHTTPTable");
+
+ const std::string filename = llformat(table.c_str(), LLVersionInfo::getVersion().c_str());
+
+ const std::string url = base + "/" + filename;
+
+ const std::string path = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename);
+
+ llinfos << "LLFeatureManager fetching " << url << " into " << path << llendl;
+
+ LLHTTPClient::get(url, new LLHTTPFeatureTableResponder(path));
+}
+
+// fetch table(s) from a website (S3)
+void LLFeatureManager::fetchHTTPTables()
+{
+ fetch_feature_table(FEATURE_TABLE_VER_FILENAME);
+ fetch_gpu_table(GPU_TABLE_VER_FILENAME);
+}
+
+
+void LLFeatureManager::cleanupFeatureTables()
+{
+ std::for_each(mMaskList.begin(), mMaskList.end(), DeletePairedPointer());
+ mMaskList.clear();
+}
+
+void LLFeatureManager::init()
+{
+ // load the tables
+ loadFeatureTables();
+
+ // get the gpu class
+ loadGPUClass();
+
+ // apply the base masks, so we know if anything is disabled
+ applyBaseMasks();
+}
+
+void LLFeatureManager::applyRecommendedSettings()
+{
+ // apply saved settings
+ // cap the level at 2 (high)
+ S32 level = llmax(GPU_CLASS_0, llmin(mGPUClass, GPU_CLASS_2));
+
+ llinfos << "Applying Recommended Features" << llendl;
+
+ setGraphicsLevel(level, false);
+ gSavedSettings.setU32("RenderQualityPerformance", level);
+
+ // now apply the tweaks to draw distance
+ // these are double negatives, because feature masks only work by
+ // downgrading values, so i needed to make a true value go to false
+ // for certain cards, thus the awkward name, "Disregard..."
+ if(!gSavedSettings.getBOOL("Disregard96DefaultDrawDistance"))
+ {
+ gSavedSettings.setF32("RenderFarClip", 96.0f);
+ }
+ else if(!gSavedSettings.getBOOL("Disregard128DefaultDrawDistance"))
+ {
+ gSavedSettings.setF32("RenderFarClip", 128.0f);
+ }
+}
+
+void LLFeatureManager::applyFeatures(bool skipFeatures)
+{
+ // see featuretable.txt / featuretable_linux.txt / featuretable_mac.txt
+
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ dump();
+#endif
+
+ // scroll through all of these and set their corresponding control value
+ for(feature_map_t::iterator mIt = mFeatures.begin();
+ mIt != mFeatures.end();
+ ++mIt)
+ {
+ // skip features you want to skip
+ // do this for when you don't want to change certain settings
+ if(skipFeatures)
+ {
+ if(mSkippedFeatures.find(mIt->first) != mSkippedFeatures.end())
+ {
+ continue;
+ }
+ }
+
+ // get the control setting
+ LLControlVariable* ctrl = gSavedSettings.getControl(mIt->first);
+ if(ctrl == NULL)
+ {
+ llwarns << "AHHH! Control setting " << mIt->first << " does not exist!" << llendl;
+ continue;
+ }
+
+ // handle all the different types
+ if(ctrl->isType(TYPE_BOOLEAN))
+ {
+ gSavedSettings.setBOOL(mIt->first, (BOOL)getRecommendedValue(mIt->first));
+ }
+ else if (ctrl->isType(TYPE_S32))
+ {
+ gSavedSettings.setS32(mIt->first, (S32)getRecommendedValue(mIt->first));
+ }
+ else if (ctrl->isType(TYPE_U32))
+ {
+ gSavedSettings.setU32(mIt->first, (U32)getRecommendedValue(mIt->first));
+ }
+ else if (ctrl->isType(TYPE_F32))
+ {
+ gSavedSettings.setF32(mIt->first, (F32)getRecommendedValue(mIt->first));
+ }
+ else
+ {
+ llwarns << "AHHH! Control variable is not a numeric type!" << llendl;
+ }
+ }
+}
+
+void LLFeatureManager::setGraphicsLevel(S32 level, bool skipFeatures)
+{
+ applyBaseMasks();
+
+ switch (level)
+ {
+ case 0:
+ maskFeatures("Low");
+ break;
+ case 1:
+ maskFeatures("Mid");
+ break;
+ case 2:
+ maskFeatures("High");
+ break;
+ case 3:
+ maskFeatures("Ultra");
+ break;
+ default:
+ maskFeatures("Low");
+ break;
+ }
+
+ applyFeatures(skipFeatures);
+}
+
+void LLFeatureManager::applyBaseMasks()
+{
+ // reapply masks
+ mFeatures.clear();
+
+ LLFeatureList* maskp = findMask("all");
+ if(maskp == NULL)
+ {
+ LL_WARNS("RenderInit") << "AHH! No \"all\" in feature table!" << LL_ENDL;
+ return;
+ }
+
+ mFeatures = maskp->getFeatures();
+
+ // mask class
+ if (mGPUClass >= 0 && mGPUClass < 4)
+ {
+ const char* class_table[] =
+ {
+ "Class0",
+ "Class1",
+ "Class2",
+ "Class3"
+ };
+
+ LL_INFOS("RenderInit") << "Setting GPU Class to " << class_table[mGPUClass] << LL_ENDL;
+ maskFeatures(class_table[mGPUClass]);
+ }
+ else
+ {
+ LL_INFOS("RenderInit") << "Setting GPU Class to Unknown" << LL_ENDL;
+ maskFeatures("Unknown");
+ }
+
+ // now all those wacky ones
+ if (!gGLManager.mHasFragmentShader)
+ {
+ maskFeatures("NoPixelShaders");
+ }
+ if (!gGLManager.mHasVertexShader)
+ {
+ maskFeatures("NoVertexShaders");
+ }
+ if (gGLManager.mIsNVIDIA)
+ {
+ maskFeatures("NVIDIA");
+ }
+ if (gGLManager.mIsGF2or4MX)
+ {
+ maskFeatures("GeForce2");
+ }
+ if (gGLManager.mIsATI)
+ {
+ maskFeatures("ATI");
+ }
+ if (gGLManager.mHasATIMemInfo && gGLManager.mVRAM < 256)
+ {
+ maskFeatures("ATIVramLT256");
+ }
+ if (gGLManager.mATIOldDriver)
+ {
+ maskFeatures("ATIOldDriver");
+ }
+ if (gGLManager.mIsGFFX)
+ {
+ maskFeatures("GeForceFX");
+ }
+ if (gGLManager.mIsIntel)
+ {
+ maskFeatures("Intel");
+ }
+ if (gGLManager.mGLVersion < 1.5f)
+ {
+ maskFeatures("OpenGLPre15");
+ }
+ if (gGLManager.mGLVersion < 3.f)
+ {
+ maskFeatures("OpenGLPre30");
+ }
+ if (gGLManager.mNumTextureImageUnits <= 8)
+ {
+ maskFeatures("TexUnit8orLess");
+ }
+
+ // now mask by gpu string
+ // Replaces ' ' with '_' in mGPUString to deal with inability for parser to handle spaces
+ std::string gpustr = mGPUString;
+ for (std::string::iterator iter = gpustr.begin(); iter != gpustr.end(); ++iter)
+ {
+ if (*iter == ' ')
+ {
+ *iter = '_';
+ }
+ }
+
+ //llinfos << "Masking features from gpu table match: " << gpustr << llendl;
+ maskFeatures(gpustr);
+
+ // now mask cpu type ones
+ if (gSysMemory.getPhysicalMemoryClamped() <= 256*1024*1024)
+ {
+ maskFeatures("RAM256MB");
+ }
+
+#if LL_SOLARIS && defined(__sparc) // even low MHz SPARCs are fast
+#error The 800 is hinky. Would something like a LL_MIN_MHZ make more sense here?
+ if (gSysCPU.getMHz() < 800)
+#else
+ if (gSysCPU.getMHz() < 1100)
+#endif
+ {
+ maskFeatures("CPUSlow");
+ }
+
+ if (isSafe())
+ {
+ maskFeatures("safe");
+ }
+}
diff --git a/indra/newview/llfirstuse.cpp b/indra/newview/llfirstuse.cpp
index 2c4153688a..a9f52282a5 100644
--- a/indra/newview/llfirstuse.cpp
+++ b/indra/newview/llfirstuse.cpp
@@ -131,7 +131,7 @@ void LLFirstUse::notMoving(bool enable)
// static
void LLFirstUse::viewPopup(bool enable)
{
- firstUseNotification("FirstViewPopup", enable, "HintView", LLSD(), LLSD().with("target", "view_popup").with("direction", "right"));
+// firstUseNotification("FirstViewPopup", enable, "HintView", LLSD(), LLSD().with("target", "view_popup").with("direction", "right"));
}
// static
diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index e8da1aa42c..3bddd020e1 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -98,7 +98,8 @@
#include "llvfile.h"
#include "llvfs.h"
#include "llcallbacklist.h"
-
+#include "llviewerobjectlist.h"
+#include "llanimationstates.h"
#include "glod/glod.h"
//static
@@ -313,7 +314,7 @@ BOOL LLFloaterModelPreview::postBuild()
childSetCommitCallback("pelvis_offset", onPelvisOffsetCommit, this);
childSetCommitCallback("lod_file_or_limit", refresh, this);
- childSetCommitCallback("physics_load_radio", refresh, this);
+ childSetCommitCallback("physics_load_radio", onPhysicsLoadRadioCommit, this);
//childSetCommitCallback("physics_optimize", refresh, this);
//childSetCommitCallback("physics_use_hull", refresh, this);
@@ -381,12 +382,6 @@ LLFloaterModelPreview::~LLFloaterModelPreview()
{
sInstance = NULL;
- if ( mModelPreview && mModelPreview->getResetJointFlag() )
- {
- gAgentAvatarp->resetJointPositions();
- }
-
-
if ( mModelPreview )
{
delete mModelPreview;
@@ -475,6 +470,29 @@ void LLFloaterModelPreview::onPelvisOffsetCommit( LLUICtrl*, void* userdata )
}
//static
+void LLFloaterModelPreview::onPhysicsLoadRadioCommit( LLUICtrl*, void *userdata)
+{
+ LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance;
+ if (fmp)
+ {
+ if (fmp->childGetValue("physics_use_lod").asBoolean())
+ {
+ onPhysicsUseLOD(NULL,NULL);
+ }
+ if (fmp->childGetValue("physics_load_from_file").asBoolean())
+ {
+
+ }
+ LLModelPreview *model_preview = fmp->mModelPreview;
+ if (model_preview)
+ {
+ model_preview->refresh();
+ model_preview->updateStatusMessages();
+ }
+ }
+}
+
+//static
void LLFloaterModelPreview::onUploadJointsCommit(LLUICtrl*,void* userdata)
{
LLFloaterModelPreview *fp =(LLFloaterModelPreview *)userdata;
@@ -1577,7 +1595,7 @@ bool LLModelLoader::doLoadModel()
{
//llinfos<<"joint "<<lookingForJoint.c_str()<<llendl;
LLMatrix4 jointTransform = mJointList[lookingForJoint];
- LLJoint* pJoint = gAgentAvatarp->getJoint( lookingForJoint );
+ LLJoint* pJoint = mPreview->getPreviewAvatar()->getJoint( lookingForJoint );
if ( pJoint )
{
pJoint->storeCurrentXform( jointTransform.getTranslation() );
@@ -1639,7 +1657,7 @@ bool LLModelLoader::doLoadModel()
{
if (pos.getCount() <= j+2)
{
- llerrs << "WTF?" << llendl;
+ llerrs << "Invalid position array size." << llendl;
}
LLVector3 v(pos[j], pos[j+1], pos[j+2]);
@@ -2301,8 +2319,11 @@ void LLModelLoader::processElement(daeElement* element)
{
domFloat3 dom_value = scale->getValue();
+
+ LLVector3 scale_vector = LLVector3(dom_value[0], dom_value[1], dom_value[2]);
+ scale_vector.abs(); // Set all values positive, since we don't currently support mirrored meshes
LLMatrix4 scaling;
- scaling.initScale(LLVector3(dom_value[0], dom_value[1], dom_value[2]));
+ scaling.initScale(scale_vector);
scaling *= mTransform;
mTransform = scaling;
@@ -2597,6 +2618,7 @@ LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp)
mLoading = false;
mLoadState = LLModelLoader::STARTING;
mGroup = 0;
+ mLODFrozen = false;
mBuildShareTolerance = 0.f;
mBuildQueueMode = GLOD_QUEUE_GREEDY;
mBuildBorderMode = GLOD_BORDER_UNLOCK;
@@ -2605,6 +2627,13 @@ LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp)
for (U32 i = 0; i < LLModel::NUM_LODS; ++i)
{
mRequestedTriangleCount[i] = 0;
+ mRequestedCreaseAngle[i] = -1.f;
+ mRequestedLoDMode[i] = 0;
+ mRequestedErrorThreshold[i] = 0.f;
+ mRequestedBuildOperator[i] = 0;
+ mRequestedQueueMode[i] = 0;
+ mRequestedBorderMode[i] = 0;
+ mRequestedShareTolerance[i] = 0.f;
}
mViewOption["show_textures"] = false;
@@ -2649,6 +2678,8 @@ LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp)
mMasterLegacyJointList.push_front("mHipLeft");
mMasterLegacyJointList.push_front("mKneeLeft");
mMasterLegacyJointList.push_front("mFootLeft");
+
+ createPreviewAvatar();
}
LLModelPreview::~LLModelPreview()
@@ -2702,7 +2733,7 @@ U32 LLModelPreview::calcResourceCost()
if ( mFMP && mFMP->childGetValue("upload_joints").asBoolean() )
{
- gAgentAvatarp->setPelvisOffset( mPelvisZOffset );
+ getPreviewAvatar()->setPelvisOffset( mPelvisZOffset );
}
F32 streaming_cost = 0.f;
@@ -3247,6 +3278,8 @@ void LLModelPreview::generateNormals()
F32 angle_cutoff = mFMP->childGetValue("crease_angle").asReal();
+ mRequestedCreaseAngle[which_lod] = angle_cutoff;
+
angle_cutoff *= DEG_TO_RAD;
if (which_lod == 3 && !mBaseModel.empty())
@@ -3266,7 +3299,7 @@ void LLModelPreview::generateNormals()
mVertexBuffer[which_lod].clear();
refresh();
-
+ updateStatusMessages();
}
void LLModelPreview::clearMaterials()
@@ -3342,6 +3375,7 @@ void LLModelPreview::genLODs(S32 which_lod, U32 decimation, bool enforce_tri_lim
{
lod_mode = iface->getFirstSelectedIndex();
}
+ mRequestedLoDMode[mPreviewLOD] = lod_mode;
F32 lod_error_threshold = mFMP->childGetValue("lod_error_threshold").asReal();
@@ -3365,6 +3399,7 @@ void LLModelPreview::genLODs(S32 which_lod, U32 decimation, bool enforce_tri_lim
{
build_operator = iface->getFirstSelectedIndex();
}
+ mRequestedBuildOperator[mPreviewLOD] = build_operator;
if (build_operator == 0)
{
@@ -3381,6 +3416,7 @@ void LLModelPreview::genLODs(S32 which_lod, U32 decimation, bool enforce_tri_lim
{
queue_mode = iface->getFirstSelectedIndex();
}
+ mRequestedQueueMode[mPreviewLOD] = queue_mode;
if (queue_mode == 0)
{
@@ -3402,6 +3438,7 @@ void LLModelPreview::genLODs(S32 which_lod, U32 decimation, bool enforce_tri_lim
{
border_mode = iface->getFirstSelectedIndex();
}
+ mRequestedBorderMode[mPreviewLOD] = border_mode;
if (border_mode == 0)
{
@@ -3437,6 +3474,7 @@ void LLModelPreview::genLODs(S32 which_lod, U32 decimation, bool enforce_tri_lim
mBuildShareTolerance = share_tolerance;
object_dirty = true;
}
+ mRequestedShareTolerance[mPreviewLOD] = share_tolerance;
if (mGroup == 0)
{
@@ -3545,6 +3583,7 @@ void LLModelPreview::genLODs(S32 which_lod, U32 decimation, bool enforce_tri_lim
U32 submeshes = 0;
mRequestedTriangleCount[lod] = triangle_count;
+ mRequestedErrorThreshold[lod] = lod_error_threshold;
glodGroupParameteri(mGroup, GLOD_ADAPT_MODE, lod_mode);
stop_gloderror();
@@ -3832,6 +3871,18 @@ void LLModelPreview::updateStatusMessages()
}
}
+
+ //make sure no hulls have more than 256 points in them
+ for (U32 i = 0; upload_ok && i < mModel[LLModel::LOD_PHYSICS].size(); ++i)
+ {
+ LLModel* mdl = mModel[LLModel::LOD_PHYSICS][i];
+
+ for (U32 j = 0; upload_ok && j < mdl->mPhysics.mHull.size(); ++j)
+ {
+ upload_ok = upload_ok && mdl->mPhysics.mHull[i].size() <= 256;
+ }
+ }
+
bool errorStateFromLoader = getLoadState() >= LLModelLoader::ERROR_PARSING ? true : false;
bool skinAndRigOk = true;
@@ -3855,6 +3906,10 @@ void LLModelPreview::updateStatusMessages()
{
mFMP->childEnable("ok_btn");
}
+ else
+ {
+ mFMP->childDisable("ok_btn");
+ }
//add up physics triangles etc
S32 start = 0;
@@ -4021,6 +4076,9 @@ void LLModelPreview::updateStatusMessages()
{ // auto generate, also the default case for wizard which has no radio selection
fmp->mLODMode[mPreviewLOD] = 1;
+ //don't actually regenerate lod when refreshing UI
+ mLODFrozen = true;
+
for (U32 i = 0; i < num_file_controls; ++i)
{
mFMP->childDisable(file_controls[i]);
@@ -4033,20 +4091,21 @@ void LLModelPreview::updateStatusMessages()
//if (threshold)
{
- U32 lod_mode = 0;
- LLCtrlSelectionInterface* iface = mFMP->childGetSelectionInterface("lod_mode");
- if (iface)
- {
- lod_mode = iface->getFirstSelectedIndex();
- }
-
LLSpinCtrl* threshold = mFMP->getChild<LLSpinCtrl>("lod_error_threshold");
LLSpinCtrl* limit = mFMP->getChild<LLSpinCtrl>("lod_triangle_limit");
limit->setMaxValue(mMaxTriangleLimit);
- limit->setValue(mRequestedTriangleCount[mPreviewLOD]);
+ limit->forceSetValue(mRequestedTriangleCount[mPreviewLOD]);
+
+ threshold->forceSetValue(mRequestedErrorThreshold[mPreviewLOD]);
- if (lod_mode == 0)
+ mFMP->getChild<LLComboBox>("lod_mode")->selectNthItem(mRequestedLoDMode[mPreviewLOD]);
+ mFMP->getChild<LLComboBox>("build_operator")->selectNthItem(mRequestedBuildOperator[mPreviewLOD]);
+ mFMP->getChild<LLComboBox>("queue_mode")->selectNthItem(mRequestedQueueMode[mPreviewLOD]);
+ mFMP->getChild<LLComboBox>("border_mode")->selectNthItem(mRequestedBorderMode[mPreviewLOD]);
+ mFMP->getChild<LLSpinCtrl>("share_tolerance")->setValue(mRequestedShareTolerance[mPreviewLOD]);
+
+ if (mRequestedLoDMode[mPreviewLOD] == 0)
{
limit->setVisible(true);
threshold->setVisible(false);
@@ -4060,6 +4119,8 @@ void LLModelPreview::updateStatusMessages()
threshold->setVisible(true);
}
}
+
+ mLODFrozen = false;
}
}
@@ -4075,6 +4136,20 @@ void LLModelPreview::updateStatusMessages()
mFMP->childDisable("physics_file");
mFMP->childDisable("physics_browse");
}
+
+ LLSpinCtrl* crease = mFMP->getChild<LLSpinCtrl>("crease_angle");
+
+ if (mRequestedCreaseAngle[mPreviewLOD] == -1.f)
+ {
+ mFMP->childSetColor("crease_label", LLColor4::grey);
+ crease->forceSetValue(75.f);
+ }
+ else
+ {
+ mFMP->childSetColor("crease_label", LLColor4::white);
+ crease->forceSetValue(mRequestedCreaseAngle[mPreviewLOD]);
+ }
+
}
void LLModelPreview::setPreviewTarget(F32 distance)
@@ -4189,11 +4264,7 @@ void LLModelPreview::genBuffers(S32 lod, bool include_skin_weights)
const LLModel::weight_list& weight_list = base_mdl->getJointInfluences(pos);
LLVector4 w(0,0,0,0);
- if (weight_list.size() > 4)
- {
- llerrs << "WTF?" << llendl;
- }
-
+
for (U32 i = 0; i < weight_list.size(); ++i)
{
F32 wght = llmin(weight_list[i].mWeight, 0.999999f);
@@ -4241,42 +4312,6 @@ void LLModelPreview::update()
}
//-----------------------------------------------------------------------------
-// changeAvatarsJointPositions()
-//-----------------------------------------------------------------------------
-void LLModelPreview::changeAvatarsJointPositions( LLModel* pModel )
-{
- if ( mMasterJointList.empty() )
- {
- return;
- }
-
- std::vector<std::string> :: const_iterator jointListItBegin = pModel->mSkinInfo.mJointNames.begin();
- std::vector<std::string> :: const_iterator jointListItEnd = pModel->mSkinInfo.mJointNames.end();
-
- S32 index = 0;
- for ( ; jointListItBegin!=jointListItEnd; ++jointListItBegin, ++index )
- {
- std::string elem = *jointListItBegin;
- //llinfos<<"joint "<<elem<<llendl;
-
- S32 matrixCnt = pModel->mSkinInfo.mAlternateBindMatrix.size();
- if ( matrixCnt < 1 )
- {
- llinfos<<"Total WTF moment :"<<matrixCnt<<llendl;
- }
- else
- {
- LLMatrix4 jointTransform = pModel->mSkinInfo.mAlternateBindMatrix[index];
-
- LLJoint* pJoint = gAgentAvatarp->getJoint( elem );
- if ( pJoint )
- {
- pJoint->storeCurrentXform( jointTransform.getTranslation() );
- }
- }
- }
-}
-//-----------------------------------------------------------------------------
// getTranslationForJointOffset()
//-----------------------------------------------------------------------------
LLVector3 LLModelPreview::getTranslationForJointOffset( std::string joint )
@@ -4290,6 +4325,30 @@ LLVector3 LLModelPreview::getTranslationForJointOffset( std::string joint )
return LLVector3(0.0f,0.0f,0.0f);
}
//-----------------------------------------------------------------------------
+// createPreviewAvatar
+//-----------------------------------------------------------------------------
+void LLModelPreview::createPreviewAvatar( void )
+{
+ mPreviewAvatar = (LLVOAvatar*)gObjectList.createObjectViewer( LL_PCODE_LEGACY_AVATAR, gAgent.getRegion() );
+ if ( mPreviewAvatar )
+ {
+ mPreviewAvatar->createDrawable( &gPipeline );
+ mPreviewAvatar->mIsDummy = TRUE;
+ mPreviewAvatar->mSpecialRenderMode = 1;
+ mPreviewAvatar->setPositionAgent( LLVector3::zero );
+ mPreviewAvatar->slamPosition();
+ mPreviewAvatar->updateJointLODs();
+ mPreviewAvatar->updateGeometry( mPreviewAvatar->mDrawable );
+ mPreviewAvatar->startMotion( ANIM_AGENT_STAND );
+ mPreviewAvatar->hideSkirt();
+ }
+ else
+ {
+ llinfos<<"Failed to create preview avatar for upload model window"<<llendl;
+ }
+}
+
+//-----------------------------------------------------------------------------
// render()
//-----------------------------------------------------------------------------
BOOL LLModelPreview::render()
@@ -4403,25 +4462,6 @@ BOOL LLModelPreview::render()
mFMP->childSetEnabled("upload_joints", upload_skin);
- //poke at avatar when we upload custom joints
- /*
- if ( upload_joints )
- {
- for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter)
- {
- for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter)
- {
- LLModelInstance& instance = *model_iter;
- LLModel* model = instance.mModel;
- if ( !model->mSkinWeights.empty() )
- {
- changeAvatarsJointPositions( model );
- }
- }
- }
- }
- */
-
F32 explode = mFMP->childGetValue("physics_explode").asReal();
glClear(GL_DEPTH_BUFFER_BIT);
@@ -4441,7 +4481,7 @@ BOOL LLModelPreview::render()
if (skin_weight)
{
- target_pos = gAgentAvatarp->getPositionAgent();
+ target_pos = getPreviewAvatar()->getPositionAgent();
z_near = 0.01f;
z_far = 1024.f;
mCameraDistance = 16.f;
@@ -4661,8 +4701,7 @@ BOOL LLModelPreview::render()
}
else
{
- LLVOAvatarSelf* avatar = gAgentAvatarp;
- target_pos = avatar->getPositionAgent();
+ target_pos = getPreviewAvatar()->getPositionAgent();
LLViewerCamera::getInstance()->setOriginAndLookAt(
target_pos + ((LLVector3(mCameraDistance, 0.f, 0.f) + offset) * av_rot), // camera
@@ -4671,7 +4710,7 @@ BOOL LLModelPreview::render()
if (joint_positions)
{
- avatar->renderCollisionVolumes();
+ getPreviewAvatar()->renderCollisionVolumes();
}
for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter)
@@ -4702,7 +4741,7 @@ BOOL LLModelPreview::render()
LLMatrix4 mat[64];
for (U32 j = 0; j < model->mSkinInfo.mJointNames.size(); ++j)
{
- LLJoint* joint = avatar->getJoint(model->mSkinInfo.mJointNames[j]);
+ LLJoint* joint = getPreviewAvatar()->getJoint(model->mSkinInfo.mJointNames[j]);
if (joint)
{
mat[j] = model->mSkinInfo.mInvBindMatrix[j];
@@ -4925,9 +4964,12 @@ void LLModelPreview::textureLoadedCallback( BOOL success, LLViewerFetchedTexture
void LLModelPreview::onLODParamCommit(bool enforce_tri_limit)
{
- genLODs(mPreviewLOD, 3, enforce_tri_limit);
- updateStatusMessages();
- refresh();
+ if (!mLODFrozen)
+ {
+ genLODs(mPreviewLOD, 3, enforce_tri_limit);
+ updateStatusMessages();
+ refresh();
+ }
}
LLFloaterModelPreview::DecompRequest::DecompRequest(const std::string& stage, LLModel* mdl)
@@ -4942,9 +4984,12 @@ LLFloaterModelPreview::DecompRequest::DecompRequest(const std::string& stage, LL
if (mdl)
{
U16 index_offset = 0;
+ U16 tri[3] ;
mPositions.clear();
mIndices.clear();
+ mBBox[1] = LLVector3(F32_MIN, F32_MIN, F32_MIN) ;
+ mBBox[0] = LLVector3(F32_MAX, F32_MAX, F32_MAX) ;
//queue up vertex positions and indices
for (S32 i = 0; i < mdl->getNumVolumeFaces(); ++i)
@@ -4958,12 +5003,28 @@ LLFloaterModelPreview::DecompRequest::DecompRequest(const std::string& stage, LL
for (U32 j = 0; j < face.mNumVertices; ++j)
{
mPositions.push_back(LLVector3(face.mPositions[j].getF32ptr()));
+ for(U32 k = 0 ; k < 3 ; k++)
+ {
+ mBBox[0].mV[k] = llmin(mBBox[0].mV[k], mPositions[j].mV[k]) ;
+ mBBox[1].mV[k] = llmax(mBBox[1].mV[k], mPositions[j].mV[k]) ;
+ }
}
- for (U32 j = 0; j < face.mNumIndices; ++j)
+ updateTriangleAreaThreshold() ;
+
+ for (U32 j = 0; j+2 < face.mNumIndices; j += 3)
{
- mIndices.push_back(face.mIndices[j]+index_offset);
- }
+ tri[0] = face.mIndices[j] + index_offset ;
+ tri[1] = face.mIndices[j + 1] + index_offset ;
+ tri[2] = face.mIndices[j + 2] + index_offset ;
+
+ if(isValidTriangle(tri[0], tri[1], tri[2]))
+ {
+ mIndices.push_back(tri[0]);
+ mIndices.push_back(tri[1]);
+ mIndices.push_back(tri[2]);
+ }
+ }
index_offset += face.mNumVertices;
}
diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h
index 4d8b46807f..ed7cbe6d1e 100644
--- a/indra/newview/llfloatermodelpreview.h
+++ b/indra/newview/llfloatermodelpreview.h
@@ -199,6 +199,8 @@ protected:
static void onUploadJointsCommit(LLUICtrl*,void*);
static void onUploadSkinCommit(LLUICtrl*,void*);
+ static void onPhysicsLoadRadioCommit(LLUICtrl*,void *data);
+
static void onPreviewLODCommit(LLUICtrl*,void*);
static void onGenerateNormalsCommit(LLUICtrl*,void*);
@@ -341,6 +343,9 @@ public:
LLVector3 getTranslationForJointOffset( std::string joint );
+ void createPreviewAvatar( void );
+ LLVOAvatar* getPreviewAvatar( void ) { return mPreviewAvatar; }
+
protected:
friend class LLModelLoader;
friend class LLFloaterModelPreview;
@@ -373,13 +378,20 @@ public:
std::map<std::string, bool> mViewOption;
//GLOD object parameters (must rebuild object if these change)
+ bool mLODFrozen;
F32 mBuildShareTolerance;
U32 mBuildQueueMode;
U32 mBuildOperator;
U32 mBuildBorderMode;
+ U32 mRequestedLoDMode[LLModel::NUM_LODS];
S32 mRequestedTriangleCount[LLModel::NUM_LODS];
+ F32 mRequestedErrorThreshold[LLModel::NUM_LODS];
+ U32 mRequestedBuildOperator[LLModel::NUM_LODS];
+ U32 mRequestedQueueMode[LLModel::NUM_LODS];
+ U32 mRequestedBorderMode[LLModel::NUM_LODS];
+ F32 mRequestedShareTolerance[LLModel::NUM_LODS];
+ F32 mRequestedCreaseAngle[LLModel::NUM_LODS];
-
LLModelLoader* mModelLoader;
LLModelLoader::scene mScene[LLModel::NUM_LODS];
@@ -415,6 +427,7 @@ public:
std::deque<std::string> mMasterLegacyJointList;
std::deque<std::string> mJointsFromNode;
JointTransformMap mJointTransformMap;
+ LLPointer<LLVOAvatar> mPreviewAvatar;
};
#endif // LL_LLFLOATERMODELPREVIEW_H
diff --git a/indra/newview/llfloatermodelwizard.cpp b/indra/newview/llfloatermodelwizard.cpp
index faf81dbc5c..e44737f39e 100644
--- a/indra/newview/llfloatermodelwizard.cpp
+++ b/indra/newview/llfloatermodelwizard.cpp
@@ -441,10 +441,13 @@ LLFloaterModelWizard::DecompRequest::DecompRequest(const std::string& stage, LLM
if (mdl)
{
U16 index_offset = 0;
+ U16 tri[3] ;
mPositions.clear();
mIndices.clear();
-
+ mBBox[1] = LLVector3(F32_MIN, F32_MIN, F32_MIN) ;
+ mBBox[0] = LLVector3(F32_MAX, F32_MAX, F32_MAX) ;
+
//queue up vertex positions and indices
for (S32 i = 0; i < mdl->getNumVolumeFaces(); ++i)
{
@@ -457,11 +460,27 @@ LLFloaterModelWizard::DecompRequest::DecompRequest(const std::string& stage, LLM
for (U32 j = 0; j < face.mNumVertices; ++j)
{
mPositions.push_back(LLVector3(face.mPositions[j].getF32ptr()));
+ for(U32 k = 0 ; k < 3 ; k++)
+ {
+ mBBox[0].mV[k] = llmin(mBBox[0].mV[k], mPositions[j].mV[k]) ;
+ mBBox[1].mV[k] = llmax(mBBox[1].mV[k], mPositions[j].mV[k]) ;
+ }
}
- for (U32 j = 0; j < face.mNumIndices; ++j)
+ updateTriangleAreaThreshold() ;
+
+ for (U32 j = 0; j+2 < face.mNumIndices; j += 3)
{
- mIndices.push_back(face.mIndices[j]+index_offset);
+ tri[0] = face.mIndices[j] + index_offset ;
+ tri[1] = face.mIndices[j + 1] + index_offset ;
+ tri[2] = face.mIndices[j + 2] + index_offset ;
+
+ if(isValidTriangle(tri[0], tri[1], tri[2]))
+ {
+ mIndices.push_back(tri[0]);
+ mIndices.push_back(tri[1]);
+ mIndices.push_back(tri[2]);
+ }
}
index_offset += face.mNumVertices;
diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp
index 34fda49375..fc6976755f 100644
--- a/indra/newview/llfloaterregioninfo.cpp
+++ b/indra/newview/llfloaterregioninfo.cpp
@@ -590,6 +590,11 @@ bool LLPanelRegionGeneralInfo::refreshFromRegion(LLViewerRegion* region)
getChildView("im_btn")->setEnabled(allow_modify);
getChildView("manage_telehub_btn")->setEnabled(allow_modify);
+ const bool enable_mesh = gSavedSettings.getBOOL("MeshEnabled") &&
+ gAgent.getRegion() &&
+ !gAgent.getRegion()->getCapability("GetMesh").empty();
+ getChildView("mesh_rez_enabled_check")->setVisible(enable_mesh);
+ getChildView("mesh_rez_enabled_check")->setEnabled(getChildView("mesh_rez_enabled_check")->getEnabled() && enable_mesh);
// Data gets filled in by processRegionInfo
return LLPanelRegionInfo::refreshFromRegion(region);
diff --git a/indra/newview/llfloatertools.cpp b/indra/newview/llfloatertools.cpp
index 73c1f99fa0..061a42ab57 100644
--- a/indra/newview/llfloatertools.cpp
+++ b/indra/newview/llfloatertools.cpp
@@ -85,6 +85,7 @@
#include "llviewerwindow.h"
#include "llvovolume.h"
#include "lluictrlfactory.h"
+#include "llaccountingquotamanager.h"
// Globals
LLFloaterTools *gFloaterTools = NULL;
diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp
index b5180854ef..318beafe65 100644
--- a/indra/newview/llinventorymodel.cpp
+++ b/indra/newview/llinventorymodel.cpp
@@ -632,10 +632,12 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item)
}
// We're hiding mesh types
+#if 0
if (item->getType() == LLAssetType::AT_MESH)
{
return mask;
}
+#endif
LLViewerInventoryItem* old_item = getItem(item->getUUID());
LLPointer<LLViewerInventoryItem> new_item;
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 0b96a3b34f..bef2a09e6c 100644..100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -1,3827 +1,3943 @@
-/**
- * @file llmeshrepository.cpp
- * @brief Mesh repository implementation.
- *
- * $LicenseInfo:firstyear=2005&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$
- */
-
-#include "llviewerprecompiledheaders.h"
-
-#include "apr_pools.h"
-#include "apr_dso.h"
-#include "llhttpstatuscodes.h"
-#include "llmeshrepository.h"
-
-#include "llagent.h"
-#include "llappviewer.h"
-#include "llbufferstream.h"
-#include "llcurl.h"
-#include "lldatapacker.h"
-#include "llfasttimer.h"
-#include "llfloatermodelpreview.h"
-#include "llfloaterperms.h"
-#include "lleconomy.h"
-#include "llimagej2c.h"
-#include "llhost.h"
-#include "llnotificationsutil.h"
-#include "llsd.h"
-#include "llsdutil_math.h"
-#include "llsdserialize.h"
-#include "llthread.h"
-#include "llvfile.h"
-#include "llviewercontrol.h"
-#include "llviewermenufile.h"
-#include "llviewerobjectlist.h"
-#include "llviewerregion.h"
-#include "llviewertexturelist.h"
-#include "llvolume.h"
-#include "llvolumemgr.h"
-#include "llvovolume.h"
-#include "llworld.h"
-#include "material_codes.h"
-#include "pipeline.h"
-#include "llinventorymodel.h"
-#include "llfoldertype.h"
-
-#ifndef LL_WINDOWS
-#include "netdb.h"
-#endif
-
-#include <queue>
-
-LLFastTimer::DeclareTimer FTM_MESH_UPDATE("Mesh Update");
-LLFastTimer::DeclareTimer FTM_LOAD_MESH("Load Mesh");
-
-LLMeshRepository gMeshRepo;
-
-const U32 MAX_MESH_REQUESTS_PER_SECOND = 100;
-
-U32 LLMeshRepository::sBytesReceived = 0;
-U32 LLMeshRepository::sHTTPRequestCount = 0;
-U32 LLMeshRepository::sHTTPRetryCount = 0;
-U32 LLMeshRepository::sCacheBytesRead = 0;
-U32 LLMeshRepository::sCacheBytesWritten = 0;
-U32 LLMeshRepository::sPeakKbps = 0;
-
-
-const U32 MAX_TEXTURE_UPLOAD_RETRIES = 5;
-
-std::string header_lod[] =
-{
- "lowest_lod",
- "low_lod",
- "medium_lod",
- "high_lod"
-};
-
-
-//get the number of bytes resident in memory for given volume
-U32 get_volume_memory_size(const LLVolume* volume)
-{
- U32 indices = 0;
- U32 vertices = 0;
-
- for (U32 i = 0; i < volume->getNumVolumeFaces(); ++i)
- {
- const LLVolumeFace& face = volume->getVolumeFace(i);
- indices += face.mNumIndices;
- vertices += face.mNumVertices;
- }
-
-
- return indices*2+vertices*11+sizeof(LLVolume)+sizeof(LLVolumeFace)*volume->getNumVolumeFaces();
-}
-
-void get_vertex_buffer_from_mesh(LLCDMeshData& mesh, LLModel::PhysicsMesh& res, F32 scale = 1.f)
-{
- res.mPositions.clear();
- res.mNormals.clear();
-
- const F32* v = mesh.mVertexBase;
-
- if (mesh.mIndexType == LLCDMeshData::INT_16)
- {
- U16* idx = (U16*) mesh.mIndexBase;
- for (S32 j = 0; j < mesh.mNumTriangles; ++j)
- {
- F32* mp0 = (F32*) ((U8*)v+idx[0]*mesh.mVertexStrideBytes);
- F32* mp1 = (F32*) ((U8*)v+idx[1]*mesh.mVertexStrideBytes);
- F32* mp2 = (F32*) ((U8*)v+idx[2]*mesh.mVertexStrideBytes);
-
- idx = (U16*) (((U8*)idx)+mesh.mIndexStrideBytes);
-
- LLVector3 v0(mp0);
- LLVector3 v1(mp1);
- LLVector3 v2(mp2);
-
- LLVector3 n = (v1-v0)%(v2-v0);
- n.normalize();
-
- res.mPositions.push_back(v0*scale);
- res.mPositions.push_back(v1*scale);
- res.mPositions.push_back(v2*scale);
-
- res.mNormals.push_back(n);
- res.mNormals.push_back(n);
- res.mNormals.push_back(n);
- }
- }
- else
- {
- U32* idx = (U32*) mesh.mIndexBase;
- for (S32 j = 0; j < mesh.mNumTriangles; ++j)
- {
- F32* mp0 = (F32*) ((U8*)v+idx[0]*mesh.mVertexStrideBytes);
- F32* mp1 = (F32*) ((U8*)v+idx[1]*mesh.mVertexStrideBytes);
- F32* mp2 = (F32*) ((U8*)v+idx[2]*mesh.mVertexStrideBytes);
-
- idx = (U32*) (((U8*)idx)+mesh.mIndexStrideBytes);
-
- LLVector3 v0(mp0);
- LLVector3 v1(mp1);
- LLVector3 v2(mp2);
-
- LLVector3 n = (v1-v0)%(v2-v0);
- n.normalize();
-
- res.mPositions.push_back(v0*scale);
- res.mPositions.push_back(v1*scale);
- res.mPositions.push_back(v2*scale);
-
- res.mNormals.push_back(n);
- res.mNormals.push_back(n);
- res.mNormals.push_back(n);
- }
- }
-}
-
-S32 LLMeshRepoThread::sActiveHeaderRequests = 0;
-S32 LLMeshRepoThread::sActiveLODRequests = 0;
-U32 LLMeshRepoThread::sMaxConcurrentRequests = 1;
-
-
-class LLTextureCostResponder : public LLCurl::Responder
-{
-public:
- LLTextureUploadData mData;
- LLMeshUploadThread* mThread;
-
- LLTextureCostResponder(LLTextureUploadData data, LLMeshUploadThread* thread)
- : mData(data), mThread(thread)
- {
-
- }
-
- virtual void completed(U32 status, const std::string& reason, const LLSD& content)
- {
- mThread->mPendingConfirmations--;
- if (isGoodStatus(status))
- {
- mThread->priceResult(mData, content);
- }
- else
- {
- llwarns << status << ": " << reason << llendl;
-
- if (mData.mRetries < MAX_TEXTURE_UPLOAD_RETRIES)
- {
- llwarns << "Retrying. (" << ++mData.mRetries << ")" << llendl;
-
- if (status == 499 || status == 500)
- {
- mThread->uploadTexture(mData);
- }
- else
- {
- llerrs << "Unhandled status " << status << llendl;
- }
- }
- else
- {
- llwarns << "Giving up after " << mData.mRetries << " retries." << llendl;
- }
- }
- }
-};
-
-class LLTextureUploadResponder : public LLCurl::Responder
-{
-public:
- LLTextureUploadData mData;
- LLMeshUploadThread* mThread;
-
- LLTextureUploadResponder(LLTextureUploadData data, LLMeshUploadThread* thread)
- : mData(data), mThread(thread)
- {
- }
-
- virtual void completed(U32 status, const std::string& reason, const LLSD& content)
- {
- mThread->mPendingUploads--;
- if (isGoodStatus(status))
- {
- mData.mUUID = content["new_asset"].asUUID();
- gMeshRepo.updateInventory(LLMeshRepository::inventory_data(mData.mPostData, content));
- mThread->onTextureUploaded(mData);
- }
- else
- {
- llwarns << status << ": " << reason << llendl;
- llwarns << "Retrying. (" << ++mData.mRetries << ")" << llendl;
-
- if (status == 404)
- {
- mThread->uploadTexture(mData);
- }
- else if (status == 499)
- {
- mThread->mConfirmedTextureQ.push(mData);
- }
- else
- {
- llerrs << "Unhandled status " << status << llendl;
- }
- }
- }
-};
-
-class LLMeshCostResponder : public LLCurl::Responder
-{
-public:
- LLMeshUploadData mData;
- LLMeshUploadThread* mThread;
-
- LLMeshCostResponder(LLMeshUploadData data, LLMeshUploadThread* thread)
- : mData(data), mThread(thread)
- {
-
- }
-
- virtual void completed(U32 status, const std::string& reason, const LLSD& content)
- {
- mThread->mPendingConfirmations--;
-
- if (isGoodStatus(status))
- {
- mThread->priceResult(mData, content);
- }
- else
- {
- llwarns << status << ": " << reason << llendl;
-
- if (status == HTTP_INTERNAL_ERROR)
- {
- llwarns << "Retrying. (" << ++mData.mRetries << ")" << llendl;
- mThread->uploadModel(mData);
- }
- else if (status == HTTP_BAD_REQUEST)
- {
- llwarns << "Status 400 received from server, giving up." << llendl;
- }
- else if (status == HTTP_NOT_FOUND)
- {
- llwarns <<"Status 404 received, server is disconnected, giving up." << llendl ;
- }
- else
- {
- llerrs << "Unhandled status " << status << llendl;
- }
- }
- }
-};
-
-class LLMeshUploadResponder : public LLCurl::Responder
-{
-public:
- LLMeshUploadData mData;
- LLMeshUploadThread* mThread;
-
- LLMeshUploadResponder(LLMeshUploadData data, LLMeshUploadThread* thread)
- : mData(data), mThread(thread)
- {
- }
-
- virtual void completed(U32 status, const std::string& reason, const LLSD& content)
- {
- mThread->mPendingUploads--;
- if (isGoodStatus(status))
- {
- mData.mUUID = content["new_asset"].asUUID();
- if (mData.mUUID.isNull())
- {
- LLSD args;
- std::string message = content["error"]["message"];
- std::string identifier = content["error"]["identifier"];
- std::string invalidity_identifier = content["error"]["invalidity_identifier"];
-
- args["MESSAGE"] = message;
- args["IDENTIFIER"] = identifier;
- args["INVALIDITY_IDENTIFIER"] = invalidity_identifier;
- args["LABEL"] = mData.mBaseModel->mLabel;
-
- gMeshRepo.uploadError(args);
- }
- else
- {
- gMeshRepo.updateInventory(LLMeshRepository::inventory_data(mData.mPostData, content));
- mThread->onModelUploaded(mData);
- }
- }
- else
- {
- llwarns << status << ": " << reason << llendl;
- llwarns << "Retrying. (" << ++mData.mRetries << ")" << llendl;
-
- if (status == 404)
- {
- mThread->uploadModel(mData);
- }
- else if (status == 499)
- {
- mThread->mConfirmedQ.push(mData);
- }
- else if (status != 500)
- { //drop internal server errors on the floor, otherwise grab
- llerrs << "Unhandled status " << status << llendl;
- }
- }
- }
-};
-
-
-class LLMeshHeaderResponder : public LLCurl::Responder
-{
-public:
- LLVolumeParams mMeshParams;
-
- LLMeshHeaderResponder(const LLVolumeParams& mesh_params)
- : mMeshParams(mesh_params)
- {
- }
-
- virtual void completedRaw(U32 status, const std::string& reason,
- const LLChannelDescriptors& channels,
- const LLIOPipe::buffer_ptr_t& buffer);
-
-};
-
-class LLMeshLODResponder : public LLCurl::Responder
-{
-public:
- LLVolumeParams mMeshParams;
- S32 mLOD;
- U32 mRequestedBytes;
- U32 mOffset;
-
- LLMeshLODResponder(const LLVolumeParams& mesh_params, S32 lod, U32 offset, U32 requested_bytes)
- : mMeshParams(mesh_params), mLOD(lod), mOffset(offset), mRequestedBytes(requested_bytes)
- {
- }
-
- virtual void completedRaw(U32 status, const std::string& reason,
- const LLChannelDescriptors& channels,
- const LLIOPipe::buffer_ptr_t& buffer);
-
-};
-
-class LLMeshSkinInfoResponder : public LLCurl::Responder
-{
-public:
- LLUUID mMeshID;
- U32 mRequestedBytes;
- U32 mOffset;
-
- LLMeshSkinInfoResponder(const LLUUID& id, U32 offset, U32 size)
- : mMeshID(id), mRequestedBytes(size), mOffset(offset)
- {
- }
-
- virtual void completedRaw(U32 status, const std::string& reason,
- const LLChannelDescriptors& channels,
- const LLIOPipe::buffer_ptr_t& buffer);
-
-};
-
-class LLMeshDecompositionResponder : public LLCurl::Responder
-{
-public:
- LLUUID mMeshID;
- U32 mRequestedBytes;
- U32 mOffset;
-
- LLMeshDecompositionResponder(const LLUUID& id, U32 offset, U32 size)
- : mMeshID(id), mRequestedBytes(size), mOffset(offset)
- {
- }
-
- virtual void completedRaw(U32 status, const std::string& reason,
- const LLChannelDescriptors& channels,
- const LLIOPipe::buffer_ptr_t& buffer);
-
-};
-
-class LLMeshPhysicsShapeResponder : public LLCurl::Responder
-{
-public:
- LLUUID mMeshID;
- U32 mRequestedBytes;
- U32 mOffset;
-
- LLMeshPhysicsShapeResponder(const LLUUID& id, U32 offset, U32 size)
- : mMeshID(id), mRequestedBytes(size), mOffset(offset)
- {
- }
-
- virtual void completedRaw(U32 status, const std::string& reason,
- const LLChannelDescriptors& channels,
- const LLIOPipe::buffer_ptr_t& buffer);
-
-};
-
-class LLModelObjectUploadResponder: public LLCurl::Responder
-{
- LLSD mObjectAsset;
- LLMeshUploadThread* mThread;
-
-public:
- LLModelObjectUploadResponder(LLMeshUploadThread* thread, const LLSD& object_asset):
- mThread(thread),
- mObjectAsset(object_asset)
- {
- }
-
- virtual void completedRaw(U32 status, const std::string& reason,
- const LLChannelDescriptors& channels,
- const LLIOPipe::buffer_ptr_t& buffer)
- {
- assert_main_thread();
-
- llinfos << "completed" << llendl;
- mThread->mPendingUploads--;
- mThread->mFinished = true;
- }
-};
-
-class LLWholeModelFeeResponder: public LLCurl::Responder
-{
- LLMeshUploadThread* mThread;
-public:
- LLWholeModelFeeResponder(LLMeshUploadThread* thread):
- mThread(thread)
- {
- }
- virtual void completedRaw(U32 status, const std::string& reason,
- const LLChannelDescriptors& channels,
- const LLIOPipe::buffer_ptr_t& buffer)
- {
- assert_main_thread();
- llinfos << "completed" << llendl;
- mThread->mPendingUploads--;
- }
-
-};
-
-LLMeshRepoThread::LLMeshRepoThread()
-: LLThread("mesh repo", NULL)
-{
- mWaiting = false;
- mMutex = new LLMutex(NULL);
- mHeaderMutex = new LLMutex(NULL);
- mSignal = new LLCondition(NULL);
-}
-
-LLMeshRepoThread::~LLMeshRepoThread()
-{
- delete mMutex;
- mMutex = NULL;
- delete mHeaderMutex;
- mHeaderMutex = NULL;
- delete mSignal;
- mSignal = NULL;
-}
-
-void LLMeshRepoThread::run()
-{
- mCurlRequest = new LLCurlRequest();
- LLCDResult res = LLConvexDecomposition::initThread();
- if (res != LLCD_OK)
- {
- llwarns << "convex decomposition unable to be loaded" << llendl;
- }
-
- while (!LLApp::isQuitting())
- {
- mWaiting = true;
- mSignal->wait();
- mWaiting = false;
-
- if (!LLApp::isQuitting())
- {
- static U32 count = 0;
-
- static F32 last_hundred = gFrameTimeSeconds;
-
- if (gFrameTimeSeconds - last_hundred > 1.f)
- { //a second has gone by, clear count
- last_hundred = gFrameTimeSeconds;
- count = 0;
- }
-
- // NOTE: throttling intentionally favors LOD requests over header requests
-
- while (!mLODReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && sActiveLODRequests < sMaxConcurrentRequests)
- {
- {
- mMutex->lock();
- LODRequest req = mLODReqQ.front();
- mLODReqQ.pop();
- mMutex->unlock();
- if (fetchMeshLOD(req.mMeshParams, req.mLOD))
- {
- count++;
- }
- }
- }
-
- while (!mHeaderReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && sActiveHeaderRequests < sMaxConcurrentRequests)
- {
- {
- mMutex->lock();
- HeaderRequest req = mHeaderReqQ.front();
- mHeaderReqQ.pop();
- mMutex->unlock();
- if (fetchMeshHeader(req.mMeshParams))
- {
- count++;
- }
- }
- }
-
- { //mSkinRequests is protected by mSignal
- std::set<LLUUID> incomplete;
- for (std::set<LLUUID>::iterator iter = mSkinRequests.begin(); iter != mSkinRequests.end(); ++iter)
- {
- LLUUID mesh_id = *iter;
- if (!fetchMeshSkinInfo(mesh_id))
- {
- incomplete.insert(mesh_id);
- }
- }
- mSkinRequests = incomplete;
- }
-
- { //mDecompositionRequests is protected by mSignal
- std::set<LLUUID> incomplete;
- for (std::set<LLUUID>::iterator iter = mDecompositionRequests.begin(); iter != mDecompositionRequests.end(); ++iter)
- {
- LLUUID mesh_id = *iter;
- if (!fetchMeshDecomposition(mesh_id))
- {
- incomplete.insert(mesh_id);
- }
- }
- mDecompositionRequests = incomplete;
- }
-
- { //mPhysicsShapeRequests is protected by mSignal
- std::set<LLUUID> incomplete;
- for (std::set<LLUUID>::iterator iter = mPhysicsShapeRequests.begin(); iter != mPhysicsShapeRequests.end(); ++iter)
- {
- LLUUID mesh_id = *iter;
- if (!fetchMeshPhysicsShape(mesh_id))
- {
- incomplete.insert(mesh_id);
- }
- }
- mPhysicsShapeRequests = incomplete;
- }
-
- mCurlRequest->process();
- }
- }
-
- if (mSignal->isLocked())
- { //make sure to let go of the mutex associated with the given signal before shutting down
- mSignal->unlock();
- }
-
- res = LLConvexDecomposition::quitThread();
- if (res != LLCD_OK)
- {
- llwarns << "convex decomposition unable to be quit" << llendl;
- }
-
- delete mCurlRequest;
- mCurlRequest = NULL;
-}
-
-void LLMeshRepoThread::loadMeshSkinInfo(const LLUUID& mesh_id)
-{ //protected by mSignal, no locking needed here
- mSkinRequests.insert(mesh_id);
-}
-
-void LLMeshRepoThread::loadMeshDecomposition(const LLUUID& mesh_id)
-{ //protected by mSignal, no locking needed here
- mDecompositionRequests.insert(mesh_id);
-}
-
-void LLMeshRepoThread::loadMeshPhysicsShape(const LLUUID& mesh_id)
-{ //protected by mSignal, no locking needed here
- mPhysicsShapeRequests.insert(mesh_id);
-}
-
-
-void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod)
-{ //protected by mSignal, no locking needed here
-
- mesh_header_map::iterator iter = mMeshHeader.find(mesh_params.getSculptID());
- if (iter != mMeshHeader.end())
- { //if we have the header, request LOD byte range
- LODRequest req(mesh_params, lod);
- {
- LLMutexLock lock(mMutex);
- mLODReqQ.push(req);
- }
- }
- else
- {
- HeaderRequest req(mesh_params);
-
- pending_lod_map::iterator pending = mPendingLOD.find(mesh_params);
-
- if (pending != mPendingLOD.end())
- { //append this lod request to existing header request
- pending->second.push_back(lod);
- if (pending->second.size() > 4)
- {
- llerrs << "WTF?" << llendl;
- }
- }
- else
- { //if no header request is pending, fetch header
- LLMutexLock lock(mMutex);
- mHeaderReqQ.push(req);
- mPendingLOD[mesh_params].push_back(lod);
- }
- }
-}
-
-//static
-std::string LLMeshRepoThread::constructUrl(LLUUID mesh_id)
-{
- std::string http_url;
-
- if (gAgent.getRegion())
- {
- http_url = gMeshRepo.mGetMeshCapability;
- }
-
- if (!http_url.empty())
- {
- http_url += "/?mesh_id=";
- http_url += mesh_id.asString().c_str();
- }
- else
- {
- llwarns << "Current region does not have GetMesh capability! Cannot load " << mesh_id << ".mesh" << llendl;
- }
-
- return http_url;
-}
-
-bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)
-{ //protected by mMutex
- mHeaderMutex->lock();
-
- if (mMeshHeader.find(mesh_id) == mMeshHeader.end())
- { //we have no header info for this mesh, do nothing
- mHeaderMutex->unlock();
- return false;
- }
-
- U32 header_size = mMeshHeaderSize[mesh_id];
-
- if (header_size > 0)
- {
- S32 offset = header_size + mMeshHeader[mesh_id]["skin"]["offset"].asInteger();
- S32 size = mMeshHeader[mesh_id]["skin"]["size"].asInteger();
-
- mHeaderMutex->unlock();
-
- if (offset >= 0 && size > 0)
- {
- //check VFS for mesh skin info
- LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH);
- if (file.getSize() >= offset+size)
- {
- LLMeshRepository::sCacheBytesRead += size;
- file.seek(offset);
- U8* buffer = new U8[size];
- file.read(buffer, size);
-
- //make sure buffer isn't all 0's (reserved block but not written)
- bool zero = true;
- for (S32 i = 0; i < llmin(size, 1024) && zero; ++i)
- {
- zero = buffer[i] > 0 ? false : true;
- }
-
- if (!zero)
- { //attempt to parse
- if (skinInfoReceived(mesh_id, buffer, size))
- {
- delete[] buffer;
- return true;
- }
- }
-
- delete[] buffer;
- }
-
- //reading from VFS failed for whatever reason, fetch from sim
- std::vector<std::string> headers;
- headers.push_back("Accept: application/octet-stream");
-
- std::string http_url = constructUrl(mesh_id);
- if (!http_url.empty())
- {
- ++sActiveLODRequests;
- LLMeshRepository::sHTTPRequestCount++;
- mCurlRequest->getByteRange(constructUrl(mesh_id), headers, offset, size,
- new LLMeshSkinInfoResponder(mesh_id, offset, size));
- }
- }
- }
- else
- {
- mHeaderMutex->unlock();
- }
-
- //early out was not hit, effectively fetched
- return true;
-}
-
-bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)
-{ //protected by mMutex
- mHeaderMutex->lock();
-
- if (mMeshHeader.find(mesh_id) == mMeshHeader.end())
- { //we have no header info for this mesh, do nothing
- mHeaderMutex->unlock();
- return false;
- }
-
- U32 header_size = mMeshHeaderSize[mesh_id];
-
- if (header_size > 0)
- {
- S32 offset = header_size + mMeshHeader[mesh_id]["decomposition"]["offset"].asInteger();
- S32 size = mMeshHeader[mesh_id]["decomposition"]["size"].asInteger();
-
- mHeaderMutex->unlock();
-
- if (offset >= 0 && size > 0)
- {
- //check VFS for mesh skin info
- LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH);
- if (file.getSize() >= offset+size)
- {
- LLMeshRepository::sCacheBytesRead += size;
- file.seek(offset);
- U8* buffer = new U8[size];
- file.read(buffer, size);
-
- //make sure buffer isn't all 0's (reserved block but not written)
- bool zero = true;
- for (S32 i = 0; i < llmin(size, 1024) && zero; ++i)
- {
- zero = buffer[i] > 0 ? false : true;
- }
-
- if (!zero)
- { //attempt to parse
- if (decompositionReceived(mesh_id, buffer, size))
- {
- delete[] buffer;
- return true;
- }
- }
-
- delete[] buffer;
- }
-
- //reading from VFS failed for whatever reason, fetch from sim
- std::vector<std::string> headers;
- headers.push_back("Accept: application/octet-stream");
-
- std::string http_url = constructUrl(mesh_id);
- if (!http_url.empty())
- {
- ++sActiveLODRequests;
- LLMeshRepository::sHTTPRequestCount++;
- mCurlRequest->getByteRange(http_url, headers, offset, size,
- new LLMeshDecompositionResponder(mesh_id, offset, size));
- }
- }
- }
- else
- {
- mHeaderMutex->unlock();
- }
-
- //early out was not hit, effectively fetched
- return true;
-}
-
-bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id)
-{ //protected by mMutex
- mHeaderMutex->lock();
-
- if (mMeshHeader.find(mesh_id) == mMeshHeader.end())
- { //we have no header info for this mesh, do nothing
- mHeaderMutex->unlock();
- return false;
- }
-
- U32 header_size = mMeshHeaderSize[mesh_id];
-
- if (header_size > 0)
- {
- S32 offset = header_size + mMeshHeader[mesh_id]["physics_shape"]["offset"].asInteger();
- S32 size = mMeshHeader[mesh_id]["physics_shape"]["size"].asInteger();
-
- mHeaderMutex->unlock();
-
- if (offset >= 0 && size > 0)
- {
- //check VFS for mesh physics shape info
- LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH);
- if (file.getSize() >= offset+size)
- {
- LLMeshRepository::sCacheBytesRead += size;
- file.seek(offset);
- U8* buffer = new U8[size];
- file.read(buffer, size);
-
- //make sure buffer isn't all 0's (reserved block but not written)
- bool zero = true;
- for (S32 i = 0; i < llmin(size, 1024) && zero; ++i)
- {
- zero = buffer[i] > 0 ? false : true;
- }
-
- if (!zero)
- { //attempt to parse
- if (physicsShapeReceived(mesh_id, buffer, size))
- {
- delete[] buffer;
- return true;
- }
- }
-
- delete[] buffer;
- }
-
- //reading from VFS failed for whatever reason, fetch from sim
- std::vector<std::string> headers;
- headers.push_back("Accept: application/octet-stream");
-
- std::string http_url = constructUrl(mesh_id);
- if (!http_url.empty())
- {
- ++sActiveLODRequests;
- LLMeshRepository::sHTTPRequestCount++;
- mCurlRequest->getByteRange(http_url, headers, offset, size,
- new LLMeshPhysicsShapeResponder(mesh_id, offset, size));
- }
- }
- else
- { //no physics shape whatsoever, report back NULL
- physicsShapeReceived(mesh_id, NULL, 0);
- }
- }
- else
- {
- mHeaderMutex->unlock();
- }
-
- //early out was not hit, effectively fetched
- return true;
-}
-
-bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params)
-{
- bool retval = false;
-
- {
- //look for mesh in asset in vfs
- LLVFile file(gVFS, mesh_params.getSculptID(), LLAssetType::AT_MESH);
-
- S32 size = file.getSize();
-
- if (size > 0)
- {
- U8 buffer[1024];
- S32 bytes = llmin(size, 1024);
- LLMeshRepository::sCacheBytesRead += bytes;
- file.read(buffer, bytes);
- if (headerReceived(mesh_params, buffer, bytes))
- { //did not do an HTTP request, return false
- return false;
- }
- }
- }
-
- //either cache entry doesn't exist or is corrupt, request header from simulator
-
- std::vector<std::string> headers;
- headers.push_back("Accept: application/octet-stream");
-
- std::string http_url = constructUrl(mesh_params.getSculptID());
- if (!http_url.empty())
- {
- ++sActiveHeaderRequests;
- retval = true;
- //grab first 4KB if we're going to bother with a fetch. Cache will prevent future fetches if a full mesh fits
- //within the first 4KB
- LLMeshRepository::sHTTPRequestCount++;
- mCurlRequest->getByteRange(http_url, headers, 0, 4096, new LLMeshHeaderResponder(mesh_params));
- }
-
- return retval;
-}
-
-bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod)
-{ //protected by mMutex
- mHeaderMutex->lock();
-
- bool retval = false;
-
- LLUUID mesh_id = mesh_params.getSculptID();
-
- U32 header_size = mMeshHeaderSize[mesh_id];
-
- if (header_size > 0)
- {
- S32 offset = header_size + mMeshHeader[mesh_id][header_lod[lod]]["offset"].asInteger();
- S32 size = mMeshHeader[mesh_id][header_lod[lod]]["size"].asInteger();
- mHeaderMutex->unlock();
- if (offset >= 0 && size > 0)
- {
-
- //check VFS for mesh asset
- LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH);
- if (file.getSize() >= offset+size)
- {
- LLMeshRepository::sCacheBytesRead += size;
- file.seek(offset);
- U8* buffer = new U8[size];
- file.read(buffer, size);
-
- //make sure buffer isn't all 0's (reserved block but not written)
- bool zero = true;
- for (S32 i = 0; i < llmin(size, 1024) && zero; ++i)
- {
- zero = buffer[i] > 0 ? false : true;
- }
-
- if (!zero)
- { //attempt to parse
- if (lodReceived(mesh_params, lod, buffer, size))
- {
- delete[] buffer;
- return false;
- }
- }
-
- delete[] buffer;
- }
-
- //reading from VFS failed for whatever reason, fetch from sim
- std::vector<std::string> headers;
- headers.push_back("Accept: application/octet-stream");
-
- std::string http_url = constructUrl(mesh_id);
- if (!http_url.empty())
- {
- ++sActiveLODRequests;
- retval = true;
- LLMeshRepository::sHTTPRequestCount++;
- mCurlRequest->getByteRange(constructUrl(mesh_id), headers, offset, size,
- new LLMeshLODResponder(mesh_params, lod, offset, size));
- }
- else
- {
- mUnavailableQ.push(LODRequest(mesh_params, lod));
- }
- }
- else
- {
- mUnavailableQ.push(LODRequest(mesh_params, lod));
- }
- }
- else
- {
- mHeaderMutex->unlock();
- }
-
- return retval;
-}
-
-bool LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size)
-{
- LLSD header;
-
- U32 header_size = 0;
- if (data_size > 0)
- {
- std::string res_str((char*) data, data_size);
-
- std::string deprecated_header("<? LLSD/Binary ?>");
-
- if (res_str.substr(0, deprecated_header.size()) == deprecated_header)
- {
- res_str = res_str.substr(deprecated_header.size()+1, data_size);
- header_size = deprecated_header.size()+1;
- }
- data_size = res_str.size();
-
- std::istringstream stream(res_str);
-
- if (!LLSDSerialize::fromBinary(header, stream, data_size))
- {
- llwarns << "Mesh header parse error. Not a valid mesh asset!" << llendl;
- return false;
- }
-
- header_size += stream.tellg();
- }
- else
- {
- llinfos
- << "Marking header as non-existent, will not retry." << llendl;
- header["404"] = 1;
- }
-
- {
- U32 cost = gMeshRepo.calcResourceCost(header);
-
- LLUUID mesh_id = mesh_params.getSculptID();
-
- mHeaderMutex->lock();
- mMeshHeaderSize[mesh_id] = header_size;
- mMeshHeader[mesh_id] = header;
- mMeshResourceCost[mesh_id] = cost;
- mHeaderMutex->unlock();
-
- //check for pending requests
- pending_lod_map::iterator iter = mPendingLOD.find(mesh_params);
- if (iter != mPendingLOD.end())
- {
- LLMutexLock lock(mMutex);
- for (U32 i = 0; i < iter->second.size(); ++i)
- {
- LODRequest req(mesh_params, iter->second[i]);
- mLODReqQ.push(req);
- }
- }
- mPendingLOD.erase(iter);
- }
-
- return true;
-}
-
-bool LLMeshRepoThread::lodReceived(const LLVolumeParams& mesh_params, S32 lod, U8* data, S32 data_size)
-{
- LLVolume* volume = new LLVolume(mesh_params, LLVolumeLODGroup::getVolumeScaleFromDetail(lod));
- std::string mesh_string((char*) data, data_size);
- std::istringstream stream(mesh_string);
-
- if (volume->unpackVolumeFaces(stream, data_size))
- {
- LoadedMesh mesh(volume, mesh_params, lod);
- if (volume->getNumFaces() > 0)
- {
- LLMutexLock lock(mMutex);
- mLoadedQ.push(mesh);
- return true;
- }
- }
-
- return false;
-}
-
-bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 data_size)
-{
- LLSD skin;
-
- if (data_size > 0)
- {
- std::string res_str((char*) data, data_size);
-
- std::istringstream stream(res_str);
-
- if (!unzip_llsd(skin, stream, data_size))
- {
- llwarns << "Mesh skin info parse error. Not a valid mesh asset!" << llendl;
- return false;
- }
- }
-
- {
- LLMeshSkinInfo info(skin);
- info.mMeshID = mesh_id;
-
- //llinfos<<"info pelvis offset"<<info.mPelvisOffset<<llendl;
- mSkinInfoQ.push(info);
- }
-
- return true;
-}
-
-bool LLMeshRepoThread::decompositionReceived(const LLUUID& mesh_id, U8* data, S32 data_size)
-{
- LLSD decomp;
-
- if (data_size > 0)
- {
- std::string res_str((char*) data, data_size);
-
- std::istringstream stream(res_str);
-
- if (!unzip_llsd(decomp, stream, data_size))
- {
- llwarns << "Mesh decomposition parse error. Not a valid mesh asset!" << llendl;
- return false;
- }
- }
-
- {
- LLModel::Decomposition* d = new LLModel::Decomposition(decomp);
- d->mMeshID = mesh_id;
- mDecompositionQ.push(d);
- }
-
- return true;
-}
-
-bool LLMeshRepoThread::physicsShapeReceived(const LLUUID& mesh_id, U8* data, S32 data_size)
-{
- LLSD physics_shape;
-
- LLModel::Decomposition* d = new LLModel::Decomposition();
- d->mMeshID = mesh_id;
-
- if (data == NULL)
- { //no data, no physics shape exists
- d->mPhysicsShapeMesh.clear();
- }
- else
- {
- LLVolumeParams volume_params;
- volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE);
- volume_params.setSculptID(mesh_id, LL_SCULPT_TYPE_MESH);
- LLPointer<LLVolume> volume = new LLVolume(volume_params,0);
- std::string mesh_string((char*) data, data_size);
- std::istringstream stream(mesh_string);
-
- if (volume->unpackVolumeFaces(stream, data_size))
- {
- //load volume faces into decomposition buffer
- S32 vertex_count = 0;
- S32 index_count = 0;
-
- for (S32 i = 0; i < volume->getNumVolumeFaces(); ++i)
- {
- const LLVolumeFace& face = volume->getVolumeFace(i);
- vertex_count += face.mNumVertices;
- index_count += face.mNumIndices;
- }
-
- d->mPhysicsShapeMesh.clear();
-
- std::vector<LLVector3>& pos = d->mPhysicsShapeMesh.mPositions;
- std::vector<LLVector3>& norm = d->mPhysicsShapeMesh.mNormals;
-
- for (S32 i = 0; i < volume->getNumVolumeFaces(); ++i)
- {
- const LLVolumeFace& face = volume->getVolumeFace(i);
-
- for (S32 i = 0; i < face.mNumIndices; ++i)
- {
- U16 idx = face.mIndices[i];
-
- pos.push_back(LLVector3(face.mPositions[idx].getF32ptr()));
- norm.push_back(LLVector3(face.mNormals[idx].getF32ptr()));
- }
- }
- }
- }
-
- mDecompositionQ.push(d);
- return true;
-}
-
-LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data, LLVector3& scale, bool upload_textures,
- bool upload_skin, bool upload_joints)
-: LLThread("mesh upload"),
- mDiscarded(FALSE)
-{
- mInstanceList = data;
- mUploadTextures = upload_textures;
- mUploadSkin = upload_skin;
- mUploadJoints = upload_joints;
- mMutex = new LLMutex(NULL);
- mCurlRequest = NULL;
- mPendingConfirmations = 0;
- mPendingUploads = 0;
- mPendingCost = 0;
- mFinished = false;
- mOrigin = gAgent.getPositionAgent();
- mHost = gAgent.getRegionHost();
-
- mUploadObjectAssetCapability = gAgent.getRegion()->getCapability("UploadObjectAsset");
- mNewInventoryCapability = gAgent.getRegion()->getCapability("NewFileAgentInventoryVariablePrice");
- mWholeModelUploadCapability = gAgent.getRegion()->getCapability("NewFileAgentInventory");
-
- mOrigin += gAgent.getAtAxis() * scale.magVec();
-}
-
-LLMeshUploadThread::~LLMeshUploadThread()
-{
-
-}
-
-LLMeshUploadThread::DecompRequest::DecompRequest(LLModel* mdl, LLModel* base_model, LLMeshUploadThread* thread)
-{
- mStage = "single_hull";
- mModel = mdl;
- mDecompID = &mdl->mDecompID;
- mBaseModel = base_model;
- mThread = thread;
-
- //copy out positions and indices
- if (mdl)
- {
- U16 index_offset = 0;
-
- mPositions.clear();
- mIndices.clear();
-
- //queue up vertex positions and indices
- for (S32 i = 0; i < mdl->getNumVolumeFaces(); ++i)
- {
- const LLVolumeFace& face = mdl->getVolumeFace(i);
- if (mPositions.size() + face.mNumVertices > 65535)
- {
- continue;
- }
-
- for (U32 j = 0; j < face.mNumVertices; ++j)
- {
- mPositions.push_back(LLVector3(face.mPositions[j].getF32ptr()));
- }
-
- for (U32 j = 0; j < face.mNumIndices; ++j)
- {
- mIndices.push_back(face.mIndices[j]+index_offset);
- }
-
- index_offset += face.mNumVertices;
- }
- }
-
- mThread->mFinalDecomp = this;
- mThread->mPhysicsComplete = false;
-}
-
-void LLMeshUploadThread::DecompRequest::completed()
-{
- if (mThread->mFinalDecomp == this)
- {
- mThread->mPhysicsComplete = true;
- }
-
- if (mHull.size() != 1)
- {
- llerrs << "WTF?" << llendl;
- }
-
- mThread->mHullMap[mBaseModel] = mHull[0];
-}
-
-//called in the main thread.
-void LLMeshUploadThread::preStart()
-{
- //build map of LLModel refs to instances for callbacks
- for (instance_list::iterator iter = mInstanceList.begin(); iter != mInstanceList.end(); ++iter)
- {
- mInstance[iter->mModel].push_back(*iter);
- }
-}
-
-void LLMeshUploadThread::discard()
-{
- LLMutexLock lock(mMutex) ;
- mDiscarded = TRUE ;
-}
-
-BOOL LLMeshUploadThread::isDiscarded()
-{
- LLMutexLock lock(mMutex) ;
- return mDiscarded ;
-}
-
-void LLMeshUploadThread::run()
-{
- if (gSavedSettings.getBOOL("MeshUseWholeModelUpload"))
- {
- doWholeModelUpload();
- }
- else
- {
- doIterativeUpload();
- }
-}
-
-#if 0
-void dumpLLSDToFile(LLSD& content, std::string& filename)
-{
- std::ofstream of(filename);
- LLSDSerialize::toPrettyXML(content,of);
-}
-#endif
-
-void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
-{
- // TODO where do textures go?
-
- LLSD result;
-
- result["folder_id"] = gInventory.findCategoryUUIDForType(LLFolderType::FT_OBJECT);
- result["asset_type"] = "mesh";
- result["inventory_type"] = "object";
- result["name"] = "your name here";
- result["description"] = "your description here";
-
- // TODO "optional" fields from the spec
-
- LLSD res;
- res["mesh_list"] = LLSD::emptyArray();
- res["texture_list"] = LLSD::emptyArray();
- S32 mesh_num = 0;
- S32 texture_num = 0;
-
- std::set<LLViewerTexture* > textures;
-
- for (instance_map::iterator iter = mInstance.begin(); iter != mInstance.end(); ++iter)
- {
- LLMeshUploadData data;
- data.mBaseModel = iter->first;
-
- LLModelInstance& instance = *(iter->second.begin());
-
- for (S32 i = 0; i < 5; i++)
- {
- data.mModel[i] = instance.mLOD[i];
- }
-
- std::stringstream ostr;
-
- LLModel::Decomposition& decomp =
- data.mModel[LLModel::LOD_PHYSICS].notNull() ?
- data.mModel[LLModel::LOD_PHYSICS]->mPhysics :
- data.mBaseModel->mPhysics;
-
- decomp.mBaseHull = mHullMap[data.mBaseModel];
-
- LLSD mesh_header = LLModel::writeModel(
- ostr,
- data.mModel[LLModel::LOD_PHYSICS],
- data.mModel[LLModel::LOD_HIGH],
- data.mModel[LLModel::LOD_MEDIUM],
- data.mModel[LLModel::LOD_LOW],
- data.mModel[LLModel::LOD_IMPOSTOR],
- decomp,
- mUploadSkin,
- mUploadJoints);
-
- data.mAssetData = ostr.str();
-
- LLSD mesh_entry;
-
- LLVector3 pos, scale;
- LLQuaternion rot;
- LLMatrix4 transformation = instance.mTransform;
- decomposeMeshMatrix(transformation,pos,rot,scale);
-
- mesh_entry["childpos"] = ll_sd_from_vector3(pos);
- mesh_entry["childrot"] = ll_sd_from_quaternion(rot);
- mesh_entry["scale"] = ll_sd_from_vector3(scale);
-
- // TODO should be binary.
- std::string str = ostr.str();
- mesh_entry["mesh_data"] = LLSD::Binary(str.begin(),str.end());
-
- res["mesh_list"][mesh_num] = mesh_entry;
-
- // TODO how do textures in the list map to textures in the meshes?
- if (mUploadTextures)
- {
- for (std::vector<LLImportMaterial>::iterator material_iter = instance.mMaterial.begin();
- material_iter != instance.mMaterial.end(); ++material_iter)
- {
-
- if (textures.find(material_iter->mDiffuseMap.get()) == textures.end())
- {
- textures.insert(material_iter->mDiffuseMap.get());
-
- std::stringstream ostr;
- if (include_textures) // otherwise data is blank.
- {
- LLTextureUploadData data(material_iter->mDiffuseMap.get(), material_iter->mDiffuseMapLabel);
- if (!data.mTexture->isRawImageValid())
- {
- data.mTexture->reloadRawImage(data.mTexture->getDiscardLevel());
- }
-
- LLPointer<LLImageJ2C> upload_file =
- LLViewerTextureList::convertToUploadFile(data.mTexture->getRawImage());
- ostr.write((const char*) upload_file->getData(), upload_file->getDataSize());
- }
- LLSD texture_entry;
- texture_entry["texture_data"] = ostr.str();
- res["texture_list"][texture_num] = texture_entry;
- texture_num++;
- }
- }
- }
-
- mesh_num++;
- }
-
- result["asset_resources"] = res;
-#if 0
- std::string name("whole_model.xml");
- dumpLLSDToFile(result,name);
-#endif
-
- dest = result;
-}
-
-void LLMeshUploadThread::doWholeModelUpload()
-{
- mCurlRequest = new LLCurlRequest();
-
- // Queue up models for hull generation (viewer-side)
- for (instance_map::iterator iter = mInstance.begin(); iter != mInstance.end(); ++iter)
- {
- LLMeshUploadData data;
- data.mBaseModel = iter->first;
-
- LLModelInstance& instance = *(iter->second.begin());
-
- for (S32 i = 0; i < 5; i++)
- {
- data.mModel[i] = instance.mLOD[i];
- }
-
- //queue up models for hull generation
- LLModel* physics = NULL;
-
- if (data.mModel[LLModel::LOD_PHYSICS].notNull())
- {
- physics = data.mModel[LLModel::LOD_PHYSICS];
- }
- else if (data.mModel[LLModel::LOD_MEDIUM].notNull())
- {
- physics = data.mModel[LLModel::LOD_MEDIUM];
- }
- else
- {
- physics = data.mModel[LLModel::LOD_HIGH];
- }
-
- if (!physics)
- {
- llerrs << "WTF?" << llendl;
- }
-
- DecompRequest* request = new DecompRequest(physics, data.mBaseModel, this);
- gMeshRepo.mDecompThread->submitRequest(request);
- }
-
- while (!mPhysicsComplete)
- {
- apr_sleep(100);
- }
-
- bool do_include_textures = false; // not needed for initial cost/validation check.
- LLSD model_data;
- wholeModelToLLSD(model_data, do_include_textures);
-
- mPendingUploads++;
- LLCurlRequest::headers_t headers;
- mCurlRequest->post(mWholeModelUploadCapability, headers, model_data.asString(),
- new LLWholeModelFeeResponder(this));
-
- // Currently a no-op.
- mFinished = true;
-}
-
-void LLMeshUploadThread::doIterativeUpload()
-{
- if(isDiscarded())
- {
- mFinished = true;
- return ;
- }
-
- mCurlRequest = new LLCurlRequest();
-
- std::set<LLViewerTexture* > textures;
-
- //populate upload queue with relevant models
- for (instance_map::iterator iter = mInstance.begin(); iter != mInstance.end(); ++iter)
- {
- LLMeshUploadData data;
- data.mBaseModel = iter->first;
-
- LLModelInstance& instance = *(iter->second.begin());
-
- for (S32 i = 0; i < 5; i++)
- {
- data.mModel[i] = instance.mLOD[i];
- }
-
- uploadModel(data);
-
- if (mUploadTextures)
- {
- for (std::vector<LLImportMaterial>::iterator material_iter = instance.mMaterial.begin();
- material_iter != instance.mMaterial.end(); ++material_iter)
- {
-
- if (textures.find(material_iter->mDiffuseMap.get()) == textures.end())
- {
- textures.insert(material_iter->mDiffuseMap.get());
-
- LLTextureUploadData data(material_iter->mDiffuseMap.get(), material_iter->mDiffuseMapLabel);
- uploadTexture(data);
- }
- }
- }
-
- //queue up models for hull generation
- DecompRequest* request = new DecompRequest(data.mModel[LLModel::LOD_HIGH], data.mBaseModel, this);
- gMeshRepo.mDecompThread->submitRequest(request);
- }
-
- while (!mPhysicsComplete)
- {
- apr_sleep(100);
- }
-
- //upload textures
- bool done = false;
- do
- {
- if (!mTextureQ.empty())
- {
- sendCostRequest(mTextureQ.front());
- mTextureQ.pop();
- }
-
- if (!mConfirmedTextureQ.empty())
- {
- doUploadTexture(mConfirmedTextureQ.front());
- mConfirmedTextureQ.pop();
- }
-
- mCurlRequest->process();
-
- done = mTextureQ.empty() && mConfirmedTextureQ.empty();
- }
- while (!done || mCurlRequest->getQueued() > 0);
-
- LLSD object_asset;
- object_asset["objects"] = LLSD::emptyArray();
-
- done = false;
- do
- {
- static S32 count = 0;
- static F32 last_hundred = gFrameTimeSeconds;
- if (gFrameTimeSeconds - last_hundred > 1.f)
- {
- last_hundred = gFrameTimeSeconds;
- count = 0;
- }
-
- //how many requests to push before calling process
- const S32 PUSH_PER_PROCESS = 32;
-
- S32 tcount = llmin(count+PUSH_PER_PROCESS, 100);
-
- while (!mUploadQ.empty() && count < tcount)
- { //send any pending upload requests
- mMutex->lock();
- LLMeshUploadData data = mUploadQ.front();
- mUploadQ.pop();
- mMutex->unlock();
- sendCostRequest(data);
- count++;
- }
-
- tcount = llmin(count+PUSH_PER_PROCESS, 100);
-
- while (!mConfirmedQ.empty() && count < tcount)
- { //process any meshes that have been confirmed for upload
- LLMeshUploadData& data = mConfirmedQ.front();
- doUploadModel(data);
- mConfirmedQ.pop();
- count++;
- }
-
- tcount = llmin(count+PUSH_PER_PROCESS, 100);
-
- while (!mInstanceQ.empty() && count < tcount && !isDiscarded())
- { //create any objects waiting for upload
- count++;
- object_asset["objects"].append(createObject(mInstanceQ.front()));
- mInstanceQ.pop();
- }
-
- mCurlRequest->process();
-
- done = isDiscarded() || (mInstanceQ.empty() && mConfirmedQ.empty() && mUploadQ.empty());
- }
- while (!done || mCurlRequest->getQueued() > 0);
-
- delete mCurlRequest;
- mCurlRequest = NULL;
-
- // now upload the object asset
- std::string url = mUploadObjectAssetCapability;
-
- if (object_asset["objects"][0].has("permissions"))
- { //copy permissions from first available object to be used for coalesced object
- object_asset["permissions"] = object_asset["objects"][0]["permissions"];
- }
-
- if(!isDiscarded())
- {
- mPendingUploads++;
- LLHTTPClient::post(url, object_asset, new LLModelObjectUploadResponder(this,object_asset));
- }
- else
- {
- mFinished = true;
- }
-}
-
-void LLMeshUploadThread::uploadModel(LLMeshUploadData& data)
-{ //called from arbitrary thread
- {
- LLMutexLock lock(mMutex);
- mUploadQ.push(data);
- }
-}
-
-void LLMeshUploadThread::uploadTexture(LLTextureUploadData& data)
-{ //called from mesh upload thread
- mTextureQ.push(data);
-}
-
-
-static LLFastTimer::DeclareTimer FTM_NOTIFY_MESH_LOADED("Notify Loaded");
-static LLFastTimer::DeclareTimer FTM_NOTIFY_MESH_UNAVAILABLE("Notify Unavailable");
-
-void LLMeshRepoThread::notifyLoadedMeshes()
-{
- while (!mLoadedQ.empty())
- {
- mMutex->lock();
- LoadedMesh mesh = mLoadedQ.front();
- mLoadedQ.pop();
- mMutex->unlock();
-
- if (mesh.mVolume && mesh.mVolume->getNumVolumeFaces() > 0)
- {
- gMeshRepo.notifyMeshLoaded(mesh.mMeshParams, mesh.mVolume);
- }
- else
- {
- gMeshRepo.notifyMeshUnavailable(mesh.mMeshParams,
- LLVolumeLODGroup::getVolumeDetailFromScale(mesh.mVolume->getDetail()));
- }
- }
-
- while (!mUnavailableQ.empty())
- {
- mMutex->lock();
- LODRequest req = mUnavailableQ.front();
- mUnavailableQ.pop();
- mMutex->unlock();
-
- gMeshRepo.notifyMeshUnavailable(req.mMeshParams, req.mLOD);
- }
-
- while (!mSkinInfoQ.empty())
- {
- gMeshRepo.notifySkinInfoReceived(mSkinInfoQ.front());
- mSkinInfoQ.pop();
- }
-
- while (!mDecompositionQ.empty())
- {
- gMeshRepo.notifyDecompositionReceived(mDecompositionQ.front());
- mDecompositionQ.pop();
- }
-}
-
-S32 LLMeshRepoThread::getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod)
-{ //only ever called from main thread
- LLMutexLock lock(mHeaderMutex);
- mesh_header_map::iterator iter = mMeshHeader.find(mesh_params.getSculptID());
-
- if (iter != mMeshHeader.end())
- {
- LLSD& header = iter->second;
-
- return LLMeshRepository::getActualMeshLOD(header, lod);
- }
-
- return lod;
-}
-
-//static
-S32 LLMeshRepository::getActualMeshLOD(LLSD& header, S32 lod)
-{
- lod = llclamp(lod, 0, 3);
-
- if (header.has("404"))
- {
- return -1;
- }
-
- if (header[header_lod[lod]]["size"].asInteger() > 0)
- {
- return lod;
- }
-
- //search down to find the next available lower lod
- for (S32 i = lod-1; i >= 0; --i)
- {
- if (header[header_lod[i]]["size"].asInteger() > 0)
- {
- return i;
- }
- }
-
- //search up to find then ext available higher lod
- for (S32 i = lod+1; i < 4; ++i)
- {
- if (header[header_lod[i]]["size"].asInteger() > 0)
- {
- return i;
- }
- }
-
- //header exists and no good lod found, treat as 404
- header["404"] = 1;
- return -1;
-}
-
-U32 LLMeshRepoThread::getResourceCost(const LLUUID& mesh_id)
-{
- LLMutexLock lock(mHeaderMutex);
-
- std::map<LLUUID, U32>::iterator iter = mMeshResourceCost.find(mesh_id);
- if (iter != mMeshResourceCost.end())
- {
- return iter->second;
- }
-
- return 0;
-}
-
-void LLMeshRepository::cacheOutgoingMesh(LLMeshUploadData& data, LLSD& header)
-{
- mThread->mMeshHeader[data.mUUID] = header;
-
- // we cache the mesh for default parameters
- LLVolumeParams volume_params;
- volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE);
- volume_params.setSculptID(data.mUUID, LL_SCULPT_TYPE_MESH);
-
- for (U32 i = 0; i < 4; i++)
- {
- if (data.mModel[i].notNull())
- {
- LLPointer<LLVolume> volume = new LLVolume(volume_params, LLVolumeLODGroup::getVolumeScaleFromDetail(i));
- volume->copyVolumeFaces(data.mModel[i]);
- }
- }
-
-}
-
-void LLMeshLODResponder::completedRaw(U32 status, const std::string& reason,
- const LLChannelDescriptors& channels,
- const LLIOPipe::buffer_ptr_t& buffer)
-{
-
- LLMeshRepoThread::sActiveLODRequests--;
- S32 data_size = buffer->countAfter(channels.in(), NULL);
-
- if (status < 200 || status > 400)
- {
- llwarns << status << ": " << reason << llendl;
- }
-
- if (data_size < mRequestedBytes)
- {
- if (status == 499 || status == 503)
- { //timeout or service unavailable, try again
- LLMeshRepository::sHTTPRetryCount++;
- gMeshRepo.mThread->loadMeshLOD(mMeshParams, mLOD);
- }
- else
- {
- llwarns << "Unhandled status " << status << llendl;
- }
- return;
- }
-
- LLMeshRepository::sBytesReceived += mRequestedBytes;
-
- U8* data = NULL;
-
- if (data_size > 0)
- {
- data = new U8[data_size];
- buffer->readAfter(channels.in(), NULL, data, data_size);
- }
-
- if (gMeshRepo.mThread->lodReceived(mMeshParams, mLOD, data, data_size))
- {
- //good fetch from sim, write to VFS for caching
- LLVFile file(gVFS, mMeshParams.getSculptID(), LLAssetType::AT_MESH, LLVFile::WRITE);
-
- S32 offset = mOffset;
- S32 size = mRequestedBytes;
-
- if (file.getSize() >= offset+size)
- {
- file.seek(offset);
- file.write(data, size);
- LLMeshRepository::sCacheBytesWritten += size;
- }
- }
-
- delete [] data;
-}
-
-void LLMeshSkinInfoResponder::completedRaw(U32 status, const std::string& reason,
- const LLChannelDescriptors& channels,
- const LLIOPipe::buffer_ptr_t& buffer)
-{
- S32 data_size = buffer->countAfter(channels.in(), NULL);
-
- if (status < 200 || status > 400)
- {
- llwarns << status << ": " << reason << llendl;
- }
-
- if (data_size < mRequestedBytes)
- {
- if (status == 499 || status == 503)
- { //timeout or service unavailable, try again
- LLMeshRepository::sHTTPRetryCount++;
- gMeshRepo.mThread->loadMeshSkinInfo(mMeshID);
- }
- else
- {
- llwarns << "Unhandled status " << status << llendl;
- }
- return;
- }
-
- LLMeshRepository::sBytesReceived += mRequestedBytes;
-
- U8* data = NULL;
-
- if (data_size > 0)
- {
- data = new U8[data_size];
- buffer->readAfter(channels.in(), NULL, data, data_size);
- }
-
- if (gMeshRepo.mThread->skinInfoReceived(mMeshID, data, data_size))
- {
- //good fetch from sim, write to VFS for caching
- LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE);
-
- S32 offset = mOffset;
- S32 size = mRequestedBytes;
-
- if (file.getSize() >= offset+size)
- {
- LLMeshRepository::sCacheBytesWritten += size;
- file.seek(offset);
- file.write(data, size);
- }
- }
-
- delete [] data;
-}
-
-void LLMeshDecompositionResponder::completedRaw(U32 status, const std::string& reason,
- const LLChannelDescriptors& channels,
- const LLIOPipe::buffer_ptr_t& buffer)
-{
- S32 data_size = buffer->countAfter(channels.in(), NULL);
-
- if (status < 200 || status > 400)
- {
- llwarns << status << ": " << reason << llendl;
- }
-
- if (data_size < mRequestedBytes)
- {
- if (status == 499 || status == 503)
- { //timeout or service unavailable, try again
- LLMeshRepository::sHTTPRetryCount++;
- gMeshRepo.mThread->loadMeshDecomposition(mMeshID);
- }
- else
- {
- llwarns << "Unhandled status " << status << llendl;
- }
- return;
- }
-
- LLMeshRepository::sBytesReceived += mRequestedBytes;
-
- U8* data = NULL;
-
- if (data_size > 0)
- {
- data = new U8[data_size];
- buffer->readAfter(channels.in(), NULL, data, data_size);
- }
-
- if (gMeshRepo.mThread->decompositionReceived(mMeshID, data, data_size))
- {
- //good fetch from sim, write to VFS for caching
- LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE);
-
- S32 offset = mOffset;
- S32 size = mRequestedBytes;
-
- if (file.getSize() >= offset+size)
- {
- LLMeshRepository::sCacheBytesWritten += size;
- file.seek(offset);
- file.write(data, size);
- }
- }
-
- delete [] data;
-}
-
-void LLMeshPhysicsShapeResponder::completedRaw(U32 status, const std::string& reason,
- const LLChannelDescriptors& channels,
- const LLIOPipe::buffer_ptr_t& buffer)
-{
- S32 data_size = buffer->countAfter(channels.in(), NULL);
-
- if (status < 200 || status > 400)
- {
- llwarns << status << ": " << reason << llendl;
- }
-
- if (data_size < mRequestedBytes)
- {
- if (status == 499 || status == 503)
- { //timeout or service unavailable, try again
- LLMeshRepository::sHTTPRetryCount++;
- gMeshRepo.mThread->loadMeshPhysicsShape(mMeshID);
- }
- else
- {
- llwarns << "Unhandled status " << status << llendl;
- }
- return;
- }
-
- LLMeshRepository::sBytesReceived += mRequestedBytes;
-
- U8* data = NULL;
-
- if (data_size > 0)
- {
- data = new U8[data_size];
- buffer->readAfter(channels.in(), NULL, data, data_size);
- }
-
- if (gMeshRepo.mThread->physicsShapeReceived(mMeshID, data, data_size))
- {
- //good fetch from sim, write to VFS for caching
- LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE);
-
- S32 offset = mOffset;
- S32 size = mRequestedBytes;
-
- if (file.getSize() >= offset+size)
- {
- LLMeshRepository::sCacheBytesWritten += size;
- file.seek(offset);
- file.write(data, size);
- }
- }
-
- delete [] data;
-}
-
-void LLMeshHeaderResponder::completedRaw(U32 status, const std::string& reason,
- const LLChannelDescriptors& channels,
- const LLIOPipe::buffer_ptr_t& buffer)
-{
- LLMeshRepoThread::sActiveHeaderRequests--;
- if (status < 200 || status > 400)
- {
- //llwarns
- // << "Header responder failed with status: "
- // << status << ": " << reason << llendl;
-
- // 503 (service unavailable) or 499 (timeout)
- // can be due to server load and can be retried
-
- // TODO*: Add maximum retry logic, exponential backoff
- // and (somewhat more optional than the others) retries
- // again after some set period of time
- if (status == 503 || status == 499)
- { //retry
- LLMeshRepository::sHTTPRetryCount++;
- LLMeshRepoThread::HeaderRequest req(mMeshParams);
- LLMutexLock lock(gMeshRepo.mThread->mMutex);
- gMeshRepo.mThread->mHeaderReqQ.push(req);
-
- return;
- }
- }
-
- S32 data_size = buffer->countAfter(channels.in(), NULL);
-
- U8* data = NULL;
-
- if (data_size > 0)
- {
- data = new U8[data_size];
- buffer->readAfter(channels.in(), NULL, data, data_size);
- }
-
- LLMeshRepository::sBytesReceived += llmin(data_size, 4096);
-
- if (!gMeshRepo.mThread->headerReceived(mMeshParams, data, data_size))
- {
- llwarns
- << "Unable to parse mesh header: "
- << status << ": " << reason << llendl;
- }
- else if (data && data_size > 0)
- {
- //header was successfully retrieved from sim, cache in vfs
- LLUUID mesh_id = mMeshParams.getSculptID();
- LLSD header = gMeshRepo.mThread->mMeshHeader[mesh_id];
-
- std::stringstream str;
-
- S32 lod_bytes = 0;
-
- for (U32 i = 0; i < LLModel::LOD_PHYSICS; ++i)
- { //figure out how many bytes we'll need to reserve in the file
- std::string lod_name = header_lod[i];
- lod_bytes = llmax(lod_bytes, header[lod_name]["offset"].asInteger()+header[lod_name]["size"].asInteger());
- }
-
- //just in case skin info or decomposition is at the end of the file (which it shouldn't be)
- lod_bytes = llmax(lod_bytes, header["skin"]["offset"].asInteger() + header["skin"]["size"].asInteger());
- lod_bytes = llmax(lod_bytes, header["decomposition"]["offset"].asInteger() + header["decomposition"]["size"].asInteger());
-
- S32 header_bytes = (S32) gMeshRepo.mThread->mMeshHeaderSize[mesh_id];
- S32 bytes = lod_bytes + header_bytes;
-
-
- //it's possible for the remote asset to have more data than is needed for the local cache
- //only allocate as much space in the VFS as is needed for the local cache
- data_size = llmin(data_size, bytes);
-
- LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH, LLVFile::WRITE);
- if (file.getMaxSize() >= bytes || file.setMaxSize(bytes))
- {
- LLMeshRepository::sCacheBytesWritten += data_size;
-
- file.write((const U8*) data, data_size);
-
- //zero out the rest of the file
- U8 block[4096];
- memset(block, 0, 4096);
-
- while (bytes-file.tell() > 4096)
- {
- file.write(block, 4096);
- }
-
- S32 remaining = bytes-file.tell();
-
- if (remaining < 0 || remaining > 4096)
- {
- llerrs << "Bad padding of mesh asset cache entry." << llendl;
- }
-
- if (remaining > 0)
- {
- file.write(block, remaining);
- }
- }
- }
-
- delete [] data;
-}
-
-
-LLMeshRepository::LLMeshRepository()
-: mMeshMutex(NULL),
- mMeshThreadCount(0),
- mThread(NULL)
-{
-
-}
-
-void LLMeshRepository::init()
-{
- mMeshMutex = new LLMutex(NULL);
-
- LLConvexDecomposition::getInstance()->initSystem();
-
- mDecompThread = new LLPhysicsDecomp();
- mDecompThread->start();
-
- while (!mDecompThread->mInited)
- { //wait for physics decomp thread to init
- apr_sleep(100);
- }
-
-
-
- mThread = new LLMeshRepoThread();
- mThread->start();
-}
-
-void LLMeshRepository::shutdown()
-{
- llinfos << "Shutting down mesh repository." << llendl;
-
- for (U32 i = 0; i < mUploads.size(); ++i)
- {
- llinfos << "Discard the pending mesh uploads " << llendl;
- mUploads[i]->discard() ; //discard the uploading requests.
- }
-
- mThread->mSignal->signal();
-
- while (!mThread->isStopped())
- {
- apr_sleep(10);
- }
- delete mThread;
- mThread = NULL;
-
- for (U32 i = 0; i < mUploads.size(); ++i)
- {
- llinfos << "Waiting for pending mesh upload " << i << "/" << mUploads.size() << llendl;
- while (!mUploads[i]->isStopped())
- {
- apr_sleep(10);
- }
- delete mUploads[i];
- }
-
- mUploads.clear();
-
- delete mMeshMutex;
- mMeshMutex = NULL;
-
- llinfos << "Shutting down decomposition system." << llendl;
-
- if (mDecompThread)
- {
- mDecompThread->shutdown();
- delete mDecompThread;
- mDecompThread = NULL;
- }
-
- LLConvexDecomposition::quitSystem();
-}
-
-//called in the main thread.
-S32 LLMeshRepository::update()
-{
- if(mUploadWaitList.empty())
- {
- return 0 ;
- }
-
- S32 size = mUploadWaitList.size() ;
- for (S32 i = 0; i < size; ++i)
- {
- mUploads.push_back(mUploadWaitList[i]);
- mUploadWaitList[i]->preStart() ;
- mUploadWaitList[i]->start() ;
- }
- mUploadWaitList.clear() ;
-
- return size ;
-}
-
-S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_params, S32 detail, S32 last_lod)
-{
- if (detail < 0 || detail > 4)
- {
- return detail;
- }
-
- LLFastTimer t(FTM_LOAD_MESH);
-
- {
- LLMutexLock lock(mMeshMutex);
- //add volume to list of loading meshes
- mesh_load_map::iterator iter = mLoadingMeshes[detail].find(mesh_params);
- if (iter != mLoadingMeshes[detail].end())
- { //request pending for this mesh, append volume id to list
- iter->second.insert(vobj->getID());
- }
- else
- {
- //first request for this mesh
- mLoadingMeshes[detail][mesh_params].insert(vobj->getID());
- mPendingRequests.push_back(LLMeshRepoThread::LODRequest(mesh_params, detail));
- }
- }
-
- //do a quick search to see if we can't display something while we wait for this mesh to load
- LLVolume* volume = vobj->getVolume();
-
- if (volume)
- {
- if (volume->getNumVolumeFaces() == 0 && !volume->isTetrahedron())
- {
- volume->makeTetrahedron();
- }
-
- LLVolumeParams params = volume->getParams();
-
- LLVolumeLODGroup* group = LLPrimitive::getVolumeManager()->getGroup(params);
-
- if (group)
- {
- //first, see if last_lod is available (don't transition down to avoid funny popping a la SH-641)
- if (last_lod >= 0)
- {
- LLVolume* lod = group->refLOD(last_lod);
- if (lod && !lod->isTetrahedron() && lod->getNumVolumeFaces() > 0)
- {
- group->derefLOD(lod);
- return last_lod;
- }
- group->derefLOD(lod);
- }
-
- //next, see what the next lowest LOD available might be
- for (S32 i = detail-1; i >= 0; --i)
- {
- LLVolume* lod = group->refLOD(i);
- if (lod && !lod->isTetrahedron() && lod->getNumVolumeFaces() > 0)
- {
- group->derefLOD(lod);
- return i;
- }
-
- group->derefLOD(lod);
- }
-
- //no lower LOD is a available, is a higher lod available?
- for (S32 i = detail+1; i < 4; ++i)
- {
- LLVolume* lod = group->refLOD(i);
- if (lod && !lod->isTetrahedron() && lod->getNumVolumeFaces() > 0)
- {
- group->derefLOD(lod);
- return i;
- }
-
- group->derefLOD(lod);
- }
- }
- else
- {
- llerrs << "WTF?" << llendl;
- }
- }
-
- return detail;
-}
-
-static LLFastTimer::DeclareTimer FTM_START_MESH_THREAD("Start Thread");
-static LLFastTimer::DeclareTimer FTM_LOAD_MESH_LOD("Load LOD");
-static LLFastTimer::DeclareTimer FTM_MESH_LOCK1("Lock 1");
-static LLFastTimer::DeclareTimer FTM_MESH_LOCK2("Lock 2");
-
-void LLMeshRepository::notifyLoadedMeshes()
-{ //called from main thread
-
- LLMeshRepoThread::sMaxConcurrentRequests = gSavedSettings.getU32("MeshMaxConcurrentRequests");
-
- //clean up completed upload threads
- for (std::vector<LLMeshUploadThread*>::iterator iter = mUploads.begin(); iter != mUploads.end(); )
- {
- LLMeshUploadThread* thread = *iter;
-
- if (thread->isStopped() && thread->finished())
- {
- iter = mUploads.erase(iter);
- delete thread;
- }
- else
- {
- ++iter;
- }
- }
-
- //update inventory
- if (!mInventoryQ.empty())
- {
- LLMutexLock lock(mMeshMutex);
- while (!mInventoryQ.empty())
- {
- inventory_data& data = mInventoryQ.front();
-
- LLAssetType::EType asset_type = LLAssetType::lookup(data.mPostData["asset_type"].asString());
- LLInventoryType::EType inventory_type = LLInventoryType::lookup(data.mPostData["inventory_type"].asString());
-
- on_new_single_inventory_upload_complete(
- asset_type,
- inventory_type,
- data.mPostData["asset_type"].asString(),
- data.mPostData["folder_id"].asUUID(),
- data.mPostData["name"],
- data.mPostData["description"],
- data.mResponse,
- 0);
-
- mInventoryQ.pop();
- }
- }
-
- //call completed callbacks on finished decompositions
- mDecompThread->notifyCompleted();
-
- if (!mThread->mWaiting)
- { //curl thread is churning, wait for it to go idle
- return;
- }
-
- static std::string region_name("never name a region this");
-
- if (gAgent.getRegion())
- { //update capability url
- if (gAgent.getRegion()->getName() != region_name && gAgent.getRegion()->capabilitiesReceived())
- {
- region_name = gAgent.getRegion()->getName();
-
- mGetMeshCapability = gAgent.getRegion()->getCapability("GetMesh");
- }
- }
-
- LLFastTimer t(FTM_MESH_UPDATE);
-
- {
- LLFastTimer t(FTM_MESH_LOCK1);
- mMeshMutex->lock();
- }
-
- {
- LLFastTimer t(FTM_MESH_LOCK2);
- mThread->mMutex->lock();
- }
-
- //popup queued error messages from background threads
- while (!mUploadErrorQ.empty())
- {
- LLNotificationsUtil::add("MeshUploadError", mUploadErrorQ.front());
- mUploadErrorQ.pop();
- }
-
- S32 push_count = LLMeshRepoThread::sMaxConcurrentRequests-(LLMeshRepoThread::sActiveHeaderRequests+LLMeshRepoThread::sActiveLODRequests);
-
- if (push_count > 0)
- {
- //calculate "score" for pending requests
-
- //create score map
- std::map<LLUUID, F32> score_map;
-
- for (U32 i = 0; i < 4; ++i)
- {
- for (mesh_load_map::iterator iter = mLoadingMeshes[i].begin(); iter != mLoadingMeshes[i].end(); ++iter)
- {
- F32 max_score = 0.f;
- for (std::set<LLUUID>::iterator obj_iter = iter->second.begin(); obj_iter != iter->second.end(); ++obj_iter)
- {
- LLViewerObject* object = gObjectList.findObject(*obj_iter);
-
- if (object)
- {
- LLDrawable* drawable = object->mDrawable;
- if (drawable)
- {
- F32 cur_score = drawable->getRadius()/llmax(drawable->mDistanceWRTCamera, 1.f);
- max_score = llmax(max_score, cur_score);
- }
- }
- }
-
- score_map[iter->first.getSculptID()] = max_score;
- }
- }
-
- //set "score" for pending requests
- for (std::vector<LLMeshRepoThread::LODRequest>::iterator iter = mPendingRequests.begin(); iter != mPendingRequests.end(); ++iter)
- {
- iter->mScore = score_map[iter->mMeshParams.getSculptID()];
- }
-
- //sort by "score"
- std::sort(mPendingRequests.begin(), mPendingRequests.end(), LLMeshRepoThread::CompareScoreGreater());
-
- while (!mPendingRequests.empty() && push_count > 0)
- {
- LLFastTimer t(FTM_LOAD_MESH_LOD);
- LLMeshRepoThread::LODRequest& request = mPendingRequests.front();
- mThread->loadMeshLOD(request.mMeshParams, request.mLOD);
- mPendingRequests.erase(mPendingRequests.begin());
- push_count--;
- }
- }
-
- //send skin info requests
- while (!mPendingSkinRequests.empty())
- {
- mThread->loadMeshSkinInfo(mPendingSkinRequests.front());
- mPendingSkinRequests.pop();
- }
-
- //send decomposition requests
- while (!mPendingDecompositionRequests.empty())
- {
- mThread->loadMeshDecomposition(mPendingDecompositionRequests.front());
- mPendingDecompositionRequests.pop();
- }
-
- //send physics shapes decomposition requests
- while (!mPendingPhysicsShapeRequests.empty())
- {
- mThread->loadMeshPhysicsShape(mPendingPhysicsShapeRequests.front());
- mPendingPhysicsShapeRequests.pop();
- }
-
- mThread->notifyLoadedMeshes();
-
- mThread->mMutex->unlock();
- mMeshMutex->unlock();
-
- mThread->mSignal->signal();
-}
-
-void LLMeshRepository::notifySkinInfoReceived(LLMeshSkinInfo& info)
-{
- mSkinMap[info.mMeshID] = info;
- mLoadingSkins.erase(info.mMeshID);
-}
-
-void LLMeshRepository::notifyDecompositionReceived(LLModel::Decomposition* decomp)
-{
- decomposition_map::iterator iter = mDecompositionMap.find(decomp->mMeshID);
- if (iter == mDecompositionMap.end())
- { //just insert decomp into map
- mDecompositionMap[decomp->mMeshID] = decomp;
- }
- else
- { //merge decomp with existing entry
- iter->second->merge(decomp);
- delete decomp;
- }
-
- mLoadingDecompositions.erase(decomp->mMeshID);
-}
-
-void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume)
-{ //called from main thread
- S32 detail = LLVolumeLODGroup::getVolumeDetailFromScale(volume->getDetail());
-
- //get list of objects waiting to be notified this mesh is loaded
- mesh_load_map::iterator obj_iter = mLoadingMeshes[detail].find(mesh_params);
-
- if (volume && obj_iter != mLoadingMeshes[detail].end())
- {
- //make sure target volume is still valid
- if (volume->getNumVolumeFaces() <= 0)
- {
- llwarns << "Mesh loading returned empty volume." << llendl;
- volume->makeTetrahedron();
- }
-
- { //update system volume
- LLVolume* sys_volume = LLPrimitive::getVolumeManager()->refVolume(mesh_params, detail);
- if (sys_volume)
- {
- sys_volume->copyVolumeFaces(volume);
- LLPrimitive::getVolumeManager()->unrefVolume(sys_volume);
- }
- else
- {
- llwarns << "Couldn't find system volume for given mesh." << llendl;
- }
- }
-
- //notify waiting LLVOVolume instances that their requested mesh is available
- for (std::set<LLUUID>::iterator vobj_iter = obj_iter->second.begin(); vobj_iter != obj_iter->second.end(); ++vobj_iter)
- {
- LLVOVolume* vobj = (LLVOVolume*) gObjectList.findObject(*vobj_iter);
- if (vobj)
- {
- vobj->notifyMeshLoaded();
- }
- }
-
- mLoadingMeshes[detail].erase(mesh_params);
- }
-}
-
-void LLMeshRepository::notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 lod)
-{ //called from main thread
- //get list of objects waiting to be notified this mesh is loaded
- mesh_load_map::iterator obj_iter = mLoadingMeshes[lod].find(mesh_params);
-
- F32 detail = LLVolumeLODGroup::getVolumeScaleFromDetail(lod);
-
- if (obj_iter != mLoadingMeshes[lod].end())
- {
- for (std::set<LLUUID>::iterator vobj_iter = obj_iter->second.begin(); vobj_iter != obj_iter->second.end(); ++vobj_iter)
- {
- LLVOVolume* vobj = (LLVOVolume*) gObjectList.findObject(*vobj_iter);
- if (vobj)
- {
- LLVolume* obj_volume = vobj->getVolume();
-
- if (obj_volume &&
- obj_volume->getDetail() == detail &&
- obj_volume->getParams() == mesh_params)
- { //should force volume to find most appropriate LOD
- vobj->setVolume(obj_volume->getParams(), lod);
- }
- }
- }
-
- mLoadingMeshes[lod].erase(mesh_params);
- }
-}
-
-S32 LLMeshRepository::getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod)
-{
- return mThread->getActualMeshLOD(mesh_params, lod);
-}
-
-U32 LLMeshRepository::calcResourceCost(LLSD& header)
-{
- U32 bytes = 0;
-
- for (U32 i = 0; i < 4; i++)
- {
- bytes += header[header_lod[i]]["size"].asInteger();
- }
-
- bytes += header["skin"]["size"].asInteger();
-
- return bytes/4096 + 1;
-}
-
-U32 LLMeshRepository::getResourceCost(const LLUUID& mesh_id)
-{
- return mThread->getResourceCost(mesh_id);
-}
-
-const LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id)
-{
- if (mesh_id.notNull())
- {
- skin_map::iterator iter = mSkinMap.find(mesh_id);
- if (iter != mSkinMap.end())
- {
- return &(iter->second);
- }
-
- //no skin info known about given mesh, try to fetch it
- {
- LLMutexLock lock(mMeshMutex);
- //add volume to list of loading meshes
- std::set<LLUUID>::iterator iter = mLoadingSkins.find(mesh_id);
- if (iter == mLoadingSkins.end())
- { //no request pending for this skin info
- mLoadingSkins.insert(mesh_id);
- mPendingSkinRequests.push(mesh_id);
- }
- }
- }
-
- return NULL;
-}
-
-void LLMeshRepository::fetchPhysicsShape(const LLUUID& mesh_id)
-{
- if (mesh_id.notNull())
- {
- LLModel::Decomposition* decomp = NULL;
- decomposition_map::iterator iter = mDecompositionMap.find(mesh_id);
- if (iter != mDecompositionMap.end())
- {
- decomp = iter->second;
- }
-
- //decomposition block hasn't been fetched yet
- if (!decomp || decomp->mPhysicsShapeMesh.empty())
- {
- LLMutexLock lock(mMeshMutex);
- //add volume to list of loading meshes
- std::set<LLUUID>::iterator iter = mLoadingPhysicsShapes.find(mesh_id);
- if (iter == mLoadingPhysicsShapes.end())
- { //no request pending for this skin info
- mLoadingPhysicsShapes.insert(mesh_id);
- mPendingPhysicsShapeRequests.push(mesh_id);
- }
- }
- }
-
-}
-
-LLModel::Decomposition* LLMeshRepository::getDecomposition(const LLUUID& mesh_id)
-{
- LLModel::Decomposition* ret = NULL;
-
- if (mesh_id.notNull())
- {
- decomposition_map::iterator iter = mDecompositionMap.find(mesh_id);
- if (iter != mDecompositionMap.end())
- {
- ret = iter->second;
- }
-
- //decomposition block hasn't been fetched yet
- if (!ret || ret->mBaseHullMesh.empty())
- {
- LLMutexLock lock(mMeshMutex);
- //add volume to list of loading meshes
- std::set<LLUUID>::iterator iter = mLoadingDecompositions.find(mesh_id);
- if (iter == mLoadingDecompositions.end())
- { //no request pending for this skin info
- mLoadingDecompositions.insert(mesh_id);
- mPendingDecompositionRequests.push(mesh_id);
- }
- }
- }
-
- return ret;
-}
-
-void LLMeshRepository::buildHull(const LLVolumeParams& params, S32 detail)
-{
- LLVolume* volume = LLPrimitive::sVolumeManager->refVolume(params, detail);
-
- if (!volume->mHullPoints)
- {
- //all default params
- //execute first stage
- //set simplify mode to retain
- //set retain percentage to zero
- //run second stage
- }
-
- LLPrimitive::sVolumeManager->unrefVolume(volume);
-}
-
-bool LLMeshRepository::hasPhysicsShape(const LLUUID& mesh_id)
-{
- LLSD mesh = mThread->getMeshHeader(mesh_id);
- return mesh.has("physics_shape") && mesh["physics_shape"].has("size") && (mesh["physics_shape"]["size"].asInteger() > 0);
-}
-
-LLSD& LLMeshRepository::getMeshHeader(const LLUUID& mesh_id)
-{
- return mThread->getMeshHeader(mesh_id);
-}
-
-LLSD& LLMeshRepoThread::getMeshHeader(const LLUUID& mesh_id)
-{
- static LLSD dummy_ret;
- if (mesh_id.notNull())
- {
- LLMutexLock lock(mHeaderMutex);
- mesh_header_map::iterator iter = mMeshHeader.find(mesh_id);
- if (iter != mMeshHeader.end())
- {
- return iter->second;
- }
- }
-
- return dummy_ret;
-}
-
-
-void LLMeshRepository::uploadModel(std::vector<LLModelInstance>& data, LLVector3& scale, bool upload_textures,
- bool upload_skin, bool upload_joints)
-{
- LLMeshUploadThread* thread = new LLMeshUploadThread(data, scale, upload_textures, upload_skin, upload_joints);
- mUploadWaitList.push_back(thread);
-}
-
-S32 LLMeshRepository::getMeshSize(const LLUUID& mesh_id, S32 lod)
-{
- if (mThread)
- {
- LLMeshRepoThread::mesh_header_map::iterator iter = mThread->mMeshHeader.find(mesh_id);
- if (iter != mThread->mMeshHeader.end())
- {
- LLSD& header = iter->second;
-
- if (header.has("404"))
- {
- return -1;
- }
-
- S32 size = header[header_lod[lod]]["size"].asInteger();
- return size;
- }
-
- }
-
- return -1;
-
-}
-
-void LLMeshUploadThread::sendCostRequest(LLMeshUploadData& data)
-{
- if(isDiscarded())
- {
- return ;
- }
-
- //write model file to memory buffer
- std::stringstream ostr;
-
- LLModel::Decomposition& decomp =
- data.mModel[LLModel::LOD_PHYSICS].notNull() ?
- data.mModel[LLModel::LOD_PHYSICS]->mPhysics :
- data.mBaseModel->mPhysics;
-
- LLSD header = LLModel::writeModel(
- ostr,
- data.mModel[LLModel::LOD_PHYSICS],
- data.mModel[LLModel::LOD_HIGH],
- data.mModel[LLModel::LOD_MEDIUM],
- data.mModel[LLModel::LOD_LOW],
- data.mModel[LLModel::LOD_IMPOSTOR],
- decomp,
- mUploadSkin,
- mUploadJoints,
- true);
-
- std::string desc = data.mBaseModel->mLabel;
-
- // Grab the total vertex count of the model
- // along with other information for the "asset_resources" map
- // to send to the server.
- LLSD asset_resources = LLSD::emptyMap();
-
-
- std::string url = mNewInventoryCapability;
-
- if (!url.empty())
- {
- LLSD body = generate_new_resource_upload_capability_body(
- LLAssetType::AT_MESH,
- desc,
- desc,
- LLFolderType::FT_MESH,
- LLInventoryType::IT_MESH,
- LLFloaterPerms::getNextOwnerPerms(),
- LLFloaterPerms::getGroupPerms(),
- LLFloaterPerms::getEveryonePerms());
-
- body["asset_resources"] = asset_resources;
-
- mPendingConfirmations++;
- LLCurlRequest::headers_t headers;
-
- data.mPostData = body;
-
- mCurlRequest->post(url, headers, body, new LLMeshCostResponder(data, this));
- }
-}
-
-void LLMeshUploadThread::sendCostRequest(LLTextureUploadData& data)
-{
- if(isDiscarded())
- {
- return ;
- }
-
- if (data.mTexture && data.mTexture->getDiscardLevel() >= 0)
- {
- LLSD asset_resources = LLSD::emptyMap();
-
- std::string url = mNewInventoryCapability;
-
- if (!url.empty())
- {
- LLSD body = generate_new_resource_upload_capability_body(
- LLAssetType::AT_TEXTURE,
- data.mLabel,
- data.mLabel,
- LLFolderType::FT_TEXTURE,
- LLInventoryType::IT_TEXTURE,
- LLFloaterPerms::getNextOwnerPerms(),
- LLFloaterPerms::getGroupPerms(),
- LLFloaterPerms::getEveryonePerms());
-
- body["asset_resources"] = asset_resources;
-
- mPendingConfirmations++;
- LLCurlRequest::headers_t headers;
-
- data.mPostData = body;
- mCurlRequest->post(url, headers, body, new LLTextureCostResponder(data, this));
- }
- }
-}
-
-
-void LLMeshUploadThread::doUploadModel(LLMeshUploadData& data)
-{
- if(isDiscarded())
- {
- return ;
- }
-
- if (!data.mRSVP.empty())
- {
- std::stringstream ostr;
-
- LLModel::Decomposition& decomp =
- data.mModel[LLModel::LOD_PHYSICS].notNull() ?
- data.mModel[LLModel::LOD_PHYSICS]->mPhysics :
- data.mBaseModel->mPhysics;
-
- decomp.mBaseHull = mHullMap[data.mBaseModel];
-
- LLModel::writeModel(
- ostr,
- data.mModel[LLModel::LOD_PHYSICS],
- data.mModel[LLModel::LOD_HIGH],
- data.mModel[LLModel::LOD_MEDIUM],
- data.mModel[LLModel::LOD_LOW],
- data.mModel[LLModel::LOD_IMPOSTOR],
- decomp,
- mUploadSkin,
- mUploadJoints);
-
- data.mAssetData = ostr.str();
-
- LLCurlRequest::headers_t headers;
- mPendingUploads++;
-
- mCurlRequest->post(data.mRSVP, headers, data.mAssetData, new LLMeshUploadResponder(data, this));
- }
-}
-
-void LLMeshUploadThread::doUploadTexture(LLTextureUploadData& data)
-{
- if(isDiscarded())
- {
- return ;
- }
-
- if (!data.mRSVP.empty())
- {
- std::stringstream ostr;
-
- if (!data.mTexture->isRawImageValid())
- {
- data.mTexture->reloadRawImage(data.mTexture->getDiscardLevel());
- }
-
- LLPointer<LLImageJ2C> upload_file = LLViewerTextureList::convertToUploadFile(data.mTexture->getRawImage());
-
- ostr.write((const char*) upload_file->getData(), upload_file->getDataSize());
-
- data.mAssetData = ostr.str();
-
- LLCurlRequest::headers_t headers;
- mPendingUploads++;
-
- mCurlRequest->post(data.mRSVP, headers, data.mAssetData, new LLTextureUploadResponder(data, this));
- }
-}
-
-
-void LLMeshUploadThread::onModelUploaded(LLMeshUploadData& data)
-{
- createObjects(data);
-}
-
-void LLMeshUploadThread::onTextureUploaded(LLTextureUploadData& data)
-{
- mTextureMap[data.mTexture] = data;
-}
-
-
-void LLMeshUploadThread::createObjects(LLMeshUploadData& data)
-{
- instance_list& instances = mInstance[data.mBaseModel];
-
- for (instance_list::iterator iter = instances.begin(); iter != instances.end(); ++iter)
- { //create prims that reference given mesh
- LLModelInstance& instance = *iter;
- instance.mMeshID = data.mUUID;
- mInstanceQ.push(instance);
- }
-}
-
-void LLMeshUploadThread::decomposeMeshMatrix(LLMatrix4& transformation,
- LLVector3& result_pos,
- LLQuaternion& result_rot,
- LLVector3& result_scale)
-{
- // check for reflection
- BOOL reflected = (transformation.determinant() < 0);
-
- // compute position
- LLVector3 position = LLVector3(0, 0, 0) * transformation;
-
- // compute scale
- LLVector3 x_transformed = LLVector3(1, 0, 0) * transformation - position;
- LLVector3 y_transformed = LLVector3(0, 1, 0) * transformation - position;
- LLVector3 z_transformed = LLVector3(0, 0, 1) * transformation - position;
- F32 x_length = x_transformed.normalize();
- F32 y_length = y_transformed.normalize();
- F32 z_length = z_transformed.normalize();
- LLVector3 scale = LLVector3(x_length, y_length, z_length);
-
- // adjust for "reflected" geometry
- LLVector3 x_transformed_reflected = x_transformed;
- if (reflected)
- {
- x_transformed_reflected *= -1.0;
- }
-
- // compute rotation
- LLMatrix3 rotation_matrix;
- rotation_matrix.setRows(x_transformed_reflected, y_transformed, z_transformed);
- LLQuaternion quat_rotation = rotation_matrix.quaternion();
- quat_rotation.normalize(); // the rotation_matrix might not have been orthoginal. make it so here.
- LLVector3 euler_rotation;
- quat_rotation.getEulerAngles(&euler_rotation.mV[VX], &euler_rotation.mV[VY], &euler_rotation.mV[VZ]);
-
- result_pos = position + mOrigin;
- result_scale = scale;
- result_rot = quat_rotation;
-}
-
-
-LLSD LLMeshUploadThread::createObject(LLModelInstance& instance)
-{
- LLMatrix4 transformation = instance.mTransform;
-
- if (instance.mMeshID.isNull())
- {
- llerrs << "WTF?" << llendl;
- }
-
- // check for reflection
- BOOL reflected = (transformation.determinant() < 0);
-
- // compute position
- LLVector3 position = LLVector3(0, 0, 0) * transformation;
-
- // compute scale
- LLVector3 x_transformed = LLVector3(1, 0, 0) * transformation - position;
- LLVector3 y_transformed = LLVector3(0, 1, 0) * transformation - position;
- LLVector3 z_transformed = LLVector3(0, 0, 1) * transformation - position;
- F32 x_length = x_transformed.normalize();
- F32 y_length = y_transformed.normalize();
- F32 z_length = z_transformed.normalize();
- LLVector3 scale = LLVector3(x_length, y_length, z_length);
-
- // adjust for "reflected" geometry
- LLVector3 x_transformed_reflected = x_transformed;
- if (reflected)
- {
- x_transformed_reflected *= -1.0;
- }
-
- // compute rotation
- LLMatrix3 rotation_matrix;
- rotation_matrix.setRows(x_transformed_reflected, y_transformed, z_transformed);
- LLQuaternion quat_rotation = rotation_matrix.quaternion();
- quat_rotation.normalize(); // the rotation_matrix might not have been orthoginal. make it so here.
- LLVector3 euler_rotation;
- quat_rotation.getEulerAngles(&euler_rotation.mV[VX], &euler_rotation.mV[VY], &euler_rotation.mV[VZ]);
-
- //
- // build parameter block to construct this prim
- //
-
- LLSD object_params;
-
- // create prim
-
- // set volume params
- U8 sculpt_type = LL_SCULPT_TYPE_MESH;
- if (reflected)
- {
- sculpt_type |= LL_SCULPT_FLAG_MIRROR;
- }
- LLVolumeParams volume_params;
- volume_params.setType( LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE );
- volume_params.setBeginAndEndS( 0.f, 1.f );
- volume_params.setBeginAndEndT( 0.f, 1.f );
- volume_params.setRatio ( 1, 1 );
- volume_params.setShear ( 0, 0 );
- volume_params.setSculptID(instance.mMeshID, sculpt_type);
- object_params["shape"] = volume_params.asLLSD();
-
- object_params["material"] = LL_MCODE_WOOD;
-
- object_params["group-id"] = gAgent.getGroupID();
- object_params["pos"] = ll_sd_from_vector3(position + mOrigin);
- object_params["rotation"] = ll_sd_from_quaternion(quat_rotation);
- object_params["scale"] = ll_sd_from_vector3(scale);
- object_params["name"] = instance.mLabel;
-
- // load material from dae file
- object_params["facelist"] = LLSD::emptyArray();
- for (S32 i = 0; i < instance.mMaterial.size(); i++)
- {
- LLTextureEntry te;
- LLImportMaterial& mat = instance.mMaterial[i];
-
- te.setColor(mat.mDiffuseColor);
-
- LLUUID diffuse_id = mTextureMap[mat.mDiffuseMap].mUUID;
-
- if (diffuse_id.notNull())
- {
- te.setID(diffuse_id);
- }
- else
- {
- te.setID(LLUUID("5748decc-f629-461c-9a36-a35a221fe21f")); // blank texture
- }
-
- te.setFullbright(mat.mFullbright);
-
- object_params["facelist"][i] = te.asLLSD();
- }
-
- // set extra parameters
- LLSculptParams sculpt_params;
- sculpt_params.setSculptTexture(instance.mMeshID);
- sculpt_params.setSculptType(sculpt_type);
- U8 buffer[MAX_OBJECT_PARAMS_SIZE+1];
- LLDataPackerBinaryBuffer dp(buffer, MAX_OBJECT_PARAMS_SIZE);
- sculpt_params.pack(dp);
- std::vector<U8> v(dp.getCurrentSize());
- memcpy(&v[0], buffer, dp.getCurrentSize());
- LLSD extra_parameter;
- extra_parameter["extra_parameter"] = sculpt_params.mType;
- extra_parameter["param_data"] = v;
- object_params["extra_parameters"].append(extra_parameter);
-
- LLPermissions perm;
- perm.setOwnerAndGroup(gAgent.getID(), gAgent.getID(), LLUUID::null, false);
- perm.setCreator(gAgent.getID());
-
- perm.initMasks(PERM_ITEM_UNRESTRICTED | PERM_MOVE, //base
- PERM_ITEM_UNRESTRICTED | PERM_MOVE, //owner
- LLFloaterPerms::getEveryonePerms(),
- LLFloaterPerms::getGroupPerms(),
- LLFloaterPerms::getNextOwnerPerms());
-
- object_params["permissions"] = ll_create_sd_from_permissions(perm);
-
- object_params["physics_shape_type"] = (U8)(LLViewerObject::PHYSICS_SHAPE_CONVEX_HULL);
-
- return object_params;
-}
-
-void LLMeshUploadThread::priceResult(LLMeshUploadData& data, const LLSD& content)
-{
- mPendingCost += content["upload_price"].asInteger();
- data.mRSVP = content["rsvp"].asString();
-
- mConfirmedQ.push(data);
-}
-
-void LLMeshUploadThread::priceResult(LLTextureUploadData& data, const LLSD& content)
-{
- mPendingCost += content["upload_price"].asInteger();
- data.mRSVP = content["rsvp"].asString();
-
- mConfirmedTextureQ.push(data);
-}
-
-
-bool LLImportMaterial::operator<(const LLImportMaterial &rhs) const
-{
- if (mDiffuseMap != rhs.mDiffuseMap)
- {
- return mDiffuseMap < rhs.mDiffuseMap;
- }
-
- if (mDiffuseMapFilename != rhs.mDiffuseMapFilename)
- {
- return mDiffuseMapFilename < rhs.mDiffuseMapFilename;
- }
-
- if (mDiffuseMapLabel != rhs.mDiffuseMapLabel)
- {
- return mDiffuseMapLabel < rhs.mDiffuseMapLabel;
- }
-
- if (mDiffuseColor != rhs.mDiffuseColor)
- {
- return mDiffuseColor < rhs.mDiffuseColor;
- }
-
- return mFullbright < rhs.mFullbright;
-}
-
-
-void LLMeshRepository::updateInventory(inventory_data data)
-{
- LLMutexLock lock(mMeshMutex);
- mInventoryQ.push(data);
-}
-
-void LLMeshRepository::uploadError(LLSD& args)
-{
- LLMutexLock lock(mMeshMutex);
- mUploadErrorQ.push(args);
-}
-
-//static
-F32 LLMeshRepository::getStreamingCost(LLSD& header, F32 radius, S32* bytes, S32* bytes_visible, S32 lod)
-{
- F32 dlowest = llmin(radius/0.03f, 256.f);
- F32 dlow = llmin(radius/0.06f, 256.f);
- F32 dmid = llmin(radius/0.24f, 256.f);
-
- F32 bytes_lowest = header["lowest_lod"]["size"].asReal()/1024.f;
- F32 bytes_low = header["low_lod"]["size"].asReal()/1024.f;
- F32 bytes_mid = header["medium_lod"]["size"].asReal()/1024.f;
- F32 bytes_high = header["high_lod"]["size"].asReal()/1024.f;
-
- if (bytes)
- {
- *bytes = 0;
- *bytes += header["lowest_lod"]["size"].asInteger();
- *bytes += header["low_lod"]["size"].asInteger();
- *bytes += header["medium_lod"]["size"].asInteger();
- *bytes += header["high_lod"]["size"].asInteger();
- }
-
-
- if (bytes_visible)
- {
- lod = LLMeshRepository::getActualMeshLOD(header, lod);
- if (lod >= 0 && lod <= 3)
- {
- *bytes_visible = header[header_lod[lod]]["size"].asInteger();
- }
- }
-
- if (bytes_high == 0.f)
- {
- return 0.f;
- }
-
- if (bytes_mid == 0.f)
- {
- bytes_mid = bytes_high;
- }
-
- if (bytes_low == 0.f)
- {
- bytes_low = bytes_mid;
- }
-
- if (bytes_lowest == 0.f)
- {
- bytes_lowest = bytes_low;
- }
-
- F32 max_area = 65536.f;
- F32 min_area = 1.f;
-
- F32 high_area = llmin(F_PI*dmid*dmid, max_area);
- F32 mid_area = llmin(F_PI*dlow*dlow, max_area);
- F32 low_area = llmin(F_PI*dlowest*dlowest, max_area);
- F32 lowest_area = max_area;
-
- lowest_area -= low_area;
- low_area -= mid_area;
- mid_area -= high_area;
-
- high_area = llclamp(high_area, min_area, max_area);
- mid_area = llclamp(mid_area, min_area, max_area);
- low_area = llclamp(low_area, min_area, max_area);
- lowest_area = llclamp(lowest_area, min_area, max_area);
-
- F32 total_area = high_area + mid_area + low_area + lowest_area;
- high_area /= total_area;
- mid_area /= total_area;
- low_area /= total_area;
- lowest_area /= total_area;
-
- F32 weighted_avg = bytes_high*high_area +
- bytes_mid*mid_area +
- bytes_low*low_area +
- bytes_lowest*lowest_area;
-
- return weighted_avg * gSavedSettings.getF32("MeshStreamingCostScaler");
-}
-
-
-LLPhysicsDecomp::LLPhysicsDecomp()
-: LLThread("Physics Decomp")
-{
- mInited = false;
- mQuitting = false;
- mDone = false;
-
- mSignal = new LLCondition(NULL);
- mMutex = new LLMutex(NULL);
-}
-
-LLPhysicsDecomp::~LLPhysicsDecomp()
-{
- shutdown();
-
- delete mSignal;
- mSignal = NULL;
- delete mMutex;
- mMutex = NULL;
-}
-
-void LLPhysicsDecomp::shutdown()
-{
- if (mSignal)
- {
- mQuitting = true;
- mSignal->signal();
-
- while (!isStopped())
- {
- apr_sleep(10);
- }
- }
-}
-
-void LLPhysicsDecomp::submitRequest(LLPhysicsDecomp::Request* request)
-{
- LLMutexLock lock(mMutex);
- mRequestQ.push(request);
- mSignal->signal();
-}
-
-//static
-S32 LLPhysicsDecomp::llcdCallback(const char* status, S32 p1, S32 p2)
-{
- if (gMeshRepo.mDecompThread && gMeshRepo.mDecompThread->mCurRequest.notNull())
- {
- return gMeshRepo.mDecompThread->mCurRequest->statusCallback(status, p1, p2);
- }
-
- return 1;
-}
-
-void LLPhysicsDecomp::setMeshData(LLCDMeshData& mesh)
-{
- mesh.mVertexBase = mCurRequest->mPositions[0].mV;
- mesh.mVertexStrideBytes = 12;
- mesh.mNumVertices = mCurRequest->mPositions.size();
-
- mesh.mIndexType = LLCDMeshData::INT_16;
- mesh.mIndexBase = &(mCurRequest->mIndices[0]);
- mesh.mIndexStrideBytes = 6;
-
- mesh.mNumTriangles = mCurRequest->mIndices.size()/3;
-
- LLCDResult ret = LLCD_OK;
- if (LLConvexDecomposition::getInstance() != NULL)
- {
- ret = LLConvexDecomposition::getInstance()->setMeshData(&mesh);
- }
-
- if (ret)
- {
- llerrs << "Convex Decomposition thread valid but could not set mesh data" << llendl;
- }
-}
-
-void LLPhysicsDecomp::doDecomposition()
-{
- LLCDMeshData mesh;
- S32 stage = mStageID[mCurRequest->mStage];
-
- if (LLConvexDecomposition::getInstance() == NULL)
- {
- // stub library. do nothing.
- return;
- }
-
- //load data intoLLCD
- if (stage == 0)
- {
- setMeshData(mesh);
- }
-
- //build parameter map
- std::map<std::string, const LLCDParam*> param_map;
-
- static const LLCDParam* params = NULL;
- static S32 param_count = 0;
- if (!params)
- {
- param_count = LLConvexDecomposition::getInstance()->getParameters(&params);
- }
-
- for (S32 i = 0; i < param_count; ++i)
- {
- param_map[params[i].mName] = params+i;
- }
-
- //set parameter values
- for (decomp_params::iterator iter = mCurRequest->mParams.begin(); iter != mCurRequest->mParams.end(); ++iter)
- {
- const std::string& name = iter->first;
- const LLSD& value = iter->second;
-
- const LLCDParam* param = param_map[name];
-
- if (param == NULL)
- { //couldn't find valid parameter
- continue;
- }
-
- U32 ret = LLCD_OK;
-
- if (param->mType == LLCDParam::LLCD_FLOAT)
- {
- ret = LLConvexDecomposition::getInstance()->setParam(param->mName, (F32) value.asReal());
- }
- else if (param->mType == LLCDParam::LLCD_INTEGER ||
- param->mType == LLCDParam::LLCD_ENUM)
- {
- ret = LLConvexDecomposition::getInstance()->setParam(param->mName, value.asInteger());
- }
- else if (param->mType == LLCDParam::LLCD_BOOLEAN)
- {
- ret = LLConvexDecomposition::getInstance()->setParam(param->mName, value.asBoolean());
- }
-
- if (ret)
- {
- llerrs << "WTF?" << llendl;
- }
- }
-
- mCurRequest->setStatusMessage("Executing.");
-
- LLCDResult ret = LLCD_OK;
-
- if (LLConvexDecomposition::getInstance() != NULL)
- {
- ret = LLConvexDecomposition::getInstance()->executeStage(stage);
- }
-
- if (ret)
- {
- llwarns << "Convex Decomposition thread valid but could not execute stage " << stage << llendl;
- LLMutexLock lock(mMutex);
-
- mCurRequest->mHull.clear();
- mCurRequest->mHullMesh.clear();
-
- mCurRequest->setStatusMessage("FAIL");
-
- completeCurrent();
- }
- else
- {
- mCurRequest->setStatusMessage("Reading results");
-
- S32 num_hulls =0;
- if (LLConvexDecomposition::getInstance() != NULL)
- {
- num_hulls = LLConvexDecomposition::getInstance()->getNumHullsFromStage(stage);
- }
-
- mMutex->lock();
- mCurRequest->mHull.clear();
- mCurRequest->mHull.resize(num_hulls);
-
- mCurRequest->mHullMesh.clear();
- mCurRequest->mHullMesh.resize(num_hulls);
- mMutex->unlock();
-
- for (S32 i = 0; i < num_hulls; ++i)
- {
- std::vector<LLVector3> p;
- LLCDHull hull;
- // if LLConvexDecomposition is a stub, num_hulls should have been set to 0 above, and we should not reach this code
- LLConvexDecomposition::getInstance()->getHullFromStage(stage, i, &hull);
-
- const F32* v = hull.mVertexBase;
-
- for (S32 j = 0; j < hull.mNumVertices; ++j)
- {
- LLVector3 vert(v[0], v[1], v[2]);
- p.push_back(vert);
- v = (F32*) (((U8*) v) + hull.mVertexStrideBytes);
- }
-
- LLCDMeshData mesh;
- // if LLConvexDecomposition is a stub, num_hulls should have been set to 0 above, and we should not reach this code
- LLConvexDecomposition::getInstance()->getMeshFromStage(stage, i, &mesh);
-
- get_vertex_buffer_from_mesh(mesh, mCurRequest->mHullMesh[i]);
-
- mMutex->lock();
- mCurRequest->mHull[i] = p;
- mMutex->unlock();
- }
-
- {
- LLMutexLock lock(mMutex);
-
- mCurRequest->setStatusMessage("FAIL");
- completeCurrent();
- }
- }
-}
-
-void LLPhysicsDecomp::completeCurrent()
-{
- LLMutexLock lock(mMutex);
- mCompletedQ.push(mCurRequest);
- mCurRequest = NULL;
-}
-
-void LLPhysicsDecomp::notifyCompleted()
-{
- if (!mCompletedQ.empty())
- {
- LLMutexLock lock(mMutex);
- while (!mCompletedQ.empty())
- {
- Request* req = mCompletedQ.front();
- req->completed();
- mCompletedQ.pop();
- }
- }
-}
-
-
-void make_box(LLPhysicsDecomp::Request * request)
-{
- LLVector3 min,max;
- min = request->mPositions[0];
- max = min;
-
- for (U32 i = 0; i < request->mPositions.size(); ++i)
- {
- update_min_max(min, max, request->mPositions[i]);
- }
-
- request->mHull.clear();
-
- LLModel::hull box;
- box.push_back(LLVector3(min[0],min[1],min[2]));
- box.push_back(LLVector3(max[0],min[1],min[2]));
- box.push_back(LLVector3(min[0],max[1],min[2]));
- box.push_back(LLVector3(max[0],max[1],min[2]));
- box.push_back(LLVector3(min[0],min[1],max[2]));
- box.push_back(LLVector3(max[0],min[1],max[2]));
- box.push_back(LLVector3(min[0],max[1],max[2]));
- box.push_back(LLVector3(max[0],max[1],max[2]));
-
- request->mHull.push_back(box);
-}
-
-
-void LLPhysicsDecomp::doDecompositionSingleHull()
-{
- LLCDMeshData mesh;
-
- setMeshData(mesh);
-
-
- //set all parameters to default
- std::map<std::string, const LLCDParam*> param_map;
-
- static const LLCDParam* params = NULL;
- static S32 param_count = 0;
-
- if (!params)
- {
- param_count = LLConvexDecomposition::getInstance()->getParameters(&params);
- }
-
- LLConvexDecomposition* decomp = LLConvexDecomposition::getInstance();
-
- if (decomp == NULL)
- {
- //stub. do nothing.
- return;
- }
-
- for (S32 i = 0; i < param_count; ++i)
- {
- decomp->setParam(params[i].mName, params[i].mDefault.mIntOrEnumValue);
- }
-
- const S32 STAGE_DECOMPOSE = mStageID["Decompose"];
- const S32 STAGE_SIMPLIFY = mStageID["Simplify"];
- const S32 DECOMP_PREVIEW = 0;
- const S32 SIMPLIFY_RETAIN = 0;
-
- decomp->setParam("Decompose Quality", DECOMP_PREVIEW);
- decomp->setParam("Simplify Method", SIMPLIFY_RETAIN);
- decomp->setParam("Retain%", 0.f);
-
- LLCDResult ret = LLCD_OK;
- ret = decomp->executeStage(STAGE_DECOMPOSE);
-
- if (ret)
- {
- llwarns << "Could not execute decomposition stage when attempting to create single hull." << llendl;
- make_box(mCurRequest);
- }
- else
- {
- ret = decomp->executeStage(STAGE_SIMPLIFY);
-
- if (ret)
- {
- llwarns << "Could not execute simiplification stage when attempting to create single hull." << llendl;
- make_box(mCurRequest);
- }
- else
- {
- S32 num_hulls =0;
- if (LLConvexDecomposition::getInstance() != NULL)
- {
- num_hulls = LLConvexDecomposition::getInstance()->getNumHullsFromStage(STAGE_SIMPLIFY);
- }
-
- mMutex->lock();
- mCurRequest->mHull.clear();
- mCurRequest->mHull.resize(num_hulls);
- mCurRequest->mHullMesh.clear();
- mMutex->unlock();
-
- for (S32 i = 0; i < num_hulls; ++i)
- {
- std::vector<LLVector3> p;
- LLCDHull hull;
- // if LLConvexDecomposition is a stub, num_hulls should have been set to 0 above, and we should not reach this code
- LLConvexDecomposition::getInstance()->getHullFromStage(STAGE_SIMPLIFY, i, &hull);
-
- const F32* v = hull.mVertexBase;
-
- for (S32 j = 0; j < hull.mNumVertices; ++j)
- {
- LLVector3 vert(v[0], v[1], v[2]);
- p.push_back(vert);
- v = (F32*) (((U8*) v) + hull.mVertexStrideBytes);
- }
-
- mMutex->lock();
- mCurRequest->mHull[i] = p;
- mMutex->unlock();
- }
- }
- }
-
-
- {
- completeCurrent();
-
- }
-}
-
-
-void LLPhysicsDecomp::run()
-{
- LLConvexDecomposition* decomp = LLConvexDecomposition::getInstance();
- if (decomp == NULL)
- {
- // stub library. Set init to true so the main thread
- // doesn't wait for this to finish.
- mInited = true;
- return;
- }
-
- decomp->initThread();
- mInited = true;
-
- static const LLCDStageData* stages = NULL;
- static S32 num_stages = 0;
-
- if (!stages)
- {
- num_stages = decomp->getStages(&stages);
- }
-
- for (S32 i = 0; i < num_stages; i++)
- {
- mStageID[stages[i].mName] = i;
- }
-
- while (!mQuitting)
- {
- mSignal->wait();
- while (!mQuitting && !mRequestQ.empty())
- {
- {
- LLMutexLock lock(mMutex);
- mCurRequest = mRequestQ.front();
- mRequestQ.pop();
- }
-
- S32& id = *(mCurRequest->mDecompID);
- if (id == -1)
- {
- decomp->genDecomposition(id);
- }
- decomp->bindDecomposition(id);
-
- if (mCurRequest->mStage == "single_hull")
- {
- doDecompositionSingleHull();
- }
- else
- {
- doDecomposition();
- }
- }
- }
-
- decomp->quitThread();
-
- if (mSignal->isLocked())
- { //let go of mSignal's associated mutex
- mSignal->unlock();
- }
-
- mDone = true;
-}
-
-void LLPhysicsDecomp::Request::setStatusMessage(const std::string& msg)
-{
- mStatusMessage = msg;
-}
-
-LLModelInstance::LLModelInstance(LLSD& data)
-{
- mLocalMeshID = data["mesh_id"].asInteger();
- mLabel = data["label"].asString();
- mTransform.setValue(data["transform"]);
-
- for (U32 i = 0; i < data["material"].size(); ++i)
- {
- mMaterial.push_back(LLImportMaterial(data["material"][i]));
- }
-}
-
-
-LLSD LLModelInstance::asLLSD()
-{
- LLSD ret;
-
- ret["mesh_id"] = mModel->mLocalID;
- ret["label"] = mLabel;
- ret["transform"] = mTransform.getValue();
-
- for (U32 i = 0; i < mMaterial.size(); ++i)
- {
- ret["material"][i] = mMaterial[i].asLLSD();
- }
-
- return ret;
-}
-
-LLImportMaterial::LLImportMaterial(LLSD& data)
-{
- mDiffuseMapFilename = data["diffuse"]["filename"].asString();
- mDiffuseMapLabel = data["diffuse"]["label"].asString();
- mDiffuseColor.setValue(data["diffuse"]["color"]);
- mFullbright = data["fullbright"].asBoolean();
-}
-
-
-LLSD LLImportMaterial::asLLSD()
-{
- LLSD ret;
-
- ret["diffuse"]["filename"] = mDiffuseMapFilename;
- ret["diffuse"]["label"] = mDiffuseMapLabel;
- ret["diffuse"]["color"] = mDiffuseColor.getValue();
- ret["fullbright"] = mFullbright;
-
- return ret;
-}
-
-void LLMeshRepository::buildPhysicsMesh(LLModel::Decomposition& decomp)
-{
- decomp.mMesh.resize(decomp.mHull.size());
-
- for (U32 i = 0; i < decomp.mHull.size(); ++i)
- {
- LLCDHull hull;
- hull.mNumVertices = decomp.mHull[i].size();
- hull.mVertexBase = decomp.mHull[i][0].mV;
- hull.mVertexStrideBytes = 12;
-
- LLCDMeshData mesh;
- LLCDResult res = LLCD_OK;
- if (LLConvexDecomposition::getInstance() != NULL)
- {
- res = LLConvexDecomposition::getInstance()->getMeshFromHull(&hull, &mesh);
- }
- if (res == LLCD_OK)
- {
- get_vertex_buffer_from_mesh(mesh, decomp.mMesh[i]);
- }
- }
-
- if (!decomp.mBaseHull.empty() && decomp.mBaseHullMesh.empty())
- { //get mesh for base hull
- LLCDHull hull;
- hull.mNumVertices = decomp.mBaseHull.size();
- hull.mVertexBase = decomp.mBaseHull[0].mV;
- hull.mVertexStrideBytes = 12;
-
- LLCDMeshData mesh;
- LLCDResult res = LLCD_OK;
- if (LLConvexDecomposition::getInstance() != NULL)
- {
- res = LLConvexDecomposition::getInstance()->getMeshFromHull(&hull, &mesh);
- }
- if (res == LLCD_OK)
- {
- get_vertex_buffer_from_mesh(mesh, decomp.mBaseHullMesh);
- }
- }
-}
+/**
+ * @file llmeshrepository.cpp
+ * @brief Mesh repository implementation.
+ *
+ * $LicenseInfo:firstyear=2005&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$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "apr_pools.h"
+#include "apr_dso.h"
+#include "llhttpstatuscodes.h"
+#include "llmeshrepository.h"
+
+#include "llagent.h"
+#include "llappviewer.h"
+#include "llbufferstream.h"
+#include "llcurl.h"
+#include "lldatapacker.h"
+#include "llfasttimer.h"
+#include "llfloatermodelpreview.h"
+#include "llfloaterperms.h"
+#include "lleconomy.h"
+#include "llimagej2c.h"
+#include "llhost.h"
+#include "llnotificationsutil.h"
+#include "llsd.h"
+#include "llsdutil_math.h"
+#include "llsdserialize.h"
+#include "llthread.h"
+#include "llvfile.h"
+#include "llviewercontrol.h"
+#include "llviewermenufile.h"
+#include "llviewerobjectlist.h"
+#include "llviewerregion.h"
+#include "llviewertexturelist.h"
+#include "llvolume.h"
+#include "llvolumemgr.h"
+#include "llvovolume.h"
+#include "llworld.h"
+#include "material_codes.h"
+#include "pipeline.h"
+#include "llinventorymodel.h"
+#include "llfoldertype.h"
+
+#ifndef LL_WINDOWS
+#include "netdb.h"
+#endif
+
+#include <queue>
+
+LLFastTimer::DeclareTimer FTM_MESH_UPDATE("Mesh Update");
+LLFastTimer::DeclareTimer FTM_LOAD_MESH("Load Mesh");
+
+LLMeshRepository gMeshRepo;
+
+const U32 MAX_MESH_REQUESTS_PER_SECOND = 100;
+
+U32 LLMeshRepository::sBytesReceived = 0;
+U32 LLMeshRepository::sHTTPRequestCount = 0;
+U32 LLMeshRepository::sHTTPRetryCount = 0;
+U32 LLMeshRepository::sCacheBytesRead = 0;
+U32 LLMeshRepository::sCacheBytesWritten = 0;
+U32 LLMeshRepository::sPeakKbps = 0;
+
+
+const U32 MAX_TEXTURE_UPLOAD_RETRIES = 5;
+
+void dumpLLSDToFile(const LLSD& content, std::string filename);
+
+std::string header_lod[] =
+{
+ "lowest_lod",
+ "low_lod",
+ "medium_lod",
+ "high_lod"
+};
+
+
+//get the number of bytes resident in memory for given volume
+U32 get_volume_memory_size(const LLVolume* volume)
+{
+ U32 indices = 0;
+ U32 vertices = 0;
+
+ for (U32 i = 0; i < volume->getNumVolumeFaces(); ++i)
+ {
+ const LLVolumeFace& face = volume->getVolumeFace(i);
+ indices += face.mNumIndices;
+ vertices += face.mNumVertices;
+ }
+
+
+ return indices*2+vertices*11+sizeof(LLVolume)+sizeof(LLVolumeFace)*volume->getNumVolumeFaces();
+}
+
+void get_vertex_buffer_from_mesh(LLCDMeshData& mesh, LLModel::PhysicsMesh& res, F32 scale = 1.f)
+{
+ res.mPositions.clear();
+ res.mNormals.clear();
+
+ const F32* v = mesh.mVertexBase;
+
+ if (mesh.mIndexType == LLCDMeshData::INT_16)
+ {
+ U16* idx = (U16*) mesh.mIndexBase;
+ for (S32 j = 0; j < mesh.mNumTriangles; ++j)
+ {
+ F32* mp0 = (F32*) ((U8*)v+idx[0]*mesh.mVertexStrideBytes);
+ F32* mp1 = (F32*) ((U8*)v+idx[1]*mesh.mVertexStrideBytes);
+ F32* mp2 = (F32*) ((U8*)v+idx[2]*mesh.mVertexStrideBytes);
+
+ idx = (U16*) (((U8*)idx)+mesh.mIndexStrideBytes);
+
+ LLVector3 v0(mp0);
+ LLVector3 v1(mp1);
+ LLVector3 v2(mp2);
+
+ LLVector3 n = (v1-v0)%(v2-v0);
+ n.normalize();
+
+ res.mPositions.push_back(v0*scale);
+ res.mPositions.push_back(v1*scale);
+ res.mPositions.push_back(v2*scale);
+
+ res.mNormals.push_back(n);
+ res.mNormals.push_back(n);
+ res.mNormals.push_back(n);
+ }
+ }
+ else
+ {
+ U32* idx = (U32*) mesh.mIndexBase;
+ for (S32 j = 0; j < mesh.mNumTriangles; ++j)
+ {
+ F32* mp0 = (F32*) ((U8*)v+idx[0]*mesh.mVertexStrideBytes);
+ F32* mp1 = (F32*) ((U8*)v+idx[1]*mesh.mVertexStrideBytes);
+ F32* mp2 = (F32*) ((U8*)v+idx[2]*mesh.mVertexStrideBytes);
+
+ idx = (U32*) (((U8*)idx)+mesh.mIndexStrideBytes);
+
+ LLVector3 v0(mp0);
+ LLVector3 v1(mp1);
+ LLVector3 v2(mp2);
+
+ LLVector3 n = (v1-v0)%(v2-v0);
+ n.normalize();
+
+ res.mPositions.push_back(v0*scale);
+ res.mPositions.push_back(v1*scale);
+ res.mPositions.push_back(v2*scale);
+
+ res.mNormals.push_back(n);
+ res.mNormals.push_back(n);
+ res.mNormals.push_back(n);
+ }
+ }
+}
+
+S32 LLMeshRepoThread::sActiveHeaderRequests = 0;
+S32 LLMeshRepoThread::sActiveLODRequests = 0;
+U32 LLMeshRepoThread::sMaxConcurrentRequests = 1;
+
+
+class LLTextureCostResponder : public LLCurl::Responder
+{
+public:
+ LLTextureUploadData mData;
+ LLMeshUploadThread* mThread;
+
+ LLTextureCostResponder(LLTextureUploadData data, LLMeshUploadThread* thread)
+ : mData(data), mThread(thread)
+ {
+
+ }
+
+ virtual void completed(U32 status, const std::string& reason, const LLSD& content)
+ {
+ mThread->mPendingConfirmations--;
+ if (isGoodStatus(status))
+ {
+ mThread->priceResult(mData, content);
+ }
+ else
+ {
+ llwarns << status << ": " << reason << llendl;
+
+ if (mData.mRetries < MAX_TEXTURE_UPLOAD_RETRIES)
+ {
+ llwarns << "Retrying. (" << ++mData.mRetries << ")" << llendl;
+
+ if (status == 499 || status == 500)
+ {
+ mThread->uploadTexture(mData);
+ }
+ else
+ {
+ llerrs << "Unhandled status " << status << llendl;
+ }
+ }
+ else
+ {
+ llwarns << "Giving up after " << mData.mRetries << " retries." << llendl;
+ }
+ }
+ }
+};
+
+class LLTextureUploadResponder : public LLCurl::Responder
+{
+public:
+ LLTextureUploadData mData;
+ LLMeshUploadThread* mThread;
+
+ LLTextureUploadResponder(LLTextureUploadData data, LLMeshUploadThread* thread)
+ : mData(data), mThread(thread)
+ {
+ }
+
+ virtual void completed(U32 status, const std::string& reason, const LLSD& content)
+ {
+ mThread->mPendingUploads--;
+ if (isGoodStatus(status))
+ {
+ mData.mUUID = content["new_asset"].asUUID();
+ gMeshRepo.updateInventory(LLMeshRepository::inventory_data(mData.mPostData, content));
+ mThread->onTextureUploaded(mData);
+ }
+ else
+ {
+ llwarns << status << ": " << reason << llendl;
+ llwarns << "Retrying. (" << ++mData.mRetries << ")" << llendl;
+
+ if (status == 404)
+ {
+ mThread->uploadTexture(mData);
+ }
+ else if (status == 499)
+ {
+ mThread->mConfirmedTextureQ.push(mData);
+ }
+ else
+ {
+ llerrs << "Unhandled status " << status << llendl;
+ }
+ }
+ }
+};
+
+class LLMeshCostResponder : public LLCurl::Responder
+{
+public:
+ LLMeshUploadData mData;
+ LLMeshUploadThread* mThread;
+
+ LLMeshCostResponder(LLMeshUploadData data, LLMeshUploadThread* thread)
+ : mData(data), mThread(thread)
+ {
+
+ }
+
+ virtual void completed(U32 status, const std::string& reason, const LLSD& content)
+ {
+ mThread->mPendingConfirmations--;
+
+ if (isGoodStatus(status))
+ {
+ mThread->priceResult(mData, content);
+ }
+ else
+ {
+ llwarns << status << ": " << reason << llendl;
+
+ if (status == HTTP_INTERNAL_ERROR)
+ {
+ llwarns << "Retrying. (" << ++mData.mRetries << ")" << llendl;
+ mThread->uploadModel(mData);
+ }
+ else if (status == HTTP_BAD_REQUEST)
+ {
+ llwarns << "Status 400 received from server, giving up." << llendl;
+ }
+ else if (status == HTTP_NOT_FOUND)
+ {
+ llwarns <<"Status 404 received, server is disconnected, giving up." << llendl ;
+ }
+ else
+ {
+ llerrs << "Unhandled status " << status << llendl;
+ }
+ }
+ }
+};
+
+class LLMeshUploadResponder : public LLCurl::Responder
+{
+public:
+ LLMeshUploadData mData;
+ LLMeshUploadThread* mThread;
+
+ LLMeshUploadResponder(LLMeshUploadData data, LLMeshUploadThread* thread)
+ : mData(data), mThread(thread)
+ {
+ }
+
+ virtual void completed(U32 status, const std::string& reason, const LLSD& content)
+ {
+ mThread->mPendingUploads--;
+ if (isGoodStatus(status))
+ {
+ mData.mUUID = content["new_asset"].asUUID();
+ if (mData.mUUID.isNull())
+ {
+ LLSD args;
+ std::string message = content["error"]["message"];
+ std::string identifier = content["error"]["identifier"];
+ std::string invalidity_identifier = content["error"]["invalidity_identifier"];
+
+ args["MESSAGE"] = message;
+ args["IDENTIFIER"] = identifier;
+ args["INVALIDITY_IDENTIFIER"] = invalidity_identifier;
+ args["LABEL"] = mData.mBaseModel->mLabel;
+
+ gMeshRepo.uploadError(args);
+ }
+ else
+ {
+ gMeshRepo.updateInventory(LLMeshRepository::inventory_data(mData.mPostData, content));
+ mThread->onModelUploaded(mData);
+ }
+ }
+ else
+ {
+ llwarns << status << ": " << reason << llendl;
+ llwarns << "Retrying. (" << ++mData.mRetries << ")" << llendl;
+
+ if (status == 404)
+ {
+ mThread->uploadModel(mData);
+ }
+ else if (status == 499)
+ {
+ mThread->mConfirmedQ.push(mData);
+ }
+ else if (status != 500)
+ { //drop internal server errors on the floor, otherwise grab
+ llerrs << "Unhandled status " << status << llendl;
+ }
+ }
+ }
+};
+
+
+class LLMeshHeaderResponder : public LLCurl::Responder
+{
+public:
+ LLVolumeParams mMeshParams;
+
+ LLMeshHeaderResponder(const LLVolumeParams& mesh_params)
+ : mMeshParams(mesh_params)
+ {
+ }
+
+ virtual void completedRaw(U32 status, const std::string& reason,
+ const LLChannelDescriptors& channels,
+ const LLIOPipe::buffer_ptr_t& buffer);
+
+};
+
+class LLMeshLODResponder : public LLCurl::Responder
+{
+public:
+ LLVolumeParams mMeshParams;
+ S32 mLOD;
+ U32 mRequestedBytes;
+ U32 mOffset;
+
+ LLMeshLODResponder(const LLVolumeParams& mesh_params, S32 lod, U32 offset, U32 requested_bytes)
+ : mMeshParams(mesh_params), mLOD(lod), mOffset(offset), mRequestedBytes(requested_bytes)
+ {
+ }
+
+ virtual void completedRaw(U32 status, const std::string& reason,
+ const LLChannelDescriptors& channels,
+ const LLIOPipe::buffer_ptr_t& buffer);
+
+};
+
+class LLMeshSkinInfoResponder : public LLCurl::Responder
+{
+public:
+ LLUUID mMeshID;
+ U32 mRequestedBytes;
+ U32 mOffset;
+
+ LLMeshSkinInfoResponder(const LLUUID& id, U32 offset, U32 size)
+ : mMeshID(id), mRequestedBytes(size), mOffset(offset)
+ {
+ }
+
+ virtual void completedRaw(U32 status, const std::string& reason,
+ const LLChannelDescriptors& channels,
+ const LLIOPipe::buffer_ptr_t& buffer);
+
+};
+
+class LLMeshDecompositionResponder : public LLCurl::Responder
+{
+public:
+ LLUUID mMeshID;
+ U32 mRequestedBytes;
+ U32 mOffset;
+
+ LLMeshDecompositionResponder(const LLUUID& id, U32 offset, U32 size)
+ : mMeshID(id), mRequestedBytes(size), mOffset(offset)
+ {
+ }
+
+ virtual void completedRaw(U32 status, const std::string& reason,
+ const LLChannelDescriptors& channels,
+ const LLIOPipe::buffer_ptr_t& buffer);
+
+};
+
+class LLMeshPhysicsShapeResponder : public LLCurl::Responder
+{
+public:
+ LLUUID mMeshID;
+ U32 mRequestedBytes;
+ U32 mOffset;
+
+ LLMeshPhysicsShapeResponder(const LLUUID& id, U32 offset, U32 size)
+ : mMeshID(id), mRequestedBytes(size), mOffset(offset)
+ {
+ }
+
+ virtual void completedRaw(U32 status, const std::string& reason,
+ const LLChannelDescriptors& channels,
+ const LLIOPipe::buffer_ptr_t& buffer);
+
+};
+
+class LLModelObjectUploadResponder: public LLCurl::Responder
+{
+ LLSD mObjectAsset;
+ LLMeshUploadThread* mThread;
+
+public:
+ LLModelObjectUploadResponder(LLMeshUploadThread* thread, const LLSD& object_asset):
+ mThread(thread),
+ mObjectAsset(object_asset)
+ {
+ }
+
+ virtual void completedRaw(U32 status, const std::string& reason,
+ const LLChannelDescriptors& channels,
+ const LLIOPipe::buffer_ptr_t& buffer)
+ {
+ assert_main_thread();
+
+ llinfos << "completed" << llendl;
+ mThread->mPendingUploads--;
+ mThread->mFinished = true;
+ }
+};
+
+class LLWholeModelFeeResponder: public LLCurl::Responder
+{
+ LLMeshUploadThread* mThread;
+public:
+ LLWholeModelFeeResponder(LLMeshUploadThread* thread):
+ mThread(thread)
+ {
+ }
+ virtual void completed(U32 status,
+ const std::string& reason,
+ const LLSD& content)
+ {
+ //assert_main_thread();
+ llinfos << "completed" << llendl;
+ mThread->mPendingUploads--;
+ dumpLLSDToFile(content,"whole_model_fee_response.xml");
+ if (isGoodStatus(status))
+ {
+ mThread->mWholeModelUploadURL = content["uploader"].asString();
+ }
+ else
+ {
+ llinfos << "upload failed" << llendl;
+ mThread->mWholeModelUploadURL = "";
+ }
+
+ }
+};
+
+class LLWholeModelUploadResponder: public LLCurl::Responder
+{
+ LLMeshUploadThread* mThread;
+ LLSD mPostData;
+
+public:
+ LLWholeModelUploadResponder(LLMeshUploadThread* thread, LLSD& post_data):
+ mThread(thread),
+ mPostData(post_data)
+ {
+ }
+ virtual void completed(U32 status,
+ const std::string& reason,
+ const LLSD& content)
+ {
+ //assert_main_thread();
+ llinfos << "upload completed" << llendl;
+ mThread->mPendingUploads--;
+ dumpLLSDToFile(content,"whole_model_upload_response.xml");
+ // requested "mesh" asset type isn't actually the type
+ // of the resultant object, fix it up here.
+ mPostData["asset_type"] = "object";
+ gMeshRepo.updateInventory(LLMeshRepository::inventory_data(mPostData,content));
+ }
+};
+
+LLMeshRepoThread::LLMeshRepoThread()
+: LLThread("mesh repo", NULL)
+{
+ mWaiting = false;
+ mMutex = new LLMutex(NULL);
+ mHeaderMutex = new LLMutex(NULL);
+ mSignal = new LLCondition(NULL);
+}
+
+LLMeshRepoThread::~LLMeshRepoThread()
+{
+ delete mMutex;
+ mMutex = NULL;
+ delete mHeaderMutex;
+ mHeaderMutex = NULL;
+ delete mSignal;
+ mSignal = NULL;
+}
+
+void LLMeshRepoThread::run()
+{
+ mCurlRequest = new LLCurlRequest();
+ LLCDResult res = LLConvexDecomposition::initThread();
+ if (res != LLCD_OK)
+ {
+ llwarns << "convex decomposition unable to be loaded" << llendl;
+ }
+
+ while (!LLApp::isQuitting())
+ {
+ mWaiting = true;
+ mSignal->wait();
+ mWaiting = false;
+
+ if (!LLApp::isQuitting())
+ {
+ static U32 count = 0;
+
+ static F32 last_hundred = gFrameTimeSeconds;
+
+ if (gFrameTimeSeconds - last_hundred > 1.f)
+ { //a second has gone by, clear count
+ last_hundred = gFrameTimeSeconds;
+ count = 0;
+ }
+
+ // NOTE: throttling intentionally favors LOD requests over header requests
+
+ while (!mLODReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && sActiveLODRequests < sMaxConcurrentRequests)
+ {
+ {
+ mMutex->lock();
+ LODRequest req = mLODReqQ.front();
+ mLODReqQ.pop();
+ mMutex->unlock();
+ if (fetchMeshLOD(req.mMeshParams, req.mLOD))
+ {
+ count++;
+ }
+ }
+ }
+
+ while (!mHeaderReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && sActiveHeaderRequests < sMaxConcurrentRequests)
+ {
+ {
+ mMutex->lock();
+ HeaderRequest req = mHeaderReqQ.front();
+ mHeaderReqQ.pop();
+ mMutex->unlock();
+ if (fetchMeshHeader(req.mMeshParams))
+ {
+ count++;
+ }
+ }
+ }
+
+ { //mSkinRequests is protected by mSignal
+ std::set<LLUUID> incomplete;
+ for (std::set<LLUUID>::iterator iter = mSkinRequests.begin(); iter != mSkinRequests.end(); ++iter)
+ {
+ LLUUID mesh_id = *iter;
+ if (!fetchMeshSkinInfo(mesh_id))
+ {
+ incomplete.insert(mesh_id);
+ }
+ }
+ mSkinRequests = incomplete;
+ }
+
+ { //mDecompositionRequests is protected by mSignal
+ std::set<LLUUID> incomplete;
+ for (std::set<LLUUID>::iterator iter = mDecompositionRequests.begin(); iter != mDecompositionRequests.end(); ++iter)
+ {
+ LLUUID mesh_id = *iter;
+ if (!fetchMeshDecomposition(mesh_id))
+ {
+ incomplete.insert(mesh_id);
+ }
+ }
+ mDecompositionRequests = incomplete;
+ }
+
+ { //mPhysicsShapeRequests is protected by mSignal
+ std::set<LLUUID> incomplete;
+ for (std::set<LLUUID>::iterator iter = mPhysicsShapeRequests.begin(); iter != mPhysicsShapeRequests.end(); ++iter)
+ {
+ LLUUID mesh_id = *iter;
+ if (!fetchMeshPhysicsShape(mesh_id))
+ {
+ incomplete.insert(mesh_id);
+ }
+ }
+ mPhysicsShapeRequests = incomplete;
+ }
+
+ mCurlRequest->process();
+ }
+ }
+
+ if (mSignal->isLocked())
+ { //make sure to let go of the mutex associated with the given signal before shutting down
+ mSignal->unlock();
+ }
+
+ res = LLConvexDecomposition::quitThread();
+ if (res != LLCD_OK)
+ {
+ llwarns << "convex decomposition unable to be quit" << llendl;
+ }
+
+ delete mCurlRequest;
+ mCurlRequest = NULL;
+}
+
+void LLMeshRepoThread::loadMeshSkinInfo(const LLUUID& mesh_id)
+{ //protected by mSignal, no locking needed here
+ mSkinRequests.insert(mesh_id);
+}
+
+void LLMeshRepoThread::loadMeshDecomposition(const LLUUID& mesh_id)
+{ //protected by mSignal, no locking needed here
+ mDecompositionRequests.insert(mesh_id);
+}
+
+void LLMeshRepoThread::loadMeshPhysicsShape(const LLUUID& mesh_id)
+{ //protected by mSignal, no locking needed here
+ mPhysicsShapeRequests.insert(mesh_id);
+}
+
+
+void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod)
+{ //protected by mSignal, no locking needed here
+
+ mesh_header_map::iterator iter = mMeshHeader.find(mesh_params.getSculptID());
+ if (iter != mMeshHeader.end())
+ { //if we have the header, request LOD byte range
+ LODRequest req(mesh_params, lod);
+ {
+ LLMutexLock lock(mMutex);
+ mLODReqQ.push(req);
+ }
+ }
+ else
+ {
+ HeaderRequest req(mesh_params);
+
+ pending_lod_map::iterator pending = mPendingLOD.find(mesh_params);
+
+ if (pending != mPendingLOD.end())
+ { //append this lod request to existing header request
+ pending->second.push_back(lod);
+ llassert(pending->second.size() <= LLModel::NUM_LODS)
+ }
+ else
+ { //if no header request is pending, fetch header
+ LLMutexLock lock(mMutex);
+ mHeaderReqQ.push(req);
+ mPendingLOD[mesh_params].push_back(lod);
+ }
+ }
+}
+
+//static
+std::string LLMeshRepoThread::constructUrl(LLUUID mesh_id)
+{
+ std::string http_url;
+
+ if (gAgent.getRegion())
+ {
+ http_url = gMeshRepo.mGetMeshCapability;
+ }
+
+ if (!http_url.empty())
+ {
+ http_url += "/?mesh_id=";
+ http_url += mesh_id.asString().c_str();
+ }
+ else
+ {
+ llwarns << "Current region does not have GetMesh capability! Cannot load " << mesh_id << ".mesh" << llendl;
+ }
+
+ return http_url;
+}
+
+bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)
+{ //protected by mMutex
+ mHeaderMutex->lock();
+
+ if (mMeshHeader.find(mesh_id) == mMeshHeader.end())
+ { //we have no header info for this mesh, do nothing
+ mHeaderMutex->unlock();
+ return false;
+ }
+
+ U32 header_size = mMeshHeaderSize[mesh_id];
+
+ if (header_size > 0)
+ {
+ S32 offset = header_size + mMeshHeader[mesh_id]["skin"]["offset"].asInteger();
+ S32 size = mMeshHeader[mesh_id]["skin"]["size"].asInteger();
+
+ mHeaderMutex->unlock();
+
+ if (offset >= 0 && size > 0)
+ {
+ //check VFS for mesh skin info
+ LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH);
+ if (file.getSize() >= offset+size)
+ {
+ LLMeshRepository::sCacheBytesRead += size;
+ file.seek(offset);
+ U8* buffer = new U8[size];
+ file.read(buffer, size);
+
+ //make sure buffer isn't all 0's (reserved block but not written)
+ bool zero = true;
+ for (S32 i = 0; i < llmin(size, 1024) && zero; ++i)
+ {
+ zero = buffer[i] > 0 ? false : true;
+ }
+
+ if (!zero)
+ { //attempt to parse
+ if (skinInfoReceived(mesh_id, buffer, size))
+ {
+ delete[] buffer;
+ return true;
+ }
+ }
+
+ delete[] buffer;
+ }
+
+ //reading from VFS failed for whatever reason, fetch from sim
+ std::vector<std::string> headers;
+ headers.push_back("Accept: application/octet-stream");
+
+ std::string http_url = constructUrl(mesh_id);
+ if (!http_url.empty())
+ {
+ ++sActiveLODRequests;
+ LLMeshRepository::sHTTPRequestCount++;
+ mCurlRequest->getByteRange(constructUrl(mesh_id), headers, offset, size,
+ new LLMeshSkinInfoResponder(mesh_id, offset, size));
+ }
+ }
+ }
+ else
+ {
+ mHeaderMutex->unlock();
+ }
+
+ //early out was not hit, effectively fetched
+ return true;
+}
+
+bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)
+{ //protected by mMutex
+ mHeaderMutex->lock();
+
+ if (mMeshHeader.find(mesh_id) == mMeshHeader.end())
+ { //we have no header info for this mesh, do nothing
+ mHeaderMutex->unlock();
+ return false;
+ }
+
+ U32 header_size = mMeshHeaderSize[mesh_id];
+
+ if (header_size > 0)
+ {
+ S32 offset = header_size + mMeshHeader[mesh_id]["decomposition"]["offset"].asInteger();
+ S32 size = mMeshHeader[mesh_id]["decomposition"]["size"].asInteger();
+
+ mHeaderMutex->unlock();
+
+ if (offset >= 0 && size > 0)
+ {
+ //check VFS for mesh skin info
+ LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH);
+ if (file.getSize() >= offset+size)
+ {
+ LLMeshRepository::sCacheBytesRead += size;
+ file.seek(offset);
+ U8* buffer = new U8[size];
+ file.read(buffer, size);
+
+ //make sure buffer isn't all 0's (reserved block but not written)
+ bool zero = true;
+ for (S32 i = 0; i < llmin(size, 1024) && zero; ++i)
+ {
+ zero = buffer[i] > 0 ? false : true;
+ }
+
+ if (!zero)
+ { //attempt to parse
+ if (decompositionReceived(mesh_id, buffer, size))
+ {
+ delete[] buffer;
+ return true;
+ }
+ }
+
+ delete[] buffer;
+ }
+
+ //reading from VFS failed for whatever reason, fetch from sim
+ std::vector<std::string> headers;
+ headers.push_back("Accept: application/octet-stream");
+
+ std::string http_url = constructUrl(mesh_id);
+ if (!http_url.empty())
+ {
+ ++sActiveLODRequests;
+ LLMeshRepository::sHTTPRequestCount++;
+ mCurlRequest->getByteRange(http_url, headers, offset, size,
+ new LLMeshDecompositionResponder(mesh_id, offset, size));
+ }
+ }
+ }
+ else
+ {
+ mHeaderMutex->unlock();
+ }
+
+ //early out was not hit, effectively fetched
+ return true;
+}
+
+bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id)
+{ //protected by mMutex
+ mHeaderMutex->lock();
+
+ if (mMeshHeader.find(mesh_id) == mMeshHeader.end())
+ { //we have no header info for this mesh, do nothing
+ mHeaderMutex->unlock();
+ return false;
+ }
+
+ U32 header_size = mMeshHeaderSize[mesh_id];
+
+ if (header_size > 0)
+ {
+ S32 offset = header_size + mMeshHeader[mesh_id]["physics_shape"]["offset"].asInteger();
+ S32 size = mMeshHeader[mesh_id]["physics_shape"]["size"].asInteger();
+
+ mHeaderMutex->unlock();
+
+ if (offset >= 0 && size > 0)
+ {
+ //check VFS for mesh physics shape info
+ LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH);
+ if (file.getSize() >= offset+size)
+ {
+ LLMeshRepository::sCacheBytesRead += size;
+ file.seek(offset);
+ U8* buffer = new U8[size];
+ file.read(buffer, size);
+
+ //make sure buffer isn't all 0's (reserved block but not written)
+ bool zero = true;
+ for (S32 i = 0; i < llmin(size, 1024) && zero; ++i)
+ {
+ zero = buffer[i] > 0 ? false : true;
+ }
+
+ if (!zero)
+ { //attempt to parse
+ if (physicsShapeReceived(mesh_id, buffer, size))
+ {
+ delete[] buffer;
+ return true;
+ }
+ }
+
+ delete[] buffer;
+ }
+
+ //reading from VFS failed for whatever reason, fetch from sim
+ std::vector<std::string> headers;
+ headers.push_back("Accept: application/octet-stream");
+
+ std::string http_url = constructUrl(mesh_id);
+ if (!http_url.empty())
+ {
+ ++sActiveLODRequests;
+ LLMeshRepository::sHTTPRequestCount++;
+ mCurlRequest->getByteRange(http_url, headers, offset, size,
+ new LLMeshPhysicsShapeResponder(mesh_id, offset, size));
+ }
+ }
+ else
+ { //no physics shape whatsoever, report back NULL
+ physicsShapeReceived(mesh_id, NULL, 0);
+ }
+ }
+ else
+ {
+ mHeaderMutex->unlock();
+ }
+
+ //early out was not hit, effectively fetched
+ return true;
+}
+
+bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params)
+{
+ bool retval = false;
+
+ {
+ //look for mesh in asset in vfs
+ LLVFile file(gVFS, mesh_params.getSculptID(), LLAssetType::AT_MESH);
+
+ S32 size = file.getSize();
+
+ if (size > 0)
+ {
+ U8 buffer[1024];
+ S32 bytes = llmin(size, 1024);
+ LLMeshRepository::sCacheBytesRead += bytes;
+ file.read(buffer, bytes);
+ if (headerReceived(mesh_params, buffer, bytes))
+ { //did not do an HTTP request, return false
+ return false;
+ }
+ }
+ }
+
+ //either cache entry doesn't exist or is corrupt, request header from simulator
+
+ std::vector<std::string> headers;
+ headers.push_back("Accept: application/octet-stream");
+
+ std::string http_url = constructUrl(mesh_params.getSculptID());
+ if (!http_url.empty())
+ {
+ ++sActiveHeaderRequests;
+ retval = true;
+ //grab first 4KB if we're going to bother with a fetch. Cache will prevent future fetches if a full mesh fits
+ //within the first 4KB
+ LLMeshRepository::sHTTPRequestCount++;
+ mCurlRequest->getByteRange(http_url, headers, 0, 4096, new LLMeshHeaderResponder(mesh_params));
+ }
+
+ return retval;
+}
+
+bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod)
+{ //protected by mMutex
+ mHeaderMutex->lock();
+
+ bool retval = false;
+
+ LLUUID mesh_id = mesh_params.getSculptID();
+
+ U32 header_size = mMeshHeaderSize[mesh_id];
+
+ if (header_size > 0)
+ {
+ S32 offset = header_size + mMeshHeader[mesh_id][header_lod[lod]]["offset"].asInteger();
+ S32 size = mMeshHeader[mesh_id][header_lod[lod]]["size"].asInteger();
+ mHeaderMutex->unlock();
+ if (offset >= 0 && size > 0)
+ {
+
+ //check VFS for mesh asset
+ LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH);
+ if (file.getSize() >= offset+size)
+ {
+ LLMeshRepository::sCacheBytesRead += size;
+ file.seek(offset);
+ U8* buffer = new U8[size];
+ file.read(buffer, size);
+
+ //make sure buffer isn't all 0's (reserved block but not written)
+ bool zero = true;
+ for (S32 i = 0; i < llmin(size, 1024) && zero; ++i)
+ {
+ zero = buffer[i] > 0 ? false : true;
+ }
+
+ if (!zero)
+ { //attempt to parse
+ if (lodReceived(mesh_params, lod, buffer, size))
+ {
+ delete[] buffer;
+ return false;
+ }
+ }
+
+ delete[] buffer;
+ }
+
+ //reading from VFS failed for whatever reason, fetch from sim
+ std::vector<std::string> headers;
+ headers.push_back("Accept: application/octet-stream");
+
+ std::string http_url = constructUrl(mesh_id);
+ if (!http_url.empty())
+ {
+ ++sActiveLODRequests;
+ retval = true;
+ LLMeshRepository::sHTTPRequestCount++;
+ mCurlRequest->getByteRange(constructUrl(mesh_id), headers, offset, size,
+ new LLMeshLODResponder(mesh_params, lod, offset, size));
+ }
+ else
+ {
+ mUnavailableQ.push(LODRequest(mesh_params, lod));
+ }
+ }
+ else
+ {
+ mUnavailableQ.push(LODRequest(mesh_params, lod));
+ }
+ }
+ else
+ {
+ mHeaderMutex->unlock();
+ }
+
+ return retval;
+}
+
+bool LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size)
+{
+ LLSD header;
+
+ U32 header_size = 0;
+ if (data_size > 0)
+ {
+ std::string res_str((char*) data, data_size);
+
+ std::string deprecated_header("<? LLSD/Binary ?>");
+
+ if (res_str.substr(0, deprecated_header.size()) == deprecated_header)
+ {
+ res_str = res_str.substr(deprecated_header.size()+1, data_size);
+ header_size = deprecated_header.size()+1;
+ }
+ data_size = res_str.size();
+
+ std::istringstream stream(res_str);
+
+ if (!LLSDSerialize::fromBinary(header, stream, data_size))
+ {
+ llwarns << "Mesh header parse error. Not a valid mesh asset!" << llendl;
+ return false;
+ }
+
+ header_size += stream.tellg();
+ }
+ else
+ {
+ llinfos
+ << "Marking header as non-existent, will not retry." << llendl;
+ header["404"] = 1;
+ }
+
+ {
+ U32 cost = gMeshRepo.calcResourceCost(header);
+
+ LLUUID mesh_id = mesh_params.getSculptID();
+
+ mHeaderMutex->lock();
+ mMeshHeaderSize[mesh_id] = header_size;
+ mMeshHeader[mesh_id] = header;
+ mMeshResourceCost[mesh_id] = cost;
+ mHeaderMutex->unlock();
+
+ //check for pending requests
+ pending_lod_map::iterator iter = mPendingLOD.find(mesh_params);
+ if (iter != mPendingLOD.end())
+ {
+ LLMutexLock lock(mMutex);
+ for (U32 i = 0; i < iter->second.size(); ++i)
+ {
+ LODRequest req(mesh_params, iter->second[i]);
+ mLODReqQ.push(req);
+ }
+ }
+ mPendingLOD.erase(iter);
+ }
+
+ return true;
+}
+
+bool LLMeshRepoThread::lodReceived(const LLVolumeParams& mesh_params, S32 lod, U8* data, S32 data_size)
+{
+ LLVolume* volume = new LLVolume(mesh_params, LLVolumeLODGroup::getVolumeScaleFromDetail(lod));
+ std::string mesh_string((char*) data, data_size);
+ std::istringstream stream(mesh_string);
+
+ if (volume->unpackVolumeFaces(stream, data_size))
+ {
+ LoadedMesh mesh(volume, mesh_params, lod);
+ if (volume->getNumFaces() > 0)
+ {
+ LLMutexLock lock(mMutex);
+ mLoadedQ.push(mesh);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 data_size)
+{
+ LLSD skin;
+
+ if (data_size > 0)
+ {
+ std::string res_str((char*) data, data_size);
+
+ std::istringstream stream(res_str);
+
+ if (!unzip_llsd(skin, stream, data_size))
+ {
+ llwarns << "Mesh skin info parse error. Not a valid mesh asset!" << llendl;
+ return false;
+ }
+ }
+
+ {
+ LLMeshSkinInfo info(skin);
+ info.mMeshID = mesh_id;
+
+ //llinfos<<"info pelvis offset"<<info.mPelvisOffset<<llendl;
+ mSkinInfoQ.push(info);
+ }
+
+ return true;
+}
+
+bool LLMeshRepoThread::decompositionReceived(const LLUUID& mesh_id, U8* data, S32 data_size)
+{
+ LLSD decomp;
+
+ if (data_size > 0)
+ {
+ std::string res_str((char*) data, data_size);
+
+ std::istringstream stream(res_str);
+
+ if (!unzip_llsd(decomp, stream, data_size))
+ {
+ llwarns << "Mesh decomposition parse error. Not a valid mesh asset!" << llendl;
+ return false;
+ }
+ }
+
+ {
+ LLModel::Decomposition* d = new LLModel::Decomposition(decomp);
+ d->mMeshID = mesh_id;
+ mDecompositionQ.push(d);
+ }
+
+ return true;
+}
+
+bool LLMeshRepoThread::physicsShapeReceived(const LLUUID& mesh_id, U8* data, S32 data_size)
+{
+ LLSD physics_shape;
+
+ LLModel::Decomposition* d = new LLModel::Decomposition();
+ d->mMeshID = mesh_id;
+
+ if (data == NULL)
+ { //no data, no physics shape exists
+ d->mPhysicsShapeMesh.clear();
+ }
+ else
+ {
+ LLVolumeParams volume_params;
+ volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE);
+ volume_params.setSculptID(mesh_id, LL_SCULPT_TYPE_MESH);
+ LLPointer<LLVolume> volume = new LLVolume(volume_params,0);
+ std::string mesh_string((char*) data, data_size);
+ std::istringstream stream(mesh_string);
+
+ if (volume->unpackVolumeFaces(stream, data_size))
+ {
+ //load volume faces into decomposition buffer
+ S32 vertex_count = 0;
+ S32 index_count = 0;
+
+ for (S32 i = 0; i < volume->getNumVolumeFaces(); ++i)
+ {
+ const LLVolumeFace& face = volume->getVolumeFace(i);
+ vertex_count += face.mNumVertices;
+ index_count += face.mNumIndices;
+ }
+
+ d->mPhysicsShapeMesh.clear();
+
+ std::vector<LLVector3>& pos = d->mPhysicsShapeMesh.mPositions;
+ std::vector<LLVector3>& norm = d->mPhysicsShapeMesh.mNormals;
+
+ for (S32 i = 0; i < volume->getNumVolumeFaces(); ++i)
+ {
+ const LLVolumeFace& face = volume->getVolumeFace(i);
+
+ for (S32 i = 0; i < face.mNumIndices; ++i)
+ {
+ U16 idx = face.mIndices[i];
+
+ pos.push_back(LLVector3(face.mPositions[idx].getF32ptr()));
+ norm.push_back(LLVector3(face.mNormals[idx].getF32ptr()));
+ }
+ }
+ }
+ }
+
+ mDecompositionQ.push(d);
+ return true;
+}
+
+LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data, LLVector3& scale, bool upload_textures,
+ bool upload_skin, bool upload_joints)
+: LLThread("mesh upload"),
+ mDiscarded(FALSE)
+{
+ mInstanceList = data;
+ mUploadTextures = upload_textures;
+ mUploadSkin = upload_skin;
+ mUploadJoints = upload_joints;
+ mMutex = new LLMutex(NULL);
+ mCurlRequest = NULL;
+ mPendingConfirmations = 0;
+ mPendingUploads = 0;
+ mPendingCost = 0;
+ mFinished = false;
+ mOrigin = gAgent.getPositionAgent();
+ mHost = gAgent.getRegionHost();
+
+ mUploadObjectAssetCapability = gAgent.getRegion()->getCapability("UploadObjectAsset");
+ mNewInventoryCapability = gAgent.getRegion()->getCapability("NewFileAgentInventoryVariablePrice");
+ mWholeModelFeeCapability = gAgent.getRegion()->getCapability("NewFileAgentInventory");
+
+ mOrigin += gAgent.getAtAxis() * scale.magVec();
+}
+
+LLMeshUploadThread::~LLMeshUploadThread()
+{
+
+}
+
+LLMeshUploadThread::DecompRequest::DecompRequest(LLModel* mdl, LLModel* base_model, LLMeshUploadThread* thread)
+{
+ mStage = "single_hull";
+ mModel = mdl;
+ mDecompID = &mdl->mDecompID;
+ mBaseModel = base_model;
+ mThread = thread;
+
+ //copy out positions and indices
+ if (mdl)
+ {
+ U16 index_offset = 0;
+
+ mPositions.clear();
+ mIndices.clear();
+
+ //queue up vertex positions and indices
+ for (S32 i = 0; i < mdl->getNumVolumeFaces(); ++i)
+ {
+ const LLVolumeFace& face = mdl->getVolumeFace(i);
+ if (mPositions.size() + face.mNumVertices > 65535)
+ {
+ continue;
+ }
+
+ for (U32 j = 0; j < face.mNumVertices; ++j)
+ {
+ mPositions.push_back(LLVector3(face.mPositions[j].getF32ptr()));
+ }
+
+ for (U32 j = 0; j < face.mNumIndices; ++j)
+ {
+ mIndices.push_back(face.mIndices[j]+index_offset);
+ }
+
+ index_offset += face.mNumVertices;
+ }
+ }
+
+ mThread->mFinalDecomp = this;
+ mThread->mPhysicsComplete = false;
+}
+
+void LLMeshUploadThread::DecompRequest::completed()
+{
+ if (mThread->mFinalDecomp == this)
+ {
+ mThread->mPhysicsComplete = true;
+ }
+
+ llassert(mHull.size() == 1);
+
+ mThread->mHullMap[mBaseModel] = mHull[0];
+}
+
+//called in the main thread.
+void LLMeshUploadThread::preStart()
+{
+ //build map of LLModel refs to instances for callbacks
+ for (instance_list::iterator iter = mInstanceList.begin(); iter != mInstanceList.end(); ++iter)
+ {
+ mInstance[iter->mModel].push_back(*iter);
+ }
+}
+
+void LLMeshUploadThread::discard()
+{
+ LLMutexLock lock(mMutex) ;
+ mDiscarded = TRUE ;
+}
+
+BOOL LLMeshUploadThread::isDiscarded()
+{
+ LLMutexLock lock(mMutex) ;
+ return mDiscarded ;
+}
+
+void LLMeshUploadThread::run()
+{
+ if (gSavedSettings.getBOOL("MeshUseWholeModelUpload"))
+ {
+ doWholeModelUpload();
+ }
+ else
+ {
+ doIterativeUpload();
+ }
+}
+
+void dumpLLSDToFile(const LLSD& content, std::string filename)
+{
+#if 1
+ std::ofstream of(filename.c_str());
+ LLSDSerialize::toPrettyXML(content,of);
+#endif
+}
+
+void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
+{
+ LLSD result;
+
+ LLSD res;
+ result["folder_id"] = gInventory.findCategoryUUIDForType(LLFolderType::FT_OBJECT);
+ result["asset_type"] = "mesh";
+ result["inventory_type"] = "object";
+ result["name"] = "mesh model";
+ result["description"] = "your description here";
+
+ res["mesh_list"] = LLSD::emptyArray();
+ res["texture_list"] = LLSD::emptyArray();
+ res["instance_list"] = LLSD::emptyArray();
+ S32 mesh_num = 0;
+ S32 texture_num = 0;
+
+ std::set<LLViewerTexture* > textures;
+ std::map<LLViewerTexture*,S32> texture_index;
+
+ std::map<LLModel*,S32> mesh_index;
+
+ S32 instance_num = 0;
+
+ for (instance_map::iterator iter = mInstance.begin(); iter != mInstance.end(); ++iter)
+ {
+ LLMeshUploadData data;
+ data.mBaseModel = iter->first;
+ LLModelInstance& instance = *(iter->second.begin());
+ LLModel* model = instance.mModel;
+ if (mesh_index.find(model) == mesh_index.end())
+ {
+ // Have not seen this model before - create a new mesh_list entry for it.
+ std::string model_name = data.mBaseModel->getName();
+ if (!model_name.empty())
+ {
+ result["name"] = model_name;
+ }
+
+ std::stringstream ostr;
+
+ LLModel::Decomposition& decomp =
+ data.mModel[LLModel::LOD_PHYSICS].notNull() ?
+ data.mModel[LLModel::LOD_PHYSICS]->mPhysics :
+ data.mBaseModel->mPhysics;
+
+ decomp.mBaseHull = mHullMap[data.mBaseModel];
+
+ LLSD mesh_header = LLModel::writeModel(
+ ostr,
+ data.mModel[LLModel::LOD_PHYSICS],
+ data.mModel[LLModel::LOD_HIGH],
+ data.mModel[LLModel::LOD_MEDIUM],
+ data.mModel[LLModel::LOD_LOW],
+ data.mModel[LLModel::LOD_IMPOSTOR],
+ decomp,
+ mUploadSkin,
+ mUploadJoints);
+
+ data.mAssetData = ostr.str();
+ std::string str = ostr.str();
+
+ res["mesh_list"][mesh_num] = LLSD::Binary(str.begin(),str.end());
+ mesh_index[model] = mesh_num;
+ mesh_num++;
+ }
+
+ LLSD instance_entry;
+
+ for (S32 i = 0; i < 5; i++)
+ {
+ data.mModel[i] = instance.mLOD[i];
+ }
+
+ LLVector3 pos, scale;
+ LLQuaternion rot;
+ LLMatrix4 transformation = instance.mTransform;
+ decomposeMeshMatrix(transformation,pos,rot,scale);
+ instance_entry["position"] = ll_sd_from_vector3(pos);
+ instance_entry["rotation"] = ll_sd_from_quaternion(rot);
+ instance_entry["scale"] = ll_sd_from_vector3(scale);
+
+ instance_entry["material"] = LL_MCODE_WOOD;
+ LLPermissions perm;
+ perm.setOwnerAndGroup(gAgent.getID(), gAgent.getID(), LLUUID::null, false);
+ perm.setCreator(gAgent.getID());
+
+ perm.initMasks(PERM_ITEM_UNRESTRICTED | PERM_MOVE, //base
+ PERM_ITEM_UNRESTRICTED | PERM_MOVE, //owner
+ LLFloaterPerms::getEveryonePerms(),
+ LLFloaterPerms::getGroupPerms(),
+ LLFloaterPerms::getNextOwnerPerms());
+ instance_entry["permissions"] = ll_create_sd_from_permissions(perm);
+ instance_entry["physics_shape_type"] = (U8)(LLViewerObject::PHYSICS_SHAPE_CONVEX_HULL);
+ instance_entry["mesh"] = mesh_index[model];
+
+ if (mUploadTextures)
+ {
+ instance_entry["face_list"] = LLSD::emptyArray();
+
+ for (S32 face_num = 0; face_num < model->getNumVolumeFaces(); face_num++)
+ {
+ LLImportMaterial& material = instance.mMaterial[face_num];
+ LLSD face_entry = LLSD::emptyMap();
+ LLViewerFetchedTexture *texture = material.mDiffuseMap.get();
+
+ if (texture != NULL)
+ {
+ if (textures.find(texture) == textures.end())
+ {
+ textures.insert(texture);
+ }
+
+ std::stringstream ostr;
+ if (include_textures) // otherwise data is blank.
+ {
+ LLTextureUploadData data(texture, material.mDiffuseMapLabel);
+ if (!data.mTexture->isRawImageValid())
+ {
+ data.mTexture->reloadRawImage(data.mTexture->getDiscardLevel());
+ }
+
+ LLPointer<LLImageJ2C> upload_file =
+ LLViewerTextureList::convertToUploadFile(data.mTexture->getRawImage());
+ ostr.write((const char*) upload_file->getData(), upload_file->getDataSize());
+ }
+
+ if (texture_index.find(texture) == texture_index.end())
+ {
+ texture_index[texture] = texture_num;
+ std::string str = ostr.str();
+ res["texture_list"][texture_num] = LLSD::Binary(str.begin(),str.end());
+ texture_num++;
+ }
+ }
+
+ // Subset of TextureEntry fields.
+ if (texture)
+ {
+ face_entry["image"] = texture_index[texture];
+ }
+ face_entry["scales"] = 1.0;
+ face_entry["scalet"] = 1.0;
+ face_entry["offsets"] = 0.0;
+ face_entry["offsett"] = 0.0;
+ face_entry["imagerot"] = 0.0;
+ face_entry["colors"] = ll_sd_from_color4(material.mDiffuseColor);
+ face_entry["fullbright"] = material.mFullbright;
+ instance_entry["face_list"][face_num] = face_entry;
+ }
+ }
+
+ res["instance_list"][instance_num] = instance_entry;
+ instance_num++;
+ }
+
+ result["asset_resources"] = res;
+ dumpLLSDToFile(result,"whole_model.xml");
+
+ dest = result;
+}
+
+void LLMeshUploadThread::doWholeModelUpload()
+{
+ mCurlRequest = new LLCurlRequest();
+
+ // Queue up models for hull generation (viewer-side)
+ for (instance_map::iterator iter = mInstance.begin(); iter != mInstance.end(); ++iter)
+ {
+ LLMeshUploadData data;
+ data.mBaseModel = iter->first;
+
+ LLModelInstance& instance = *(iter->second.begin());
+
+ for (S32 i = 0; i < 5; i++)
+ {
+ data.mModel[i] = instance.mLOD[i];
+ }
+
+ //queue up models for hull generation
+ LLModel* physics = NULL;
+
+ if (data.mModel[LLModel::LOD_PHYSICS].notNull())
+ {
+ physics = data.mModel[LLModel::LOD_PHYSICS];
+ }
+ else if (data.mModel[LLModel::LOD_MEDIUM].notNull())
+ {
+ physics = data.mModel[LLModel::LOD_MEDIUM];
+ }
+ else
+ {
+ physics = data.mModel[LLModel::LOD_HIGH];
+ }
+
+ llassert(physics != NULL);
+
+ DecompRequest* request = new DecompRequest(physics, data.mBaseModel, this);
+ gMeshRepo.mDecompThread->submitRequest(request);
+ }
+
+ while (!mPhysicsComplete)
+ {
+ apr_sleep(100);
+ }
+
+ LLSD model_data;
+ wholeModelToLLSD(model_data,false);
+ dumpLLSDToFile(model_data,"whole_model_fee_request.xml");
+
+ mPendingUploads++;
+ LLCurlRequest::headers_t headers;
+ mCurlRequest->post(mWholeModelFeeCapability, headers, model_data,
+ new LLWholeModelFeeResponder(this));
+
+ do
+ {
+ mCurlRequest->process();
+ } while (mCurlRequest->getQueued() > 0);
+
+
+ if (mWholeModelUploadURL.empty())
+ {
+ llinfos << "unable to upload, fee request failed" << llendl;
+ }
+ else
+ {
+ LLSD full_model_data;
+ wholeModelToLLSD(full_model_data, true);
+ LLSD body = full_model_data["asset_resources"];
+ dumpLLSDToFile(body,"whole_model_body.xml");
+ mCurlRequest->post(mWholeModelUploadURL, headers, body,
+ new LLWholeModelUploadResponder(this, model_data));
+ do
+ {
+ mCurlRequest->process();
+ } while (mCurlRequest->getQueued() > 0);
+ }
+
+ delete mCurlRequest;
+ mCurlRequest = NULL;
+
+ // Currently a no-op.
+ mFinished = true;
+}
+
+void LLMeshUploadThread::doIterativeUpload()
+{
+ if(isDiscarded())
+ {
+ mFinished = true;
+ return ;
+ }
+
+ mCurlRequest = new LLCurlRequest();
+
+ std::set<LLViewerTexture* > textures;
+
+ //populate upload queue with relevant models
+ for (instance_map::iterator iter = mInstance.begin(); iter != mInstance.end(); ++iter)
+ {
+ LLMeshUploadData data;
+ data.mBaseModel = iter->first;
+
+ LLModelInstance& instance = *(iter->second.begin());
+
+ for (S32 i = 0; i < 5; i++)
+ {
+ data.mModel[i] = instance.mLOD[i];
+ }
+
+ uploadModel(data);
+
+ if (mUploadTextures)
+ {
+ for (std::vector<LLImportMaterial>::iterator material_iter = instance.mMaterial.begin();
+ material_iter != instance.mMaterial.end(); ++material_iter)
+ {
+
+ if (textures.find(material_iter->mDiffuseMap.get()) == textures.end())
+ {
+ textures.insert(material_iter->mDiffuseMap.get());
+
+ LLTextureUploadData data(material_iter->mDiffuseMap.get(), material_iter->mDiffuseMapLabel);
+ uploadTexture(data);
+ }
+ }
+ }
+
+ //queue up models for hull generation
+ LLModel* physics = data.mModel[LLModel::LOD_PHYSICS];
+ if (physics == NULL)
+ { //no physics model available, use high lod
+ physics = data.mModel[LLModel::LOD_HIGH];
+ }
+
+ DecompRequest* request = new DecompRequest(physics, data.mBaseModel, this);
+ gMeshRepo.mDecompThread->submitRequest(request);
+ }
+
+ while (!mPhysicsComplete)
+ {
+ apr_sleep(100);
+ }
+
+ //upload textures
+ bool done = false;
+ do
+ {
+ if (!mTextureQ.empty())
+ {
+ sendCostRequest(mTextureQ.front());
+ mTextureQ.pop();
+ }
+
+ if (!mConfirmedTextureQ.empty())
+ {
+ doUploadTexture(mConfirmedTextureQ.front());
+ mConfirmedTextureQ.pop();
+ }
+
+ mCurlRequest->process();
+
+ done = mTextureQ.empty() && mConfirmedTextureQ.empty();
+ }
+ while (!done || mCurlRequest->getQueued() > 0);
+
+ LLSD object_asset;
+ object_asset["objects"] = LLSD::emptyArray();
+
+ done = false;
+ do
+ {
+ static S32 count = 0;
+ static F32 last_hundred = gFrameTimeSeconds;
+ if (gFrameTimeSeconds - last_hundred > 1.f)
+ {
+ last_hundred = gFrameTimeSeconds;
+ count = 0;
+ }
+
+ //how many requests to push before calling process
+ const S32 PUSH_PER_PROCESS = 32;
+
+ S32 tcount = llmin(count+PUSH_PER_PROCESS, 100);
+
+ while (!mUploadQ.empty() && count < tcount)
+ { //send any pending upload requests
+ mMutex->lock();
+ LLMeshUploadData data = mUploadQ.front();
+ mUploadQ.pop();
+ mMutex->unlock();
+ sendCostRequest(data);
+ count++;
+ }
+
+ tcount = llmin(count+PUSH_PER_PROCESS, 100);
+
+ while (!mConfirmedQ.empty() && count < tcount)
+ { //process any meshes that have been confirmed for upload
+ LLMeshUploadData& data = mConfirmedQ.front();
+ doUploadModel(data);
+ mConfirmedQ.pop();
+ count++;
+ }
+
+ tcount = llmin(count+PUSH_PER_PROCESS, 100);
+
+ while (!mInstanceQ.empty() && count < tcount && !isDiscarded())
+ { //create any objects waiting for upload
+ count++;
+ object_asset["objects"].append(createObject(mInstanceQ.front()));
+ mInstanceQ.pop();
+ }
+
+ mCurlRequest->process();
+
+ done = isDiscarded() || (mInstanceQ.empty() && mConfirmedQ.empty() && mUploadQ.empty());
+ }
+ while (!done || mCurlRequest->getQueued() > 0);
+
+ delete mCurlRequest;
+ mCurlRequest = NULL;
+
+ // now upload the object asset
+ std::string url = mUploadObjectAssetCapability;
+
+ if (object_asset["objects"][0].has("permissions"))
+ { //copy permissions from first available object to be used for coalesced object
+ object_asset["permissions"] = object_asset["objects"][0]["permissions"];
+ }
+
+ if(!isDiscarded())
+ {
+ mPendingUploads++;
+ LLHTTPClient::post(url, object_asset, new LLModelObjectUploadResponder(this,object_asset));
+ }
+ else
+ {
+ mFinished = true;
+ }
+}
+
+void LLMeshUploadThread::uploadModel(LLMeshUploadData& data)
+{ //called from arbitrary thread
+ {
+ LLMutexLock lock(mMutex);
+ mUploadQ.push(data);
+ }
+}
+
+void LLMeshUploadThread::uploadTexture(LLTextureUploadData& data)
+{ //called from mesh upload thread
+ mTextureQ.push(data);
+}
+
+
+static LLFastTimer::DeclareTimer FTM_NOTIFY_MESH_LOADED("Notify Loaded");
+static LLFastTimer::DeclareTimer FTM_NOTIFY_MESH_UNAVAILABLE("Notify Unavailable");
+
+void LLMeshRepoThread::notifyLoadedMeshes()
+{
+ while (!mLoadedQ.empty())
+ {
+ mMutex->lock();
+ LoadedMesh mesh = mLoadedQ.front();
+ mLoadedQ.pop();
+ mMutex->unlock();
+
+ if (mesh.mVolume && mesh.mVolume->getNumVolumeFaces() > 0)
+ {
+ gMeshRepo.notifyMeshLoaded(mesh.mMeshParams, mesh.mVolume);
+ }
+ else
+ {
+ gMeshRepo.notifyMeshUnavailable(mesh.mMeshParams,
+ LLVolumeLODGroup::getVolumeDetailFromScale(mesh.mVolume->getDetail()));
+ }
+ }
+
+ while (!mUnavailableQ.empty())
+ {
+ mMutex->lock();
+ LODRequest req = mUnavailableQ.front();
+ mUnavailableQ.pop();
+ mMutex->unlock();
+
+ gMeshRepo.notifyMeshUnavailable(req.mMeshParams, req.mLOD);
+ }
+
+ while (!mSkinInfoQ.empty())
+ {
+ gMeshRepo.notifySkinInfoReceived(mSkinInfoQ.front());
+ mSkinInfoQ.pop();
+ }
+
+ while (!mDecompositionQ.empty())
+ {
+ gMeshRepo.notifyDecompositionReceived(mDecompositionQ.front());
+ mDecompositionQ.pop();
+ }
+}
+
+S32 LLMeshRepoThread::getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod)
+{ //only ever called from main thread
+ LLMutexLock lock(mHeaderMutex);
+ mesh_header_map::iterator iter = mMeshHeader.find(mesh_params.getSculptID());
+
+ if (iter != mMeshHeader.end())
+ {
+ LLSD& header = iter->second;
+
+ return LLMeshRepository::getActualMeshLOD(header, lod);
+ }
+
+ return lod;
+}
+
+//static
+S32 LLMeshRepository::getActualMeshLOD(LLSD& header, S32 lod)
+{
+ lod = llclamp(lod, 0, 3);
+
+ if (header.has("404"))
+ {
+ return -1;
+ }
+
+ if (header[header_lod[lod]]["size"].asInteger() > 0)
+ {
+ return lod;
+ }
+
+ //search down to find the next available lower lod
+ for (S32 i = lod-1; i >= 0; --i)
+ {
+ if (header[header_lod[i]]["size"].asInteger() > 0)
+ {
+ return i;
+ }
+ }
+
+ //search up to find then ext available higher lod
+ for (S32 i = lod+1; i < 4; ++i)
+ {
+ if (header[header_lod[i]]["size"].asInteger() > 0)
+ {
+ return i;
+ }
+ }
+
+ //header exists and no good lod found, treat as 404
+ header["404"] = 1;
+ return -1;
+}
+
+U32 LLMeshRepoThread::getResourceCost(const LLUUID& mesh_id)
+{
+ LLMutexLock lock(mHeaderMutex);
+
+ std::map<LLUUID, U32>::iterator iter = mMeshResourceCost.find(mesh_id);
+ if (iter != mMeshResourceCost.end())
+ {
+ return iter->second;
+ }
+
+ return 0;
+}
+
+void LLMeshRepository::cacheOutgoingMesh(LLMeshUploadData& data, LLSD& header)
+{
+ mThread->mMeshHeader[data.mUUID] = header;
+
+ // we cache the mesh for default parameters
+ LLVolumeParams volume_params;
+ volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE);
+ volume_params.setSculptID(data.mUUID, LL_SCULPT_TYPE_MESH);
+
+ for (U32 i = 0; i < 4; i++)
+ {
+ if (data.mModel[i].notNull())
+ {
+ LLPointer<LLVolume> volume = new LLVolume(volume_params, LLVolumeLODGroup::getVolumeScaleFromDetail(i));
+ volume->copyVolumeFaces(data.mModel[i]);
+ }
+ }
+
+}
+
+void LLMeshLODResponder::completedRaw(U32 status, const std::string& reason,
+ const LLChannelDescriptors& channels,
+ const LLIOPipe::buffer_ptr_t& buffer)
+{
+
+ LLMeshRepoThread::sActiveLODRequests--;
+ S32 data_size = buffer->countAfter(channels.in(), NULL);
+
+ if (status < 200 || status > 400)
+ {
+ llwarns << status << ": " << reason << llendl;
+ }
+
+ if (data_size < mRequestedBytes)
+ {
+ if (status == 499 || status == 503)
+ { //timeout or service unavailable, try again
+ LLMeshRepository::sHTTPRetryCount++;
+ gMeshRepo.mThread->loadMeshLOD(mMeshParams, mLOD);
+ }
+ else
+ {
+ llwarns << "Unhandled status " << status << llendl;
+ }
+ return;
+ }
+
+ LLMeshRepository::sBytesReceived += mRequestedBytes;
+
+ U8* data = NULL;
+
+ if (data_size > 0)
+ {
+ data = new U8[data_size];
+ buffer->readAfter(channels.in(), NULL, data, data_size);
+ }
+
+ if (gMeshRepo.mThread->lodReceived(mMeshParams, mLOD, data, data_size))
+ {
+ //good fetch from sim, write to VFS for caching
+ LLVFile file(gVFS, mMeshParams.getSculptID(), LLAssetType::AT_MESH, LLVFile::WRITE);
+
+ S32 offset = mOffset;
+ S32 size = mRequestedBytes;
+
+ if (file.getSize() >= offset+size)
+ {
+ file.seek(offset);
+ file.write(data, size);
+ LLMeshRepository::sCacheBytesWritten += size;
+ }
+ }
+
+ delete [] data;
+}
+
+void LLMeshSkinInfoResponder::completedRaw(U32 status, const std::string& reason,
+ const LLChannelDescriptors& channels,
+ const LLIOPipe::buffer_ptr_t& buffer)
+{
+ S32 data_size = buffer->countAfter(channels.in(), NULL);
+
+ if (status < 200 || status > 400)
+ {
+ llwarns << status << ": " << reason << llendl;
+ }
+
+ if (data_size < mRequestedBytes)
+ {
+ if (status == 499 || status == 503)
+ { //timeout or service unavailable, try again
+ LLMeshRepository::sHTTPRetryCount++;
+ gMeshRepo.mThread->loadMeshSkinInfo(mMeshID);
+ }
+ else
+ {
+ llwarns << "Unhandled status " << status << llendl;
+ }
+ return;
+ }
+
+ LLMeshRepository::sBytesReceived += mRequestedBytes;
+
+ U8* data = NULL;
+
+ if (data_size > 0)
+ {
+ data = new U8[data_size];
+ buffer->readAfter(channels.in(), NULL, data, data_size);
+ }
+
+ if (gMeshRepo.mThread->skinInfoReceived(mMeshID, data, data_size))
+ {
+ //good fetch from sim, write to VFS for caching
+ LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE);
+
+ S32 offset = mOffset;
+ S32 size = mRequestedBytes;
+
+ if (file.getSize() >= offset+size)
+ {
+ LLMeshRepository::sCacheBytesWritten += size;
+ file.seek(offset);
+ file.write(data, size);
+ }
+ }
+
+ delete [] data;
+}
+
+void LLMeshDecompositionResponder::completedRaw(U32 status, const std::string& reason,
+ const LLChannelDescriptors& channels,
+ const LLIOPipe::buffer_ptr_t& buffer)
+{
+ S32 data_size = buffer->countAfter(channels.in(), NULL);
+
+ if (status < 200 || status > 400)
+ {
+ llwarns << status << ": " << reason << llendl;
+ }
+
+ if (data_size < mRequestedBytes)
+ {
+ if (status == 499 || status == 503)
+ { //timeout or service unavailable, try again
+ LLMeshRepository::sHTTPRetryCount++;
+ gMeshRepo.mThread->loadMeshDecomposition(mMeshID);
+ }
+ else
+ {
+ llwarns << "Unhandled status " << status << llendl;
+ }
+ return;
+ }
+
+ LLMeshRepository::sBytesReceived += mRequestedBytes;
+
+ U8* data = NULL;
+
+ if (data_size > 0)
+ {
+ data = new U8[data_size];
+ buffer->readAfter(channels.in(), NULL, data, data_size);
+ }
+
+ if (gMeshRepo.mThread->decompositionReceived(mMeshID, data, data_size))
+ {
+ //good fetch from sim, write to VFS for caching
+ LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE);
+
+ S32 offset = mOffset;
+ S32 size = mRequestedBytes;
+
+ if (file.getSize() >= offset+size)
+ {
+ LLMeshRepository::sCacheBytesWritten += size;
+ file.seek(offset);
+ file.write(data, size);
+ }
+ }
+
+ delete [] data;
+}
+
+void LLMeshPhysicsShapeResponder::completedRaw(U32 status, const std::string& reason,
+ const LLChannelDescriptors& channels,
+ const LLIOPipe::buffer_ptr_t& buffer)
+{
+ S32 data_size = buffer->countAfter(channels.in(), NULL);
+
+ if (status < 200 || status > 400)
+ {
+ llwarns << status << ": " << reason << llendl;
+ }
+
+ if (data_size < mRequestedBytes)
+ {
+ if (status == 499 || status == 503)
+ { //timeout or service unavailable, try again
+ LLMeshRepository::sHTTPRetryCount++;
+ gMeshRepo.mThread->loadMeshPhysicsShape(mMeshID);
+ }
+ else
+ {
+ llwarns << "Unhandled status " << status << llendl;
+ }
+ return;
+ }
+
+ LLMeshRepository::sBytesReceived += mRequestedBytes;
+
+ U8* data = NULL;
+
+ if (data_size > 0)
+ {
+ data = new U8[data_size];
+ buffer->readAfter(channels.in(), NULL, data, data_size);
+ }
+
+ if (gMeshRepo.mThread->physicsShapeReceived(mMeshID, data, data_size))
+ {
+ //good fetch from sim, write to VFS for caching
+ LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE);
+
+ S32 offset = mOffset;
+ S32 size = mRequestedBytes;
+
+ if (file.getSize() >= offset+size)
+ {
+ LLMeshRepository::sCacheBytesWritten += size;
+ file.seek(offset);
+ file.write(data, size);
+ }
+ }
+
+ delete [] data;
+}
+
+void LLMeshHeaderResponder::completedRaw(U32 status, const std::string& reason,
+ const LLChannelDescriptors& channels,
+ const LLIOPipe::buffer_ptr_t& buffer)
+{
+ LLMeshRepoThread::sActiveHeaderRequests--;
+ if (status < 200 || status > 400)
+ {
+ //llwarns
+ // << "Header responder failed with status: "
+ // << status << ": " << reason << llendl;
+
+ // 503 (service unavailable) or 499 (timeout)
+ // can be due to server load and can be retried
+
+ // TODO*: Add maximum retry logic, exponential backoff
+ // and (somewhat more optional than the others) retries
+ // again after some set period of time
+ if (status == 503 || status == 499)
+ { //retry
+ LLMeshRepository::sHTTPRetryCount++;
+ LLMeshRepoThread::HeaderRequest req(mMeshParams);
+ LLMutexLock lock(gMeshRepo.mThread->mMutex);
+ gMeshRepo.mThread->mHeaderReqQ.push(req);
+
+ return;
+ }
+ }
+
+ S32 data_size = buffer->countAfter(channels.in(), NULL);
+
+ U8* data = NULL;
+
+ if (data_size > 0)
+ {
+ data = new U8[data_size];
+ buffer->readAfter(channels.in(), NULL, data, data_size);
+ }
+
+ LLMeshRepository::sBytesReceived += llmin(data_size, 4096);
+
+ if (!gMeshRepo.mThread->headerReceived(mMeshParams, data, data_size))
+ {
+ llwarns
+ << "Unable to parse mesh header: "
+ << status << ": " << reason << llendl;
+ }
+ else if (data && data_size > 0)
+ {
+ //header was successfully retrieved from sim, cache in vfs
+ LLUUID mesh_id = mMeshParams.getSculptID();
+ LLSD header = gMeshRepo.mThread->mMeshHeader[mesh_id];
+
+ std::stringstream str;
+
+ S32 lod_bytes = 0;
+
+ for (U32 i = 0; i < LLModel::LOD_PHYSICS; ++i)
+ { //figure out how many bytes we'll need to reserve in the file
+ std::string lod_name = header_lod[i];
+ lod_bytes = llmax(lod_bytes, header[lod_name]["offset"].asInteger()+header[lod_name]["size"].asInteger());
+ }
+
+ //just in case skin info or decomposition is at the end of the file (which it shouldn't be)
+ lod_bytes = llmax(lod_bytes, header["skin"]["offset"].asInteger() + header["skin"]["size"].asInteger());
+ lod_bytes = llmax(lod_bytes, header["decomposition"]["offset"].asInteger() + header["decomposition"]["size"].asInteger());
+
+ S32 header_bytes = (S32) gMeshRepo.mThread->mMeshHeaderSize[mesh_id];
+ S32 bytes = lod_bytes + header_bytes;
+
+
+ //it's possible for the remote asset to have more data than is needed for the local cache
+ //only allocate as much space in the VFS as is needed for the local cache
+ data_size = llmin(data_size, bytes);
+
+ LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH, LLVFile::WRITE);
+ if (file.getMaxSize() >= bytes || file.setMaxSize(bytes))
+ {
+ LLMeshRepository::sCacheBytesWritten += data_size;
+
+ file.write((const U8*) data, data_size);
+
+ //zero out the rest of the file
+ U8 block[4096];
+ memset(block, 0, 4096);
+
+ while (bytes-file.tell() > 4096)
+ {
+ file.write(block, 4096);
+ }
+
+ S32 remaining = bytes-file.tell();
+
+ if (remaining < 0 || remaining > 4096)
+ {
+ llerrs << "Bad padding of mesh asset cache entry." << llendl;
+ }
+
+ if (remaining > 0)
+ {
+ file.write(block, remaining);
+ }
+ }
+ }
+
+ delete [] data;
+}
+
+
+LLMeshRepository::LLMeshRepository()
+: mMeshMutex(NULL),
+ mMeshThreadCount(0),
+ mThread(NULL)
+{
+
+}
+
+void LLMeshRepository::init()
+{
+ mMeshMutex = new LLMutex(NULL);
+
+ LLConvexDecomposition::getInstance()->initSystem();
+
+ mDecompThread = new LLPhysicsDecomp();
+ mDecompThread->start();
+
+ while (!mDecompThread->mInited)
+ { //wait for physics decomp thread to init
+ apr_sleep(100);
+ }
+
+
+
+ mThread = new LLMeshRepoThread();
+ mThread->start();
+}
+
+void LLMeshRepository::shutdown()
+{
+ llinfos << "Shutting down mesh repository." << llendl;
+
+ for (U32 i = 0; i < mUploads.size(); ++i)
+ {
+ llinfos << "Discard the pending mesh uploads " << llendl;
+ mUploads[i]->discard() ; //discard the uploading requests.
+ }
+
+ mThread->mSignal->signal();
+
+ while (!mThread->isStopped())
+ {
+ apr_sleep(10);
+ }
+ delete mThread;
+ mThread = NULL;
+
+ for (U32 i = 0; i < mUploads.size(); ++i)
+ {
+ llinfos << "Waiting for pending mesh upload " << i << "/" << mUploads.size() << llendl;
+ while (!mUploads[i]->isStopped())
+ {
+ apr_sleep(10);
+ }
+ delete mUploads[i];
+ }
+
+ mUploads.clear();
+
+ delete mMeshMutex;
+ mMeshMutex = NULL;
+
+ llinfos << "Shutting down decomposition system." << llendl;
+
+ if (mDecompThread)
+ {
+ mDecompThread->shutdown();
+ delete mDecompThread;
+ mDecompThread = NULL;
+ }
+
+ LLConvexDecomposition::quitSystem();
+}
+
+//called in the main thread.
+S32 LLMeshRepository::update()
+{
+ if(mUploadWaitList.empty())
+ {
+ return 0 ;
+ }
+
+ S32 size = mUploadWaitList.size() ;
+ for (S32 i = 0; i < size; ++i)
+ {
+ mUploads.push_back(mUploadWaitList[i]);
+ mUploadWaitList[i]->preStart() ;
+ mUploadWaitList[i]->start() ;
+ }
+ mUploadWaitList.clear() ;
+
+ return size ;
+}
+
+S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_params, S32 detail, S32 last_lod)
+{
+ if (detail < 0 || detail > 4)
+ {
+ return detail;
+ }
+
+ LLFastTimer t(FTM_LOAD_MESH);
+
+ {
+ LLMutexLock lock(mMeshMutex);
+ //add volume to list of loading meshes
+ mesh_load_map::iterator iter = mLoadingMeshes[detail].find(mesh_params);
+ if (iter != mLoadingMeshes[detail].end())
+ { //request pending for this mesh, append volume id to list
+ iter->second.insert(vobj->getID());
+ }
+ else
+ {
+ //first request for this mesh
+ mLoadingMeshes[detail][mesh_params].insert(vobj->getID());
+ mPendingRequests.push_back(LLMeshRepoThread::LODRequest(mesh_params, detail));
+ }
+ }
+
+ //do a quick search to see if we can't display something while we wait for this mesh to load
+ LLVolume* volume = vobj->getVolume();
+
+ if (volume)
+ {
+ if (volume->getNumVolumeFaces() == 0 && !volume->isTetrahedron())
+ {
+ volume->makeTetrahedron();
+ }
+
+ LLVolumeParams params = volume->getParams();
+
+ LLVolumeLODGroup* group = LLPrimitive::getVolumeManager()->getGroup(params);
+
+ if (group)
+ {
+ //first, see if last_lod is available (don't transition down to avoid funny popping a la SH-641)
+ if (last_lod >= 0)
+ {
+ LLVolume* lod = group->refLOD(last_lod);
+ if (lod && !lod->isTetrahedron() && lod->getNumVolumeFaces() > 0)
+ {
+ group->derefLOD(lod);
+ return last_lod;
+ }
+ group->derefLOD(lod);
+ }
+
+ //next, see what the next lowest LOD available might be
+ for (S32 i = detail-1; i >= 0; --i)
+ {
+ LLVolume* lod = group->refLOD(i);
+ if (lod && !lod->isTetrahedron() && lod->getNumVolumeFaces() > 0)
+ {
+ group->derefLOD(lod);
+ return i;
+ }
+
+ group->derefLOD(lod);
+ }
+
+ //no lower LOD is a available, is a higher lod available?
+ for (S32 i = detail+1; i < 4; ++i)
+ {
+ LLVolume* lod = group->refLOD(i);
+ if (lod && !lod->isTetrahedron() && lod->getNumVolumeFaces() > 0)
+ {
+ group->derefLOD(lod);
+ return i;
+ }
+
+ group->derefLOD(lod);
+ }
+ }
+ }
+
+ return detail;
+}
+
+static LLFastTimer::DeclareTimer FTM_START_MESH_THREAD("Start Thread");
+static LLFastTimer::DeclareTimer FTM_LOAD_MESH_LOD("Load LOD");
+static LLFastTimer::DeclareTimer FTM_MESH_LOCK1("Lock 1");
+static LLFastTimer::DeclareTimer FTM_MESH_LOCK2("Lock 2");
+
+void LLMeshRepository::notifyLoadedMeshes()
+{ //called from main thread
+
+ LLMeshRepoThread::sMaxConcurrentRequests = gSavedSettings.getU32("MeshMaxConcurrentRequests");
+
+ //clean up completed upload threads
+ for (std::vector<LLMeshUploadThread*>::iterator iter = mUploads.begin(); iter != mUploads.end(); )
+ {
+ LLMeshUploadThread* thread = *iter;
+
+ if (thread->isStopped() && thread->finished())
+ {
+ iter = mUploads.erase(iter);
+ delete thread;
+ }
+ else
+ {
+ ++iter;
+ }
+ }
+
+ //update inventory
+ if (!mInventoryQ.empty())
+ {
+ LLMutexLock lock(mMeshMutex);
+ while (!mInventoryQ.empty())
+ {
+ inventory_data& data = mInventoryQ.front();
+
+ LLAssetType::EType asset_type = LLAssetType::lookup(data.mPostData["asset_type"].asString());
+ LLInventoryType::EType inventory_type = LLInventoryType::lookup(data.mPostData["inventory_type"].asString());
+
+ on_new_single_inventory_upload_complete(
+ asset_type,
+ inventory_type,
+ data.mPostData["asset_type"].asString(),
+ data.mPostData["folder_id"].asUUID(),
+ data.mPostData["name"],
+ data.mPostData["description"],
+ data.mResponse,
+ 0);
+
+ mInventoryQ.pop();
+ }
+ }
+
+ //call completed callbacks on finished decompositions
+ mDecompThread->notifyCompleted();
+
+ if (!mThread->mWaiting)
+ { //curl thread is churning, wait for it to go idle
+ return;
+ }
+
+ static std::string region_name("never name a region this");
+
+ if (gAgent.getRegion())
+ { //update capability url
+ if (gAgent.getRegion()->getName() != region_name && gAgent.getRegion()->capabilitiesReceived())
+ {
+ region_name = gAgent.getRegion()->getName();
+
+ mGetMeshCapability = gAgent.getRegion()->getCapability("GetMesh");
+ }
+ }
+
+ LLFastTimer t(FTM_MESH_UPDATE);
+
+ {
+ LLFastTimer t(FTM_MESH_LOCK1);
+ mMeshMutex->lock();
+ }
+
+ {
+ LLFastTimer t(FTM_MESH_LOCK2);
+ mThread->mMutex->lock();
+ }
+
+ //popup queued error messages from background threads
+ while (!mUploadErrorQ.empty())
+ {
+ LLNotificationsUtil::add("MeshUploadError", mUploadErrorQ.front());
+ mUploadErrorQ.pop();
+ }
+
+ S32 push_count = LLMeshRepoThread::sMaxConcurrentRequests-(LLMeshRepoThread::sActiveHeaderRequests+LLMeshRepoThread::sActiveLODRequests);
+
+ if (push_count > 0)
+ {
+ //calculate "score" for pending requests
+
+ //create score map
+ std::map<LLUUID, F32> score_map;
+
+ for (U32 i = 0; i < 4; ++i)
+ {
+ for (mesh_load_map::iterator iter = mLoadingMeshes[i].begin(); iter != mLoadingMeshes[i].end(); ++iter)
+ {
+ F32 max_score = 0.f;
+ for (std::set<LLUUID>::iterator obj_iter = iter->second.begin(); obj_iter != iter->second.end(); ++obj_iter)
+ {
+ LLViewerObject* object = gObjectList.findObject(*obj_iter);
+
+ if (object)
+ {
+ LLDrawable* drawable = object->mDrawable;
+ if (drawable)
+ {
+ F32 cur_score = drawable->getRadius()/llmax(drawable->mDistanceWRTCamera, 1.f);
+ max_score = llmax(max_score, cur_score);
+ }
+ }
+ }
+
+ score_map[iter->first.getSculptID()] = max_score;
+ }
+ }
+
+ //set "score" for pending requests
+ for (std::vector<LLMeshRepoThread::LODRequest>::iterator iter = mPendingRequests.begin(); iter != mPendingRequests.end(); ++iter)
+ {
+ iter->mScore = score_map[iter->mMeshParams.getSculptID()];
+ }
+
+ //sort by "score"
+ std::sort(mPendingRequests.begin(), mPendingRequests.end(), LLMeshRepoThread::CompareScoreGreater());
+
+ while (!mPendingRequests.empty() && push_count > 0)
+ {
+ LLFastTimer t(FTM_LOAD_MESH_LOD);
+ LLMeshRepoThread::LODRequest& request = mPendingRequests.front();
+ mThread->loadMeshLOD(request.mMeshParams, request.mLOD);
+ mPendingRequests.erase(mPendingRequests.begin());
+ push_count--;
+ }
+ }
+
+ //send skin info requests
+ while (!mPendingSkinRequests.empty())
+ {
+ mThread->loadMeshSkinInfo(mPendingSkinRequests.front());
+ mPendingSkinRequests.pop();
+ }
+
+ //send decomposition requests
+ while (!mPendingDecompositionRequests.empty())
+ {
+ mThread->loadMeshDecomposition(mPendingDecompositionRequests.front());
+ mPendingDecompositionRequests.pop();
+ }
+
+ //send physics shapes decomposition requests
+ while (!mPendingPhysicsShapeRequests.empty())
+ {
+ mThread->loadMeshPhysicsShape(mPendingPhysicsShapeRequests.front());
+ mPendingPhysicsShapeRequests.pop();
+ }
+
+ mThread->notifyLoadedMeshes();
+
+ mThread->mMutex->unlock();
+ mMeshMutex->unlock();
+
+ mThread->mSignal->signal();
+}
+
+void LLMeshRepository::notifySkinInfoReceived(LLMeshSkinInfo& info)
+{
+ mSkinMap[info.mMeshID] = info;
+ mLoadingSkins.erase(info.mMeshID);
+}
+
+void LLMeshRepository::notifyDecompositionReceived(LLModel::Decomposition* decomp)
+{
+ decomposition_map::iterator iter = mDecompositionMap.find(decomp->mMeshID);
+ if (iter == mDecompositionMap.end())
+ { //just insert decomp into map
+ mDecompositionMap[decomp->mMeshID] = decomp;
+ }
+ else
+ { //merge decomp with existing entry
+ iter->second->merge(decomp);
+ delete decomp;
+ }
+
+ mLoadingDecompositions.erase(decomp->mMeshID);
+}
+
+void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume)
+{ //called from main thread
+ S32 detail = LLVolumeLODGroup::getVolumeDetailFromScale(volume->getDetail());
+
+ //get list of objects waiting to be notified this mesh is loaded
+ mesh_load_map::iterator obj_iter = mLoadingMeshes[detail].find(mesh_params);
+
+ if (volume && obj_iter != mLoadingMeshes[detail].end())
+ {
+ //make sure target volume is still valid
+ if (volume->getNumVolumeFaces() <= 0)
+ {
+ llwarns << "Mesh loading returned empty volume." << llendl;
+ volume->makeTetrahedron();
+ }
+
+ { //update system volume
+ LLVolume* sys_volume = LLPrimitive::getVolumeManager()->refVolume(mesh_params, detail);
+ if (sys_volume)
+ {
+ sys_volume->copyVolumeFaces(volume);
+ LLPrimitive::getVolumeManager()->unrefVolume(sys_volume);
+ }
+ else
+ {
+ llwarns << "Couldn't find system volume for given mesh." << llendl;
+ }
+ }
+
+ //notify waiting LLVOVolume instances that their requested mesh is available
+ for (std::set<LLUUID>::iterator vobj_iter = obj_iter->second.begin(); vobj_iter != obj_iter->second.end(); ++vobj_iter)
+ {
+ LLVOVolume* vobj = (LLVOVolume*) gObjectList.findObject(*vobj_iter);
+ if (vobj)
+ {
+ vobj->notifyMeshLoaded();
+ }
+ }
+
+ mLoadingMeshes[detail].erase(mesh_params);
+ }
+}
+
+void LLMeshRepository::notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 lod)
+{ //called from main thread
+ //get list of objects waiting to be notified this mesh is loaded
+ mesh_load_map::iterator obj_iter = mLoadingMeshes[lod].find(mesh_params);
+
+ F32 detail = LLVolumeLODGroup::getVolumeScaleFromDetail(lod);
+
+ if (obj_iter != mLoadingMeshes[lod].end())
+ {
+ for (std::set<LLUUID>::iterator vobj_iter = obj_iter->second.begin(); vobj_iter != obj_iter->second.end(); ++vobj_iter)
+ {
+ LLVOVolume* vobj = (LLVOVolume*) gObjectList.findObject(*vobj_iter);
+ if (vobj)
+ {
+ LLVolume* obj_volume = vobj->getVolume();
+
+ if (obj_volume &&
+ obj_volume->getDetail() == detail &&
+ obj_volume->getParams() == mesh_params)
+ { //should force volume to find most appropriate LOD
+ vobj->setVolume(obj_volume->getParams(), lod);
+ }
+ }
+ }
+
+ mLoadingMeshes[lod].erase(mesh_params);
+ }
+}
+
+S32 LLMeshRepository::getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod)
+{
+ return mThread->getActualMeshLOD(mesh_params, lod);
+}
+
+U32 LLMeshRepository::calcResourceCost(LLSD& header)
+{
+ U32 bytes = 0;
+
+ for (U32 i = 0; i < 4; i++)
+ {
+ bytes += header[header_lod[i]]["size"].asInteger();
+ }
+
+ bytes += header["skin"]["size"].asInteger();
+
+ return bytes/4096 + 1;
+}
+
+U32 LLMeshRepository::getResourceCost(const LLUUID& mesh_id)
+{
+ return mThread->getResourceCost(mesh_id);
+}
+
+const LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id)
+{
+ if (mesh_id.notNull())
+ {
+ skin_map::iterator iter = mSkinMap.find(mesh_id);
+ if (iter != mSkinMap.end())
+ {
+ return &(iter->second);
+ }
+
+ //no skin info known about given mesh, try to fetch it
+ {
+ LLMutexLock lock(mMeshMutex);
+ //add volume to list of loading meshes
+ std::set<LLUUID>::iterator iter = mLoadingSkins.find(mesh_id);
+ if (iter == mLoadingSkins.end())
+ { //no request pending for this skin info
+ mLoadingSkins.insert(mesh_id);
+ mPendingSkinRequests.push(mesh_id);
+ }
+ }
+ }
+
+ return NULL;
+}
+
+void LLMeshRepository::fetchPhysicsShape(const LLUUID& mesh_id)
+{
+ if (mesh_id.notNull())
+ {
+ LLModel::Decomposition* decomp = NULL;
+ decomposition_map::iterator iter = mDecompositionMap.find(mesh_id);
+ if (iter != mDecompositionMap.end())
+ {
+ decomp = iter->second;
+ }
+
+ //decomposition block hasn't been fetched yet
+ if (!decomp || decomp->mPhysicsShapeMesh.empty())
+ {
+ LLMutexLock lock(mMeshMutex);
+ //add volume to list of loading meshes
+ std::set<LLUUID>::iterator iter = mLoadingPhysicsShapes.find(mesh_id);
+ if (iter == mLoadingPhysicsShapes.end())
+ { //no request pending for this skin info
+ mLoadingPhysicsShapes.insert(mesh_id);
+ mPendingPhysicsShapeRequests.push(mesh_id);
+ }
+ }
+ }
+
+}
+
+LLModel::Decomposition* LLMeshRepository::getDecomposition(const LLUUID& mesh_id)
+{
+ LLModel::Decomposition* ret = NULL;
+
+ if (mesh_id.notNull())
+ {
+ decomposition_map::iterator iter = mDecompositionMap.find(mesh_id);
+ if (iter != mDecompositionMap.end())
+ {
+ ret = iter->second;
+ }
+
+ //decomposition block hasn't been fetched yet
+ if (!ret || ret->mBaseHullMesh.empty())
+ {
+ LLMutexLock lock(mMeshMutex);
+ //add volume to list of loading meshes
+ std::set<LLUUID>::iterator iter = mLoadingDecompositions.find(mesh_id);
+ if (iter == mLoadingDecompositions.end())
+ { //no request pending for this skin info
+ mLoadingDecompositions.insert(mesh_id);
+ mPendingDecompositionRequests.push(mesh_id);
+ }
+ }
+ }
+
+ return ret;
+}
+
+void LLMeshRepository::buildHull(const LLVolumeParams& params, S32 detail)
+{
+ LLVolume* volume = LLPrimitive::sVolumeManager->refVolume(params, detail);
+
+ if (!volume->mHullPoints)
+ {
+ //all default params
+ //execute first stage
+ //set simplify mode to retain
+ //set retain percentage to zero
+ //run second stage
+ }
+
+ LLPrimitive::sVolumeManager->unrefVolume(volume);
+}
+
+bool LLMeshRepository::hasPhysicsShape(const LLUUID& mesh_id)
+{
+ LLSD mesh = mThread->getMeshHeader(mesh_id);
+ return mesh.has("physics_shape") && mesh["physics_shape"].has("size") && (mesh["physics_shape"]["size"].asInteger() > 0);
+}
+
+LLSD& LLMeshRepository::getMeshHeader(const LLUUID& mesh_id)
+{
+ return mThread->getMeshHeader(mesh_id);
+}
+
+LLSD& LLMeshRepoThread::getMeshHeader(const LLUUID& mesh_id)
+{
+ static LLSD dummy_ret;
+ if (mesh_id.notNull())
+ {
+ LLMutexLock lock(mHeaderMutex);
+ mesh_header_map::iterator iter = mMeshHeader.find(mesh_id);
+ if (iter != mMeshHeader.end())
+ {
+ return iter->second;
+ }
+ }
+
+ return dummy_ret;
+}
+
+
+void LLMeshRepository::uploadModel(std::vector<LLModelInstance>& data, LLVector3& scale, bool upload_textures,
+ bool upload_skin, bool upload_joints)
+{
+ LLMeshUploadThread* thread = new LLMeshUploadThread(data, scale, upload_textures, upload_skin, upload_joints);
+ mUploadWaitList.push_back(thread);
+}
+
+S32 LLMeshRepository::getMeshSize(const LLUUID& mesh_id, S32 lod)
+{
+ if (mThread)
+ {
+ LLMeshRepoThread::mesh_header_map::iterator iter = mThread->mMeshHeader.find(mesh_id);
+ if (iter != mThread->mMeshHeader.end())
+ {
+ LLSD& header = iter->second;
+
+ if (header.has("404"))
+ {
+ return -1;
+ }
+
+ S32 size = header[header_lod[lod]]["size"].asInteger();
+ return size;
+ }
+
+ }
+
+ return -1;
+
+}
+
+void LLMeshUploadThread::sendCostRequest(LLMeshUploadData& data)
+{
+ if(isDiscarded())
+ {
+ return ;
+ }
+
+ //write model file to memory buffer
+ std::stringstream ostr;
+
+ LLModel::Decomposition& decomp =
+ data.mModel[LLModel::LOD_PHYSICS].notNull() ?
+ data.mModel[LLModel::LOD_PHYSICS]->mPhysics :
+ data.mBaseModel->mPhysics;
+
+ LLSD header = LLModel::writeModel(
+ ostr,
+ data.mModel[LLModel::LOD_PHYSICS],
+ data.mModel[LLModel::LOD_HIGH],
+ data.mModel[LLModel::LOD_MEDIUM],
+ data.mModel[LLModel::LOD_LOW],
+ data.mModel[LLModel::LOD_IMPOSTOR],
+ decomp,
+ mUploadSkin,
+ mUploadJoints,
+ true);
+
+ std::string desc = data.mBaseModel->mLabel;
+
+ // Grab the total vertex count of the model
+ // along with other information for the "asset_resources" map
+ // to send to the server.
+ LLSD asset_resources = LLSD::emptyMap();
+
+
+ std::string url = mNewInventoryCapability;
+
+ if (!url.empty())
+ {
+ LLSD body = generate_new_resource_upload_capability_body(
+ LLAssetType::AT_MESH,
+ desc,
+ desc,
+ LLFolderType::FT_MESH,
+ LLInventoryType::IT_MESH,
+ LLFloaterPerms::getNextOwnerPerms(),
+ LLFloaterPerms::getGroupPerms(),
+ LLFloaterPerms::getEveryonePerms());
+
+ body["asset_resources"] = asset_resources;
+
+ mPendingConfirmations++;
+ LLCurlRequest::headers_t headers;
+
+ data.mPostData = body;
+
+ mCurlRequest->post(url, headers, body, new LLMeshCostResponder(data, this));
+ }
+}
+
+void LLMeshUploadThread::sendCostRequest(LLTextureUploadData& data)
+{
+ if(isDiscarded())
+ {
+ return ;
+ }
+
+ if (data.mTexture && data.mTexture->getDiscardLevel() >= 0)
+ {
+ LLSD asset_resources = LLSD::emptyMap();
+
+ std::string url = mNewInventoryCapability;
+
+ if (!url.empty())
+ {
+ LLSD body = generate_new_resource_upload_capability_body(
+ LLAssetType::AT_TEXTURE,
+ data.mLabel,
+ data.mLabel,
+ LLFolderType::FT_TEXTURE,
+ LLInventoryType::IT_TEXTURE,
+ LLFloaterPerms::getNextOwnerPerms(),
+ LLFloaterPerms::getGroupPerms(),
+ LLFloaterPerms::getEveryonePerms());
+
+ body["asset_resources"] = asset_resources;
+
+ mPendingConfirmations++;
+ LLCurlRequest::headers_t headers;
+
+ data.mPostData = body;
+ mCurlRequest->post(url, headers, body, new LLTextureCostResponder(data, this));
+ }
+ }
+}
+
+
+void LLMeshUploadThread::doUploadModel(LLMeshUploadData& data)
+{
+ if(isDiscarded())
+ {
+ return ;
+ }
+
+ if (!data.mRSVP.empty())
+ {
+ std::stringstream ostr;
+
+ LLModel::Decomposition& decomp =
+ data.mModel[LLModel::LOD_PHYSICS].notNull() ?
+ data.mModel[LLModel::LOD_PHYSICS]->mPhysics :
+ data.mBaseModel->mPhysics;
+
+ decomp.mBaseHull = mHullMap[data.mBaseModel];
+
+ LLModel::writeModel(
+ ostr,
+ data.mModel[LLModel::LOD_PHYSICS],
+ data.mModel[LLModel::LOD_HIGH],
+ data.mModel[LLModel::LOD_MEDIUM],
+ data.mModel[LLModel::LOD_LOW],
+ data.mModel[LLModel::LOD_IMPOSTOR],
+ decomp,
+ mUploadSkin,
+ mUploadJoints);
+
+ data.mAssetData = ostr.str();
+
+ LLCurlRequest::headers_t headers;
+ mPendingUploads++;
+
+ mCurlRequest->post(data.mRSVP, headers, data.mAssetData, new LLMeshUploadResponder(data, this));
+ }
+}
+
+void LLMeshUploadThread::doUploadTexture(LLTextureUploadData& data)
+{
+ if(isDiscarded())
+ {
+ return ;
+ }
+
+ if (!data.mRSVP.empty())
+ {
+ std::stringstream ostr;
+
+ if (!data.mTexture->isRawImageValid())
+ {
+ data.mTexture->reloadRawImage(data.mTexture->getDiscardLevel());
+ }
+
+ LLPointer<LLImageJ2C> upload_file = LLViewerTextureList::convertToUploadFile(data.mTexture->getRawImage());
+
+ ostr.write((const char*) upload_file->getData(), upload_file->getDataSize());
+
+ data.mAssetData = ostr.str();
+
+ LLCurlRequest::headers_t headers;
+ mPendingUploads++;
+
+ mCurlRequest->post(data.mRSVP, headers, data.mAssetData, new LLTextureUploadResponder(data, this));
+ }
+}
+
+
+void LLMeshUploadThread::onModelUploaded(LLMeshUploadData& data)
+{
+ createObjects(data);
+}
+
+void LLMeshUploadThread::onTextureUploaded(LLTextureUploadData& data)
+{
+ mTextureMap[data.mTexture] = data;
+}
+
+
+void LLMeshUploadThread::createObjects(LLMeshUploadData& data)
+{
+ instance_list& instances = mInstance[data.mBaseModel];
+
+ for (instance_list::iterator iter = instances.begin(); iter != instances.end(); ++iter)
+ { //create prims that reference given mesh
+ LLModelInstance& instance = *iter;
+ instance.mMeshID = data.mUUID;
+ mInstanceQ.push(instance);
+ }
+}
+
+void LLMeshUploadThread::decomposeMeshMatrix(LLMatrix4& transformation,
+ LLVector3& result_pos,
+ LLQuaternion& result_rot,
+ LLVector3& result_scale)
+{
+ // check for reflection
+ BOOL reflected = (transformation.determinant() < 0);
+
+ // compute position
+ LLVector3 position = LLVector3(0, 0, 0) * transformation;
+
+ // compute scale
+ LLVector3 x_transformed = LLVector3(1, 0, 0) * transformation - position;
+ LLVector3 y_transformed = LLVector3(0, 1, 0) * transformation - position;
+ LLVector3 z_transformed = LLVector3(0, 0, 1) * transformation - position;
+ F32 x_length = x_transformed.normalize();
+ F32 y_length = y_transformed.normalize();
+ F32 z_length = z_transformed.normalize();
+ LLVector3 scale = LLVector3(x_length, y_length, z_length);
+
+ // adjust for "reflected" geometry
+ LLVector3 x_transformed_reflected = x_transformed;
+ if (reflected)
+ {
+ x_transformed_reflected *= -1.0;
+ }
+
+ // compute rotation
+ LLMatrix3 rotation_matrix;
+ rotation_matrix.setRows(x_transformed_reflected, y_transformed, z_transformed);
+ LLQuaternion quat_rotation = rotation_matrix.quaternion();
+ quat_rotation.normalize(); // the rotation_matrix might not have been orthoginal. make it so here.
+ LLVector3 euler_rotation;
+ quat_rotation.getEulerAngles(&euler_rotation.mV[VX], &euler_rotation.mV[VY], &euler_rotation.mV[VZ]);
+
+ result_pos = position + mOrigin;
+ result_scale = scale;
+ result_rot = quat_rotation;
+}
+
+
+LLSD LLMeshUploadThread::createObject(LLModelInstance& instance)
+{
+ LLMatrix4 transformation = instance.mTransform;
+
+ llassert(instance.mMeshID.notNull());
+
+ // check for reflection
+ BOOL reflected = (transformation.determinant() < 0);
+
+ // compute position
+ LLVector3 position = LLVector3(0, 0, 0) * transformation;
+
+ // compute scale
+ LLVector3 x_transformed = LLVector3(1, 0, 0) * transformation - position;
+ LLVector3 y_transformed = LLVector3(0, 1, 0) * transformation - position;
+ LLVector3 z_transformed = LLVector3(0, 0, 1) * transformation - position;
+ F32 x_length = x_transformed.normalize();
+ F32 y_length = y_transformed.normalize();
+ F32 z_length = z_transformed.normalize();
+ LLVector3 scale = LLVector3(x_length, y_length, z_length);
+
+ // adjust for "reflected" geometry
+ LLVector3 x_transformed_reflected = x_transformed;
+ if (reflected)
+ {
+ x_transformed_reflected *= -1.0;
+ }
+
+ // compute rotation
+ LLMatrix3 rotation_matrix;
+ rotation_matrix.setRows(x_transformed_reflected, y_transformed, z_transformed);
+ LLQuaternion quat_rotation = rotation_matrix.quaternion();
+ quat_rotation.normalize(); // the rotation_matrix might not have been orthoginal. make it so here.
+ LLVector3 euler_rotation;
+ quat_rotation.getEulerAngles(&euler_rotation.mV[VX], &euler_rotation.mV[VY], &euler_rotation.mV[VZ]);
+
+ //
+ // build parameter block to construct this prim
+ //
+
+ LLSD object_params;
+
+ // create prim
+
+ // set volume params
+ U8 sculpt_type = LL_SCULPT_TYPE_MESH;
+ if (reflected)
+ {
+ sculpt_type |= LL_SCULPT_FLAG_MIRROR;
+ }
+ LLVolumeParams volume_params;
+ volume_params.setType( LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE );
+ volume_params.setBeginAndEndS( 0.f, 1.f );
+ volume_params.setBeginAndEndT( 0.f, 1.f );
+ volume_params.setRatio ( 1, 1 );
+ volume_params.setShear ( 0, 0 );
+ volume_params.setSculptID(instance.mMeshID, sculpt_type);
+ object_params["shape"] = volume_params.asLLSD();
+
+ object_params["material"] = LL_MCODE_WOOD;
+
+ object_params["group-id"] = gAgent.getGroupID();
+ object_params["pos"] = ll_sd_from_vector3(position + mOrigin);
+ object_params["rotation"] = ll_sd_from_quaternion(quat_rotation);
+ object_params["scale"] = ll_sd_from_vector3(scale);
+ object_params["name"] = instance.mLabel;
+
+ // load material from dae file
+ object_params["facelist"] = LLSD::emptyArray();
+ for (S32 i = 0; i < instance.mMaterial.size(); i++)
+ {
+ LLTextureEntry te;
+ LLImportMaterial& mat = instance.mMaterial[i];
+
+ te.setColor(mat.mDiffuseColor);
+
+ LLUUID diffuse_id = mTextureMap[mat.mDiffuseMap].mUUID;
+
+ if (diffuse_id.notNull())
+ {
+ te.setID(diffuse_id);
+ }
+ else
+ {
+ te.setID(LLUUID("5748decc-f629-461c-9a36-a35a221fe21f")); // blank texture
+ }
+
+ te.setFullbright(mat.mFullbright);
+
+ object_params["facelist"][i] = te.asLLSD();
+ }
+
+ // set extra parameters
+ LLSculptParams sculpt_params;
+ sculpt_params.setSculptTexture(instance.mMeshID);
+ sculpt_params.setSculptType(sculpt_type);
+ U8 buffer[MAX_OBJECT_PARAMS_SIZE+1];
+ LLDataPackerBinaryBuffer dp(buffer, MAX_OBJECT_PARAMS_SIZE);
+ sculpt_params.pack(dp);
+ std::vector<U8> v(dp.getCurrentSize());
+ memcpy(&v[0], buffer, dp.getCurrentSize());
+ LLSD extra_parameter;
+ extra_parameter["extra_parameter"] = sculpt_params.mType;
+ extra_parameter["param_data"] = v;
+ object_params["extra_parameters"].append(extra_parameter);
+
+ LLPermissions perm;
+ perm.setOwnerAndGroup(gAgent.getID(), gAgent.getID(), LLUUID::null, false);
+ perm.setCreator(gAgent.getID());
+
+ perm.initMasks(PERM_ITEM_UNRESTRICTED | PERM_MOVE, //base
+ PERM_ITEM_UNRESTRICTED | PERM_MOVE, //owner
+ LLFloaterPerms::getEveryonePerms(),
+ LLFloaterPerms::getGroupPerms(),
+ LLFloaterPerms::getNextOwnerPerms());
+
+ object_params["permissions"] = ll_create_sd_from_permissions(perm);
+
+ object_params["physics_shape_type"] = (U8)(LLViewerObject::PHYSICS_SHAPE_CONVEX_HULL);
+
+ return object_params;
+}
+
+void LLMeshUploadThread::priceResult(LLMeshUploadData& data, const LLSD& content)
+{
+ mPendingCost += content["upload_price"].asInteger();
+ data.mRSVP = content["rsvp"].asString();
+
+ mConfirmedQ.push(data);
+}
+
+void LLMeshUploadThread::priceResult(LLTextureUploadData& data, const LLSD& content)
+{
+ mPendingCost += content["upload_price"].asInteger();
+ data.mRSVP = content["rsvp"].asString();
+
+ mConfirmedTextureQ.push(data);
+}
+
+
+bool LLImportMaterial::operator<(const LLImportMaterial &rhs) const
+{
+ if (mDiffuseMap != rhs.mDiffuseMap)
+ {
+ return mDiffuseMap < rhs.mDiffuseMap;
+ }
+
+ if (mDiffuseMapFilename != rhs.mDiffuseMapFilename)
+ {
+ return mDiffuseMapFilename < rhs.mDiffuseMapFilename;
+ }
+
+ if (mDiffuseMapLabel != rhs.mDiffuseMapLabel)
+ {
+ return mDiffuseMapLabel < rhs.mDiffuseMapLabel;
+ }
+
+ if (mDiffuseColor != rhs.mDiffuseColor)
+ {
+ return mDiffuseColor < rhs.mDiffuseColor;
+ }
+
+ return mFullbright < rhs.mFullbright;
+}
+
+
+void LLMeshRepository::updateInventory(inventory_data data)
+{
+ LLMutexLock lock(mMeshMutex);
+ dumpLLSDToFile(data.mPostData,"update_inventory_post_data.xml");
+ dumpLLSDToFile(data.mResponse,"update_inventory_response.xml");
+ mInventoryQ.push(data);
+}
+
+void LLMeshRepository::uploadError(LLSD& args)
+{
+ LLMutexLock lock(mMeshMutex);
+ mUploadErrorQ.push(args);
+}
+
+//static
+F32 LLMeshRepository::getStreamingCost(LLSD& header, F32 radius, S32* bytes, S32* bytes_visible, S32 lod)
+{
+ F32 dlowest = llmin(radius/0.03f, 256.f);
+ F32 dlow = llmin(radius/0.06f, 256.f);
+ F32 dmid = llmin(radius/0.24f, 256.f);
+
+ F32 bytes_lowest = header["lowest_lod"]["size"].asReal()/1024.f;
+ F32 bytes_low = header["low_lod"]["size"].asReal()/1024.f;
+ F32 bytes_mid = header["medium_lod"]["size"].asReal()/1024.f;
+ F32 bytes_high = header["high_lod"]["size"].asReal()/1024.f;
+
+ if (bytes)
+ {
+ *bytes = 0;
+ *bytes += header["lowest_lod"]["size"].asInteger();
+ *bytes += header["low_lod"]["size"].asInteger();
+ *bytes += header["medium_lod"]["size"].asInteger();
+ *bytes += header["high_lod"]["size"].asInteger();
+ }
+
+
+ if (bytes_visible)
+ {
+ lod = LLMeshRepository::getActualMeshLOD(header, lod);
+ if (lod >= 0 && lod <= 3)
+ {
+ *bytes_visible = header[header_lod[lod]]["size"].asInteger();
+ }
+ }
+
+ if (bytes_high == 0.f)
+ {
+ return 0.f;
+ }
+
+ if (bytes_mid == 0.f)
+ {
+ bytes_mid = bytes_high;
+ }
+
+ if (bytes_low == 0.f)
+ {
+ bytes_low = bytes_mid;
+ }
+
+ if (bytes_lowest == 0.f)
+ {
+ bytes_lowest = bytes_low;
+ }
+
+ F32 max_area = 65536.f;
+ F32 min_area = 1.f;
+
+ F32 high_area = llmin(F_PI*dmid*dmid, max_area);
+ F32 mid_area = llmin(F_PI*dlow*dlow, max_area);
+ F32 low_area = llmin(F_PI*dlowest*dlowest, max_area);
+ F32 lowest_area = max_area;
+
+ lowest_area -= low_area;
+ low_area -= mid_area;
+ mid_area -= high_area;
+
+ high_area = llclamp(high_area, min_area, max_area);
+ mid_area = llclamp(mid_area, min_area, max_area);
+ low_area = llclamp(low_area, min_area, max_area);
+ lowest_area = llclamp(lowest_area, min_area, max_area);
+
+ F32 total_area = high_area + mid_area + low_area + lowest_area;
+ high_area /= total_area;
+ mid_area /= total_area;
+ low_area /= total_area;
+ lowest_area /= total_area;
+
+ F32 weighted_avg = bytes_high*high_area +
+ bytes_mid*mid_area +
+ bytes_low*low_area +
+ bytes_lowest*lowest_area;
+
+ return weighted_avg * gSavedSettings.getF32("MeshStreamingCostScaler");
+}
+
+
+LLPhysicsDecomp::LLPhysicsDecomp()
+: LLThread("Physics Decomp")
+{
+ mInited = false;
+ mQuitting = false;
+ mDone = false;
+
+ mSignal = new LLCondition(NULL);
+ mMutex = new LLMutex(NULL);
+}
+
+LLPhysicsDecomp::~LLPhysicsDecomp()
+{
+ shutdown();
+
+ delete mSignal;
+ mSignal = NULL;
+ delete mMutex;
+ mMutex = NULL;
+}
+
+void LLPhysicsDecomp::shutdown()
+{
+ if (mSignal)
+ {
+ mQuitting = true;
+ mSignal->signal();
+
+ while (!isStopped())
+ {
+ apr_sleep(10);
+ }
+ }
+}
+
+void LLPhysicsDecomp::submitRequest(LLPhysicsDecomp::Request* request)
+{
+ LLMutexLock lock(mMutex);
+ mRequestQ.push(request);
+ mSignal->signal();
+}
+
+//static
+S32 LLPhysicsDecomp::llcdCallback(const char* status, S32 p1, S32 p2)
+{
+ if (gMeshRepo.mDecompThread && gMeshRepo.mDecompThread->mCurRequest.notNull())
+ {
+ return gMeshRepo.mDecompThread->mCurRequest->statusCallback(status, p1, p2);
+ }
+
+ return 1;
+}
+
+void LLPhysicsDecomp::setMeshData(LLCDMeshData& mesh)
+{
+ mesh.mVertexBase = mCurRequest->mPositions[0].mV;
+ mesh.mVertexStrideBytes = 12;
+ mesh.mNumVertices = mCurRequest->mPositions.size();
+
+ mesh.mIndexType = LLCDMeshData::INT_16;
+ mesh.mIndexBase = &(mCurRequest->mIndices[0]);
+ mesh.mIndexStrideBytes = 6;
+
+ mesh.mNumTriangles = mCurRequest->mIndices.size()/3;
+
+ LLCDResult ret = LLCD_OK;
+ if (LLConvexDecomposition::getInstance() != NULL)
+ {
+ ret = LLConvexDecomposition::getInstance()->setMeshData(&mesh);
+ }
+
+ if (ret)
+ {
+ llerrs << "Convex Decomposition thread valid but could not set mesh data" << llendl;
+ }
+}
+
+void LLPhysicsDecomp::doDecomposition()
+{
+ LLCDMeshData mesh;
+ S32 stage = mStageID[mCurRequest->mStage];
+
+ if (LLConvexDecomposition::getInstance() == NULL)
+ {
+ // stub library. do nothing.
+ return;
+ }
+
+ //load data intoLLCD
+ if (stage == 0)
+ {
+ setMeshData(mesh);
+ }
+
+ //build parameter map
+ std::map<std::string, const LLCDParam*> param_map;
+
+ static const LLCDParam* params = NULL;
+ static S32 param_count = 0;
+ if (!params)
+ {
+ param_count = LLConvexDecomposition::getInstance()->getParameters(&params);
+ }
+
+ for (S32 i = 0; i < param_count; ++i)
+ {
+ param_map[params[i].mName] = params+i;
+ }
+
+ //set parameter values
+ for (decomp_params::iterator iter = mCurRequest->mParams.begin(); iter != mCurRequest->mParams.end(); ++iter)
+ {
+ const std::string& name = iter->first;
+ const LLSD& value = iter->second;
+
+ const LLCDParam* param = param_map[name];
+
+ if (param == NULL)
+ { //couldn't find valid parameter
+ continue;
+ }
+
+ U32 ret = LLCD_OK;
+
+ if (param->mType == LLCDParam::LLCD_FLOAT)
+ {
+ ret = LLConvexDecomposition::getInstance()->setParam(param->mName, (F32) value.asReal());
+ }
+ else if (param->mType == LLCDParam::LLCD_INTEGER ||
+ param->mType == LLCDParam::LLCD_ENUM)
+ {
+ ret = LLConvexDecomposition::getInstance()->setParam(param->mName, value.asInteger());
+ }
+ else if (param->mType == LLCDParam::LLCD_BOOLEAN)
+ {
+ ret = LLConvexDecomposition::getInstance()->setParam(param->mName, value.asBoolean());
+ }
+ }
+
+ mCurRequest->setStatusMessage("Executing.");
+
+ LLCDResult ret = LLCD_OK;
+
+ if (LLConvexDecomposition::getInstance() != NULL)
+ {
+ ret = LLConvexDecomposition::getInstance()->executeStage(stage);
+ }
+
+ if (ret)
+ {
+ llwarns << "Convex Decomposition thread valid but could not execute stage " << stage << llendl;
+ LLMutexLock lock(mMutex);
+
+ mCurRequest->mHull.clear();
+ mCurRequest->mHullMesh.clear();
+
+ mCurRequest->setStatusMessage("FAIL");
+
+ completeCurrent();
+ }
+ else
+ {
+ mCurRequest->setStatusMessage("Reading results");
+
+ S32 num_hulls =0;
+ if (LLConvexDecomposition::getInstance() != NULL)
+ {
+ num_hulls = LLConvexDecomposition::getInstance()->getNumHullsFromStage(stage);
+ }
+
+ mMutex->lock();
+ mCurRequest->mHull.clear();
+ mCurRequest->mHull.resize(num_hulls);
+
+ mCurRequest->mHullMesh.clear();
+ mCurRequest->mHullMesh.resize(num_hulls);
+ mMutex->unlock();
+
+ for (S32 i = 0; i < num_hulls; ++i)
+ {
+ std::vector<LLVector3> p;
+ LLCDHull hull;
+ // if LLConvexDecomposition is a stub, num_hulls should have been set to 0 above, and we should not reach this code
+ LLConvexDecomposition::getInstance()->getHullFromStage(stage, i, &hull);
+
+ const F32* v = hull.mVertexBase;
+
+ for (S32 j = 0; j < hull.mNumVertices; ++j)
+ {
+ LLVector3 vert(v[0], v[1], v[2]);
+ p.push_back(vert);
+ v = (F32*) (((U8*) v) + hull.mVertexStrideBytes);
+ }
+
+ LLCDMeshData mesh;
+ // if LLConvexDecomposition is a stub, num_hulls should have been set to 0 above, and we should not reach this code
+ LLConvexDecomposition::getInstance()->getMeshFromStage(stage, i, &mesh);
+
+ get_vertex_buffer_from_mesh(mesh, mCurRequest->mHullMesh[i]);
+
+ mMutex->lock();
+ mCurRequest->mHull[i] = p;
+ mMutex->unlock();
+ }
+
+ {
+ LLMutexLock lock(mMutex);
+
+ mCurRequest->setStatusMessage("FAIL");
+ completeCurrent();
+ }
+ }
+}
+
+void LLPhysicsDecomp::completeCurrent()
+{
+ LLMutexLock lock(mMutex);
+ mCompletedQ.push(mCurRequest);
+ mCurRequest = NULL;
+}
+
+void LLPhysicsDecomp::notifyCompleted()
+{
+ if (!mCompletedQ.empty())
+ {
+ LLMutexLock lock(mMutex);
+ while (!mCompletedQ.empty())
+ {
+ Request* req = mCompletedQ.front();
+ req->completed();
+ mCompletedQ.pop();
+ }
+ }
+}
+
+
+void make_box(LLPhysicsDecomp::Request * request)
+{
+ LLVector3 min,max;
+ min = request->mPositions[0];
+ max = min;
+
+ for (U32 i = 0; i < request->mPositions.size(); ++i)
+ {
+ update_min_max(min, max, request->mPositions[i]);
+ }
+
+ request->mHull.clear();
+
+ LLModel::hull box;
+ box.push_back(LLVector3(min[0],min[1],min[2]));
+ box.push_back(LLVector3(max[0],min[1],min[2]));
+ box.push_back(LLVector3(min[0],max[1],min[2]));
+ box.push_back(LLVector3(max[0],max[1],min[2]));
+ box.push_back(LLVector3(min[0],min[1],max[2]));
+ box.push_back(LLVector3(max[0],min[1],max[2]));
+ box.push_back(LLVector3(min[0],max[1],max[2]));
+ box.push_back(LLVector3(max[0],max[1],max[2]));
+
+ request->mHull.push_back(box);
+}
+
+
+void LLPhysicsDecomp::doDecompositionSingleHull()
+{
+ LLCDMeshData mesh;
+
+ setMeshData(mesh);
+
+
+ //set all parameters to default
+ std::map<std::string, const LLCDParam*> param_map;
+
+ static const LLCDParam* params = NULL;
+ static S32 param_count = 0;
+
+ if (!params)
+ {
+ param_count = LLConvexDecomposition::getInstance()->getParameters(&params);
+ }
+
+ LLConvexDecomposition* decomp = LLConvexDecomposition::getInstance();
+
+ if (decomp == NULL)
+ {
+ //stub. do nothing.
+ return;
+ }
+
+ for (S32 i = 0; i < param_count; ++i)
+ {
+ decomp->setParam(params[i].mName, params[i].mDefault.mIntOrEnumValue);
+ }
+
+ const S32 STAGE_DECOMPOSE = mStageID["Decompose"];
+ const S32 STAGE_SIMPLIFY = mStageID["Simplify"];
+ const S32 DECOMP_PREVIEW = 0;
+ const S32 SIMPLIFY_RETAIN = 0;
+
+ decomp->setParam("Decompose Quality", DECOMP_PREVIEW);
+ decomp->setParam("Simplify Method", SIMPLIFY_RETAIN);
+ decomp->setParam("Retain%", 0.f);
+
+ LLCDResult ret = LLCD_OK;
+ ret = decomp->executeStage(STAGE_DECOMPOSE);
+
+ if (ret)
+ {
+ llwarns << "Could not execute decomposition stage when attempting to create single hull." << llendl;
+ make_box(mCurRequest);
+ }
+ else
+ {
+ ret = decomp->executeStage(STAGE_SIMPLIFY);
+
+ if (ret)
+ {
+ llwarns << "Could not execute simiplification stage when attempting to create single hull." << llendl;
+ make_box(mCurRequest);
+ }
+ else
+ {
+ S32 num_hulls =0;
+ if (LLConvexDecomposition::getInstance() != NULL)
+ {
+ num_hulls = LLConvexDecomposition::getInstance()->getNumHullsFromStage(STAGE_SIMPLIFY);
+ }
+
+ mMutex->lock();
+ mCurRequest->mHull.clear();
+ mCurRequest->mHull.resize(num_hulls);
+ mCurRequest->mHullMesh.clear();
+ mMutex->unlock();
+
+ for (S32 i = 0; i < num_hulls; ++i)
+ {
+ std::vector<LLVector3> p;
+ LLCDHull hull;
+ // if LLConvexDecomposition is a stub, num_hulls should have been set to 0 above, and we should not reach this code
+ LLConvexDecomposition::getInstance()->getHullFromStage(STAGE_SIMPLIFY, i, &hull);
+
+ const F32* v = hull.mVertexBase;
+
+ for (S32 j = 0; j < hull.mNumVertices; ++j)
+ {
+ LLVector3 vert(v[0], v[1], v[2]);
+ p.push_back(vert);
+ v = (F32*) (((U8*) v) + hull.mVertexStrideBytes);
+ }
+
+ mMutex->lock();
+ mCurRequest->mHull[i] = p;
+ mMutex->unlock();
+ }
+ }
+ }
+
+
+ {
+ completeCurrent();
+
+ }
+}
+
+
+void LLPhysicsDecomp::run()
+{
+ LLConvexDecomposition* decomp = LLConvexDecomposition::getInstance();
+ if (decomp == NULL)
+ {
+ // stub library. Set init to true so the main thread
+ // doesn't wait for this to finish.
+ mInited = true;
+ return;
+ }
+
+ decomp->initThread();
+ mInited = true;
+
+ static const LLCDStageData* stages = NULL;
+ static S32 num_stages = 0;
+
+ if (!stages)
+ {
+ num_stages = decomp->getStages(&stages);
+ }
+
+ for (S32 i = 0; i < num_stages; i++)
+ {
+ mStageID[stages[i].mName] = i;
+ }
+
+ while (!mQuitting)
+ {
+ mSignal->wait();
+ while (!mQuitting && !mRequestQ.empty())
+ {
+ {
+ LLMutexLock lock(mMutex);
+ mCurRequest = mRequestQ.front();
+ mRequestQ.pop();
+ }
+
+ S32& id = *(mCurRequest->mDecompID);
+ if (id == -1)
+ {
+ decomp->genDecomposition(id);
+ }
+ decomp->bindDecomposition(id);
+
+ if (mCurRequest->mStage == "single_hull")
+ {
+ doDecompositionSingleHull();
+ }
+ else
+ {
+ doDecomposition();
+ }
+ }
+ }
+
+ decomp->quitThread();
+
+ if (mSignal->isLocked())
+ { //let go of mSignal's associated mutex
+ mSignal->unlock();
+ }
+
+ mDone = true;
+}
+
+void LLPhysicsDecomp::Request::updateTriangleAreaThreshold()
+{
+ F32 range = mBBox[1].mV[0] - mBBox[0].mV[0] ;
+ range = llmin(range, mBBox[1].mV[1] - mBBox[0].mV[1]) ;
+ range = llmin(range, mBBox[1].mV[2] - mBBox[0].mV[2]) ;
+
+ mTriangleAreaThreshold = llmin(0.0002f, range * 0.000002f) ;
+}
+
+//check if the triangle area is large enough to qualify for a valid triangle
+bool LLPhysicsDecomp::Request::isValidTriangle(U16 idx1, U16 idx2, U16 idx3)
+{
+ LLVector3 a = mPositions[idx2] - mPositions[idx1] ;
+ LLVector3 b = mPositions[idx3] - mPositions[idx1] ;
+ F32 c = a * b ;
+
+ return ((a*a) * (b*b) - c * c) > mTriangleAreaThreshold ;
+}
+
+void LLPhysicsDecomp::Request::setStatusMessage(const std::string& msg)
+{
+ mStatusMessage = msg;
+}
+
+LLModelInstance::LLModelInstance(LLSD& data)
+{
+ mLocalMeshID = data["mesh_id"].asInteger();
+ mLabel = data["label"].asString();
+ mTransform.setValue(data["transform"]);
+
+ for (U32 i = 0; i < data["material"].size(); ++i)
+ {
+ mMaterial.push_back(LLImportMaterial(data["material"][i]));
+ }
+}
+
+
+LLSD LLModelInstance::asLLSD()
+{
+ LLSD ret;
+
+ ret["mesh_id"] = mModel->mLocalID;
+ ret["label"] = mLabel;
+ ret["transform"] = mTransform.getValue();
+
+ for (U32 i = 0; i < mMaterial.size(); ++i)
+ {
+ ret["material"][i] = mMaterial[i].asLLSD();
+ }
+
+ return ret;
+}
+
+LLImportMaterial::LLImportMaterial(LLSD& data)
+{
+ mDiffuseMapFilename = data["diffuse"]["filename"].asString();
+ mDiffuseMapLabel = data["diffuse"]["label"].asString();
+ mDiffuseColor.setValue(data["diffuse"]["color"]);
+ mFullbright = data["fullbright"].asBoolean();
+}
+
+
+LLSD LLImportMaterial::asLLSD()
+{
+ LLSD ret;
+
+ ret["diffuse"]["filename"] = mDiffuseMapFilename;
+ ret["diffuse"]["label"] = mDiffuseMapLabel;
+ ret["diffuse"]["color"] = mDiffuseColor.getValue();
+ ret["fullbright"] = mFullbright;
+
+ return ret;
+}
+
+void LLMeshRepository::buildPhysicsMesh(LLModel::Decomposition& decomp)
+{
+ decomp.mMesh.resize(decomp.mHull.size());
+
+ for (U32 i = 0; i < decomp.mHull.size(); ++i)
+ {
+ LLCDHull hull;
+ hull.mNumVertices = decomp.mHull[i].size();
+ hull.mVertexBase = decomp.mHull[i][0].mV;
+ hull.mVertexStrideBytes = 12;
+
+ LLCDMeshData mesh;
+ LLCDResult res = LLCD_OK;
+ if (LLConvexDecomposition::getInstance() != NULL)
+ {
+ res = LLConvexDecomposition::getInstance()->getMeshFromHull(&hull, &mesh);
+ }
+ if (res == LLCD_OK)
+ {
+ get_vertex_buffer_from_mesh(mesh, decomp.mMesh[i]);
+ }
+ }
+
+ if (!decomp.mBaseHull.empty() && decomp.mBaseHullMesh.empty())
+ { //get mesh for base hull
+ LLCDHull hull;
+ hull.mNumVertices = decomp.mBaseHull.size();
+ hull.mVertexBase = decomp.mBaseHull[0].mV;
+ hull.mVertexStrideBytes = 12;
+
+ LLCDMeshData mesh;
+ LLCDResult res = LLCD_OK;
+ if (LLConvexDecomposition::getInstance() != NULL)
+ {
+ res = LLConvexDecomposition::getInstance()->getMeshFromHull(&hull, &mesh);
+ }
+ if (res == LLCD_OK)
+ {
+ get_vertex_buffer_from_mesh(mesh, decomp.mBaseHullMesh);
+ }
+ }
+}
diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h
index 802e3e1aba..0a6954bade 100644
--- a/indra/newview/llmeshrepository.h
+++ b/indra/newview/llmeshrepository.h
@@ -152,7 +152,7 @@ public:
std::string mStatusMessage;
std::vector<LLModel::PhysicsMesh> mHullMesh;
LLModel::convex_hull_decomposition mHull;
-
+
//status message callback, called from decomposition thread
virtual S32 statusCallback(const char* status, S32 p1, S32 p2) = 0;
@@ -160,6 +160,14 @@ public:
virtual void completed() = 0;
virtual void setStatusMessage(const std::string& msg);
+
+ protected:
+ //internal use
+ LLVector3 mBBox[2] ;
+ F32 mTriangleAreaThreshold ;
+
+ void updateTriangleAreaThreshold() ;
+ bool isValidTriangle(U16 idx1, U16 idx2, U16 idx3) ;
};
LLCondition* mSignal;
@@ -387,7 +395,8 @@ public:
LLHost mHost;
std::string mUploadObjectAssetCapability;
std::string mNewInventoryCapability;
- std::string mWholeModelUploadCapability;
+ std::string mWholeModelFeeCapability;
+ std::string mWholeModelUploadURL;
std::queue<LLMeshUploadData> mUploadQ;
std::queue<LLMeshUploadData> mConfirmedQ;
diff --git a/indra/newview/llpanelobject.cpp b/indra/newview/llpanelobject.cpp
index 64af6c2157..34a92cd0ac 100644
--- a/indra/newview/llpanelobject.cpp
+++ b/indra/newview/llpanelobject.cpp
@@ -33,11 +33,9 @@
#include "lleconomy.h"
#include "llerror.h"
#include "llfontgl.h"
-#include "llmaterialtable.h"
#include "llpermissionsflags.h"
#include "llstring.h"
#include "llvolume.h"
-#include "material_codes.h"
#include "m3math.h"
// project includes
@@ -57,7 +55,6 @@
#include "lltool.h"
#include "lltoolcomp.h"
#include "lltoolmgr.h"
-#include "lltrans.h"
#include "llui.h"
#include "llviewerobject.h"
#include "llviewerregion.h"
@@ -101,17 +98,6 @@ BOOL LLPanelObject::postBuild()
{
setMouseOpaque(FALSE);
- std::map<std::string, std::string> material_name_map;
- material_name_map["Stone"]= LLTrans::getString("Stone");
- material_name_map["Metal"]= LLTrans::getString("Metal");
- material_name_map["Glass"]= LLTrans::getString("Glass");
- material_name_map["Wood"]= LLTrans::getString("Wood");
- material_name_map["Flesh"]= LLTrans::getString("Flesh");
- material_name_map["Plastic"]= LLTrans::getString("Plastic");
- material_name_map["Rubber"]= LLTrans::getString("Rubber");
- material_name_map["Light"]= LLTrans::getString("Light");
-
- LLMaterialTable::basic.initTableTransNames(material_name_map);
//--------------------------------------------------------
// Top
//--------------------------------------------------------
@@ -166,22 +152,6 @@ BOOL LLPanelObject::postBuild()
//--------------------------------------------------------
- // material type popup
- mComboMaterial = getChild<LLComboBox>("material");
- childSetCommitCallback("material",onCommitMaterial,this);
- mComboMaterial->removeall();
-
- for (LLMaterialTable::info_list_t::iterator iter = LLMaterialTable::basic.mMaterialInfoList.begin();
- iter != LLMaterialTable::basic.mMaterialInfoList.end(); ++iter)
- {
- LLMaterialInfo* minfop = *iter;
- if (minfop->mMCode != LL_MCODE_LIGHT)
- {
- mComboMaterial->add(minfop->mName);
- }
- }
- mComboMaterialItemCount = mComboMaterial->getItemCount();
-
// Base Type
mComboBaseType = getChild<LLComboBox>("comboBaseType");
childSetCommitCallback("comboBaseType",onCommitParametric,this);
@@ -309,7 +279,6 @@ BOOL LLPanelObject::postBuild()
LLPanelObject::LLPanelObject()
: LLPanel(),
- mComboMaterialItemCount(0),
mIsPhysical(FALSE),
mIsTemporary(FALSE),
mIsPhantom(FALSE),
@@ -527,43 +496,6 @@ void LLPanelObject::getState( )
mCheckCastShadows->setEnabled( roots_selected==1 && editable );
#endif
- // Update material part
- // slightly inefficient - materials are unique per object, not per TE
- U8 material_code = 0;
- struct f : public LLSelectedTEGetFunctor<U8>
- {
- U8 get(LLViewerObject* object, S32 te)
- {
- return object->getMaterial();
- }
- } func;
- bool material_same = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func, material_code );
- std::string LEGACY_FULLBRIGHT_DESC = LLTrans::getString("Fullbright");
- if (editable && single_volume && material_same)
- {
- mComboMaterial->setEnabled( TRUE );
- if (material_code == LL_MCODE_LIGHT)
- {
- if (mComboMaterial->getItemCount() == mComboMaterialItemCount)
- {
- mComboMaterial->add(LEGACY_FULLBRIGHT_DESC);
- }
- mComboMaterial->setSimple(LEGACY_FULLBRIGHT_DESC);
- }
- else
- {
- if (mComboMaterial->getItemCount() != mComboMaterialItemCount)
- {
- mComboMaterial->remove(LEGACY_FULLBRIGHT_DESC);
- }
-
- mComboMaterial->setSimple(std::string(LLMaterialTable::basic.getName(material_code)));
- }
- }
- else
- {
- mComboMaterial->setEnabled( FALSE );
- }
//----------------------------------------------------------------------------
S32 selected_item = MI_BOX;
@@ -1095,12 +1027,9 @@ void LLPanelObject::getState( )
mCtrlSculptTexture->setVisible(sculpt_texture_visible);
mLabelSculptType->setVisible(sculpt_texture_visible);
mCtrlSculptType->setVisible(sculpt_texture_visible);
- mCtrlSculptMirror->setVisible(sculpt_texture_visible);
- mCtrlSculptInvert->setVisible(sculpt_texture_visible);
// sculpt texture
-
if (selected_item == MI_SCULPT)
{
@@ -1145,7 +1074,7 @@ void LLPanelObject::getState( )
if (mCtrlSculptMirror)
{
mCtrlSculptMirror->set(sculpt_mirror);
- mCtrlSculptMirror->setEnabled(editable);
+ mCtrlSculptMirror->setEnabled(editable && !isMesh);
}
if (mCtrlSculptInvert)
@@ -1166,6 +1095,9 @@ void LLPanelObject::getState( )
mSculptTextureRevert = LLUUID::null;
}
+ mCtrlSculptMirror->setVisible(sculpt_texture_visible && !isMesh);
+ mCtrlSculptInvert->setVisible(sculpt_texture_visible && !isMesh);
+
//----------------------------------------------------------------------------
mObject = objectp;
@@ -1245,25 +1177,6 @@ void LLPanelObject::sendCastShadows()
}
// static
-void LLPanelObject::onCommitMaterial( LLUICtrl* ctrl, void* userdata )
-{
- //LLPanelObject* self = (LLPanelObject*) userdata;
- LLComboBox* box = (LLComboBox*) ctrl;
-
- if (box)
- {
- // apply the currently selected material to the object
- const std::string& material_name = box->getSimple();
- std::string LEGACY_FULLBRIGHT_DESC = LLTrans::getString("Fullbright");
- if (material_name != LEGACY_FULLBRIGHT_DESC)
- {
- U8 material_code = LLMaterialTable::basic.getMCode(material_name);
- LLSelectMgr::getInstance()->selectionSetMaterial(material_code);
- }
- }
-}
-
-// static
void LLPanelObject::onCommitParametric( LLUICtrl* ctrl, void* userdata )
{
LLPanelObject* self = (LLPanelObject*) userdata;
@@ -1937,7 +1850,6 @@ void LLPanelObject::clearCtrls()
mCheckCastShadows->set(FALSE);
mCheckCastShadows->setEnabled( FALSE );
#endif
- mComboMaterial ->setEnabled( FALSE );
// Disable text labels
mLabelPosition ->setEnabled( FALSE );
mLabelSize ->setEnabled( FALSE );
diff --git a/indra/newview/llpanelobject.h b/indra/newview/llpanelobject.h
index e2f2a4400d..475dfdaedb 100644
--- a/indra/newview/llpanelobject.h
+++ b/indra/newview/llpanelobject.h
@@ -66,7 +66,6 @@ public:
static void onCommitPhantom( LLUICtrl* ctrl, void* userdata);
static void onCommitCastShadows( LLUICtrl* ctrl, void* userdata);
static void onCommitPhysics( LLUICtrl* ctrl, void* userdata);
- static void onCommitMaterial( LLUICtrl* ctrl, void* userdata);
static void onCommitParametric(LLUICtrl* ctrl, void* userdata);
@@ -94,10 +93,6 @@ protected:
void getVolumeParams(LLVolumeParams& volume_params);
protected:
- S32 mComboMaterialItemCount;
-
- LLComboBox* mComboMaterial;
-
// Per-object options
LLComboBox* mComboBaseType;
diff --git a/indra/newview/llpanelvolume.cpp b/indra/newview/llpanelvolume.cpp
index c443814c89..ebddaa90bc 100644
--- a/indra/newview/llpanelvolume.cpp
+++ b/indra/newview/llpanelvolume.cpp
@@ -59,6 +59,7 @@
#include "lltool.h"
#include "lltoolcomp.h"
#include "lltoolmgr.h"
+#include "lltrans.h"
#include "llui.h"
#include "llviewerobject.h"
#include "llviewerregion.h"
@@ -156,6 +157,34 @@ BOOL LLPanelVolume::postBuild()
mSpinPhysicsRestitution = getChild<LLSpinCtrl>("Physics Restitution");
mSpinPhysicsRestitution->setCommitCallback(boost::bind(&LLPanelVolume::sendPhysicsRestitution, this, _1, mSpinPhysicsRestitution));
}
+
+ std::map<std::string, std::string> material_name_map;
+ material_name_map["Stone"]= LLTrans::getString("Stone");
+ material_name_map["Metal"]= LLTrans::getString("Metal");
+ material_name_map["Glass"]= LLTrans::getString("Glass");
+ material_name_map["Wood"]= LLTrans::getString("Wood");
+ material_name_map["Flesh"]= LLTrans::getString("Flesh");
+ material_name_map["Plastic"]= LLTrans::getString("Plastic");
+ material_name_map["Rubber"]= LLTrans::getString("Rubber");
+ material_name_map["Light"]= LLTrans::getString("Light");
+
+ LLMaterialTable::basic.initTableTransNames(material_name_map);
+
+ // material type popup
+ mComboMaterial = getChild<LLComboBox>("material");
+ childSetCommitCallback("material",onCommitMaterial,this);
+ mComboMaterial->removeall();
+
+ for (LLMaterialTable::info_list_t::iterator iter = LLMaterialTable::basic.mMaterialInfoList.begin();
+ iter != LLMaterialTable::basic.mMaterialInfoList.end(); ++iter)
+ {
+ LLMaterialInfo* minfop = *iter;
+ if (minfop->mMCode != LL_MCODE_LIGHT)
+ {
+ mComboMaterial->add(minfop->mName);
+ }
+ }
+ mComboMaterialItemCount = mComboMaterial->getItemCount();
// Start with everyone disabled
clearCtrls();
@@ -164,7 +193,8 @@ BOOL LLPanelVolume::postBuild()
}
LLPanelVolume::LLPanelVolume()
- : LLPanel()
+ : LLPanel(),
+ mComboMaterialItemCount(0)
{
setMouseOpaque(FALSE);
@@ -379,6 +409,46 @@ void LLPanelVolume::getState( )
getChildView("FlexForceZ")->setEnabled(false);
}
+ // Material properties
+
+ // Update material part
+ // slightly inefficient - materials are unique per object, not per TE
+ U8 material_code = 0;
+ struct f : public LLSelectedTEGetFunctor<U8>
+ {
+ U8 get(LLViewerObject* object, S32 te)
+ {
+ return object->getMaterial();
+ }
+ } func;
+ bool material_same = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func, material_code );
+ std::string LEGACY_FULLBRIGHT_DESC = LLTrans::getString("Fullbright");
+ if (editable && single_volume && material_same)
+ {
+ mComboMaterial->setEnabled( TRUE );
+ if (material_code == LL_MCODE_LIGHT)
+ {
+ if (mComboMaterial->getItemCount() == mComboMaterialItemCount)
+ {
+ mComboMaterial->add(LEGACY_FULLBRIGHT_DESC);
+ }
+ mComboMaterial->setSimple(LEGACY_FULLBRIGHT_DESC);
+ }
+ else
+ {
+ if (mComboMaterial->getItemCount() != mComboMaterialItemCount)
+ {
+ mComboMaterial->remove(LEGACY_FULLBRIGHT_DESC);
+ }
+
+ mComboMaterial->setSimple(std::string(LLMaterialTable::basic.getName(material_code)));
+ }
+ }
+ else
+ {
+ mComboMaterial->setEnabled( FALSE );
+ }
+
// Physics properties
mSpinPhysicsGravity->set(objectp->getPhysicsGravity());
@@ -467,7 +537,6 @@ void LLPanelVolume::refresh()
getChildView("label physicsshapetype")->setVisible(enable_mesh);
getChildView("Physics Shape Type Combo Ctrl")->setVisible(enable_mesh);
getChildView("Physics Gravity")->setVisible(enable_mesh);
- getChildView("Physics Material Override")->setVisible(enable_mesh);
getChildView("Physics Friction")->setVisible(enable_mesh);
getChildView("Physics Density")->setVisible(enable_mesh);
getChildView("Physics Restitution")->setVisible(enable_mesh);
@@ -522,6 +591,8 @@ void LLPanelVolume::clearCtrls()
mSpinPhysicsFriction->setEnabled(FALSE);
mSpinPhysicsDensity->setEnabled(FALSE);
mSpinPhysicsRestitution->setEnabled(FALSE);
+
+ mComboMaterial->setEnabled( FALSE );
}
//
@@ -674,6 +745,25 @@ void LLPanelVolume::onLightSelectTexture(const LLSD& data)
}
// static
+void LLPanelVolume::onCommitMaterial( LLUICtrl* ctrl, void* userdata )
+{
+ //LLPanelObject* self = (LLPanelObject*) userdata;
+ LLComboBox* box = (LLComboBox*) ctrl;
+
+ if (box)
+ {
+ // apply the currently selected material to the object
+ const std::string& material_name = box->getSimple();
+ std::string LEGACY_FULLBRIGHT_DESC = LLTrans::getString("Fullbright");
+ if (material_name != LEGACY_FULLBRIGHT_DESC)
+ {
+ U8 material_code = LLMaterialTable::basic.getMCode(material_name);
+ LLSelectMgr::getInstance()->selectionSetMaterial(material_code);
+ }
+ }
+}
+
+// static
void LLPanelVolume::onCommitLight( LLUICtrl* ctrl, void* userdata )
{
LLPanelVolume* self = (LLPanelVolume*) userdata;
diff --git a/indra/newview/llpanelvolume.h b/indra/newview/llpanelvolume.h
index 776a2c1f4a..0ef47db0d9 100644
--- a/indra/newview/llpanelvolume.h
+++ b/indra/newview/llpanelvolume.h
@@ -63,8 +63,8 @@ public:
static void onCommitLight( LLUICtrl* ctrl, void* userdata);
static void onCommitIsFlexible( LLUICtrl* ctrl, void* userdata);
static void onCommitFlexible( LLUICtrl* ctrl, void* userdata);
-
static void onCommitPhysicsParam( LLUICtrl* ctrl, void* userdata);
+ static void onCommitMaterial( LLUICtrl* ctrl, void* userdata);
void onLightCancelColor(const LLSD& data);
void onLightSelectColor(const LLSD& data);
@@ -104,6 +104,10 @@ protected:
LLSpinCtrl* mSpinForce[3];
*/
+ S32 mComboMaterialItemCount;
+ LLComboBox* mComboMaterial;
+
+
LLColor4 mLightSavedColor;
LLUUID mLightSavedTexture;
LLPointer<LLViewerObject> mObject;
diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp
index 9b264b81c7..8fa4065fa6 100644
--- a/indra/newview/llselectmgr.cpp
+++ b/indra/newview/llselectmgr.cpp
@@ -1997,7 +1997,7 @@ void LLSelectMgr::selectionSetPhysicsType(U8 type)
if (object->permModify())
{
object->setPhysicsShapeType(mType);
- object->updateFlags();
+ object->updateFlags(TRUE);
}
return true;
}
@@ -2016,7 +2016,7 @@ void LLSelectMgr::selectionSetFriction(F32 friction)
if (object->permModify())
{
object->setPhysicsFriction(mFriction);
- object->updateFlags();
+ object->updateFlags(TRUE);
}
return true;
}
@@ -2035,7 +2035,7 @@ void LLSelectMgr::selectionSetGravity(F32 gravity )
if (object->permModify())
{
object->setPhysicsGravity(mGravity);
- object->updateFlags();
+ object->updateFlags(TRUE);
}
return true;
}
@@ -2054,7 +2054,7 @@ void LLSelectMgr::selectionSetDensity(F32 density )
if (object->permModify())
{
object->setPhysicsDensity(mDensity);
- object->updateFlags();
+ object->updateFlags(TRUE);
}
return true;
}
@@ -2073,7 +2073,7 @@ void LLSelectMgr::selectionSetRestitution(F32 restitution)
if (object->permModify())
{
object->setPhysicsRestitution(mRestitution);
- object->updateFlags();
+ object->updateFlags(TRUE);
}
return true;
}
diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp
index fa329eb0ae..8143d6a41f 100644
--- a/indra/newview/llspatialpartition.cpp
+++ b/indra/newview/llspatialpartition.cpp
@@ -69,6 +69,7 @@ U32 LLSpatialGroup::sNodeCount = 0;
std::set<GLuint> LLSpatialGroup::sPendingQueries;
+U32 gOctreeMaxCapacity;
BOOL LLSpatialGroup::sNoDelete = FALSE;
@@ -630,7 +631,7 @@ BOOL LLSpatialGroup::updateInGroup(LLDrawable *drawablep, BOOL immediate)
if (mOctreeNode->isInside(drawablep->getPositionGroup()) &&
(mOctreeNode->contains(drawablep) ||
(drawablep->getBinRadius() > mOctreeNode->getSize()[0] &&
- parent && parent->getElementCount() >= LL_OCTREE_MAX_CAPACITY)))
+ parent && parent->getElementCount() >= gOctreeMaxCapacity)))
{
unbound();
setState(OBJECT_DIRTY);
@@ -689,17 +690,8 @@ static LLFastTimer::DeclareTimer FTM_REBUILD_VBO("VBO Rebuilt");
void LLSpatialPartition::rebuildGeom(LLSpatialGroup* group)
{
- /*if (!gPipeline.hasRenderType(mDrawableType))
- {
- return;
- }*/
-
if (group->isDead() || !group->isState(LLSpatialGroup::GEOM_DIRTY))
{
- /*if (!group->isState(LLSpatialGroup::GEOM_DIRTY) && mRenderByGroup)
- {
- llerrs << "WTF?" << llendl;
- }*/
return;
}
@@ -961,21 +953,15 @@ void LLSpatialGroup::setState(U32 state)
{
mState |= state;
- if (state > LLSpatialGroup::STATE_MASK)
- {
- llerrs << "WTF?" << llendl;
- }
+ llassert(state <= LLSpatialGroup::STATE_MASK);
}
void LLSpatialGroup::setState(U32 state, S32 mode)
{
LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
- if (state > LLSpatialGroup::STATE_MASK)
- {
- llerrs << "WTF?" << llendl;
- }
-
+ llassert(state <= LLSpatialGroup::STATE_MASK);
+
if (mode > STATE_MODE_SINGLE)
{
if (mode == STATE_MODE_DIFF)
@@ -1021,20 +1007,14 @@ public:
void LLSpatialGroup::clearState(U32 state)
{
- if (state > LLSpatialGroup::STATE_MASK)
- {
- llerrs << "WTF?" << llendl;
- }
+ llassert(state <= LLSpatialGroup::STATE_MASK);
mState &= ~state;
}
void LLSpatialGroup::clearState(U32 state, S32 mode)
{
- if (state > LLSpatialGroup::STATE_MASK)
- {
- llerrs << "WTF?" << llendl;
- }
+ llassert(state <= LLSpatialGroup::STATE_MASK);
LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
@@ -1059,10 +1039,7 @@ void LLSpatialGroup::clearState(U32 state, S32 mode)
BOOL LLSpatialGroup::isState(U32 state) const
{
- if (state > LLSpatialGroup::STATE_MASK)
- {
- llerrs << "WTF?" << llendl;
- }
+ llassert(state <= LLSpatialGroup::STATE_MASK);
return mState & state ? TRUE : FALSE;
}
@@ -1250,7 +1227,8 @@ void LLSpatialGroup::updateDistance(LLCamera &camera)
{
if (LLViewerCamera::sCurCameraID != LLViewerCamera::CAMERA_WORLD)
{
- llerrs << "WTF?" << llendl;
+ llwarns << "Attempted to update distance for camera other than world camera!" << llendl;
+ return;
}
#if !LL_RELEASE_FOR_DOWNLOAD
@@ -2064,11 +2042,8 @@ public:
virtual void processGroup(LLSpatialGroup* group)
{
- if (group->isState(LLSpatialGroup::DIRTY) || group->getData().empty())
- {
- llerrs << "WTF?" << llendl;
- }
-
+ llassert(!group->isState(LLSpatialGroup::DIRTY) && !group->getData().empty())
+
if (mRes < 2)
{
if (mCamera->AABBInFrustum(group->mObjectBounds[0], group->mObjectBounds[1]) > 0)
@@ -2541,7 +2516,7 @@ void renderOctree(LLSpatialGroup* group)
//coded by buffer usage and activity
gGL.setSceneBlendType(LLRender::BT_ADD_WITH_ALPHA);
LLVector4 col;
- if (group->mBuilt > 0.f)
+ /*if (group->mBuilt > 0.f)
{
group->mBuilt -= 2.f * gFrameIntervalSeconds;
if (group->mBufferUsage == GL_STATIC_DRAW_ARB)
@@ -2610,7 +2585,7 @@ void renderOctree(LLSpatialGroup* group)
gGL.color4f(1,1,1,1);
}
}
- else
+ else*/
{
if (group->mBufferUsage == GL_STATIC_DRAW_ARB && !group->getData().empty()
&& group->mSpatialPartition->mRenderByGroup)
@@ -2630,33 +2605,24 @@ void renderOctree(LLSpatialGroup* group)
size.mul(1.01f);
size.add(fudge);
- {
- LLGLDepthTest depth(GL_TRUE, GL_FALSE);
- drawBox(group->mObjectBounds[0], fudge);
- }
+ //{
+ // LLGLDepthTest depth(GL_TRUE, GL_FALSE);
+ // drawBox(group->mObjectBounds[0], fudge);
+ //}
gGL.setSceneBlendType(LLRender::BT_ALPHA);
- if (group->mBuilt <= 0.f)
+ //if (group->mBuilt <= 0.f)
{
//draw opaque outline
- gGL.color4f(col.mV[0], col.mV[1], col.mV[2], 1.f);
- drawBoxOutline(group->mObjectBounds[0], group->mObjectBounds[1]);
+ //gGL.color4f(col.mV[0], col.mV[1], col.mV[2], 1.f);
+ //drawBoxOutline(group->mObjectBounds[0], group->mObjectBounds[1]);
- if (group->mOctreeNode->isLeaf())
- {
- gGL.color4f(1,1,1,1);
- }
- else
- {
- gGL.color4f(0,1,1,1);
- }
-
+ gGL.color4f(0,1,1,1);
drawBoxOutline(group->mBounds[0],group->mBounds[1]);
-
-
+
//draw bounding box for draw info
- if (group->mSpatialPartition->mRenderByGroup)
+ /*if (group->mSpatialPartition->mRenderByGroup)
{
gGL.color4f(1.0f, 0.75f, 0.25f, 0.6f);
for (LLSpatialGroup::draw_map_t::iterator i = group->mDrawMap.begin(); i != group->mDrawMap.end(); ++i)
@@ -2673,7 +2639,7 @@ void renderOctree(LLSpatialGroup* group)
drawBoxOutline(center, size);
}
}
- }
+ }*/
}
// LLSpatialGroup::OctreeNode* node = group->mOctreeNode;
@@ -2716,7 +2682,7 @@ void renderVisibility(LLSpatialGroup* group, LLCamera* camera)
gGL.color4f(0.f, 0.75f, 0.f, 0.5f);
pushBufferVerts(group, LLVertexBuffer::MAP_VERTEX);
}
- else if (camera && group->mOcclusionVerts.notNull())
+ /*else if (camera && group->mOcclusionVerts.notNull())
{
LLVertexBuffer::unbind();
group->mOcclusionVerts->setBuffer(LLVertexBuffer::MAP_VERTEX);
@@ -2728,7 +2694,7 @@ void renderVisibility(LLSpatialGroup* group, LLCamera* camera)
glColor4f(1.0f, 1.f, 1.f, 1.0f);
group->mOcclusionVerts->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, get_box_fan_indices(camera, group->mBounds[0]));
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
- }
+ }*/
}
}
diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp
index 379bbe614d..61200db2b9 100644
--- a/indra/newview/llviewercontrol.cpp
+++ b/indra/newview/llviewercontrol.cpp
@@ -57,6 +57,7 @@
#include "llworld.h"
#include "pipeline.h"
#include "llviewerjoystick.h"
+#include "llviewerobjectlist.h"
#include "llviewerparcelmgr.h"
#include "llparcel.h"
#include "llkeyboard.h"
@@ -183,6 +184,21 @@ static bool handleReleaseGLBufferChanged(const LLSD& newvalue)
return true;
}
+static bool handleFSAASamplesChanged(const LLSD& newvalue)
+{
+ if (gPipeline.isInit())
+ {
+ gPipeline.releaseGLBuffers();
+ gPipeline.createGLBuffers();
+
+ if (LLPipeline::sRenderDeferred)
+ {
+ LLViewerShaderMgr::instance()->setShaders();
+ }
+ }
+ return true;
+}
+
static bool handleAnisotropicChanged(const LLSD& newvalue)
{
LLImageGL::sGlobalUseAnisotropic = newvalue.asBoolean();
@@ -357,6 +373,16 @@ static bool handleResetVertexBuffersChanged(const LLSD&)
return true;
}
+static bool handleRepartition(const LLSD&)
+{
+ if (gPipeline.isInit())
+ {
+ gOctreeMaxCapacity = gSavedSettings.getU32("OctreeMaxNodeCapacity");
+ gObjectList.repartitionObjects();
+ }
+ return true;
+}
+
static bool handleRenderDynamicLODChanged(const LLSD& newvalue)
{
LLPipeline::sDynamicLOD = newvalue.asBoolean();
@@ -560,6 +586,11 @@ void settings_setup_listeners()
gSavedSettings.getControl("FirstPersonAvatarVisible")->getSignal()->connect(boost::bind(&handleRenderAvatarMouselookChanged, _2));
gSavedSettings.getControl("RenderFarClip")->getSignal()->connect(boost::bind(&handleRenderFarClipChanged, _2));
gSavedSettings.getControl("RenderTerrainDetail")->getSignal()->connect(boost::bind(&handleTerrainDetailChanged, _2));
+ gSavedSettings.getControl("OctreeStaticObjectSizeFactor")->getSignal()->connect(boost::bind(&handleRepartition, _2));
+ gSavedSettings.getControl("OctreeDistanceFactor")->getSignal()->connect(boost::bind(&handleRepartition, _2));
+ gSavedSettings.getControl("OctreeMaxNodeCapacity")->getSignal()->connect(boost::bind(&handleRepartition, _2));
+ gSavedSettings.getControl("OctreeAlphaDistanceFactor")->getSignal()->connect(boost::bind(&handleRepartition, _2));
+ gSavedSettings.getControl("OctreeAttachmentSizeFactor")->getSignal()->connect(boost::bind(&handleRepartition, _2));
gSavedSettings.getControl("RenderUseTriStrips")->getSignal()->connect(boost::bind(&handleResetVertexBuffersChanged, _2));
gSavedSettings.getControl("RenderAnimateTrees")->getSignal()->connect(boost::bind(&handleResetVertexBuffersChanged, _2));
gSavedSettings.getControl("RenderAvatarVP")->getSignal()->connect(boost::bind(&handleSetShaderChanged, _2));
@@ -568,7 +599,7 @@ void settings_setup_listeners()
gSavedSettings.getControl("RenderSpecularResX")->getSignal()->connect(boost::bind(&handleReleaseGLBufferChanged, _2));
gSavedSettings.getControl("RenderSpecularResY")->getSignal()->connect(boost::bind(&handleReleaseGLBufferChanged, _2));
gSavedSettings.getControl("RenderSpecularExponent")->getSignal()->connect(boost::bind(&handleReleaseGLBufferChanged, _2));
- gSavedSettings.getControl("RenderFSAASamples")->getSignal()->connect(boost::bind(&handleReleaseGLBufferChanged, _2));
+ gSavedSettings.getControl("RenderFSAASamples")->getSignal()->connect(boost::bind(&handleFSAASamplesChanged, _2));
gSavedSettings.getControl("RenderAnisotropic")->getSignal()->connect(boost::bind(&handleAnisotropicChanged, _2));
gSavedSettings.getControl("RenderShadowResolutionScale")->getSignal()->connect(boost::bind(&handleReleaseGLBufferChanged, _2));
gSavedSettings.getControl("RenderGlow")->getSignal()->connect(boost::bind(&handleReleaseGLBufferChanged, _2));
diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp
index e41773d273..5dadd2c33b 100644
--- a/indra/newview/llviewerdisplay.cpp
+++ b/indra/newview/llviewerdisplay.cpp
@@ -582,6 +582,7 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot)
LLMemType mt_ug(LLMemType::MTYPE_DISPLAY_UPDATE_GEOM);
const F32 max_geom_update_time = 0.005f*10.f*gFrameIntervalSeconds; // 50 ms/second update time
gPipeline.createObjects(max_geom_update_time);
+ gPipeline.processPartitionQ();
gPipeline.updateGeom(max_geom_update_time);
stop_glerror();
}
@@ -836,7 +837,7 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot)
if (LLPipeline::sRenderDeferred && !LLPipeline::sUnderWaterRender)
{
gPipeline.mDeferredScreen.bindTarget();
- glClearColor(0,0,0,0);
+ glClearColor(1,0,1,1);
gPipeline.mDeferredScreen.clear();
}
else
diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp
index 6d493bfcd5..72cdb4b355 100644
--- a/indra/newview/llviewerobject.cpp
+++ b/indra/newview/llviewerobject.cpp
@@ -102,6 +102,7 @@
#include "lltrans.h"
#include "llsdutil.h"
#include "llmediaentry.h"
+#include "llaccountingquota.h"
//#define DEBUG_UPDATE_TYPE
@@ -1893,7 +1894,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys,
//
//
- // WTF? If we're going to skip this message, why are we
+ // If we're going to skip this message, why are we
// doing all the parenting, etc above?
U32 packet_id = mesgsys->getCurrentRecvPacketID();
if (packet_id < mLatestRecvPacketID &&
@@ -5282,7 +5283,7 @@ bool LLViewerObject::specialHoverCursor() const
|| (mClickAction != 0);
}
-void LLViewerObject::updateFlags()
+void LLViewerObject::updateFlags(BOOL physics_changed)
{
LLViewerRegion* regionp = getRegion();
if(!regionp) return;
@@ -5295,12 +5296,15 @@ void LLViewerObject::updateFlags()
gMessageSystem->addBOOL("IsTemporary", flagTemporaryOnRez() );
gMessageSystem->addBOOL("IsPhantom", flagPhantom() );
gMessageSystem->addBOOL("CastsShadows", flagCastShadows() );
- gMessageSystem->nextBlock("ExtraPhysics");
- gMessageSystem->addU8("PhysicsShapeType", getPhysicsShapeType() );
- gMessageSystem->addF32("Density", getPhysicsDensity() );
- gMessageSystem->addF32("Friction", getPhysicsFriction() );
- gMessageSystem->addF32("Restitution", getPhysicsRestitution() );
- gMessageSystem->addF32("GravityMultiplier", getPhysicsGravity() );
+ if (physics_changed)
+ {
+ gMessageSystem->nextBlock("ExtraPhysics");
+ gMessageSystem->addU8("PhysicsShapeType", getPhysicsShapeType() );
+ gMessageSystem->addF32("Density", getPhysicsDensity() );
+ gMessageSystem->addF32("Friction", getPhysicsFriction() );
+ gMessageSystem->addF32("Restitution", getPhysicsRestitution() );
+ gMessageSystem->addF32("GravityMultiplier", getPhysicsGravity() );
+ }
gMessageSystem->sendReliable( regionp->getHost() );
}
@@ -5699,3 +5703,10 @@ public:
LLHTTPRegistration<ObjectPhysicsProperties>
gHTTPRegistrationObjectPhysicsProperties("/message/ObjectPhysicsProperties");
+
+
+void LLViewerObject::updateQuota( const SelectionQuota& quota )
+{
+ //update quotas
+ mSelectionQuota = quota;
+}
diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h
index e417343bec..a0ad52df6b 100644
--- a/indra/newview/llviewerobject.h
+++ b/indra/newview/llviewerobject.h
@@ -43,6 +43,7 @@
#include "v3dmath.h"
#include "v3math.h"
#include "llvertexbuffer.h"
+#include "llaccountingquota.h"
class LLAgent; // TODO: Get rid of this.
class LLAudioSource;
@@ -488,7 +489,7 @@ public:
void setRegion(LLViewerRegion *regionp);
virtual void updateRegion(LLViewerRegion *regionp);
- void updateFlags();
+ void updateFlags(BOOL physics_changed = FALSE);
BOOL setFlags(U32 flag, BOOL state);
void setPhysicsShapeType(U8 type);
void setPhysicsGravity(F32 gravity);
@@ -643,7 +644,11 @@ protected:
void unpackParticleSource(LLDataPacker &dp, const LLUUID& owner_id);
void deleteParticleSource();
void setParticleSource(const LLPartSysData& particle_parameters, const LLUUID& owner_id);
-
+
+public:
+ void updateQuota( const SelectionQuota& quota );
+ const SelectionQuota& getQuota( void ) { return mSelectionQuota; }
+
private:
void setNameValueList(const std::string& list); // clears nv pairs and then individually adds \n separated NV pairs from \0 terminated string
void deleteTEImages(); // correctly deletes list of images
@@ -705,6 +710,8 @@ protected:
F32 mPhysicsCost;
F32 mLinksetPhysicsCost;
+ SelectionQuota mSelectionQuota;
+
bool mCostStale;
mutable bool mPhysicsShapeUnknown;
diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp
index ab2e07e4df..45c6777ae8 100644
--- a/indra/newview/llviewerobjectlist.cpp
+++ b/indra/newview/llviewerobjectlist.cpp
@@ -1418,6 +1418,15 @@ void LLViewerObjectList::onObjectCostFetchFailure(const LLUUID& object_id)
mPendingObjectCost.erase(object_id);
}
+void LLViewerObjectList::updateQuota( const LLUUID& objectId, const SelectionQuota& quota )
+{
+ LLViewerObject* pVO = findObject( objectId );
+ if ( pVO )
+ {
+ pVO->updateQuota( quota );
+ }
+}
+
void LLViewerObjectList::updatePhysicsFlags(const LLViewerObject* object)
{
mStalePhysicsFlags.insert(object->getID());
@@ -1488,6 +1497,24 @@ void LLViewerObjectList::shiftObjects(const LLVector3 &offset)
LLWorld::getInstance()->shiftRegions(offset);
}
+void LLViewerObjectList::repartitionObjects()
+{
+ for (vobj_list_t::iterator iter = mObjects.begin(); iter != mObjects.end(); ++iter)
+ {
+ LLViewerObject* objectp = *iter;
+ if (!objectp->isDead())
+ {
+ LLDrawable* drawable = objectp->mDrawable;
+ if (drawable && !drawable->isDead())
+ {
+ drawable->updateBinRadius();
+ drawable->updateSpatialExtents();
+ drawable->movePartition();
+ }
+ }
+ }
+}
+
//debug code
bool LLViewerObjectList::hasMapObjectInRegion(LLViewerRegion* regionp)
{
diff --git a/indra/newview/llviewerobjectlist.h b/indra/newview/llviewerobjectlist.h
index 65374bca70..9d1b5cb56f 100644
--- a/indra/newview/llviewerobjectlist.h
+++ b/indra/newview/llviewerobjectlist.h
@@ -36,6 +36,7 @@
// project includes
#include "llviewerobject.h"
+#include "llaccountingquota.h"
class LLCamera;
class LLNetMap;
@@ -101,7 +102,10 @@ public:
F32 restitution,
F32 gravity_multiplier);
+ void updateQuota( const LLUUID& objectId, const SelectionQuota& costs );
+
void shiftObjects(const LLVector3 &offset);
+ void repartitionObjects();
bool hasMapObjectInRegion(LLViewerRegion* regionp) ;
void clearAllMapObjectsInRegion(LLViewerRegion* regionp) ;
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index f835351c04..c4ca5d37ce 100644
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -1480,6 +1480,8 @@ void LLViewerRegion::setSeedCapability(const std::string& url)
LLSD capabilityNames = LLSD::emptyArray();
+ capabilityNames.append("AccountingParcel");
+ capabilityNames.append("AccountingSelection");
capabilityNames.append("AttachmentResources");
capabilityNames.append("AvatarPickerSearch");
capabilityNames.append("ChatSessionRequest");
@@ -1508,6 +1510,7 @@ void LLViewerRegion::setSeedCapability(const std::string& url)
capabilityNames.append("LandResources");
capabilityNames.append("MapLayer");
capabilityNames.append("MapLayerGod");
+ capabilityNames.append("NewAccountingEnabled");
capabilityNames.append("NewFileAgentInventory");
capabilityNames.append("NewFileAgentInventoryVariablePrice");
capabilityNames.append("ObjectAdd");
@@ -1545,6 +1548,7 @@ void LLViewerRegion::setSeedCapability(const std::string& url)
capabilityNames.append("ViewerMetrics");
capabilityNames.append("ViewerStartAuction");
capabilityNames.append("ViewerStats");
+
// Please add new capabilities alphabetically to reduce
// merge conflicts.
@@ -1658,3 +1662,4 @@ std::string LLViewerRegion::getDescription() const
{
return stringize(*this);
}
+
diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h
index 9c5b85b77f..a6e5c47b86 100644
--- a/indra/newview/llviewerregion.h
+++ b/indra/newview/llviewerregion.h
@@ -275,6 +275,7 @@ public:
F32 getLandHeightRegion(const LLVector3& region_pos);
void getInfo(LLSD& info);
+
typedef enum
{
diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp
index 3e85802ba6..b818da205e 100644
--- a/indra/newview/llviewershadermgr.cpp
+++ b/indra/newview/llviewershadermgr.cpp
@@ -138,7 +138,9 @@ LLGLSLShader gDeferredGIFinalProgram;
LLGLSLShader gDeferredPostGIProgram;
LLGLSLShader gDeferredPostProgram;
LLGLSLShader gDeferredPostNoDoFProgram;
-
+LLGLSLShader gDeferredWLSkyProgram;
+LLGLSLShader gDeferredWLCloudProgram;
+LLGLSLShader gDeferredStarProgram;
LLGLSLShader gLuminanceGatherProgram;
@@ -190,6 +192,9 @@ LLViewerShaderMgr::LLViewerShaderMgr() :
mShaderList.push_back(&gDeferredGIFinalProgram);
mShaderList.push_back(&gDeferredWaterProgram);
mShaderList.push_back(&gDeferredAvatarAlphaProgram);
+ mShaderList.push_back(&gDeferredWLSkyProgram);
+ mShaderList.push_back(&gDeferredWLCloudProgram);
+ mShaderList.push_back(&gDeferredStarProgram);
}
LLViewerShaderMgr::~LLViewerShaderMgr()
@@ -347,6 +352,9 @@ void LLViewerShaderMgr::setShaders()
return;
}
+ //setup preprocessor definitions
+ LLShaderMgr::instance()->mDefinitions["samples"] = llformat("%d", gGLManager.getNumFBOFSAASamples(gSavedSettings.getU32("RenderFSAASamples")));
+
reentrance = true;
// Make sure the compiled shader map is cleared before we recompile shaders.
@@ -833,6 +841,9 @@ BOOL LLViewerShaderMgr::loadShadersEffects()
{
BOOL success = TRUE;
+ U32 samples = gGLManager.getNumFBOFSAASamples(gSavedSettings.getU32("RenderFSAASamples"));
+ bool multisample = samples > 1 && LLPipeline::sRenderDeferred && gGLManager.mHasTextureMultisample;
+
if (mVertexShaderLevel[SHADER_EFFECT] == 0)
{
gGlowProgram.unload();
@@ -858,10 +869,21 @@ BOOL LLViewerShaderMgr::loadShadersEffects()
if (success)
{
+ std::string fragment;
+
+ if (multisample)
+ {
+ fragment = "effects/glowExtractMSF.glsl";
+ }
+ else
+ {
+ fragment = "effects/glowExtractF.glsl";
+ }
+
gGlowExtractProgram.mName = "Glow Extract Shader (Post)";
gGlowExtractProgram.mShaderFiles.clear();
gGlowExtractProgram.mShaderFiles.push_back(make_pair("effects/glowExtractV.glsl", GL_VERTEX_SHADER_ARB));
- gGlowExtractProgram.mShaderFiles.push_back(make_pair("effects/glowExtractF.glsl", GL_FRAGMENT_SHADER_ARB));
+ gGlowExtractProgram.mShaderFiles.push_back(make_pair(fragment, GL_FRAGMENT_SHADER_ARB));
gGlowExtractProgram.mShaderLevel = mVertexShaderLevel[SHADER_EFFECT];
success = gGlowExtractProgram.createShader(NULL, &mGlowExtractUniforms);
if (!success)
@@ -952,6 +974,9 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
gDeferredGIProgram.unload();
gDeferredGIFinalProgram.unload();
gDeferredWaterProgram.unload();
+ gDeferredWLSkyProgram.unload();
+ gDeferredWLCloudProgram.unload();
+ gDeferredStarProgram.unload();
return TRUE;
}
@@ -959,6 +984,9 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
BOOL success = TRUE;
+ U32 samples = gGLManager.getNumFBOFSAASamples(gSavedSettings.getU32("RenderFSAASamples"));
+ bool multisample = samples > 1 && gGLManager.mHasTextureMultisample;
+
if (success)
{
gDeferredDiffuseProgram.mName = "Deferred Diffuse Shader";
@@ -1039,40 +1067,83 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
if (success)
{
+ std::string fragment;
+
+ if (multisample)
+ {
+ fragment = "deferred/pointLightMSF.glsl";
+ }
+ else
+ {
+ fragment = "deferred/pointLightF.glsl";
+ }
+
gDeferredLightProgram.mName = "Deferred Light Shader";
gDeferredLightProgram.mShaderFiles.clear();
gDeferredLightProgram.mShaderFiles.push_back(make_pair("deferred/pointLightV.glsl", GL_VERTEX_SHADER_ARB));
- gDeferredLightProgram.mShaderFiles.push_back(make_pair("deferred/pointLightF.glsl", GL_FRAGMENT_SHADER_ARB));
+ gDeferredLightProgram.mShaderFiles.push_back(make_pair(fragment, GL_FRAGMENT_SHADER_ARB));
gDeferredLightProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED];
success = gDeferredLightProgram.createShader(NULL, NULL);
}
if (success)
{
+ std::string fragment;
+ if (multisample)
+ {
+ fragment = "deferred/multiPointLightMSF.glsl";
+ }
+ else
+ {
+ fragment = "deferred/multiPointLightF.glsl";
+ }
+
gDeferredMultiLightProgram.mName = "Deferred MultiLight Shader";
gDeferredMultiLightProgram.mShaderFiles.clear();
gDeferredMultiLightProgram.mShaderFiles.push_back(make_pair("deferred/multiPointLightV.glsl", GL_VERTEX_SHADER_ARB));
- gDeferredMultiLightProgram.mShaderFiles.push_back(make_pair("deferred/multiPointLightF.glsl", GL_FRAGMENT_SHADER_ARB));
+ gDeferredMultiLightProgram.mShaderFiles.push_back(make_pair(fragment, GL_FRAGMENT_SHADER_ARB));
gDeferredMultiLightProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED];
success = gDeferredMultiLightProgram.createShader(NULL, NULL);
}
if (success)
{
+ std::string fragment;
+
+ if (multisample)
+ {
+ fragment = "deferred/spotLightMSF.glsl";
+ }
+ else
+ {
+ fragment = "deferred/multiSpotLightF.glsl";
+ }
+
gDeferredSpotLightProgram.mName = "Deferred SpotLight Shader";
gDeferredSpotLightProgram.mShaderFiles.clear();
gDeferredSpotLightProgram.mShaderFiles.push_back(make_pair("deferred/pointLightV.glsl", GL_VERTEX_SHADER_ARB));
- gDeferredSpotLightProgram.mShaderFiles.push_back(make_pair("deferred/multiSpotLightF.glsl", GL_FRAGMENT_SHADER_ARB));
+ gDeferredSpotLightProgram.mShaderFiles.push_back(make_pair(fragment, GL_FRAGMENT_SHADER_ARB));
gDeferredSpotLightProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED];
success = gDeferredSpotLightProgram.createShader(NULL, NULL);
}
if (success)
{
+ std::string fragment;
+
+ if (multisample)
+ {
+ fragment = "deferred/multiSpotLightMSF.glsl";
+ }
+ else
+ {
+ fragment = "deferred/multiSpotLightF.glsl";
+ }
+
gDeferredMultiSpotLightProgram.mName = "Deferred MultiSpotLight Shader";
gDeferredMultiSpotLightProgram.mShaderFiles.clear();
gDeferredMultiSpotLightProgram.mShaderFiles.push_back(make_pair("deferred/pointLightV.glsl", GL_VERTEX_SHADER_ARB));
- gDeferredMultiSpotLightProgram.mShaderFiles.push_back(make_pair("deferred/multiSpotLightF.glsl", GL_FRAGMENT_SHADER_ARB));
+ gDeferredMultiSpotLightProgram.mShaderFiles.push_back(make_pair(fragment, GL_FRAGMENT_SHADER_ARB));
gDeferredMultiSpotLightProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED];
success = gDeferredMultiSpotLightProgram.createShader(NULL, NULL);
}
@@ -1083,11 +1154,25 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
if (gSavedSettings.getBOOL("RenderDeferredSSAO"))
{
- fragment = "deferred/sunLightSSAOF.glsl";
+ if (multisample)
+ {
+ fragment = "deferred/sunlightSSAOMSF.glsl";
+ }
+ else
+ {
+ fragment = "deferred/sunLightSSAOF.glsl";
+ }
}
else
{
- fragment = "deferred/sunLightF.glsl";
+ if (multisample)
+ {
+ fragment = "deferred/sunlightMSF.glsl";
+ }
+ else
+ {
+ fragment = "deferred/sunLightF.glsl";
+ }
}
gDeferredSunProgram.mName = "Deferred Sun Shader";
@@ -1100,10 +1185,21 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
if (success)
{
+ std::string fragment;
+
+ if (multisample)
+ {
+ fragment = "deferred/blurLightMSF.glsl";
+ }
+ else
+ {
+ fragment = "deferred/blurLightF.glsl";
+ }
+
gDeferredBlurLightProgram.mName = "Deferred Blur Light Shader";
gDeferredBlurLightProgram.mShaderFiles.clear();
gDeferredBlurLightProgram.mShaderFiles.push_back(make_pair("deferred/blurLightV.glsl", GL_VERTEX_SHADER_ARB));
- gDeferredBlurLightProgram.mShaderFiles.push_back(make_pair("deferred/blurLightF.glsl", GL_FRAGMENT_SHADER_ARB));
+ gDeferredBlurLightProgram.mShaderFiles.push_back(make_pair(fragment, GL_FRAGMENT_SHADER_ARB));
gDeferredBlurLightProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED];
success = gDeferredBlurLightProgram.createShader(NULL, NULL);
}
@@ -1153,10 +1249,21 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
if (success)
{
+ std::string fragment;
+
+ if (multisample)
+ {
+ fragment = "deferred/softenLightMSF.glsl";
+ }
+ else
+ {
+ fragment = "deferred/softenLightF.glsl";
+ }
+
gDeferredSoftenProgram.mName = "Deferred Soften Shader";
gDeferredSoftenProgram.mShaderFiles.clear();
gDeferredSoftenProgram.mShaderFiles.push_back(make_pair("deferred/softenLightV.glsl", GL_VERTEX_SHADER_ARB));
- gDeferredSoftenProgram.mShaderFiles.push_back(make_pair("deferred/softenLightF.glsl", GL_FRAGMENT_SHADER_ARB));
+ gDeferredSoftenProgram.mShaderFiles.push_back(make_pair(fragment, GL_FRAGMENT_SHADER_ARB));
gDeferredSoftenProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED];
@@ -1239,32 +1346,96 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
if (success)
{
+ std::string fragment;
+ if (multisample)
+ {
+ fragment = "deferred/postDeferredMSF.glsl";
+ }
+ else
+ {
+ fragment = "deferred/postDeferredF.glsl";
+ }
+
gDeferredPostProgram.mName = "Deferred Post Shader";
gDeferredPostProgram.mShaderFiles.clear();
gDeferredPostProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredV.glsl", GL_VERTEX_SHADER_ARB));
- gDeferredPostProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredF.glsl", GL_FRAGMENT_SHADER_ARB));
+ gDeferredPostProgram.mShaderFiles.push_back(make_pair(fragment, GL_FRAGMENT_SHADER_ARB));
gDeferredPostProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED];
success = gDeferredPostProgram.createShader(NULL, NULL);
}
if (success)
{
+ std::string fragment;
+ if (multisample)
+ {
+ fragment = "deferred/postDeferredNoDoFMSF.glsl";
+ }
+ else
+ {
+ fragment = "deferred/postDeferredNoDoFF.glsl";
+ }
+
gDeferredPostNoDoFProgram.mName = "Deferred Post Shader";
gDeferredPostNoDoFProgram.mShaderFiles.clear();
gDeferredPostNoDoFProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredV.glsl", GL_VERTEX_SHADER_ARB));
- gDeferredPostNoDoFProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredNoDoFF.glsl", GL_FRAGMENT_SHADER_ARB));
+ gDeferredPostNoDoFProgram.mShaderFiles.push_back(make_pair(fragment, GL_FRAGMENT_SHADER_ARB));
gDeferredPostNoDoFProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED];
success = gDeferredPostNoDoFProgram.createShader(NULL, NULL);
}
+ if (success)
+ {
+ gDeferredWLSkyProgram.mName = "Deferred Windlight Sky Shader";
+ //gWLSkyProgram.mFeatures.hasGamma = true;
+ gDeferredWLSkyProgram.mShaderFiles.clear();
+ gDeferredWLSkyProgram.mShaderFiles.push_back(make_pair("deferred/skyV.glsl", GL_VERTEX_SHADER_ARB));
+ gDeferredWLSkyProgram.mShaderFiles.push_back(make_pair("deferred/skyF.glsl", GL_FRAGMENT_SHADER_ARB));
+ gDeferredWLSkyProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED];
+ gDeferredWLSkyProgram.mShaderGroup = LLGLSLShader::SG_SKY;
+ success = gDeferredWLSkyProgram.createShader(NULL, &mWLUniforms);
+ }
+
+ if (success)
+ {
+ gDeferredWLCloudProgram.mName = "Deferred Windlight Cloud Program";
+ gDeferredWLCloudProgram.mShaderFiles.clear();
+ gDeferredWLCloudProgram.mShaderFiles.push_back(make_pair("deferred/cloudsV.glsl", GL_VERTEX_SHADER_ARB));
+ gDeferredWLCloudProgram.mShaderFiles.push_back(make_pair("deferred/cloudsF.glsl", GL_FRAGMENT_SHADER_ARB));
+ gDeferredWLCloudProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED];
+ gDeferredWLCloudProgram.mShaderGroup = LLGLSLShader::SG_SKY;
+ success = gDeferredWLCloudProgram.createShader(NULL, &mWLUniforms);
+ }
+
+ if (success)
+ {
+ gDeferredStarProgram.mName = "Deferred Star Program";
+ gDeferredStarProgram.mShaderFiles.clear();
+ gDeferredStarProgram.mShaderFiles.push_back(make_pair("deferred/starsV.glsl", GL_VERTEX_SHADER_ARB));
+ gDeferredStarProgram.mShaderFiles.push_back(make_pair("deferred/starsF.glsl", GL_FRAGMENT_SHADER_ARB));
+ gDeferredStarProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED];
+ gDeferredStarProgram.mShaderGroup = LLGLSLShader::SG_SKY;
+ success = gDeferredStarProgram.createShader(NULL, &mWLUniforms);
+ }
+
if (mVertexShaderLevel[SHADER_DEFERRED] > 1)
{
if (success)
{
+ std::string fragment;
+ if (multisample)
+ {
+ fragment = "deferred/edgeMSF.glsl";
+ }
+ else
+ {
+ fragment = "deferred/edgeF.glsl";
+ }
+
gDeferredEdgeProgram.mName = "Deferred Edge Shader";
gDeferredEdgeProgram.mShaderFiles.clear();
gDeferredEdgeProgram.mShaderFiles.push_back(make_pair("deferred/edgeV.glsl", GL_VERTEX_SHADER_ARB));
- gDeferredEdgeProgram.mShaderFiles.push_back(make_pair("deferred/edgeF.glsl", GL_FRAGMENT_SHADER_ARB));
+ gDeferredEdgeProgram.mShaderFiles.push_back(make_pair(fragment, GL_FRAGMENT_SHADER_ARB));
gDeferredEdgeProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED];
success = gDeferredEdgeProgram.createShader(NULL, NULL);
}
@@ -1272,8 +1443,6 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
if (mVertexShaderLevel[SHADER_DEFERRED] > 2)
{
-
-
if (success)
{
gDeferredPostGIProgram.mName = "Deferred Post GI Shader";
diff --git a/indra/newview/llviewershadermgr.h b/indra/newview/llviewershadermgr.h
index 72ac5e02ee..f5371f0619 100644
--- a/indra/newview/llviewershadermgr.h
+++ b/indra/newview/llviewershadermgr.h
@@ -374,7 +374,9 @@ extern LLGLSLShader gDeferredAttachmentShadowProgram;
extern LLGLSLShader gDeferredAlphaProgram;
extern LLGLSLShader gDeferredFullbrightProgram;
extern LLGLSLShader gDeferredAvatarAlphaProgram;
-
+extern LLGLSLShader gDeferredWLSkyProgram;
+extern LLGLSLShader gDeferredWLCloudProgram;
+extern LLGLSLShader gDeferredStarProgram;
extern LLGLSLShader gLuminanceGatherProgram;
//current avatar shader parameter pointer
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index ec2b5a4c98..dc355362ce 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -4968,19 +4968,6 @@ void LLVOAvatar::resetSpecificJointPosition( const std::string& name )
//-----------------------------------------------------------------------------
void LLVOAvatar::resetJointPositionsToDefault( void )
{
- const LLVector3& avPos = getCharacterPosition();
-
- //Reposition the pelvis
- LLJoint* pPelvis = mRoot.findJoint("mPelvis");
- if ( pPelvis )
- {
- pPelvis->setPosition( avPos + pPelvis->getPosition() );
- }
- else
- {
- llwarns<<"Can't get pelvis joint."<<llendl;
- return;
- }
//Subsequent joints are relative to pelvis
for( S32 i = 0; i < (S32)mNumJoints; ++i )
@@ -4991,7 +4978,7 @@ void LLVOAvatar::resetJointPositionsToDefault( void )
pJoint->setId( LLUUID::null );
//restore joints to default positions, however skip over the pelvis
- if ( pJoint && pPelvis != pJoint )
+ if ( pJoint )
{
pJoint->restoreOldXform();
}
@@ -6028,6 +6015,14 @@ void LLVOAvatar::cleanupAttachedMesh( LLViewerObject* pVO )
if ( bindCnt > 0 )
{
LLVOAvatar::resetJointPositionsToDefault();
+ //Need to handle the repositioning of the cam, updating rig data etc during outfit editing
+ //This handles the case where we detach a replacement rig.
+ if ( gAgentCamera.cameraCustomizeAvatar() )
+ {
+ gAgent.unpauseAnimation();
+ //Still want to refocus on head bone
+ gAgentCamera.changeCameraToCustomizeAvatar();
+ }
}
}
}
diff --git a/indra/newview/llvotree.cpp b/indra/newview/llvotree.cpp
index 8946d4e0b6..3c7fe708e6 100644
--- a/indra/newview/llvotree.cpp
+++ b/indra/newview/llvotree.cpp
@@ -980,11 +980,6 @@ void LLVOTree::appendMesh(LLStrider<LLVector3>& vertices,
for (S32 i = 0; i < index_count; i++)
{
U16 index = index_offset + i;
- if (idx[index] >= vert_start + vert_count ||
- idx[index] < vert_start)
- {
- llerrs << "WTF?" << llendl;
- }
*indices++ = idx[index]-vert_start+cur_idx;
}
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index e9a8c9b80a..cc443d32fb 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -1245,7 +1245,19 @@ BOOL LLVOVolume::calcLOD()
if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_LOD_INFO))
{
- setDebugText(llformat("%.2f:%.2f, %d", debug_distance, radius, cur_detail));
+ //setDebugText(llformat("%.2f:%.2f, %d", debug_distance, radius, cur_detail));
+
+ F32 bin_radius = getBinRadius();
+ F32 node_size = 0.f;
+
+ LLSpatialGroup* group = mDrawable->getSpatialGroup();
+ if (group)
+ {
+ LLSpatialGroup::OctreeNode* node = group->mOctreeNode;
+ node_size = node->getSize()[0];
+ }
+
+ setDebugText(llformat("%.2f:%.2f", bin_radius, node_size));
}
if (cur_detail != mLOD)
@@ -1274,6 +1286,15 @@ BOOL LLVOVolume::updateLOD()
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();
@@ -3187,6 +3208,10 @@ F32 LLVOVolume::getBinRadius()
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();
@@ -3216,6 +3241,8 @@ F32 LLVOVolume::getBinRadius()
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)
{
@@ -3226,24 +3253,19 @@ F32 LLVOVolume::getBinRadius()
}
else if (mDrawable->isStatic())
{
- /*if (mDrawable->getRadius() < 2.0f)
- {
- radius = 16.f;
- }
- else
- {
- radius = llmax(mDrawable->getRadius(), 32.f);
- }*/
-
- radius = (((S32) mDrawable->getRadius())/2+1)*8;
+ 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 = (((S32) (mDrawable->getRadius()*4)+1))*2;
+ radius = llmax((S32) mDrawable->getRadius(),1)*attachment_size_factor;
}
else
{
- radius = 8.f;
+ 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);
@@ -4297,11 +4319,6 @@ void LLVolumeGeometryManager::rebuildMesh(LLSpatialGroup* group)
face->getGeometryVolume(*volume, face->getTEOffset(),
vobj->getRelativeXform(), vobj->getRelativeXformInvTrans(), face->getGeomIndex());
}
-
- if (!face)
- {
- llerrs << "WTF?" << llendl;
- }
}
drawablep->clearState(LLDrawable::REBUILD_ALL);
@@ -4356,10 +4373,7 @@ void LLVolumeGeometryManager::rebuildMesh(LLSpatialGroup* group)
group->clearState(LLSpatialGroup::MESH_DIRTY | LLSpatialGroup::NEW_DRAWINFO);
}
- if (group && group->isState(LLSpatialGroup::NEW_DRAWINFO))
- {
- llerrs << "WTF?" << llendl;
- }
+ llassert(!group || !group->isState(LLSpatialGroup::NEW_DRAWINFO));
}
void LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, std::vector<LLFace*>& faces, BOOL distance_sort)
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
index f64eb89866..fadae7c71e 100644
--- a/indra/newview/pipeline.cpp
+++ b/indra/newview/pipeline.cpp
@@ -392,6 +392,7 @@ void LLPipeline::init()
{
LLMemType mt(LLMemType::MTYPE_PIPELINE_INIT);
+ gOctreeMaxCapacity = gSavedSettings.getU32("OctreeMaxNodeCapacity");
sDynamicLOD = gSavedSettings.getBOOL("RenderDynamicLOD");
sRenderBump = gSavedSettings.getBOOL("RenderObjectBump");
sUseTriStrips = gSavedSettings.getBOOL("RenderUseTriStrips");
@@ -580,11 +581,6 @@ void LLPipeline::allocatePhysicsBuffer()
if (mPhysicsDisplay.getWidth() != resX || mPhysicsDisplay.getHeight() != resY)
{
mPhysicsDisplay.allocate(resX, resY, GL_RGBA, TRUE, FALSE, LLTexUnit::TT_RECT_TEXTURE, FALSE);
- if (mSampleBuffer.getWidth() == mPhysicsDisplay.getWidth() &&
- mSampleBuffer.getHeight() == mPhysicsDisplay.getHeight())
- {
- mPhysicsDisplay.setSampleBuffer(&mSampleBuffer);
- }
}
}
@@ -594,8 +590,9 @@ void LLPipeline::allocateScreenBuffer(U32 resX, U32 resY)
mScreenWidth = resX;
mScreenHeight = resY;
- //never use more than 4 samples for render targets
- U32 samples = llmin(gSavedSettings.getU32("RenderFSAASamples"), (U32) 4);
+ //cap samples at 4 for render targets to avoid out of memory errors
+ U32 samples = gGLManager.getNumFBOFSAASamples(gSavedSettings.getU32("RenderFSAASamples"));
+
if (gGLManager.mIsATI)
{ //disable multisampling of render targets where ATI is involved
samples = 0;
@@ -621,16 +618,22 @@ void LLPipeline::allocateScreenBuffer(U32 resX, U32 resY)
bool gi = LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_DEFERRED);
//allocate deferred rendering color buffers
- mDeferredScreen.allocate(resX, resY, GL_RGBA, TRUE, TRUE, LLTexUnit::TT_RECT_TEXTURE, FALSE);
- mDeferredDepth.allocate(resX, resY, 0, TRUE, FALSE, LLTexUnit::TT_RECT_TEXTURE, FALSE);
+ mDeferredScreen.allocate(resX, resY, GL_RGBA, TRUE, TRUE, LLTexUnit::TT_RECT_TEXTURE, FALSE, samples);
+ mDeferredDepth.allocate(resX, resY, 0, TRUE, FALSE, LLTexUnit::TT_RECT_TEXTURE, FALSE, samples);
addDeferredAttachments(mDeferredScreen);
- mScreen.allocate(resX, resY, GL_RGBA, FALSE, FALSE, LLTexUnit::TT_RECT_TEXTURE, FALSE);
+ mScreen.allocate(resX, resY, GL_RGBA, FALSE, FALSE, LLTexUnit::TT_RECT_TEXTURE, FALSE, samples);
+
+#if LL_DARWIN
+ // As of OS X 10.6.7, Apple doesn't support multiple color formats in a single FBO
+ mEdgeMap.allocate(resX, resY, GL_RGBA, FALSE, FALSE, LLTexUnit::TT_RECT_TEXTURE, FALSE);
+#else
mEdgeMap.allocate(resX, resY, GL_ALPHA, FALSE, FALSE, LLTexUnit::TT_RECT_TEXTURE, FALSE);
+#endif
if (shadow_detail > 0 || ssao)
{ //only need mDeferredLight[0] for shadows OR ssao
- mDeferredLight[0].allocate(resX, resY, GL_RGBA, FALSE, FALSE, LLTexUnit::TT_RECT_TEXTURE);
+ mDeferredLight[0].allocate(resX, resY, GL_RGBA, FALSE, FALSE, LLTexUnit::TT_RECT_TEXTURE, FALSE);
}
else
{
@@ -639,7 +642,7 @@ void LLPipeline::allocateScreenBuffer(U32 resX, U32 resY)
if (ssao)
{ //only need mDeferredLight[1] for ssao
- mDeferredLight[1].allocate(resX, resY, GL_RGBA, FALSE, FALSE, LLTexUnit::TT_RECT_TEXTURE);
+ mDeferredLight[1].allocate(resX, resY, GL_RGBA, FALSE, FALSE, LLTexUnit::TT_RECT_TEXTURE, false);
}
else
{
@@ -648,10 +651,15 @@ void LLPipeline::allocateScreenBuffer(U32 resX, U32 resY)
if (gi)
{ //only need mDeferredLight[2] and mGIMapPost for gi
- mDeferredLight[2].allocate(resX, resY, GL_RGBA, FALSE, FALSE, LLTexUnit::TT_RECT_TEXTURE);
+ mDeferredLight[2].allocate(resX, resY, GL_RGBA, FALSE, FALSE, LLTexUnit::TT_RECT_TEXTURE, false);
for (U32 i = 0; i < 2; i++)
{
+#if LL_DARWIN
+ // As of OS X 10.6.7, Apple doesn't support multiple color formats in a single FBO
+ mGIMapPost[i].allocate(resX,resY, GL_RGBA, FALSE, FALSE, LLTexUnit::TT_RECT_TEXTURE);
+#else
mGIMapPost[i].allocate(resX,resY, GL_RGB, FALSE, FALSE, LLTexUnit::TT_RECT_TEXTURE);
+#endif
}
}
else
@@ -666,8 +674,12 @@ void LLPipeline::allocateScreenBuffer(U32 resX, U32 resY)
F32 scale = gSavedSettings.getF32("RenderShadowResolutionScale");
+#if LL_DARWIN
+ U32 shadow_fmt = 0;
+#else
//HACK: make alpha masking work on ATI depth shadows (work around for ATI driver bug)
U32 shadow_fmt = gGLManager.mIsATI ? GL_ALPHA : 0;
+#endif
if (shadow_detail > 0)
{ //allocate 4 sun shadow maps
@@ -729,35 +741,9 @@ void LLPipeline::allocateScreenBuffer(U32 resX, U32 resY)
mScreen.allocate(resX, resY, GL_RGBA, TRUE, TRUE, LLTexUnit::TT_RECT_TEXTURE, FALSE);
}
- if (LLRenderTarget::sUseFBO && samples > 1)
- {
- mSampleBuffer.allocate(resX,resY,GL_RGBA,TRUE,TRUE,LLTexUnit::TT_RECT_TEXTURE,FALSE,samples);
- if (LLPipeline::sRenderDeferred)
- {
- addDeferredAttachments(mSampleBuffer);
- mDeferredScreen.setSampleBuffer(&mSampleBuffer);
- mEdgeMap.setSampleBuffer(&mSampleBuffer);
- }
-
- mScreen.setSampleBuffer(&mSampleBuffer);
-
- stop_glerror();
- }
- else
- {
- mSampleBuffer.release();
- }
-
if (LLPipeline::sRenderDeferred)
{ //share depth buffer between deferred targets
mDeferredScreen.shareDepthBuffer(mScreen);
- for (U32 i = 0; i < 3; i++)
- { //share stencil buffer with screen space lightmap to stencil out sky
- if (mDeferredLight[i].getTexture(0))
- {
- mDeferredScreen.shareDepthBuffer(mDeferredLight[i]);
- }
- }
}
gGL.getTexUnit(0)->disable();
@@ -826,7 +812,6 @@ void LLPipeline::releaseGLBuffers()
mScreen.release();
mPhysicsDisplay.release();
mUIScreen.release();
- mSampleBuffer.release();
mDeferredScreen.release();
mDeferredDepth.release();
for (U32 i = 0; i < 3; i++)
@@ -2535,6 +2520,32 @@ void LLPipeline::markGLRebuild(LLGLUpdate* glu)
}
}
+void LLPipeline::markPartitionMove(LLDrawable* drawable)
+{
+ if (!drawable->isState(LLDrawable::PARTITION_MOVE) &&
+ !drawable->getPositionGroup().equals3(LLVector4a::getZero()))
+ {
+ drawable->setState(LLDrawable::PARTITION_MOVE);
+ mPartitionQ.push_back(drawable);
+ }
+}
+
+void LLPipeline::processPartitionQ()
+{
+ for (LLDrawable::drawable_list_t::iterator iter = mPartitionQ.begin(); iter != mPartitionQ.end(); ++iter)
+ {
+ LLDrawable* drawable = *iter;
+ if (!drawable->isDead())
+ {
+ drawable->updateBinRadius();
+ drawable->movePartition();
+ }
+ drawable->clearState(LLDrawable::PARTITION_MOVE);
+ }
+
+ mPartitionQ.clear();
+}
+
void LLPipeline::markRebuild(LLSpatialGroup* group, BOOL priority)
{
LLMemType mt(LLMemType::MTYPE_PIPELINE);
@@ -5904,7 +5915,6 @@ LLSpatialPartition* LLPipeline::getSpatialPartition(LLViewerObject* vobj)
return NULL;
}
-
void LLPipeline::resetVertexBuffers(LLDrawable* drawable)
{
if (!drawable || drawable->isDead())
@@ -6180,11 +6190,8 @@ void LLPipeline::renderBloom(BOOL for_snapshot, F32 zoom_factor, int subfield)
gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT);
gGL.setSceneBlendType(LLRender::BT_ADD_WITH_ALPHA);
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
- gGL.getTexUnit(0)->disable();
- gGL.getTexUnit(0)->enable(LLTexUnit::TT_RECT_TEXTURE);
- gGL.getTexUnit(0)->bind(&mScreen);
-
+ mScreen.bindTexture(0, 0);
+
gGL.color4f(1,1,1,1);
gPipeline.enableLightsFullbright(LLColor4(1,1,1,1));
gGL.begin(LLRender::TRIANGLE_STRIP);
@@ -6199,7 +6206,7 @@ void LLPipeline::renderBloom(BOOL for_snapshot, F32 zoom_factor, int subfield)
gGL.end();
- gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE);
+ gGL.getTexUnit(0)->unbind(mScreen.getUsage());
mGlow[2].flush();
}
@@ -6227,7 +6234,6 @@ void LLPipeline::renderBloom(BOOL for_snapshot, F32 zoom_factor, int subfield)
for (S32 i = 0; i < kernel; i++)
{
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
{
LLFastTimer ftm(FTM_RENDER_BLOOM_FBO);
mGlow[i%2].bindTarget();
@@ -6420,11 +6426,10 @@ void LLPipeline::renderBloom(BOOL for_snapshot, F32 zoom_factor, int subfield)
shader->uniform1f("magnification", magnification);
}
- S32 channel = shader->enableTexture(LLViewerShaderMgr::DEFERRED_DIFFUSE, LLTexUnit::TT_RECT_TEXTURE);
+ S32 channel = shader->enableTexture(LLViewerShaderMgr::DEFERRED_DIFFUSE, mScreen.getUsage());
if (channel > -1)
{
mScreen.bindTexture(0, channel);
- gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR);
}
//channel = shader->enableTexture(LLViewerShaderMgr::DEFERRED_DEPTH, LLTexUnit::TT_RECT_TEXTURE);
//if (channel > -1)
@@ -6567,21 +6572,21 @@ void LLPipeline::bindDeferredShader(LLGLSLShader& shader, U32 light_index, LLRen
shader.bind();
S32 channel = 0;
- channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_DIFFUSE, LLTexUnit::TT_RECT_TEXTURE);
+ channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_DIFFUSE, mDeferredScreen.getUsage());
if (channel > -1)
{
mDeferredScreen.bindTexture(0,channel);
gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_POINT);
}
- channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_SPECULAR, LLTexUnit::TT_RECT_TEXTURE);
+ channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_SPECULAR, mDeferredScreen.getUsage());
if (channel > -1)
{
mDeferredScreen.bindTexture(1, channel);
gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_POINT);
}
- channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_NORMAL, LLTexUnit::TT_RECT_TEXTURE);
+ channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_NORMAL, mDeferredScreen.getUsage());
if (channel > -1)
{
mDeferredScreen.bindTexture(2, channel);
@@ -6704,22 +6709,16 @@ void LLPipeline::bindDeferredShader(LLGLSLShader& shader, U32 light_index, LLRen
shader.uniformMatrix4fv("gi_norm_mat", 1, FALSE, mGINormalMatrix.m);
}
}
-
- /*channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_POSITION, LLTexUnit::TT_RECT_TEXTURE);
- if (channel > -1)
- {
- mDeferredScreen.bindTexture(3, channel);
- }*/
+ stop_glerror();
- channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_DEPTH, LLTexUnit::TT_RECT_TEXTURE);
+ channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_DEPTH, mDeferredDepth.getUsage());
if (channel > -1)
{
gGL.getTexUnit(channel)->bind(&mDeferredDepth, TRUE);
- gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_POINT);
stop_glerror();
- glTexParameteri(LLTexUnit::getInternalType(mDeferredDepth.getUsage()), GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
- glTexParameteri(LLTexUnit::getInternalType(mDeferredDepth.getUsage()), GL_DEPTH_TEXTURE_MODE_ARB, GL_ALPHA);
+ //glTexParameteri(LLTexUnit::getInternalType(mDeferredDepth.getUsage()), GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
+ //glTexParameteri(LLTexUnit::getInternalType(mDeferredDepth.getUsage()), GL_DEPTH_TEXTURE_MODE_ARB, GL_ALPHA);
stop_glerror();
@@ -6748,7 +6747,7 @@ void LLPipeline::bindDeferredShader(LLGLSLShader& shader, U32 light_index, LLRen
stop_glerror();
- channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_LIGHT, LLTexUnit::TT_RECT_TEXTURE);
+ channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_LIGHT, mDeferredLight[light_index].getUsage());
if (channel > -1)
{
mDeferredLight[light_index].bindTexture(0, channel);
@@ -6968,9 +6967,9 @@ void LLPipeline::renderDeferredLighting()
}
//ati doesn't seem to love actually using the stencil buffer on FBO's
- LLGLEnable stencil(GL_STENCIL_TEST);
- glStencilFunc(GL_EQUAL, 1, 0xFFFFFFFF);
- glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+ LLGLDisable stencil(GL_STENCIL_TEST);
+ //glStencilFunc(GL_EQUAL, 1, 0xFFFFFFFF);
+ //glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
gGL.setColorMask(true, true);
@@ -7772,33 +7771,41 @@ void LLPipeline::setupSpotLight(LLGLSLShader& shader, LLDrawable* drawablep)
LLViewerTexture* img = volume->getLightTexture();
+ if (img == NULL)
+ {
+ img = LLViewerFetchedTexture::sWhiteImagep;
+ }
+
S32 channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_PROJECTION);
- if (channel > -1 && img)
+ if (channel > -1)
{
- gGL.getTexUnit(channel)->bind(img);
+ if (img)
+ {
+ gGL.getTexUnit(channel)->bind(img);
- F32 lod_range = logf(img->getWidth())/logf(2.f);
+ F32 lod_range = logf(img->getWidth())/logf(2.f);
- shader.uniform1f("proj_focus", focus);
- shader.uniform1f("proj_lod", lod_range);
- shader.uniform1f("proj_ambient_lod", llclamp((proj_range-focus)/proj_range*lod_range, 0.f, 1.f));
+ shader.uniform1f("proj_focus", focus);
+ shader.uniform1f("proj_lod", lod_range);
+ shader.uniform1f("proj_ambient_lod", llclamp((proj_range-focus)/proj_range*lod_range, 0.f, 1.f));
+ }
}
+
}
void LLPipeline::unbindDeferredShader(LLGLSLShader &shader)
{
stop_glerror();
- shader.disableTexture(LLViewerShaderMgr::DEFERRED_POSITION, LLTexUnit::TT_RECT_TEXTURE);
- shader.disableTexture(LLViewerShaderMgr::DEFERRED_NORMAL, LLTexUnit::TT_RECT_TEXTURE);
- shader.disableTexture(LLViewerShaderMgr::DEFERRED_DIFFUSE, LLTexUnit::TT_RECT_TEXTURE);
- shader.disableTexture(LLViewerShaderMgr::DEFERRED_SPECULAR, LLTexUnit::TT_RECT_TEXTURE);
- shader.disableTexture(LLViewerShaderMgr::DEFERRED_DEPTH, LLTexUnit::TT_RECT_TEXTURE);
- shader.disableTexture(LLViewerShaderMgr::DEFERRED_LIGHT, LLTexUnit::TT_RECT_TEXTURE);
+ shader.disableTexture(LLViewerShaderMgr::DEFERRED_NORMAL, mDeferredScreen.getUsage());
+ shader.disableTexture(LLViewerShaderMgr::DEFERRED_DIFFUSE, mDeferredScreen.getUsage());
+ shader.disableTexture(LLViewerShaderMgr::DEFERRED_SPECULAR, mDeferredScreen.getUsage());
+ shader.disableTexture(LLViewerShaderMgr::DEFERRED_DEPTH, mDeferredScreen.getUsage());
+ shader.disableTexture(LLViewerShaderMgr::DEFERRED_LIGHT, mDeferredLight[0].getUsage());
shader.disableTexture(LLViewerShaderMgr::DEFERRED_GI_LIGHT, LLTexUnit::TT_RECT_TEXTURE);
- shader.disableTexture(LLViewerShaderMgr::DEFERRED_EDGE, LLTexUnit::TT_RECT_TEXTURE);
- shader.disableTexture(LLViewerShaderMgr::DEFERRED_SUN_LIGHT, LLTexUnit::TT_RECT_TEXTURE);
- shader.disableTexture(LLViewerShaderMgr::DEFERRED_LOCAL_LIGHT, LLTexUnit::TT_RECT_TEXTURE);
+ shader.disableTexture(LLViewerShaderMgr::DEFERRED_EDGE, mEdgeMap.getUsage());
+ shader.disableTexture(LLViewerShaderMgr::DEFERRED_SUN_LIGHT, mDeferredLight[1].getUsage());
+ shader.disableTexture(LLViewerShaderMgr::DEFERRED_LOCAL_LIGHT, mDeferredLight[2].getUsage());
shader.disableTexture(LLViewerShaderMgr::DEFERRED_LUMINANCE);
shader.disableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
shader.disableTexture(LLViewerShaderMgr::DEFERRED_GI_MIP);
@@ -9394,6 +9401,11 @@ void LLPipeline::generateSunShadow(LLCamera& camera)
mShadow[i+4].flush();
}
}
+ else
+ { //no spotlight shadows
+ mShadowSpotLight[0] = mShadowSpotLight[1] = NULL;
+ }
+
if (!gSavedSettings.getBOOL("CameraOffset"))
{
diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h
index e9a250cd6d..e967b8c31e 100644
--- a/indra/newview/pipeline.h
+++ b/indra/newview/pipeline.h
@@ -157,7 +157,8 @@ public:
void markGLRebuild(LLGLUpdate* glu);
void markRebuild(LLSpatialGroup* group, BOOL priority = FALSE);
void markRebuild(LLDrawable *drawablep, LLDrawable::EDrawableFlags flag = LLDrawable::REBUILD_ALL, BOOL priority = FALSE);
-
+ void markPartitionMove(LLDrawable* drawablep);
+
//get the object between start and end that's closest to start.
LLViewerObject* lineSegmentIntersectInWorld(const LLVector3& start, const LLVector3& end,
BOOL pick_transparent,
@@ -211,6 +212,7 @@ public:
void updateCull(LLCamera& camera, LLCullResult& result, S32 water_clip = 0, LLPlane* plane = NULL); //if water_clip is 0, ignore water plane, 1, cull to above plane, -1, cull to below plane
void createObjects(F32 max_dtime);
void createObject(LLViewerObject* vobj);
+ void processPartitionQ();
void updateGeom(F32 max_dtime);
void updateGL();
void rebuildPriorityGroups();
@@ -524,7 +526,6 @@ public:
LLRenderTarget mEdgeMap;
LLRenderTarget mDeferredDepth;
LLRenderTarget mDeferredLight[3];
- LLMultisampleBuffer mSampleBuffer;
LLRenderTarget mGIMap;
LLRenderTarget mGIMapPost[2];
LLRenderTarget mLuminanceMap;
@@ -637,6 +638,9 @@ protected:
LLDrawable::drawable_list_t mBuildQ2; // non-priority
LLSpatialGroup::sg_vector_t mGroupQ1; //priority
LLSpatialGroup::sg_vector_t mGroupQ2; // non-priority
+
+ LLDrawable::drawable_list_t mPartitionQ; //drawables that need to update their spatial partition radius
+
bool mGroupQ2Locked;
bool mGroupQ1Locked;
diff --git a/indra/newview/skins/default/xui/en/floater_model_preview.xml b/indra/newview/skins/default/xui/en/floater_model_preview.xml
index d08c3e7078..dce55dae12 100644
--- a/indra/newview/skins/default/xui/en/floater_model_preview.xml
+++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml
@@ -26,7 +26,7 @@
<text left="15" bottom="25" follows="top|left" height="15" name="name_label">
Name:
</text>
- <line_editor bottom_delta="20" follows="top|left|right" height="19"
+ <line_editor bottom_delta="20" follows="top|left|right" height="19" max_length_bytes="64"
name="description_form" prevalidate_callback="ascii" width="290" />
<text bottom_delta="20" left="15" follows="left|top" height="15" name="lod_label">
@@ -219,7 +219,7 @@
<text left="10" top_pad="35" follows="top|left" width="240" height="15">
Generate Normals
</text>
- <text left="35" top_pad="5" follows="top|left" width="100" height="15">
+ <text left="35" top_pad="5" follows="top|left" width="100" height="15" name="crease_label">
Crease Angle:
</text>
<spinner follows="top|left" left_pad="5" min_val="0" max_val="180" value="75" width="60" height="20" name="crease_angle"/>
diff --git a/indra/newview/skins/default/xui/en/floater_model_wizard.xml b/indra/newview/skins/default/xui/en/floater_model_wizard.xml
index 92d57b20be..47b2e5fd79 100644
--- a/indra/newview/skins/default/xui/en/floater_model_wizard.xml
+++ b/indra/newview/skins/default/xui/en/floater_model_wizard.xml
@@ -572,9 +572,9 @@ Advanced users familiar with 3d content creation tools may prefer to use the [se
bg_opaque_color="DkGray2"
background_visible="true"
background_opaque="true">
- <text top="25" left="30" text_color="White" font="SansSerifSmallBold" width="300" height="4">Performance</text>
+ <text top="15" left="20" text_color="White" font="SansSerifSmallBold" width="110" height="30" wrap="true" halign="center">Higher Performance</text>
<text top="45" left="10" halign="center" width="130" word_wrap="true" font="SansSerifSmall" height="80">Faster rendering but less detailed; lowers Resource (prim) cost.</text>
- <text top="25" left="390" text_color="White" font="SansSerifSmallBold" width="300" height="4">Accuracy</text>
+ <text top="15" left="372" text_color="White" font="SansSerifSmallBold" width="90" height="30" wrap="true" halign="center">Higher Accuracy</text>
<text top="45" left="360" halign="center" width="130" word_wrap="true" font="SansSerifSmall" height="80">More detailed model but slower; increases Resource (prim) cost.</text>
<slider
diff --git a/indra/newview/skins/default/xui/en/floater_tools.xml b/indra/newview/skins/default/xui/en/floater_tools.xml
index 05d47506db..40bf7bfed7 100644
--- a/indra/newview/skins/default/xui/en/floater_tools.xml
+++ b/indra/newview/skins/default/xui/en/floater_tools.xml
@@ -1608,41 +1608,6 @@ even though the user gets a free copy.
name="Sculpted"
value="Sculpted" />
</combo_box>
- <combo_box
- height="19"
- layout="topleft"
- name="material"
- top_pad="5"
- width="150">
- <combo_box.item
- label="Stone"
- name="Stone"
- value="Stone" />
- <combo_box.item
- label="Metal"
- name="Metal"
- value="Metal" />
- <combo_box.item
- label="Glass"
- name="Glass"
- value="Glass" />
- <combo_box.item
- label="Wood"
- name="Wood"
- value="Wood" />
- <combo_box.item
- label="Flesh"
- name="Flesh"
- value="Flesh" />
- <combo_box.item
- label="Plastic"
- name="Plastic"
- value="Plastic" />
- <combo_box.item
- label="Rubber"
- name="Rubber"
- value="Rubber" />
- </combo_box>
<text
type="string"
length="1"
@@ -2426,6 +2391,41 @@ even though the user gets a free copy.
name="Physics Shape Type Combo Ctrl"
tool_tip="Choose the physics shape type"
width="108"/>
+ <combo_box
+ height="19"
+ layout="topleft"
+ name="material"
+ top_pad="5"
+ width="150">
+ <combo_box.item
+ label="Stone"
+ name="Stone"
+ value="Stone" />
+ <combo_box.item
+ label="Metal"
+ name="Metal"
+ value="Metal" />
+ <combo_box.item
+ label="Glass"
+ name="Glass"
+ value="Glass" />
+ <combo_box.item
+ label="Wood"
+ name="Wood"
+ value="Wood" />
+ <combo_box.item
+ label="Flesh"
+ name="Flesh"
+ value="Flesh" />
+ <combo_box.item
+ label="Plastic"
+ name="Plastic"
+ value="Plastic" />
+ <combo_box.item
+ label="Rubber"
+ name="Rubber"
+ value="Rubber" />
+ </combo_box>
<spinner
follows="left|top"
@@ -2441,16 +2441,6 @@ even though the user gets a free copy.
top_pad="10"
width="132" />
- <check_box
- height="19"
- label="Override material"
- layout="topleft"
- left_delta="0"
- name="Physics Material Override"
- tool_tip="Override Material"
- top_pad="10"
- width="132" />
-
<spinner
follows="left|top"
height="19"
diff --git a/indra/newview/skins/default/xui/en/menu_inventory_add.xml b/indra/newview/skins/default/xui/en/menu_inventory_add.xml
index 484af63097..b36b82ebd8 100644
--- a/indra/newview/skins/default/xui/en/menu_inventory_add.xml
+++ b/indra/newview/skins/default/xui/en/menu_inventory_add.xml
@@ -42,7 +42,7 @@
<menu_item_call.on_enable
function="File.EnableUpload" />
</menu_item_call>
- <menu_item_call
+ <menu_item_call
label="Model..."
layout="topleft"
name="Upload Model">
@@ -54,6 +54,18 @@
<menu_item_call.on_visible
function="File.VisibleUploadModel"/>
</menu_item_call>
+ <menu_item_call
+ label="Model Wizard..."
+ layout="topleft"
+ name="Upload Model Wizard">
+ <menu_item_call.on_click
+ function="Floater.Show"
+ parameter="upload_model_wizard" />
+ <menu_item_call.on_enable
+ function="File.EnableUploadModel" />
+ <menu_item_call.on_visible
+ function="File.VisibleUploadModel"/>
+ </menu_item_call>
<menu_item_call
label="Bulk (L$[COST] per file)..."
layout="topleft"
diff --git a/indra/newview/skins/default/xui/en/panel_region_general.xml b/indra/newview/skins/default/xui/en/panel_region_general.xml
index e0d9f3f714..3f9195d092 100644
--- a/indra/newview/skins/default/xui/en/panel_region_general.xml
+++ b/indra/newview/skins/default/xui/en/panel_region_general.xml
@@ -134,6 +134,7 @@
top="190"
width="80" />
<check_box
+ visible="FALSE"
height="20"
label="Allow Mesh Objects"
layout="topleft"