summaryrefslogtreecommitdiff
path: root/indra/newview/llmeshrepository.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview/llmeshrepository.cpp')
-rwxr-xr-x[-rw-r--r--]indra/newview/llmeshrepository.cpp1317
1 files changed, 1076 insertions, 241 deletions
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 96a170ef07..1ff11f2c01 100644..100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -2,31 +2,25 @@
* @file llmeshrepository.cpp
* @brief Mesh repository implementation.
*
- * $LicenseInfo:firstyear=2005&license=viewergpl$
- *
- * Copyright (c) 2005-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2005&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 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.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 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.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * 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
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -65,11 +59,12 @@
#include "material_codes.h"
#include "pipeline.h"
+#ifndef LL_WINDOWS
+#include "netdb.h"
+#endif
#include <queue>
-#if LL_MESH_ENABLED
-
LLFastTimer::DeclareTimer FTM_MESH_UPDATE("Mesh Update");
LLFastTimer::DeclareTimer FTM_LOAD_MESH("Load Mesh");
@@ -85,6 +80,8 @@ U32 LLMeshRepository::sCacheBytesWritten = 0;
U32 LLMeshRepository::sPeakKbps = 0;
+const U32 MAX_TEXTURE_UPLOAD_RETRIES = 5;
+
std::string header_lod[] =
{
"lowest_lod",
@@ -111,18 +108,44 @@ U32 get_volume_memory_size(const LLVolume* volume)
return indices*2+vertices*11+sizeof(LLVolume)+sizeof(LLVolumeFace)*volume->getNumVolumeFaces();
}
-std::string scrub_host_name(std::string http_url, const LLHost& host)
+std::string scrub_host_name(std::string http_url)
{ //curl loves to abuse the DNS cache, so scrub host names out of urls where trivial to prevent DNS timeouts
- std::string ip_string = host.getIPString();
- std::string host_string = host.getHostName();
+ if (http_url.empty())
+ {
+ return http_url;
+ }
+ // Not safe to scrub amazon paths
+ if (http_url.find("s3.amazonaws.com") != std::string::npos)
+ {
+ return http_url;
+ }
+ std::string::size_type begin_host = http_url.find("://")+3;
+ std::string host_string = http_url.substr(begin_host);
+
+ std::string::size_type end_host = host_string.find(":");
+ if (end_host == std::string::npos)
+ {
+ end_host = host_string.find("/");
+ }
+
+ host_string = host_string.substr(0, end_host);
+
std::string::size_type idx = http_url.find(host_string);
-
- if (!ip_string.empty() && !host_string.empty() && idx != std::string::npos)
+
+ hostent* ent = gethostbyname(host_string.c_str());
+
+ if (ent && ent->h_length > 0)
{
- http_url.replace(idx, host_string.length(), ip_string);
+ U8* addr = (U8*) ent->h_addr_list[0];
+
+ std::string ip_string = llformat("%d.%d.%d.%d", addr[0], addr[1], addr[2], addr[3]);
+ if (!ip_string.empty() && !host_string.empty() && idx != std::string::npos)
+ {
+ http_url.replace(idx, host_string.length(), ip_string);
+ }
}
-
+
return http_url;
}
@@ -133,7 +156,7 @@ LLVertexBuffer* get_vertex_buffer_from_mesh(LLCDMeshData& mesh, F32 scale = 1.f)
LLStrider<LLVector3> pos;
LLStrider<LLVector3> norm;
-
+
buff->getVertexStrider(pos);
buff->getNormalStrider(norm);
@@ -224,15 +247,23 @@ public:
else
{
llwarns << status << ": " << reason << llendl;
- llwarns << "Retrying. (" << ++mData.mRetries << ")" << llendl;
-
- if (status == 499)
+
+ if (mData.mRetries < MAX_TEXTURE_UPLOAD_RETRIES)
{
- mThread->uploadTexture(mData);
+ llwarns << "Retrying. (" << ++mData.mRetries << ")" << llendl;
+
+ if (status == 499 || status == 500)
+ {
+ mThread->uploadTexture(mData);
+ }
+ else
+ {
+ llerrs << "Unhandled status " << status << llendl;
+ }
}
else
- {
- llerrs << "Unhandled status " << status << llendl;
+ {
+ llwarns << "Giving up after " << mData.mRetries << " retries." << llendl;
}
}
}
@@ -450,6 +481,24 @@ public:
};
+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);
+
+};
+
LLMeshRepoThread::LLMeshRepoThread()
: LLThread("mesh repo", NULL)
@@ -468,7 +517,7 @@ LLMeshRepoThread::~LLMeshRepoThread()
void LLMeshRepoThread::run()
{
mCurlRequest = new LLCurlRequest();
- LLCDResult res = LLConvexDecomposition::initThread();
+ LLCDResult res = LLConvexDecomposition::initThread();
if (res != LLCD_OK)
{
llwarns << "convex decomposition unable to be loaded" << llendl;
@@ -497,9 +546,10 @@ void LLMeshRepoThread::run()
while (!mLODReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && sActiveLODRequests < sMaxConcurrentRequests)
{
{
- LLMutexLock lock(mMutex);
+ mMutex->lock();
LODRequest req = mLODReqQ.front();
mLODReqQ.pop();
+ mMutex->unlock();
if (fetchMeshLOD(req.mMeshParams, req.mLOD))
{
count++;
@@ -510,9 +560,10 @@ void LLMeshRepoThread::run()
while (!mHeaderReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && sActiveHeaderRequests < sMaxConcurrentRequests)
{
{
- LLMutexLock lock(mMutex);
+ mMutex->lock();
HeaderRequest req = mHeaderReqQ.front();
mHeaderReqQ.pop();
+ mMutex->unlock();
if (fetchMeshHeader(req.mMeshParams))
{
count++;
@@ -520,7 +571,7 @@ void LLMeshRepoThread::run()
}
}
- {
+ { //mSkinRequests is protected by mSignal
std::set<LLUUID> incomplete;
for (std::set<LLUUID>::iterator iter = mSkinRequests.begin(); iter != mSkinRequests.end(); ++iter)
{
@@ -533,7 +584,7 @@ void LLMeshRepoThread::run()
mSkinRequests = incomplete;
}
- {
+ { //mDecompositionRequests is protected by mSignal
std::set<LLUUID> incomplete;
for (std::set<LLUUID>::iterator iter = mDecompositionRequests.begin(); iter != mDecompositionRequests.end(); ++iter)
{
@@ -546,10 +597,21 @@ void LLMeshRepoThread::run()
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();
}
-
- mCurlRequest->process();
}
res = LLConvexDecomposition::quitThread();
@@ -571,7 +633,13 @@ 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
@@ -579,7 +647,10 @@ void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod)
if (iter != mMeshHeader.end())
{ //if we have the header, request LOD byte range
LODRequest req(mesh_params, lod);
- mLODReqQ.push(req);
+ {
+ LLMutexLock lock(mMutex);
+ mLODReqQ.push(req);
+ }
}
else
{
@@ -597,6 +668,7 @@ void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod)
}
else
{ //if no header request is pending, fetch header
+ LLMutexLock lock(mMutex);
mHeaderReqQ.push(req);
mPendingLOD[mesh_params].push_back(lod);
}
@@ -610,8 +682,7 @@ std::string LLMeshRepoThread::constructUrl(LLUUID mesh_id)
if (gAgent.getRegion())
{
- http_url = gAgent.getRegion()->getCapability("GetMesh");
- scrub_host_name(http_url, gAgent.getRegionHost());
+ http_url = gMeshRepo.mGetMeshCapability;
}
if (!http_url.empty())
@@ -771,6 +842,82 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)
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(constructUrl(mesh_id), 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;
@@ -899,18 +1046,29 @@ bool LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* dat
{
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::deserialize(header, stream, data_size))
+ if (!LLSDSerialize::fromBinary(header, stream, data_size))
{
llwarns << "Mesh header parse error. Not a valid mesh asset!" << llendl;
return false;
}
- header_size = stream.tellg();
+ header_size += stream.tellg();
}
else
{
+ llinfos
+ << "Marking header as non-existent, will not retry." << llendl;
header["404"] = 1;
}
@@ -929,6 +1087,7 @@ bool LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* dat
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]);
@@ -1018,6 +1177,23 @@ bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 dat
}
}
+ if (skin.has("alt_inverse_bind_matrix"))
+ {
+ for (U32 i = 0; i < skin["alt_inverse_bind_matrix"].size(); ++i)
+ {
+ LLMatrix4 mat;
+ for (U32 j = 0; j < 4; j++)
+ {
+ for (U32 k = 0; k < 4; k++)
+ {
+ mat.mMatrix[j][k] = skin["alt_inverse_bind_matrix"][i][j*4+k].asReal();
+ }
+ }
+
+ info.mAlternateBindMatrix.push_back(mat);
+ }
+ }
+
mSkinInfoQ.push(info);
}
@@ -1045,43 +1221,93 @@ bool LLMeshRepoThread::decompositionReceived(const LLUUID& mesh_id, U8* data, S3
LLMeshDecomposition* d = new LLMeshDecomposition();
d->mMeshID = mesh_id;
- // updated for const-correctness. gcc is picky about this type of thing - Nyx
- const LLSD::Binary& hulls = decomp["HullList"].asBinary();
- const LLSD::Binary& position = decomp["Position"].asBinary();
+ if (decomp.has("HullList"))
+ {
+ // updated for const-correctness. gcc is picky about this type of thing - Nyx
+ const LLSD::Binary& hulls = decomp["HullList"].asBinary();
+ const LLSD::Binary& position = decomp["Position"].asBinary();
+
+ U16* p = (U16*) &position[0];
- U16* p = (U16*) &position[0];
+ d->mHull.resize(hulls.size());
- d->mHull.resize(hulls.size());
+ LLVector3 min;
+ LLVector3 max;
+ LLVector3 range;
- LLVector3 min;
- LLVector3 max;
- LLVector3 range;
+ min.setValue(decomp["Min"]);
+ max.setValue(decomp["Max"]);
+ range = max-min;
- min.setValue(decomp["Min"]);
- max.setValue(decomp["Max"]);
- range = max-min;
+ for (U32 i = 0; i < hulls.size(); ++i)
+ {
+ U16 count = (hulls[i] == 0) ? 256 : hulls[i];
+
+ for (U32 j = 0; j < count; ++j)
+ {
+ d->mHull[i].push_back(LLVector3(
+ (F32) p[0]/65535.f*range.mV[0]+min.mV[0],
+ (F32) p[1]/65535.f*range.mV[1]+min.mV[1],
+ (F32) p[2]/65535.f*range.mV[2]+min.mV[2]));
+ p += 3;
+ }
- for (U32 i = 0; i < hulls.size(); ++i)
+ }
+
+ //get mesh for decomposition
+ for (U32 i = 0; i < d->mHull.size(); ++i)
+ {
+ LLCDHull hull;
+ hull.mNumVertices = d->mHull[i].size();
+ hull.mVertexBase = d->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)
+ {
+ llwarns << "could not get mesh from hull from convex decomposition lib." << llendl;
+ return false;
+ }
+
+
+ d->mMesh.push_back(get_vertex_buffer_from_mesh(mesh));
+ }
+ }
+
+ if (decomp.has("Hull"))
{
- U8 count = hulls[i];
+ const LLSD::Binary& position = decomp["Hull"].asBinary();
+
+ U16* p = (U16*) &position[0];
+
+ LLVector3 min;
+ LLVector3 max;
+ LLVector3 range;
+
+ min.setValue(decomp["Min"]);
+ max.setValue(decomp["Max"]);
+ range = max-min;
+
+ U16 count = position.size()/6;
for (U32 j = 0; j < count; ++j)
{
- d->mHull[i].push_back(LLVector3(
+ d->mBaseHull.push_back(LLVector3(
(F32) p[0]/65535.f*range.mV[0]+min.mV[0],
(F32) p[1]/65535.f*range.mV[1]+min.mV[1],
(F32) p[2]/65535.f*range.mV[2]+min.mV[2]));
p += 3;
}
-
- }
-
- //get mesh for decomposition
- for (U32 i = 0; i < d->mHull.size(); ++i)
- {
+
+ //get mesh for decomposition
LLCDHull hull;
- hull.mNumVertices = d->mHull[i].size();
- hull.mVertexBase = d->mHull[i][0].mV;
+ hull.mNumVertices = d->mBaseHull.size();
+ hull.mVertexBase = d->mBaseHull[0].mV;
hull.mVertexStrideBytes = 12;
LLCDMeshData mesh;
@@ -1096,9 +1322,14 @@ bool LLMeshRepoThread::decompositionReceived(const LLUUID& mesh_id, U8* data, S3
return false;
}
-
- d->mMesh.push_back(get_vertex_buffer_from_mesh(mesh));
- }
+ d->mBaseHullMesh = get_vertex_buffer_from_mesh(mesh);
+ }
+ else
+ {
+ //empty vertex buffer to indicate decomposition has been fetched
+ //but contains no base hull
+ d->mBaseHullMesh = new LLVertexBuffer(0, 0);
+ }
mDecompositionQ.push(d);
}
@@ -1106,11 +1337,82 @@ bool LLMeshRepoThread::decompositionReceived(const LLUUID& mesh_id, U8* data, S3
return true;
}
-LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data, LLVector3& scale, bool upload_textures)
+bool LLMeshRepoThread::physicsShapeReceived(const LLUUID& mesh_id, U8* data, S32 data_size)
+{
+ LLSD physics_shape;
+
+ LLMeshDecomposition* d = new LLMeshDecomposition();
+ d->mMeshID = mesh_id;
+
+ if (data == NULL)
+ { //no data, no physics shape exists
+ d->mPhysicsShapeMesh = new LLVertexBuffer(0,0);
+ }
+ 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 = new LLVertexBuffer(LLVertexBuffer::MAP_VERTEX, 0);
+
+ d->mPhysicsShapeMesh->allocateBuffer(vertex_count, index_count, true);
+
+ LLStrider<LLVector3> pos;
+ LLStrider<U16> idx;
+
+ d->mPhysicsShapeMesh->getVertexStrider(pos);
+ d->mPhysicsShapeMesh->getIndexStrider(idx);
+
+ S32 idx_offset = 0;
+ for (S32 i = 0; i < volume->getNumVolumeFaces(); ++i)
+ {
+ const LLVolumeFace& face = volume->getVolumeFace(i);
+ if (idx_offset + face.mNumIndices > 65535)
+ { //avoid 16-bit index overflow
+ continue;
+ }
+
+ LLVector4a::memcpyNonAliased16(pos[idx_offset].mV, face.mPositions[0].getF32ptr(), face.mNumVertices*sizeof(LLVector4a));
+
+ for (S32 i = 0; i < face.mNumIndices; ++i)
+ {
+ *idx++ = face.mIndices[i] + idx_offset;
+ }
+
+ idx_offset += face.mNumVertices;
+ }
+ }
+ }
+
+ 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")
{
mInstanceList = data;
mUploadTextures = upload_textures;
+ mUploadSkin = upload_skin;
+ mUploadJoints = upload_joints;
mMutex = new LLMutex(NULL);
mCurlRequest = NULL;
mPendingConfirmations = 0;
@@ -1119,13 +1421,14 @@ LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data,
mFinished = false;
mOrigin = gAgent.getPositionAgent();
mHost = gAgent.getRegionHost();
+
mUploadObjectAssetCapability = gAgent.getRegion()->getCapability("UploadObjectAsset");
mNewInventoryCapability = gAgent.getRegion()->getCapability("NewFileAgentInventoryVariablePrice");
+ //mUploadObjectAssetCapability = scrub_host_name(mUploadObjectAssetCapability);
+ //mNewInventoryCapability = scrub_host_name(mNewInventoryCapability);
+
mOrigin += gAgent.getAtAxis() * scale.magVec();
-
- scrub_host_name(mUploadObjectAssetCapability, mHost);
- scrub_host_name(mNewInventoryCapability, mHost);
}
LLMeshUploadThread::~LLMeshUploadThread()
@@ -1133,6 +1436,63 @@ LLMeshUploadThread::~LLMeshUploadThread()
}
+LLMeshUploadThread::DecompRequest::DecompRequest(LLModel* mdl, LLModel* base_model, LLMeshUploadThread* thread)
+{
+ mStage = "single_hull";
+ mModel = mdl;
+ mBaseModel = base_model;
+ mThread = thread;
+
+ //copy out positions and indices
+ if (mdl)
+ {
+ U16 index_offset = 0;
+
+ mPositions.clear();
+ mIndices.clear();
+
+ //queue up vertex positions and indices
+ for (S32 i = 0; i < mdl->getNumVolumeFaces(); ++i)
+ {
+ const LLVolumeFace& face = mdl->getVolumeFace(i);
+ if (mPositions.size() + face.mNumVertices > 65535)
+ {
+ continue;
+ }
+
+ for (U32 j = 0; j < face.mNumVertices; ++j)
+ {
+ mPositions.push_back(LLVector3(face.mPositions[j].getF32ptr()));
+ }
+
+ for (U32 j = 0; j < face.mNumIndices; ++j)
+ {
+ mIndices.push_back(face.mIndices[j]+index_offset);
+ }
+
+ index_offset += face.mNumVertices;
+ }
+ }
+
+ mThread->mFinalDecomp = this;
+ mThread->mPhysicsComplete = false;
+}
+
+void LLMeshUploadThread::DecompRequest::completed()
+{
+ if (mThread->mFinalDecomp == this)
+ {
+ mThread->mPhysicsComplete = true;
+ }
+
+ if (mHull.size() != 1)
+ {
+ llerrs << "WTF?" << llendl;
+ }
+
+ mThread->mHullMap[mBaseModel] = mHull[0];
+}
+
void LLMeshUploadThread::run()
{
mCurlRequest = new LLCurlRequest();
@@ -1175,8 +1535,36 @@ void LLMeshUploadThread::run()
}
}
}
+
+ //queue up models for hull generation
+ LLModel* physics = NULL;
+
+ if (data.mModel[LLModel::LOD_PHYSICS].notNull())
+ {
+ physics = data.mModel[LLModel::LOD_PHYSICS];
+ }
+ else if (data.mModel[LLModel::LOD_MEDIUM].notNull())
+ {
+ physics = data.mModel[LLModel::LOD_MEDIUM];
+ }
+ else
+ {
+ physics = data.mModel[LLModel::LOD_HIGH];
+ }
+
+ if (!physics)
+ {
+ llerrs << "WTF?" << llendl;
+ }
+
+ DecompRequest* request = new DecompRequest(physics, data.mBaseModel, this);
+ gMeshRepo.mDecompThread->submitRequest(request);
}
+ while (!mPhysicsComplete)
+ {
+ apr_sleep(100);
+ }
//upload textures
bool done = false;
@@ -1259,6 +1647,12 @@ void LLMeshUploadThread::run()
// 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"];
+ }
+
LLHTTPClient::post(url, object_asset, new LLHTTPClient::Responder());
mFinished = true;
@@ -1568,17 +1962,32 @@ void LLMeshDecompositionResponder::completedRaw(U32 status, const std::string& r
delete [] data;
}
-void LLMeshHeaderResponder::completedRaw(U32 status, const std::string& reason,
+void LLMeshPhysicsShapeResponder::completedRaw(U32 status, const std::string& reason,
const LLChannelDescriptors& channels,
const LLIOPipe::buffer_ptr_t& buffer)
{
- LLMeshRepoThread::sActiveHeaderRequests--;
+ S32 data_size = buffer->countAfter(channels.in(), NULL);
+
if (status < 200 || status > 400)
{
llwarns << status << ": " << reason << llendl;
}
- S32 data_size = buffer->countAfter(channels.in(), NULL);
+ 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;
@@ -1588,18 +1997,71 @@ void LLMeshHeaderResponder::completedRaw(U32 status, const std::string& reason,
buffer->readAfter(channels.in(), NULL, data, data_size);
}
- LLMeshRepository::sBytesReceived += llmin(data_size, 4096);
+ 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);
- if (!gMeshRepo.mThread->headerReceived(mMeshParams, data, data_size))
+ 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;
+ //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
@@ -1674,6 +2136,8 @@ void LLMeshRepository::init()
{
mMeshMutex = new LLMutex(NULL);
+ LLConvexDecomposition::getInstance()->initSystem();
+
mDecompThread = new LLPhysicsDecomp();
mDecompThread->start();
@@ -1682,6 +2146,8 @@ void LLMeshRepository::init()
apr_sleep(100);
}
+
+
mThread = new LLMeshRepoThread();
mThread->start();
}
@@ -1709,6 +2175,8 @@ void LLMeshRepository::shutdown()
delete mDecompThread;
mDecompThread = NULL;
}
+
+ LLConvexDecomposition::quitSystem();
}
@@ -1844,6 +2312,19 @@ void LLMeshRepository::notifyLoadedMeshes()
return;
}
+ static std::string region_name("never name a region this");
+
+ if (gAgent.getRegion())
+ { //update capability url
+ if (gAgent.getRegion()->getName() != region_name)
+ {
+ region_name = gAgent.getRegion()->getName();
+
+ mGetMeshCapability = gAgent.getRegion()->getCapability("GetMesh");
+ mGetMeshCapability = scrub_host_name(mGetMeshCapability);
+ }
+ }
+
LLFastTimer t(FTM_MESH_UPDATE);
{
@@ -1929,6 +2410,13 @@ void LLMeshRepository::notifyLoadedMeshes()
mPendingDecompositionRequests.pop();
}
+ //send physics shapes decomposition requests
+ while (!mPendingPhysicsShapeRequests.empty())
+ {
+ mThread->loadMeshPhysicsShape(mPendingPhysicsShapeRequests.front());
+ mPendingPhysicsShapeRequests.pop();
+ }
+
mThread->notifyLoadedMeshes();
mThread->mMutex->unlock();
@@ -1943,14 +2431,50 @@ void LLMeshRepository::notifySkinInfoReceived(LLMeshSkinInfo& info)
mLoadingSkins.erase(info.mMeshID);
}
+void LLMeshDecomposition::merge(const LLMeshDecomposition* rhs)
+{
+ if (!rhs)
+ {
+ return;
+ }
+
+ if (mMeshID != rhs->mMeshID)
+ {
+ llerrs << "Attempted to merge with decomposition of some other mesh." << llendl;
+ }
+
+ if (mBaseHull.empty())
+ { //take base hull and decomposition from rhs
+ mHull = rhs->mHull;
+ mBaseHull = rhs->mBaseHull;
+ mMesh = rhs->mMesh;
+ mBaseHullMesh = rhs->mBaseHullMesh;
+ }
+
+ if (mPhysicsShapeMesh.isNull())
+ { //take physics shape mesh from rhs
+ mPhysicsShapeMesh = rhs->mPhysicsShapeMesh;
+ }
+}
+
void LLMeshRepository::notifyDecompositionReceived(LLMeshDecomposition* decomp)
{
- mDecompositionMap[decomp->mMeshID] = 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
@@ -1993,7 +2517,7 @@ void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVol
}
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);
@@ -2071,17 +2595,47 @@ const LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id)
return NULL;
}
+void LLMeshRepository::fetchPhysicsShape(const LLUUID& mesh_id)
+{
+ if (mesh_id.notNull())
+ {
+ LLMeshDecomposition* 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.isNull())
+ {
+ 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);
+ }
+ }
+ }
+
+}
+
const LLMeshDecomposition* LLMeshRepository::getDecomposition(const LLUUID& mesh_id)
{
+ LLMeshDecomposition* ret = NULL;
+
if (mesh_id.notNull())
{
decomposition_map::iterator iter = mDecompositionMap.find(mesh_id);
if (iter != mDecompositionMap.end())
{
- return iter->second;
+ ret = iter->second;
}
- //no skin info known about given mesh, try to fetch it
+ //decomposition block hasn't been fetched yet
+ if (!ret || ret->mBaseHullMesh.isNull())
{
LLMutexLock lock(mMeshMutex);
//add volume to list of loading meshes
@@ -2094,32 +2648,102 @@ const LLMeshDecomposition* LLMeshRepository::getDecomposition(const LLUUID& mesh
}
}
- return NULL;
+ 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);
}
-void LLMeshRepository::uploadModel(std::vector<LLModelInstance>& data, LLVector3& scale, bool upload_textures)
+const LLSD& LLMeshRepository::getMeshHeader(const LLUUID& mesh_id)
{
- LLMeshUploadThread* thread = new LLMeshUploadThread(data, scale, upload_textures);
+ return mThread->getMeshHeader(mesh_id);
+}
+
+const 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);
mUploads.push_back(thread);
thread->start();
}
+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)
{
//write model file to memory buffer
std::stringstream ostr;
-
- LLModel::physics_shape& phys_shape = data.mModel[LLModel::LOD_PHYSICS].notNull() ?
- data.mModel[LLModel::LOD_PHYSICS]->mPhysicsShape :
- data.mBaseModel->mPhysicsShape;
- LLSD header = LLModel::writeModel(ostr,
+ LLModel::convex_hull_decomposition& decomp =
+ data.mModel[LLModel::LOD_PHYSICS].notNull() ?
+ data.mModel[LLModel::LOD_PHYSICS]->mConvexHullDecomp :
+ data.mBaseModel->mConvexHullDecomp;
+
+ LLModel::hull dummy_hull;
+
+ 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],
- phys_shape,
+ decomp,
+ dummy_hull,
+ mUploadSkin,
+ mUploadJoints,
true);
std::string desc = data.mBaseModel->mLabel;
@@ -2193,17 +2817,22 @@ void LLMeshUploadThread::doUploadModel(LLMeshUploadData& data)
{
std::stringstream ostr;
- LLModel::physics_shape& phys_shape = data.mModel[LLModel::LOD_PHYSICS].notNull() ?
- data.mModel[LLModel::LOD_PHYSICS]->mPhysicsShape :
- data.mBaseModel->mPhysicsShape;
+ LLModel::convex_hull_decomposition& decomp =
+ data.mModel[LLModel::LOD_PHYSICS].notNull() ?
+ data.mModel[LLModel::LOD_PHYSICS]->mConvexHullDecomp :
+ data.mBaseModel->mConvexHullDecomp;
- LLModel::writeModel(ostr,
+ 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],
- phys_shape);
+ decomp,
+ mHullMap[data.mBaseModel],
+ mUploadSkin,
+ mUploadJoints);
data.mAssetData = ostr.str();
@@ -2371,6 +3000,20 @@ LLSD LLMeshUploadThread::createObject(LLModelInstance& instance)
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;
}
@@ -2378,7 +3021,7 @@ void LLMeshUploadThread::priceResult(LLMeshUploadData& data, const LLSD& content
{
mPendingCost += content["upload_price"].asInteger();
data.mRSVP = content["rsvp"].asString();
- data.mRSVP = scrub_host_name(data.mRSVP, mHost);
+ data.mRSVP = scrub_host_name(data.mRSVP);
mConfirmedQ.push(data);
}
@@ -2387,7 +3030,7 @@ void LLMeshUploadThread::priceResult(LLTextureUploadData& data, const LLSD& cont
{
mPendingCost += content["upload_price"].asInteger();
data.mRSVP = content["rsvp"].asString();
- data.mRSVP = scrub_host_name(data.mRSVP, mHost);
+ data.mRSVP = scrub_host_name(data.mRSVP);
mConfirmedTextureQ.push(data);
}
@@ -2431,6 +3074,50 @@ void LLMeshRepository::uploadError(LLSD& args)
mUploadErrorQ.push(args);
}
+//static
+F32 LLMeshRepository::getStreamingCost(const LLSD& header, F32 radius)
+{
+ F32 dlowest = llmin(radius/0.06f, 256.f);
+ F32 dlow = llmin(radius/0.24f, 256.f);
+ F32 dmid = llmin(radius/1.0f, 256.f);
+ F32 dhigh = 0.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_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 cost = 0.f;
+ cost += llmax(256.f-dlowest, 1.f)/32.f*bytes_lowest;
+ cost += llmax(dlowest-dlow, 1.f)/32.f*bytes_low;
+ cost += llmax(dlow-dmid, 1.f)/32.f*bytes_mid;
+ cost += llmax(dmid-dhigh, 1.f)/32.f*bytes_high;
+
+ cost *= gSavedSettings.getF32("MeshStreamingCostScaler");
+ return cost;
+}
+
LLPhysicsDecomp::LLPhysicsDecomp()
: LLThread("Physics Decomp")
@@ -2438,13 +3125,9 @@ LLPhysicsDecomp::LLPhysicsDecomp()
mInited = false;
mQuitting = false;
mDone = false;
- mStage = -1;
- mContinue = 1;
mSignal = new LLCondition(NULL);
mMutex = new LLMutex(NULL);
-
- setStatusMessage("Idle");
}
LLPhysicsDecomp::~LLPhysicsDecomp()
@@ -2457,7 +3140,6 @@ void LLPhysicsDecomp::shutdown()
if (mSignal)
{
mQuitting = true;
- mContinue = 0;
mSignal->signal();
while (!mDone)
@@ -2467,149 +3149,277 @@ void LLPhysicsDecomp::shutdown()
}
}
-void LLPhysicsDecomp::setStatusMessage(std::string msg)
+void LLPhysicsDecomp::submitRequest(LLPhysicsDecomp::Request* request)
{
LLMutexLock lock(mMutex);
- mStatus = msg;
+ 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::execute(const char* stage, LLModel* mdl)
+void LLPhysicsDecomp::setMeshData(LLCDMeshData& mesh)
{
- LLMutexLock lock(mMutex);
+ mesh.mVertexBase = mCurRequest->mPositions[0].mV;
+ mesh.mVertexStrideBytes = 12;
+ mesh.mNumVertices = mCurRequest->mPositions.size();
- if (mModel.notNull())
+ 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)
{
- llwarns << "Not done processing previous model." << llendl;
- return;
+ ret = LLConvexDecomposition::getInstance()->setMeshData(&mesh);
}
- mModel = mdl;
- //load model into LLCD
- if (mdl)
+ 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(&params);
+ }
+
+ for (S32 i = 0; i < param_count; ++i)
+ {
+ param_map[params[i].mName] = params+i;
+ }
+
+ //set parameter values
+ for (decomp_params::iterator iter = mCurRequest->mParams.begin(); iter != mCurRequest->mParams.end(); ++iter)
{
- mStage = mStageID[stage];
+ const std::string& name = iter->first;
+ const LLSD& value = iter->second;
+
+ const LLCDParam* param = param_map[name];
+
+ if (param == NULL)
+ { //couldn't find valid parameter
+ continue;
+ }
+
+ U32 ret = LLCD_OK;
+
+ if (param->mType == LLCDParam::LLCD_FLOAT)
+ {
+ ret = LLConvexDecomposition::getInstance()->setParam(param->mName, (F32) value.asReal());
+ }
+ else if (param->mType == LLCDParam::LLCD_INTEGER ||
+ param->mType == LLCDParam::LLCD_ENUM)
+ {
+ ret = LLConvexDecomposition::getInstance()->setParam(param->mName, value.asInteger());
+ }
+ else if (param->mType == LLCDParam::LLCD_BOOLEAN)
+ {
+ ret = LLConvexDecomposition::getInstance()->setParam(param->mName, value.asBoolean());
+ }
+
+ if (ret)
+ {
+ llerrs << "WTF?" << llendl;
+ }
+ }
+
+ mCurRequest->setStatusMessage("Executing.");
+
+ LLCDResult ret = LLCD_OK;
+
+ if (LLConvexDecomposition::getInstance() != NULL)
+ {
+ ret = LLConvexDecomposition::getInstance()->executeStage(stage);
+ }
+
+ if (ret)
+ {
+ llwarns << "Convex Decomposition thread valid but could not execute stage " << stage << llendl;
+ LLMutexLock lock(mMutex);
+
+ mCurRequest->mHull.clear();
+ mCurRequest->mHullMesh.clear();
+
+ mCurRequest->setStatusMessage("FAIL");
+ mCurRequest->completed();
- U16 index_offset = 0;
+ mCurRequest = NULL;
+ }
+ else
+ {
+ mCurRequest->setStatusMessage("Reading results");
- if (mStage == 0)
+ S32 num_hulls =0;
+ if (LLConvexDecomposition::getInstance() != NULL)
{
- 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;
- }
+ num_hulls = LLConvexDecomposition::getInstance()->getNumHullsFromStage(stage);
+ }
+
+ mMutex->lock();
+ mCurRequest->mHull.clear();
+ mCurRequest->mHull.resize(num_hulls);
- for (U32 j = 0; j < face.mNumVertices; ++j)
- {
- mPositions.push_back(LLVector3(face.mPositions[j].getF32ptr()));
- }
+ mCurRequest->mHullMesh.clear();
+ mCurRequest->mHullMesh.resize(num_hulls);
+ mMutex->unlock();
- for (U32 j = 0; j < face.mNumIndices; ++j)
- {
- mIndices.push_back(face.mIndices[j]+index_offset);
- }
+ 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);
- index_offset += face.mNumVertices;
+ 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);
+
+ mCurRequest->mHullMesh[i] = get_vertex_buffer_from_mesh(mesh);
+
+ mMutex->lock();
+ mCurRequest->mHull[i] = p;
+ mMutex->unlock();
}
+
+ {
+ LLMutexLock lock(mMutex);
- //signal decomposition thread
- mSignal->signal();
+ mCurRequest->setStatusMessage("FAIL");
+ mCurRequest->completed();
+
+ mCurRequest = NULL;
+ }
}
}
-//static
-S32 LLPhysicsDecomp::llcdCallback(const char* status, S32 p1, S32 p2)
-{
- LLPhysicsDecomp* comp = gMeshRepo.mDecompThread;
- comp->setStatusMessage(llformat("%s: %d/%d", status, p1, p2));
- return comp->mContinue;
-}
-
-void LLPhysicsDecomp::cancel()
-{
- mContinue = 0;
-}
-
-void LLPhysicsDecomp::run()
+void make_box(LLPhysicsDecomp::Request * request)
{
- LLConvexDecomposition::initSystem();
- mInited = true;
+ LLVector3 min,max;
+ min = request->mPositions[0];
+ max = min;
- while (!mQuitting)
+ for (U32 i = 0; i < request->mPositions.size(); ++i)
{
- mSignal->wait();
- if (!mQuitting)
- {
- //load data intoLLCD
- if (mStage == 0)
- {
- mMesh.mVertexBase = mPositions[0].mV;
- mMesh.mVertexStrideBytes = 12;
- mMesh.mNumVertices = mPositions.size();
+ update_min_max(min, max, request->mPositions[i]);
+ }
- mMesh.mIndexType = LLCDMeshData::INT_16;
- mMesh.mIndexBase = &(mIndices[0]);
- mMesh.mIndexStrideBytes = 6;
-
- mMesh.mNumTriangles = mIndices.size()/3;
+ 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]));
- LLCDResult ret = LLCD_OK;
- if (LLConvexDecomposition::getInstance() != NULL)
- {
- ret = LLConvexDecomposition::getInstance()->setMeshData(&mMesh);
- }
+ request->mHull.push_back(box);
+}
- if (ret)
- {
- llerrs << "Convex Decomposition thread valid but could not set mesh data" << llendl;
- }
- }
- setStatusMessage("Executing.");
+void LLPhysicsDecomp::doDecompositionSingleHull()
+{
+ LLCDMeshData mesh;
+
+ setMeshData(mesh);
+
+
+ //set all parameters to default
+ std::map<std::string, const LLCDParam*> param_map;
- mContinue = 1;
- LLCDResult ret = LLCD_OK;
- if (LLConvexDecomposition::getInstance() != NULL)
- {
- ret = LLConvexDecomposition::getInstance()->executeStage(mStage);
- }
+ static const LLCDParam* params = NULL;
+ static S32 param_count = 0;
- mContinue = 0;
- if (ret)
- {
- llerrs << "Convex Decomposition thread valid but could not execute stage " << mStage << llendl;
- }
+ if (!params)
+ {
+ param_count = LLConvexDecomposition::getInstance()->getParameters(&params);
+ }
+
+ 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);
- setStatusMessage("Reading results");
+ 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(mStage);
+ num_hulls = LLConvexDecomposition::getInstance()->getNumHullsFromStage(STAGE_SIMPLIFY);
}
- if (mModel.isNull())
- {
- llerrs << "mModel should never be null here!" << llendl;
- }
-
mMutex->lock();
- mModel->mPhysicsShape.clear();
- mModel->mPhysicsShape.resize(num_hulls);
- mModel->mHullCenter.clear();
- mModel->mHullCenter.resize(num_hulls);
- std::vector<LLPointer<LLVertexBuffer> > mesh_buffer;
- mesh_buffer.resize(num_hulls);
- mModel->mPhysicsCenter.clearVec();
- mModel->mPhysicsPoints = 0;
+ mCurRequest->mHull.clear();
+ mCurRequest->mHull.resize(num_hulls);
+ mCurRequest->mHullMesh.clear();
mMutex->unlock();
for (S32 i = 0; i < num_hulls; ++i)
@@ -2617,54 +3427,76 @@ void LLPhysicsDecomp::run()
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(mStage, i, &hull);
+ LLConvexDecomposition::getInstance()->getHullFromStage(STAGE_SIMPLIFY, i, &hull);
const F32* v = hull.mVertexBase;
- LLVector3 hull_center;
-
for (S32 j = 0; j < hull.mNumVertices; ++j)
{
LLVector3 vert(v[0], v[1], v[2]);
p.push_back(vert);
- hull_center += vert;
v = (F32*) (((U8*) v) + hull.mVertexStrideBytes);
}
+
+ mMutex->lock();
+ mCurRequest->mHull[i] = p;
+ mMutex->unlock();
+ }
+ }
+ }
-
- hull_center *= 1.f/hull.mNumVertices;
- 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(mStage, i, &mesh);
+ {
+ LLMutexLock lock(mMutex);
+ mCurRequest->completed();
+ mCurRequest = NULL;
+ }
+}
- mesh_buffer[i] = get_vertex_buffer_from_mesh(mesh);
+void LLPhysicsDecomp::run()
+{
+ LLConvexDecomposition::getInstance()->initThread();
+ mInited = true;
- mMutex->lock();
- mModel->mPhysicsShape[i] = p;
- mModel->mHullCenter[i] = hull_center;
- mModel->mPhysicsPoints += hull.mNumVertices;
- mModel->mPhysicsCenter += hull_center;
+ LLConvexDecomposition* decomp = LLConvexDecomposition::getInstance();
- mMutex->unlock();
- }
+ 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);
- mModel->mPhysicsCenter *= 1.f/mModel->mPhysicsPoints;
-
- LLFloaterModelPreview::onModelDecompositionComplete(mModel, mesh_buffer);
- //done updating model
- mModel = NULL;
+ mCurRequest = mRequestQ.front();
+ mRequestQ.pop();
}
- setStatusMessage("Done.");
- LLFloaterModelPreview::sInstance->mModelPreview->refresh();
+ if (mCurRequest->mStage == "single_hull")
+ {
+ doDecompositionSingleHull();
+ }
+ else
+ {
+ doDecomposition();
+ }
}
}
- LLConvexDecomposition::quitSystem();
-
+ LLConvexDecomposition::getInstance()->quitThread();
+
//delete mSignal;
delete mMutex;
mSignal = NULL;
@@ -2672,5 +3504,8 @@ void LLPhysicsDecomp::run()
mDone = true;
}
-#endif
+void LLPhysicsDecomp::Request::setStatusMessage(const std::string& msg)
+{
+ mStatusMessage = msg;
+}