diff options
Diffstat (limited to 'indra/newview')
32 files changed, 4070 insertions, 4050 deletions
| diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 597fe8d7fa..bef2a09e6c 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -1,3923 +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; - -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]; - -	//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(¶ms); -	} -	 -	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(¶ms); -	} -	 -	LLConvexDecomposition* decomp = LLConvexDecomposition::getInstance(); - -	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(); -	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); -		} -	} -} +/** 
 + * @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(¶ms);
 +	}
 +	
 +	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(¶ms);
 +	}
 +	
 +	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/skins/default/xui/da/panel_media_settings_security.xml b/indra/newview/skins/default/xui/da/panel_media_settings_security.xml index 278fe0eeea..1b64888271 100644 --- a/indra/newview/skins/default/xui/da/panel_media_settings_security.xml +++ b/indra/newview/skins/default/xui/da/panel_media_settings_security.xml @@ -2,7 +2,8 @@  <panel label="Sikkerhed" name="Media Settings Security">  	<check_box initial_value="false" label="Tillad kun adgang til angivne URL mønstre" name="whitelist_enable"/>  	<text name="home_url_fails_some_items_in_whitelist"> -		Angivelser som hjemmesiden fejler imod er markeret: +		Angivelser som hjemmesiden fejler imod er  +markeret:  	</text>  	<button label="Tilføj" name="whitelist_add"/>  	<button label="Slet" name="whitelist_del"/> diff --git a/indra/newview/skins/default/xui/de/floater_buy_land.xml b/indra/newview/skins/default/xui/de/floater_buy_land.xml index 5369155cf9..ca4ee8981b 100644 --- a/indra/newview/skins/default/xui/de/floater_buy_land.xml +++ b/indra/newview/skins/default/xui/de/floater_buy_land.xml @@ -127,41 +127,41 @@ unterstützt [AMOUNT2] Objekte  	<text name="region_name_label">  		Region:  	</text> -	<text left="680" left_delta="140" name="region_name_text"> +	<text name="region_name_text">  		(unbekannt)  	</text>  	<text name="region_type_label">  		Typ:  	</text> -	<text left="680" left_delta="140" name="region_type_text"> +	<text name="region_type_text">  		(unbekannt)  	</text>  	<text name="estate_name_label">  		Grundbesitz:  	</text> -	<text left="680" left_delta="140" name="estate_name_text"> +	<text name="estate_name_text">  		(unbekannt)  	</text> -	<text name="estate_owner_label" right="600" width="200"> +	<text name="estate_owner_label">  		Grundbesitzer:  	</text> -	<text left="680" left_delta="140" name="estate_owner_text"> +	<text name="estate_owner_text">  		(unbekannt)  	</text> -	<text left="410" name="resellable_changeable_label"> +	<text name="resellable_changeable_label">  		Gekauftes Land in dieser Region:  	</text> -	<text left="410" name="resellable_clause"> +	<text name="resellable_clause">  		Wiederverkauf möglich oder nicht möglich.  	</text> -	<text left="410" name="changeable_clause"> +	<text name="changeable_clause">  		Darft oder darf nicht zusammengelegt/unterteilt werden.  	</text> -	<text left="410" name="covenant_text"> +	<text name="covenant_text">  		Sie müssen dem Grundbesitzvertrag zustimmen:  	</text> -	<text left="470" name="covenant_timestamp_text"/> -	<text_editor left="470" name="covenant_editor"> +	<text name="covenant_timestamp_text"/> +	<text_editor name="covenant_editor">  		Wird geladen...  	</text_editor>  	<check_box label="Ich stimme dem obigen Vertrag zu." name="agree_covenant"/> @@ -191,7 +191,7 @@ Objekte im Verkauf eingeschlossen  	<text name="error_message">  		Irgendetwas stimmt nicht.  	</text> -	<button label="Gehe zu Website" name="error_web" top_delta="136"/> +	<button label="Gehe zu Website" name="error_web" />  	<text name="account_action">  		Macht Sie zum Premium-Mitglied.  	</text> @@ -203,9 +203,8 @@ Objekte im Verkauf eingeschlossen  		<combo_box.item label="7,50 US$/Monat, vierteljährliche Abrechnung" name="US$7.50/month,billedquarterly"/>  		<combo_box.item label="6,00 US$/Monat, jährliche Abrechnung" name="US$6.00/month,billedannually"/>  	</combo_box> -	<text height="36" name="land_use_action" top="270"> -		Erhöhen Sie Ihre monatlichen Landnutzungsgebühren  -auf 40 US$/month. +	<text name="land_use_action"> +		Erhöhen Sie Ihre monatlichen Landnutzungsgebühren auf 40 US$/month.  	</text>  	<text name="land_use_reason">  		Sie besitzen 1309 m² Land. diff --git a/indra/newview/skins/default/xui/de/floater_sell_land.xml b/indra/newview/skins/default/xui/de/floater_sell_land.xml index 8f67fae464..646138eaad 100644 --- a/indra/newview/skins/default/xui/de/floater_sell_land.xml +++ b/indra/newview/skins/default/xui/de/floater_sell_land.xml @@ -5,16 +5,16 @@  			<text name="info_parcel_label">  				Parzelle:  			</text> -			<text bottom_delta="-5" height="16" name="info_parcel" left="70"> +			<text name="info_parcel">  				PARZELLENNAME  			</text>  			<text name="info_size_label">  				Größe:  			</text> -			<text bottom_delta="-21" height="32" name="info_size" left="70"> +			<text name="info_size">  				[AREA] m².  			</text> -			<text bottom_delta="-57" height="28" name="info_action"> +			<text name="info_action">  				Zum Verkauf dieser Parzelle:  			</text>  			<text name="price_label"> @@ -32,13 +32,13 @@  			<text name="price_per_m">  				([PER_METER] L$ pro m²)  			</text> -			<text bottom_delta="38" left="72" name="sell_to_label" right="-20"> +			<text name="sell_to_label">  				2. Land verkaufen an:  			</text> -			<text bottom_delta="-16" height="16" left="72" name="sell_to_text" right="-10"> +			<text name="sell_to_text">  				Offener Verkauf oder Verkauf an bestimmte Person?  			</text> -			<combo_box bottom_delta="-32" height="16" left="72" name="sell_to" width="140"> +			<combo_box name="sell_to">  				<combo_box.item label="-- Auswählen --" name="--selectone--"/>  				<combo_box.item label="An jeden" name="Anyone"/>  				<combo_box.item label="An bestimmte Person:" name="Specificuser:"/> @@ -50,15 +50,15 @@  			<text name="sell_objects_text">  				Die transferierbaren Landeigentümer-Objekte auf der Parzelle wechseln den Eigentümer.  			</text> -			<radio_group bottom_delta="-58" name="sell_objects"> +			<radio_group name="sell_objects">  				<radio_item label="Nein, Objekte behalten" name="no"/>  				<radio_item label="Ja, Objekte mit Land verkaufen" name="yes"/>  			</radio_group> -			<button label="Objekte anzeigen" name="show_objects" width="116"/> +			<button label="Objekte anzeigen" name="show_objects"/>  			<text name="nag_message_label">  				ACHTUNG: Verkäufe sind endgültig.  			</text> -			<button label="Zum Verkauf freigeben" name="sell_btn" width="180"/> +			<button label="Zum Verkauf freigeben" name="sell_btn"/>  			<button label="Abbrechen" name="cancel_btn"/>  		</panel>  	</scroll_container> diff --git a/indra/newview/skins/default/xui/de/floater_top_objects.xml b/indra/newview/skins/default/xui/de/floater_top_objects.xml index dad550227e..d2055a53db 100644 --- a/indra/newview/skins/default/xui/de/floater_top_objects.xml +++ b/indra/newview/skins/default/xui/de/floater_top_objects.xml @@ -39,21 +39,18 @@  	<text name="id_text">  		Objekt-ID:  	</text> -	<line_editor bg_readonly_color="clear" bottom_delta="3" enabled="false" follows="left|bottom|right" font="SansSerifSmall" height="20" left="80" name="id_editor" text_readonly_color="white" width="244"/> -	<button bottom_delta="0" follows="bottom|right" height="20" label="Beacon anzeigen" name="show_beacon_btn" right="-10" width="110"/> +	<button label="Beacon anzeigen" name="show_beacon_btn"/>  	<text name="obj_name_text">  		Objektname: -	</text> -	<line_editor bg_readonly_color="clear" bottom_delta="3" enabled="false" follows="left|bottom|right" font="SansSerifSmall" height="20" left="80" name="object_name_editor" text_readonly_color="white" width="244"/> -	<button bottom_delta="0" follows="bottom|right" height="20" label="Filter" name="filter_object_btn" right="-10" width="110"/> +	</text>	 +	<button label="Filter" name="filter_object_btn"/>  	<text name="owner_name_text">  		Eigentümer: -	</text> -	<line_editor bg_readonly_color="clear" bottom_delta="3" enabled="true" follows="left|bottom|right" font="SansSerifSmall" height="20" left="106" name="owner_name_editor" text_readonly_color="white" width="218"/> -	<button bottom_delta="0" follows="bottom|right" height="20" label="Filter" name="filter_owner_btn" right="-10" width="110"/> -	<button bottom_delta="0" follows="bottom|right" height="20" label="Aktualisieren" name="refresh_btn" right="-10" width="110"/> -	<button bottom="35" follows="bottom|left" height="20" label="Auswahl zurückgeben" left="10" name="return_selected_btn" width="134"/> -	<button bottom="35" follows="bottom|left" height="20" label="Alle zurückgeben" left="150" name="return_all_btn" width="134"/> -	<button bottom="10" follows="bottom|left" height="20" label="Auswahl deaktivieren" left="10" name="disable_selected_btn" width="134"/> -	<button bottom="10" follows="bottom|left" height="20" label="Alle deaktivieren" left="150" name="disable_all_btn" width="134"/> +	</text>	 +	<button label="Filter" name="filter_owner_btn"/> +	<button label="Aktualisieren" name="refresh_btn"/> +	<button label="Auswahl zurückgeben" name="return_selected_btn" width="134"/> +	<button label="Alle zurückgeben" left="150" name="return_all_btn" width="134"/> +	<button label="Auswahl deaktivieren" name="disable_selected_btn" width="134"/> +	<button label="Alle deaktivieren" left="150" name="disable_all_btn" width="134"/>  </floater> diff --git a/indra/newview/skins/default/xui/de/floater_water.xml b/indra/newview/skins/default/xui/de/floater_water.xml index 097a60a444..bb0dd9c75d 100644 --- a/indra/newview/skins/default/xui/de/floater_water.xml +++ b/indra/newview/skins/default/xui/de/floater_water.xml @@ -3,9 +3,10 @@  	<floater.string name="WLDefaultWaterNames">  		Default:Glassy:Pond:Murky:Second Plague:SNAKE!!!:Valdez  	</floater.string> -	<text name="KeyFramePresetsText" width="116"> +	<text name="KeyFramePresetsText" width="110">  		Voreinstellungen:  	</text> +	<combo_box left_delta="110" name="WaterPresetsCombo"/>  	<button label="Neu" label_selected="Neu" name="WaterNewPreset"/>  	<button label="Speichern" label_selected="Speichern" name="WaterSavePreset"/>  	<button label="Löschen" label_selected="Löschen" name="WaterDeletePreset"/> diff --git a/indra/newview/skins/default/xui/en/floater_buy_land.xml b/indra/newview/skins/default/xui/en/floater_buy_land.xml index d5d4565ca1..ab81a86720 100644 --- a/indra/newview/skins/default/xui/en/floater_buy_land.xml +++ b/indra/newview/skins/default/xui/en/floater_buy_land.xml @@ -204,7 +204,7 @@ supports [AMOUNT2] objects       follows="left|top"       height="14"       layout="topleft" -     left_delta="110" +     left_delta="125"       name="region_name_text"       top_delta="0"       use_ellipses="true" @@ -237,7 +237,7 @@ supports [AMOUNT2] objects       follows="left|top"       height="14"       layout="topleft" -     left_delta="110" +     left_delta="125"       name="region_type_text"       top_delta="0"       width="175"> @@ -262,7 +262,7 @@ supports [AMOUNT2] objects       follows="left|top"       height="14"       layout="topleft" -     left_delta="110" +     left_delta="125"       name="estate_name_text"       top_delta="0"       width="175"> @@ -276,8 +276,8 @@ supports [AMOUNT2] objects       layout="topleft"       left="440"       name="estate_owner_label" -     right="550" -     width="100" +     right="565" +     width="120"       word_wrap="true">          Estate Owner:      </text> @@ -287,7 +287,7 @@ supports [AMOUNT2] objects       follows="left|top"       height="20"       layout="topleft" -     left_delta="110" +     left_delta="125"       name="estate_owner_text"       top_delta="0"       width="175"> diff --git a/indra/newview/skins/default/xui/en/floater_sell_land.xml b/indra/newview/skins/default/xui/en/floater_sell_land.xml index 619669d28a..38b305db7e 100644 --- a/indra/newview/skins/default/xui/en/floater_sell_land.xml +++ b/indra/newview/skins/default/xui/en/floater_sell_land.xml @@ -41,14 +41,14 @@       follows="top|left"       left="16"       name="info_parcel_label" -     width="48"> +     width="70">          Parcel:      </text>      <text       top_delta="0"       follows="top|left"       height="16" -     left="56" +     left="78"       name="info_parcel"       right="-20">          PARCEL NAME @@ -57,14 +57,14 @@       follows="top|left"       left="16"       name="info_size_label" -     width="48"> +     width="70">          Size:      </text>      <text       follows="top|left"       top_delta="0"       height="32" -     left="56" +     left="78"       name="info_size"       right="-20">          [AREA] m² @@ -164,14 +164,14 @@       left_delta="0"       name="sell_to_agent"       top_pad="4" -     width="170" /> +     width="150" />      <button       height="20"       label="Select"       left_pad="5"       name="sell_to_select_agent"       top_delta="0" -     width="60" /> +     width="90" />      <text       follows="top|left"       font="SansSerif" @@ -199,20 +199,20 @@          <radio_item           bottom="40"           height="0" -         left="10" +         left="2"           name="none"           visible="false" />          <radio_item           top_pad="10" -         height="16" +         height="18"           label="No, keep ownership of objects" -         left="10" +         left="2"           name="no" />          <radio_item           top_pad="10"           height="16"           label="Yes, sell objects with land" -         left="10" +         left="2"           name="yes" />      </radio_group>      <button @@ -221,7 +221,7 @@       name="show_objects"       left="70"       top_pad="10" -     width="110" /> +     width="140" />      <text       bottom_delta="30"       follows="top|left" @@ -229,7 +229,7 @@       height="16"       left="16"       name="nag_message_label" -     right="-20"> +     right="-5">          REMEMBER: All sales are final.      </text>      <button @@ -239,15 +239,15 @@       left_delta="0"       name="sell_btn"       top_pad="10" -     width="130" /> +     width="185" />      <button       follows="bottom|left"       height="20"       label="Cancel" -     left_pad="30" +     left_pad="5"       name="cancel_btn"       top_delta="0" -     width="90" /> +     width="85" />      </panel>      </scroll_container>  </floater> diff --git a/indra/newview/skins/default/xui/en/floater_snapshot.xml b/indra/newview/skins/default/xui/en/floater_snapshot.xml index e413228ddc..ec190ab656 100644 --- a/indra/newview/skins/default/xui/en/floater_snapshot.xml +++ b/indra/newview/skins/default/xui/en/floater_snapshot.xml @@ -100,7 +100,7 @@       right="-5"       name="upload_btn"       top_delta="0" -     width="100" /> +     width="110" />      <flyout_button       follows="left|top"       height="23" @@ -147,7 +147,7 @@       right="-5"       left_pad="5"       name="discard_btn" -     width="100" /> +     width="110" />      <text       type="string"       length="1" diff --git a/indra/newview/skins/default/xui/en/floater_top_objects.xml b/indra/newview/skins/default/xui/en/floater_top_objects.xml index b06c6dc215..4dfdcd15c7 100644 --- a/indra/newview/skins/default/xui/en/floater_top_objects.xml +++ b/indra/newview/skins/default/xui/en/floater_top_objects.xml @@ -105,7 +105,7 @@       left_delta="0"       name="id_text"       top_pad="10" -     width="100"> +     width="107">          Object ID:      </text>      <line_editor @@ -116,7 +116,7 @@       left_pad="3"       name="id_editor"       top_delta="-3" -     width="575" /> +     width="568" />      <button       follows="bottom|right"       height="23" @@ -138,7 +138,7 @@       left="10"       top_pad="5"       name="obj_name_text" -     width="100"> +     width="107">          Object name:      </text>      <line_editor @@ -148,7 +148,7 @@       left_pad="3"       name="object_name_editor"       top_delta="-3" -     width="575" /> +     width="568" />      <button       follows="bottom|right"       height="23" @@ -170,7 +170,7 @@       left="10"       top_pad="5"       name="owner_name_text" -     width="100"> +     width="107">          Owner:      </text>      <line_editor @@ -180,7 +180,7 @@       left_pad="3"       name="owner_name_editor"       top_delta="-3" -     width="575" /> +     width="568" />      <button       follows="bottom|right"       height="23" diff --git a/indra/newview/skins/default/xui/en/panel_media_settings_general.xml b/indra/newview/skins/default/xui/en/panel_media_settings_general.xml index 38e8b9844f..cdf14572fe 100644 --- a/indra/newview/skins/default/xui/en/panel_media_settings_general.xml +++ b/indra/newview/skins/default/xui/en/panel_media_settings_general.xml @@ -196,7 +196,7 @@     initial_val="256"      label=""      label_width="0" -   left_delta="40"  +   left_delta="68"      max_val="2048"      min_val="0"      mouse_opaque="true" diff --git a/indra/newview/skins/default/xui/es/floater_buy_land.xml b/indra/newview/skins/default/xui/es/floater_buy_land.xml index 74243a4d06..9d33b69de9 100644 --- a/indra/newview/skins/default/xui/es/floater_buy_land.xml +++ b/indra/newview/skins/default/xui/es/floater_buy_land.xml @@ -129,25 +129,25 @@ para cubrir esta parcela.  	<text name="region_name_label">  		Región:  	</text> -	<text left="565" name="region_name_text"> +	<text name="region_name_text">  		(desconocida)  	</text>  	<text name="region_type_label">  		Tipo:  	</text> -	<text left="565" name="region_type_text"> +	<text name="region_type_text">  		(desconocido)  	</text>  	<text name="estate_name_label">  		Estado:  	</text> -	<text left="565" name="estate_name_text"> +	<text name="estate_name_text">  		(desconocido)  	</text> -	<text name="estate_owner_label" right="565" width="115"> +	<text name="estate_owner_label">  		Propietario del estado:  	</text> -	<text left="565" name="estate_owner_text"> +	<text name="estate_owner_text">  		(desconocido)  	</text>  	<text name="resellable_changeable_label"> diff --git a/indra/newview/skins/default/xui/es/floater_sell_land.xml b/indra/newview/skins/default/xui/es/floater_sell_land.xml index efedb5d689..d883683945 100644 --- a/indra/newview/skins/default/xui/es/floater_sell_land.xml +++ b/indra/newview/skins/default/xui/es/floater_sell_land.xml @@ -54,8 +54,8 @@  				<radio_item label="No, mantener la propiedad de los objetos" name="no"/>  				<radio_item label="Sí, vender los objetos con el terreno" name="yes"/>  			</radio_group> -			<button label="Mostrar los objetos" name="show_objects" width="120"/> -			<text name="nag_message_label"> +			<button label="Mostrar los objetos" name="show_objects"/> +			<text name="nag_message_label" font="SansSerifSmallBold" left="10">  				RECUERDA: todas las ventas son definitivas.  			</text>  			<button label="Poner en venta el terreno" name="sell_btn"/> diff --git a/indra/newview/skins/default/xui/es/floater_top_objects.xml b/indra/newview/skins/default/xui/es/floater_top_objects.xml index 7c2522e8a9..033633bd22 100644 --- a/indra/newview/skins/default/xui/es/floater_top_objects.xml +++ b/indra/newview/skins/default/xui/es/floater_top_objects.xml @@ -39,19 +39,16 @@  	<text name="id_text">  		ID del objeto:  	</text> -	<line_editor font="SansSerifSmall" left="140" name="id_editor" width="280"/> -	<button label="Mostrar la baliza" name="show_beacon_btn" width="115"/> +	<button label="Mostrar la baliza" name="show_beacon_btn"/>  	<text name="obj_name_text">  		Nombre del objeto:  	</text> -	<line_editor font="SansSerifSmall" left="140" name="object_name_editor" width="280"/> -	<button label="Filtro" name="filter_object_btn" width="115"/> -	<text name="owner_name_text" width="130"> +	<button label="Filtro" name="filter_object_btn"/> +	<text name="owner_name_text">  		Propietario:  	</text> -	<line_editor font="SansSerifSmall" left="140" name="owner_name_editor" width="280"/> -	<button label="Filtro" name="filter_owner_btn" width="115"/> -	<button label="Actualizar" name="refresh_btn" width="115"/> +	<button label="Filtro" name="filter_owner_btn"/> +	<button label="Actualizar" name="refresh_btn"/>  	<button label="Devolver lo seleccionado" name="return_selected_btn" width="170"/>  	<button label="Devolver todo" left="190" name="return_all_btn"/>  	<button label="Desactivar lo seleccionado" name="disable_selected_btn" width="170"/> diff --git a/indra/newview/skins/default/xui/es/panel_media_settings_security.xml b/indra/newview/skins/default/xui/es/panel_media_settings_security.xml index c72f562a68..a1a3ec86cf 100644 --- a/indra/newview/skins/default/xui/es/panel_media_settings_security.xml +++ b/indra/newview/skins/default/xui/es/panel_media_settings_security.xml @@ -2,7 +2,8 @@  <panel label="Seguridad" name="Media Settings Security">  	<check_box initial_value="false" label="Permitir el acceso sólo a los patrones de URL especificados" name="whitelist_enable"/>  	<text name="home_url_fails_some_items_in_whitelist"> -		Están marcadas las entradas que la página web no admite: +		Están marcadas las entradas que la página web no  +admite:  	</text>  	<button label="Añadir" name="whitelist_add"/>  	<button label="Borrar" name="whitelist_del"/> diff --git a/indra/newview/skins/default/xui/fr/floater_sell_land.xml b/indra/newview/skins/default/xui/fr/floater_sell_land.xml index b5ffc8f9c7..b835cc6d87 100644 --- a/indra/newview/skins/default/xui/fr/floater_sell_land.xml +++ b/indra/newview/skins/default/xui/fr/floater_sell_land.xml @@ -5,13 +5,13 @@  			<text name="info_parcel_label">  				Parcelle :  			</text> -			<text name="info_parcel" left="70"> +			<text name="info_parcel">  				NOM DE LA PARCELLE  			</text>  			<text name="info_size_label">  				Taille :  			</text> -			<text name="info_size" left="70"> +			<text name="info_size">  				[AREA] m²  			</text>  			<text bottom_delta="-60" name="info_action"> @@ -38,27 +38,27 @@  			<text name="sell_to_text">  				Vendez votre terrain à n'importe qui ou uniquement à un acheteur spécifique.  			</text> -			<combo_box bottom_delta="-32" name="sell_to"> +			<combo_box name="sell_to">  				<combo_box.item label="- Sélectionnez -" name="--selectone--"/>  				<combo_box.item label="Tout le monde" name="Anyone"/>  				<combo_box.item label="Personne spécifique :" name="Specificuser:"/>  			</combo_box> -			<button label="Sélectionner" name="sell_to_select_agent" width="100"/> +			<button label="Sélectionner" name="sell_to_select_agent"/>  			<text name="sell_objects_label">  				3. Vendre les objets avec ce terrain ?  			</text>  			<text name="sell_objects_text">  				Les objets transférables se trouvant sur la parcelle changeront de propriétaire.  			</text> -			<radio_group bottom_delta="-54" name="sell_objects" right="430"> +			<radio_group name="sell_objects">  				<radio_item label="Non, rester le propriétaire des objets" name="no"/>  				<radio_item label="Oui, vendre les objets avec le terrain" name="yes"/>  			</radio_group> -			<button label="Afficher les objets" name="show_objects" right="420" width="120"/> -			<text bottom_delta="-30" name="nag_message_label"> +			<button label="Afficher les objets" name="show_objects"/> +			<text name="nag_message_label" font="SansSerif">  				Rappel : Toutes les ventes sont définitives.  			</text> -			<button label="Indiquer le terrain à vendre" name="sell_btn" width="165"/> +			<button label="Indiquer le terrain à vendre" name="sell_btn"/>  			<button label="Annuler" name="cancel_btn"/>  		</panel>  	</scroll_container> diff --git a/indra/newview/skins/default/xui/it/floater_buy_land.xml b/indra/newview/skins/default/xui/it/floater_buy_land.xml index f3b30f7048..3940c43a3d 100644 --- a/indra/newview/skins/default/xui/it/floater_buy_land.xml +++ b/indra/newview/skins/default/xui/it/floater_buy_land.xml @@ -142,10 +142,10 @@ consente [AMOUNT2] oggetti  	<text name="estate_name_text">  		(sconosciuto)  	</text> -	<text name="estate_owner_label" right="575" width="120"> +	<text name="estate_owner_label">  		Proprietario della regione:  	</text> -	<text left="580" name="estate_owner_text" width="155"> +	<text name="estate_owner_text">  		(sconosciuto)  	</text>  	<text name="resellable_changeable_label"> diff --git a/indra/newview/skins/default/xui/it/floater_sell_land.xml b/indra/newview/skins/default/xui/it/floater_sell_land.xml index 0f8d24ebbd..106ae0373c 100644 --- a/indra/newview/skins/default/xui/it/floater_sell_land.xml +++ b/indra/newview/skins/default/xui/it/floater_sell_land.xml @@ -55,7 +55,7 @@  				<radio_item label="Si, vendi gli oggetti con la terra" name="yes"/>  			</radio_group>  			<button label="Mostra oggetti" name="show_objects"/> -			<text name="nag_message_label"> +			<text name="nag_message_label" font="SansSerifSmallBold" left="9">  				RICORDA: Tutte le vendite sono definitive.  			</text>  			<button label="Imposta terreno come in vendita" name="sell_btn"/> diff --git a/indra/newview/skins/default/xui/it/floater_top_objects.xml b/indra/newview/skins/default/xui/it/floater_top_objects.xml index 939c5e83a0..7d062db23b 100644 --- a/indra/newview/skins/default/xui/it/floater_top_objects.xml +++ b/indra/newview/skins/default/xui/it/floater_top_objects.xml @@ -39,13 +39,13 @@  	<text name="id_text">  		ID oggetto:  	</text> -	<line_editor font="SansSerifSmall" left="90" name="id_editor" width="280"/> -	<button label="Mostra segnali luminosi" name="show_beacon_btn" width="150"/> +	<line_editor font="SansSerifSmall" name="id_editor"/> +	<button label="Mostra segnali luminosi" name="show_beacon_btn"/>  	<text name="obj_name_text">  		Nome dell'oggetto:  	</text> -	<line_editor font="SansSerifSmall" left="90" name="object_name_editor" width="280"/> -	<button label="Filtro" name="filter_object_btn" width="150"/> +	<line_editor font="SansSerifSmall" name="object_name_editor"/> +	<button label="Filtro" name="filter_object_btn"/>  	<text name="owner_name_text">  		Proprietario:  	</text> diff --git a/indra/newview/skins/default/xui/it/floater_water.xml b/indra/newview/skins/default/xui/it/floater_water.xml index c6ab646fbf..b25f0a6266 100644 --- a/indra/newview/skins/default/xui/it/floater_water.xml +++ b/indra/newview/skins/default/xui/it/floater_water.xml @@ -1,9 +1,9 @@  <?xml version="1.0" encoding="utf-8" standalone="yes"?>  <floater name="Water Floater" title="EDITOR AVANZATO DELL'ACQUA"> -	<text name="KeyFramePresetsText" width="224"> +	<text name="KeyFramePresetsText" width="245">  		Impostazioni predeterminate dell'acqua:  	</text> -	<combo_box left_delta="230" name="WaterPresetsCombo" width="150"/> +	<combo_box left_delta="245" name="WaterPresetsCombo" width="150"/>  	<button label="Nuovo" label_selected="Nuovo" name="WaterNewPreset"/>  	<button label="Salva" label_selected="Salva" name="WaterSavePreset"/>  	<button label="Cancella" label_selected="Cancella" name="WaterDeletePreset"/> diff --git a/indra/newview/skins/default/xui/it/floater_windlight_options.xml b/indra/newview/skins/default/xui/it/floater_windlight_options.xml index 1f6f0fab58..6828d05be0 100644 --- a/indra/newview/skins/default/xui/it/floater_windlight_options.xml +++ b/indra/newview/skins/default/xui/it/floater_windlight_options.xml @@ -1,8 +1,9 @@  <?xml version="1.0" encoding="utf-8" standalone="yes"?>  <floater name="WindLight floater" title="EDITOR AVANZATO DEL CIELO"> -	<text name="KeyFramePresetsText"> +	<text name="KeyFramePresetsText" width="105">  		Cieli predefiniti:  	</text> +	<combo_box left_delta="105" name="WLPresetsCombo"/>  	<button label="Nuovo" label_selected="Nuovo" name="WLNewPreset"/>  	<button label="Salva" label_selected="Salva" left_delta="72" name="WLSavePreset"/>  	<button label="Elimina" label_selected="Elimina" left_delta="72" name="WLDeletePreset"/> diff --git a/indra/newview/skins/default/xui/it/panel_media_settings_general.xml b/indra/newview/skins/default/xui/it/panel_media_settings_general.xml index 5ed7b23679..f11b2415ee 100644 --- a/indra/newview/skins/default/xui/it/panel_media_settings_general.xml +++ b/indra/newview/skins/default/xui/it/panel_media_settings_general.xml @@ -22,7 +22,7 @@  	<text name="media_setting_note">  		Nota: I residenti possono annullare questa impostazione  	</text> -	<check_box initial_value="false" label="Messa in scala automatica dell'elemento multimediale sulla faccia dell'oggetto" name="auto_scale"/> +	<check_box initial_value="false" label="Messa in scala automatica dell'elemento multimediale sulla 
faccia dell'oggetto" name="auto_scale"/>  	<text name="size_label">  		Dimensioni:  	</text> diff --git a/indra/newview/skins/default/xui/nl/floater_about.xml b/indra/newview/skins/default/xui/nl/floater_about.xml index f543ebbbe3..4e22d865fe 100644 --- a/indra/newview/skins/default/xui/nl/floater_about.xml +++ b/indra/newview/skins/default/xui/nl/floater_about.xml @@ -8,7 +8,7 @@  		Gemaakt met [COMPILER] versie [COMPILER_VERSION]  	</floater.string>  	<floater.string name="AboutPosition"> -		U bent op [POSITION_0,number,1], [POSITION_1,number,1], [POSITION_2,number,1] in [REGION] gelegen op [HOSTNAME] ([HOSTIP]) +		U bent op [POSITION_0,number,1], [POSITION_1,number,1], [POSITION_2,number,1] in [REGION] gelegen op <nolink>[HOSTNAME]</nolink> ([HOSTIP])  [SERVER_VERSION]  [[SERVER_RELEASE_NOTES_URL] [ReleaseNotes]]  	</floater.string> diff --git a/indra/newview/skins/default/xui/pl/panel_media_settings_security.xml b/indra/newview/skins/default/xui/pl/panel_media_settings_security.xml index da3142b54e..7e95c4942f 100644 --- a/indra/newview/skins/default/xui/pl/panel_media_settings_security.xml +++ b/indra/newview/skins/default/xui/pl/panel_media_settings_security.xml @@ -2,7 +2,8 @@  <panel label="Ochrona" name="Media Settings Security">  	<check_box initial_value="false" label="Dostęp dozwolony tylko dla wybranych URL" name="whitelist_enable"/>  	<text name="home_url_fails_some_items_in_whitelist"> -		Wejścia na stronę WWW, które się nie powiodły są zaznaczone: +		Wejścia na stronę WWW, które się nie powiodły są  +zaznaczone:  	</text>  	<button label="Dodaj" name="whitelist_add"/>  	<button label="Usuń" name="whitelist_del"/> diff --git a/indra/newview/skins/default/xui/pt/floater_about.xml b/indra/newview/skins/default/xui/pt/floater_about.xml index ac365f1702..a9da2a18af 100644 --- a/indra/newview/skins/default/xui/pt/floater_about.xml +++ b/indra/newview/skins/default/xui/pt/floater_about.xml @@ -7,7 +7,7 @@  		Construído com [COMPILER] versão [COMPILER_VERSION]  	</floater.string>  	<floater.string name="AboutPosition"> -		Você está em [POSITION_0,number,1], [POSITION_1,number,1], [POSITION_2,number,1] em [REGION] localizado em [HOSTNAME]</nolink>([HOSTIP]) +		Você está em [POSITION_0,number,1], [POSITION_1,number,1], [POSITION_2,number,1] em [REGION] localizado em <nolink>[HOSTNAME]</nolink>([HOSTIP])  [SERVER_VERSION]  [[SERVER_RELEASE_NOTES_URL] [ReleaseNotes]]  	</floater.string> diff --git a/indra/newview/skins/default/xui/pt/floater_build_options.xml b/indra/newview/skins/default/xui/pt/floater_build_options.xml index 71a1483dde..666e185819 100644 --- a/indra/newview/skins/default/xui/pt/floater_build_options.xml +++ b/indra/newview/skins/default/xui/pt/floater_build_options.xml @@ -1,11 +1,11 @@  <?xml version="1.0" encoding="utf-8" standalone="yes"?>  <floater name="build options floater" title="OPÇÕES DE GRADE"> -	<spinner label="Unidade da grade (metros)" label_width="122" name="GridResolution" width="180"/> -	<spinner label="Ext. da Grade (metros)" label_width="122" name="GridDrawSize" width="180"/> +	<spinner label="Unidade da grade (metros)" name="GridResolution"/> +	<spinner label="Ext. da Grade (metros)" name="GridDrawSize"/>  	<check_box label="Encaixar em sub-unidades" name="GridSubUnit"/>  	<check_box label="Ver corte transversal" name="GridCrossSection"/>  	<text name="grid_opacity_label" tool_tip="Opacidade da grade">  		Opacidade:  	</text> -	<slider label="Opacidade da grade" name="GridOpacity" width="220"/> +	<slider label="Opacidade da grade" name="GridOpacity"/>  </floater> diff --git a/indra/newview/skins/default/xui/pt/floater_buy_land.xml b/indra/newview/skins/default/xui/pt/floater_buy_land.xml index 5c5ee3b7a0..258c95cc7d 100644 --- a/indra/newview/skins/default/xui/pt/floater_buy_land.xml +++ b/indra/newview/skins/default/xui/pt/floater_buy_land.xml @@ -127,25 +127,25 @@ contribuídas para cobrir este lote antes da aquisição se completar.  	<text name="region_name_label">  		Região:  	</text> -	<text left="560" name="region_name_text"> +	<text name="region_name_text">  		(desconhecido)  	</text>  	<text name="region_type_label">  		Tipo:  	</text> -	<text left="560" name="region_type_text"> +	<text name="region_type_text">  		(desconhecido)  	</text>  	<text name="estate_name_label">  		Propriedade:  	</text> -	<text left="560" name="estate_name_text"> +	<text name="estate_name_text">  		(desconhecido)  	</text> -	<text name="estate_owner_label" right="565" width="105"> +	<text name="estate_owner_label">  		Dono da propriedade:  	</text> -	<text left="560" name="estate_owner_text"> +	<text name="estate_owner_text">  		(desconhecido)  	</text>  	<text name="resellable_changeable_label"> @@ -160,11 +160,11 @@ contribuídas para cobrir este lote antes da aquisição se completar.  	<text name="covenant_text">  		Você deve concordar com o Corretor da Propriedade:  	</text> -	<text left="470" name="covenant_timestamp_text"/> +	<text name="covenant_timestamp_text"/>  	<text_editor name="covenant_editor">  		Carregando...  	</text_editor> -	<check_box label="Eu concordo com as definições do  Corretor feitas acima." name="agree_covenant"/> +	<check_box label="Eu concordo com as definições do Corretor feitas acima." name="agree_covenant" left="-330"/>  	<text name="info_parcel_label">  		Lote:  	</text> diff --git a/indra/newview/skins/default/xui/pt/floater_sell_land.xml b/indra/newview/skins/default/xui/pt/floater_sell_land.xml index e6d4dc7ed6..014ae0845e 100644 --- a/indra/newview/skins/default/xui/pt/floater_sell_land.xml +++ b/indra/newview/skins/default/xui/pt/floater_sell_land.xml @@ -55,8 +55,9 @@  				<radio_item label="Sim, vender o terreno com os objetos" name="yes"/>  			</radio_group>  			<button label="Mostrar objetos" name="show_objects"/> -			<text name="nag_message_label"> -				LEMBRE-SE: Qualquer transação de compra e venda é irreversível. +			<text name="nag_message_label" font="SansSerifSmallBold"> +				LEMBRE-SE: Qualquer transação de compra  +e venda é irreversível.  			</text>  			<button label="Colocar terreno à venda" name="sell_btn"/>  			<button label="Cancelar" name="cancel_btn"/> diff --git a/indra/newview/skins/default/xui/pt/floater_top_objects.xml b/indra/newview/skins/default/xui/pt/floater_top_objects.xml index dc3bf73818..c3d5820616 100644 --- a/indra/newview/skins/default/xui/pt/floater_top_objects.xml +++ b/indra/newview/skins/default/xui/pt/floater_top_objects.xml @@ -39,14 +39,14 @@  	<text name="id_text">  		ID do Objeto:  	</text> -	<line_editor font="SansSerifSmall" left="140" name="id_editor" width="280"/> +	<line_editor font="SansSerifSmall" name="id_editor"/>  	<button label="Mostrar Avisos" name="show_beacon_btn"/>  	<text name="obj_name_text">  		Nome do objeto:  	</text> -	<line_editor font="SansSerifSmall" left="140" name="object_name_editor" width="280"/> +	<line_editor font="SansSerifSmall" name="object_name_editor"/>  	<button label="Filtro" name="filter_object_btn"/> -	<text name="owner_name_text" width="130"> +	<text name="owner_name_text">  		Proprietário:  	</text>  	<line_editor font="SansSerifSmall" left="140" name="owner_name_editor" width="280"/> diff --git a/indra/newview/skins/default/xui/pt/floater_water.xml b/indra/newview/skins/default/xui/pt/floater_water.xml index b4613e0890..b2a06f4ff2 100644 --- a/indra/newview/skins/default/xui/pt/floater_water.xml +++ b/indra/newview/skins/default/xui/pt/floater_water.xml @@ -1,9 +1,9 @@  <?xml version="1.0" encoding="utf-8" standalone="yes"?>  <floater name="Water Floater" title="EDITOR DE ÁGUA AVANÇADO"> -	<text name="KeyFramePresetsText" width="154"> +	<text name="KeyFramePresetsText" width="175">  		Pré-configurações da Água:  	</text> -	<combo_box left_delta="160" name="WaterPresetsCombo" width="150"/> +	<combo_box left_delta="175" name="WaterPresetsCombo" width="150"/>  	<button label="Novo" label_selected="Novo" name="WaterNewPreset"/>  	<button label="Salvar" label_selected="Salvar" name="WaterSavePreset"/>  	<button label="Deletar" label_selected="Deletar" name="WaterDeletePreset"/> diff --git a/indra/newview/skins/default/xui/pt/floater_windlight_options.xml b/indra/newview/skins/default/xui/pt/floater_windlight_options.xml index 22632a4ef8..ec459bbb26 100644 --- a/indra/newview/skins/default/xui/pt/floater_windlight_options.xml +++ b/indra/newview/skins/default/xui/pt/floater_windlight_options.xml @@ -1,9 +1,9 @@  <?xml version="1.0" encoding="utf-8" standalone="yes"?>  <floater name="WindLight floater" title="EDITOR DE CÉU AVANÇADO"> -	<text name="KeyFramePresetsText" width="130"> +	<text name="KeyFramePresetsText" width="140">  		Pré-definições de Céu:  	</text> -	<combo_box left_delta="130" name="WLPresetsCombo"/> +	<combo_box left_delta="140" name="WLPresetsCombo"/>  	<button label="Novo" label_selected="Novo" name="WLNewPreset"/>  	<button label="Salvar" label_selected="Salvar" left_delta="72" name="WLSavePreset"/>  	<button label="Deletar" label_selected="Deletar" left_delta="72" name="WLDeletePreset"/> diff --git a/indra/newview/skins/default/xui/pt/panel_media_settings_security.xml b/indra/newview/skins/default/xui/pt/panel_media_settings_security.xml index 646969946c..e38c44d8fe 100644 --- a/indra/newview/skins/default/xui/pt/panel_media_settings_security.xml +++ b/indra/newview/skins/default/xui/pt/panel_media_settings_security.xml @@ -2,7 +2,8 @@  <panel label="Segurança" name="Media Settings Security">  	<check_box initial_value="false" label="Acesso permitido a URLs com padrão específico" name="whitelist_enable"/>  	<text name="home_url_fails_some_items_in_whitelist"> -		URLs com falha de acesso na página inicial são indicados com um: +		URLs com falha de acesso na página inicial são  +indicados com um:  	</text>  	<button label="Adicionar" name="whitelist_add"/>  	<button label="Excluir" name="whitelist_del"/> | 
