diff -r 6f4836fb74ce indra/llmath/llvolume.cpp --- a/indra/llmath/llvolume.cpp Sat Apr 14 22:23:24 2012 -0400 +++ b/indra/llmath/llvolume.cpp Tue Apr 24 18:25:35 2012 -0500 @@ -309,16 +309,261 @@ } } + + +// find the point on a triangle closest to a given target point +// algorithm derived from: www.geometrictools.com/Documentation/DistancePoint3Triangle3.pdf +// (returns distance squared and barycentric coordinates) + +F32 LLTriangleClosestPoint(const LLVector3& vert0, const LLVector3& vert1, const LLVector3& vert2, const LLVector3& target, + F32& closest_a, F32& closest_b) +{ + // edges of triangle + LLVector3 edge0 = vert1 - vert0; + LLVector3 edge1 = vert2 - vert0; + + LLVector3 delta = vert0 - target; + + // length of triangle edges + F32 a00 = edge0.lengthSquared(); + F32 a01 = edge0 * edge1; + F32 a11 = edge1.lengthSquared(); + + F32 b0 = delta * edge0; + F32 b1 = delta * edge1; + + F32 c = delta.lengthSquared(); + + F32 det = fabs(a00*a11-a01*a01); + + F32 s = a01*b1-a11*b0; + F32 t = a01*b0-a00*b1; + + F32 dist_squared; + + if ( s + t <= det ) + { + if ( s < 0.0f ) + { + if ( t < 0.0f ) // region 4 + { + if ( b0 < 0.0f ) + { + t = 0.0f; + if ( -b0 >= a00 ) + { + s = 1.0f; + dist_squared = a00+(2.0f)*b0+c; + } + else + { + s = -b0/a00; + dist_squared = b0*s+c; + } + } + else + { + s = 0.0f; + if ( b1 >= 0.0f ) + { + t = 0.0f; + dist_squared = c; + } + else if ( -b1 >= a11 ) + { + t = 1.0f; + dist_squared = a11+(2.0f)*b1+c; + } + else + { + t = -b1/a11; + dist_squared = b1*t+c; + } + } + } + else // region 3 + { + s = 0.0f; + if ( b1 >= 0.0f ) + { + t = 0.0f; + dist_squared = c; + } + else if ( -b1 >= a11 ) + { + t = 1.0f; + dist_squared = a11+(2.0f)*b1+c; + } + else + { + t = -b1/a11; + dist_squared = b1*t+c; + } + } + } + else if ( t < 0.0f ) // region 5 + { + t = 0.0f; + if ( b0 >= 0.0f ) + { + s = 0.0f; + dist_squared = c; + } + else if ( -b0 >= a00 ) + { + s = 1.0f; + dist_squared = a00+(2.0f)*b0+c; + } + else + { + s = -b0/a00; + dist_squared = b0*s+c; + } + } + else // region 0 + { + // minimum at interior point + F32 det_inv = (1.0f)/det; + s *= det_inv; + t *= det_inv; + dist_squared = s*(a00*s+a01*t+(2.0f)*b0) + + t*(a01*s+a11*t+(2.0f)*b1)+c; + } + } + else + { + F32 tmp0, tmp1, numerator, denominator; + + if ( s < 0.0f ) // region 2 + { + tmp0 = a01 + b0; + tmp1 = a11 + b1; + if ( tmp1 > tmp0 ) + { + numerator = tmp1 - tmp0; + denominator = a00-2.0f*a01+a11; + if ( numerator >= denominator ) + { + s = 1.0f; + t = 0.0f; + dist_squared = a00+(2.0f)*b0+c; + } + else + { + s = numerator/denominator; + t = 1.0f - s; + dist_squared = s*(a00*s+a01*t+2.0f*b0) + + t*(a01*s+a11*t+(2.0f)*b1)+c; + } + } + else + { + s = 0.0f; + if ( tmp1 <= 0.0f ) + { + t = 1.0f; + dist_squared = a11+(2.0f)*b1+c; + } + else if ( b1 >= 0.0f ) + { + t = 0.0f; + dist_squared = c; + } + else + { + t = -b1/a11; + dist_squared = b1*t+c; + } + } + } + else if ( t < 0.0f ) // region 6 + { + tmp0 = a01 + b1; + tmp1 = a00 + b0; + if ( tmp1 > tmp0 ) + { + numerator = tmp1 - tmp0; + denominator = a00-(2.0f)*a01+a11; + if ( numerator >= denominator ) + { + t = 1.0f; + s = 0.0f; + dist_squared = a11+(2.0f)*b1+c; + } + else + { + t = numerator/denominator; + s = 1.0f - t; + dist_squared = s*(a00*s+a01*t+(2.0f)*b0) + + t*(a01*s+a11*t+(2.0f)*b1)+c; + } + } + else + { + t = 0.0f; + if ( tmp1 <= 0.0f ) + { + s = 1.0f; + dist_squared = a00+(2.0f)*b0+c; + } + else if ( b0 >= 0.0f ) + { + s = 0.0f; + dist_squared = c; + } + else + { + s = -b0/a00; + dist_squared = b0*s+c; + } + } + } + else // region 1 + { + numerator = a11 + b1 - a01 - b0; + if ( numerator <= 0.0f ) + { + s = 0.0f; + t = 1.0f; + dist_squared = a11+(2.0f)*b1+c; + } + else + { + denominator = a00-2.0f*a01+a11; + if ( numerator >= denominator ) + { + s = 1.0f; + t = 0.0f; + dist_squared = a00+(2.0f)*b0+c; + } + else + { + s = numerator/denominator; + t = 1.0f - s; + dist_squared = s*(a00*s+a01*t+(2.0f)*b0) + + t*(a01*s+a11*t+(2.0f)*b1)+c; + } + } + } + } + + closest_a = s; + closest_b = t; + + return fabs(dist_squared); +} + + class LLVolumeOctreeRebound : public LLOctreeTravelerDepthFirst { public: const LLVolumeFace* mFace; - + LLVolumeOctreeRebound(const LLVolumeFace* face) { mFace = face; } - + virtual void visit(const LLOctreeNode* branch) { //this is a depth first traversal, so it's safe to assum all children have complete //bounding data diff -r 6f4836fb74ce indra/llmath/llvolume.h --- a/indra/llmath/llvolume.h Sat Apr 14 22:23:24 2012 -0400 +++ b/indra/llmath/llvolume.h Tue Apr 24 18:25:35 2012 -0500 @@ -1107,6 +1107,9 @@ F32& intersection_a, F32& intersection_b, F32& intersection_t); BOOL LLTriangleRayIntersectTwoSided(const LLVector4a& vert0, const LLVector4a& vert1, const LLVector4a& vert2, const LLVector4a& orig, const LLVector4a& dir, F32& intersection_a, F32& intersection_b, F32& intersection_t); + +F32 LLTriangleClosestPoint(const LLVector3& vert0, const LLVector3& vert1, const LLVector3& vert2, const LLVector3& target, + F32& closest_a, F32& closest_b); diff -r 6f4836fb74ce indra/llmath/m4math.cpp --- a/indra/llmath/m4math.cpp Sat Apr 14 22:23:24 2012 -0400 +++ b/indra/llmath/m4math.cpp Tue Apr 24 18:25:35 2012 -0500 @@ -274,6 +274,68 @@ return *this; } + +// does an actual inversion +const LLMatrix4& LLMatrix4::invert_real() +{ + F32* m = &mMatrix[0][0]; + + F32 inv[16]; + F32 det; + + inv[0] = m[5]*m[10]*m[15] - m[5]*m[11]*m[14] - m[9]*m[6]*m[15] + + m[9]*m[7]*m[14] + m[13]*m[6]*m[11] - m[13]*m[7]*m[10]; + inv[4] = -m[4]*m[10]*m[15] + m[4]*m[11]*m[14] + m[8]*m[6]*m[15] + - m[8]*m[7]*m[14] - m[12]*m[6]*m[11] + m[12]*m[7]*m[10]; + inv[8] = m[4]*m[9]*m[15] - m[4]*m[11]*m[13] - m[8]*m[5]*m[15] + + m[8]*m[7]*m[13] + m[12]*m[5]*m[11] - m[12]*m[7]*m[9]; + inv[12] = -m[4]*m[9]*m[14] + m[4]*m[10]*m[13] + m[8]*m[5]*m[14] + - m[8]*m[6]*m[13] - m[12]*m[5]*m[10] + m[12]*m[6]*m[9]; + inv[1] = -m[1]*m[10]*m[15] + m[1]*m[11]*m[14] + m[9]*m[2]*m[15] + - m[9]*m[3]*m[14] - m[13]*m[2]*m[11] + m[13]*m[3]*m[10]; + inv[5] = m[0]*m[10]*m[15] - m[0]*m[11]*m[14] - m[8]*m[2]*m[15] + + m[8]*m[3]*m[14] + m[12]*m[2]*m[11] - m[12]*m[3]*m[10]; + inv[9] = -m[0]*m[9]*m[15] + m[0]*m[11]*m[13] + m[8]*m[1]*m[15] + - m[8]*m[3]*m[13] - m[12]*m[1]*m[11] + m[12]*m[3]*m[9]; + inv[13] = m[0]*m[9]*m[14] - m[0]*m[10]*m[13] - m[8]*m[1]*m[14] + + m[8]*m[2]*m[13] + m[12]*m[1]*m[10] - m[12]*m[2]*m[9]; + inv[2] = m[1]*m[6]*m[15] - m[1]*m[7]*m[14] - m[5]*m[2]*m[15] + + m[5]*m[3]*m[14] + m[13]*m[2]*m[7] - m[13]*m[3]*m[6]; + inv[6] = -m[0]*m[6]*m[15] + m[0]*m[7]*m[14] + m[4]*m[2]*m[15] + - m[4]*m[3]*m[14] - m[12]*m[2]*m[7] + m[12]*m[3]*m[6]; + inv[10] = m[0]*m[5]*m[15] - m[0]*m[7]*m[13] - m[4]*m[1]*m[15] + + m[4]*m[3]*m[13] + m[12]*m[1]*m[7] - m[12]*m[3]*m[5]; + inv[14] = -m[0]*m[5]*m[14] + m[0]*m[6]*m[13] + m[4]*m[1]*m[14] + - m[4]*m[2]*m[13] - m[12]*m[1]*m[6] + m[12]*m[2]*m[5]; + inv[3] = -m[1]*m[6]*m[11] + m[1]*m[7]*m[10] + m[5]*m[2]*m[11] + - m[5]*m[3]*m[10] - m[9]*m[2]*m[7] + m[9]*m[3]*m[6]; + inv[7] = m[0]*m[6]*m[11] - m[0]*m[7]*m[10] - m[4]*m[2]*m[11] + + m[4]*m[3]*m[10] + m[8]*m[2]*m[7] - m[8]*m[3]*m[6]; + inv[11] = -m[0]*m[5]*m[11] + m[0]*m[7]*m[9] + m[4]*m[1]*m[11] + - m[4]*m[3]*m[9] - m[8]*m[1]*m[7] + m[8]*m[3]*m[5]; + inv[15] = m[0]*m[5]*m[10] - m[0]*m[6]*m[9] - m[4]*m[1]*m[10] + + m[4]*m[2]*m[9] + m[8]*m[1]*m[6] - m[8]*m[2]*m[5]; + + det = m[0]*inv[0] + m[1]*inv[4] + m[2]*inv[8] + m[3]*inv[12]; + + if (det == 0) + { + setZero(); + } + else + { + det = 1.0 / det; + + for (int i = 0; i < 16; i++) + m[i] = inv[i] * det; + + } + + + return *this; + +} + LLVector4 LLMatrix4::getFwdRow4() const { return LLVector4(mMatrix[VX][VX], mMatrix[VX][VY], mMatrix[VX][VZ], mMatrix[VX][VW]); diff -r 6f4836fb74ce indra/llmath/m4math.h --- a/indra/llmath/m4math.h Sat Apr 14 22:23:24 2012 -0400 +++ b/indra/llmath/m4math.h Tue Apr 24 18:25:35 2012 -0500 @@ -202,7 +202,9 @@ // const LLMatrix4& transpose(); // Transpose LLMatrix4 - const LLMatrix4& invert(); // Invert LLMatrix4 + const LLMatrix4& invert(); // Invert LLMatrix4 + const LLMatrix4& invert_real(); // Invert LLMatrix4 - works for all matrices + // Rotate existing matrix // These are really, really, inefficient as implemented! - djs diff -r 6f4836fb74ce indra/llprimitive/llmodel.cpp --- a/indra/llprimitive/llmodel.cpp Sat Apr 14 22:23:24 2012 -0400 +++ b/indra/llprimitive/llmodel.cpp Tue Apr 24 18:25:35 2012 -0500 @@ -2127,6 +2127,16 @@ { mPelvisOffset = skin["pelvis_offset"].asReal(); } + + if (skin.has("deform")) + { + mDeform = skin["deform"].asBoolean(); + } + else + { + mDeform = FALSE; + } + } LLSD LLMeshSkinInfo::asLLSD(bool include_joints) const @@ -2169,6 +2179,8 @@ ret["pelvis_offset"] = mPelvisOffset; } + + ret["deform"] = mDeform; return ret; } diff -r 6f4836fb74ce indra/llprimitive/llmodel.h --- a/indra/llprimitive/llmodel.h Sat Apr 14 22:23:24 2012 -0400 +++ b/indra/llprimitive/llmodel.h Tue Apr 24 18:25:35 2012 -0500 @@ -46,6 +46,7 @@ std::vector mInvBindMatrix; std::vector mAlternateBindMatrix; std::map mJointMap; + BOOL mDeform; LLMeshSkinInfo() { } LLMeshSkinInfo(LLSD& data); diff -r 6f4836fb74ce indra/newview/CMakeLists.txt --- a/indra/newview/CMakeLists.txt Sat Apr 14 22:23:24 2012 -0400 +++ b/indra/newview/CMakeLists.txt Tue Apr 24 18:25:35 2012 -0500 @@ -134,6 +134,7 @@ lldaycyclemanager.cpp lldebugmessagebox.cpp lldebugview.cpp + lldeformerworker.cpp lldelayedgestureerror.cpp lldirpicker.cpp lldndbutton.cpp @@ -690,6 +691,7 @@ lldaycyclemanager.h lldebugmessagebox.h lldebugview.h + lldeformerworker.h lldelayedgestureerror.h lldirpicker.h lldndbutton.h diff -r 6f4836fb74ce indra/newview/lldrawpoolavatar.cpp --- a/indra/newview/lldrawpoolavatar.cpp Sat Apr 14 22:23:24 2012 -0400 +++ b/indra/newview/lldrawpoolavatar.cpp Tue Apr 24 18:25:35 2012 -0500 @@ -1265,7 +1265,10 @@ } } -void LLDrawPoolAvatar::updateRiggedFaceVertexBuffer(LLVOAvatar* avatar, LLFace* face, const LLMeshSkinInfo* skin, LLVolume* volume, const LLVolumeFace& vol_face) + +void LLDrawPoolAvatar::updateRiggedFaceVertexBuffer(LLVOAvatar* avatar, LLFace* face, + const LLMeshSkinInfo* skin, LLVolume* volume, + const LLVolumeFace& vol_face, LLVOVolume* vobj) { LLVector4a* weight = vol_face.mWeights; if (!weight) @@ -1274,7 +1277,7 @@ } LLPointer buffer = face->getVertexBuffer(); - LLDrawable* drawable = face->getDrawable(); + LLPointer drawable = face->getDrawable(); U32 data_mask = face->getRiggedVertexBufferDataMask(); @@ -1331,8 +1334,34 @@ { face->setPoolType(LLDrawPool::POOL_AVATAR); } + + + // swap in deformed volume if conditions are met: + LLVolume* which_volume = NULL; + if (skin->mDeform) + { + LLDeformedVolume* deformed_volume = vobj->getDeformedVolume(); + BOOL success = deformed_volume->deform(volume, avatar, skin, face->getTEOffset(), drawable); + + if (success) + { + which_volume = deformed_volume; + } + else + { + which_volume = volume; + } - face->getGeometryVolume(*volume, face->getTEOffset(), mat_vert, mat_normal, offset, true); + } + else + { + which_volume = volume; + } + + + + + face->getGeometryVolume(*which_volume, face->getTEOffset(), mat_vert, mat_normal, offset, true); buffer->flush(); } @@ -1412,6 +1441,10 @@ if (norm) { + // normals are not transformed by the same math as points. + // you need the inverse transpose. + // these matrices are non-uniformly scaled (by a lot) so this + // math is wrong. (i think.) -qarl LLVector4a& n = vol_face.mNormals[j]; bind_shape_matrix.rotate(n, t); final_mat.rotate(t, dst); @@ -1601,7 +1634,7 @@ stop_glerror(); const LLVolumeFace& vol_face = volume->getVolumeFace(te); - updateRiggedFaceVertexBuffer(avatar, face, skin, volume, vol_face); + updateRiggedFaceVertexBuffer(avatar, face, skin, volume, vol_face, vobj); } } } diff -r 6f4836fb74ce indra/newview/lldrawpoolavatar.h --- a/indra/newview/lldrawpoolavatar.h Sat Apr 14 22:23:24 2012 -0400 +++ b/indra/newview/lldrawpoolavatar.h Tue Apr 24 18:25:35 2012 -0500 @@ -35,6 +35,7 @@ class LLMeshSkinInfo; class LLVolume; class LLVolumeFace; +class LLVOVolume; class LLDrawPoolAvatar : public LLFacePool @@ -133,7 +134,8 @@ LLFace* facep, const LLMeshSkinInfo* skin, LLVolume* volume, - const LLVolumeFace& vol_face); + const LLVolumeFace& vol_face, + LLVOVolume* vobj); void updateRiggedVertexBuffers(LLVOAvatar* avatar); void renderRigged(LLVOAvatar* avatar, U32 type, bool glow = false); diff -r 6f4836fb74ce indra/newview/llface.h --- a/indra/newview/llface.h Sat Apr 14 22:23:24 2012 -0400 +++ b/indra/newview/llface.h Tue Apr 24 18:25:35 2012 -0500 @@ -130,7 +130,7 @@ LLFacePool* getPool() const { return mDrawPoolp; } U32 getPoolType() const { return mPoolType; } - LLDrawable* getDrawable() const { return mDrawablep; } + LLPointer getDrawable() const { return mDrawablep; } LLViewerObject* getViewerObject() const { return mVObjp; } S32 getLOD() const { return mVObjp.notNull() ? mVObjp->getLOD() : 0; } void setPoolType(U32 type) { mPoolType = type; } diff -r 6f4836fb74ce indra/newview/llfloatermodelpreview.cpp --- a/indra/newview/llfloatermodelpreview.cpp Sat Apr 14 22:23:24 2012 -0400 +++ b/indra/newview/llfloatermodelpreview.cpp Tue Apr 24 18:25:35 2012 -0500 @@ -462,6 +462,7 @@ childDisable("upload_skin"); childDisable("upload_joints"); + childDisable("deform"); initDecompControls(); @@ -3215,6 +3216,7 @@ F32 debug_scale = mFMP ? mFMP->childGetValue("import_scale").asReal() : 1.f; mPelvisZOffset = mFMP ? mFMP->childGetValue("pelvis_offset").asReal() : 3.0f; + BOOL deform = mFMP ? mFMP->childGetValue("deform").asBoolean() : FALSE; if ( mFMP && mFMP->childGetValue("upload_joints").asBoolean() ) { @@ -3236,12 +3238,13 @@ instance.mLOD[LLModel::LOD_PHYSICS]->mPhysics : instance.mModel->mPhysics; - //update instance skin info for each lods pelvisZoffset + //update instance skin info for each lods pelvisZoffset and deform for ( int j=0; jmSkinInfo.mPelvisOffset = mPelvisZOffset; + instance.mLOD[j]->mSkinInfo.mDeform = deform; } } @@ -4981,6 +4984,7 @@ bool has_skin_weights = false; bool upload_skin = mFMP->childGetValue("upload_skin").asBoolean(); bool upload_joints = mFMP->childGetValue("upload_joints").asBoolean(); + bool deform = mFMP->childGetValue("deform").asBoolean(); bool resetJoints = false; if ( upload_joints != mLastJointUpdate ) @@ -5015,11 +5019,13 @@ fmp->enableViewOption("show_skin_weight"); fmp->setViewOptionEnabled("show_joint_positions", skin_weight); mFMP->childEnable("upload_skin"); + mFMP->childEnable("deform"); } } else { mFMP->childDisable("upload_skin"); + mFMP->childDisable("deform"); if (fmp) { mViewOption["show_skin_weight"] = false; @@ -5040,12 +5046,22 @@ mFMP->childSetValue("upload_joints", false); upload_joints = false; } + + if (!upload_skin && deform) + { // can't deform if model has no skin weights + mFMP->childSetValue("deform", false); + deform = false; + } + //Only enable joint offsets if it passed the earlier critiquing if ( isRigValidForJointPositionUpload() ) { mFMP->childSetEnabled("upload_joints", upload_skin); } + + // Only enable deform if skins are enabled + mFMP->childSetEnabled("deform", upload_skin); F32 explode = mFMP->childGetValue("physics_explode").asReal(); diff -r 6f4836fb74ce indra/newview/llpolymesh.cpp --- a/indra/newview/llpolymesh.cpp Sat Apr 14 22:23:24 2012 -0400 +++ b/indra/newview/llpolymesh.cpp Tue Apr 24 18:25:35 2012 -0500 @@ -162,9 +162,11 @@ // mVertFaceMap.deleteAllData(); } + // compate_int is used by the qsort function to sort the index array int compare_int(const void *a, const void *b); +/* dead code //----------------------------------------------------------------------------- // genIndices() //----------------------------------------------------------------------------- @@ -191,6 +193,7 @@ mLastIndexOffset = index_offset; } +*/ //-------------------------------------------------------------------- // LLPolyMeshSharedData::getNumKB() @@ -804,6 +807,8 @@ } + + //----------------------------------------------------------------------------- // LLPolyMesh::getMesh() //----------------------------------------------------------------------------- @@ -812,7 +817,7 @@ //------------------------------------------------------------------------- // search for an existing mesh by this name //------------------------------------------------------------------------- - LLPolyMeshSharedData* meshSharedData = get_if_there(sGlobalSharedMeshList, name, (LLPolyMeshSharedData*)NULL); + LLPolyMeshSharedData* meshSharedData = get_if_there(sGlobalSharedMeshList, name, (LLPolyMeshSharedData*)NULL); if (meshSharedData) { // llinfos << "Polymesh " << name << " found in global mesh table." << llendl; diff -r 6f4836fb74ce indra/newview/llpolymesh.h --- a/indra/newview/llpolymesh.h Sat Apr 14 22:23:24 2012 -0400 +++ b/indra/newview/llpolymesh.h Tue Apr 24 18:25:35 2012 -0500 @@ -133,7 +133,7 @@ BOOL loadMesh( const std::string& fileName ); public: - void genIndices(S32 offset); + // void genIndices(S32 offset); - dead code const LLVector2 &getUVs(U32 index); @@ -168,7 +168,7 @@ // If the mesh already exists in the global mesh table, it is returned, // otherwise it is loaded from file, added to the table, and returned. static LLPolyMesh *getMesh( const std::string &name, LLPolyMesh* reference_mesh = NULL); - + // Frees all loaded meshes. // This should only be called once you know there are no outstanding // references to these objects. Generally, upon exit of the application. @@ -234,6 +234,12 @@ return mBinormals; } + // Get base mesh coords + const LLVector3 *getBaseCoords() const{ + llassert(mSharedData); + return mSharedData->mBaseCoords; + } + // Get base mesh normals const LLVector3 *getBaseNormals() const{ llassert(mSharedData); diff -r 6f4836fb74ce indra/newview/llvoavatar.cpp --- a/indra/newview/llvoavatar.cpp Sat Apr 14 22:23:24 2012 -0400 +++ b/indra/newview/llvoavatar.cpp Tue Apr 24 18:25:35 2012 -0500 @@ -5214,6 +5214,10 @@ return mMeshLOD[MESH_ID_UPPER_BODY]->mMeshParts[0]->getMesh(); } +LLPolyMesh* LLVOAvatar::getMesh(S32 which) +{ + return mMeshLOD[which]->mMeshParts[0]->getMesh(); +} //----------------------------------------------------------------------------- // LLVOAvatar::getPosGlobalFromAgent() @@ -5730,9 +5734,10 @@ mLastSkeletonSerialNum = mSkeletonSerialNum; mRoot.updateWorldMatrixChildren(); } - + dirtyMesh(); updateHeadOffset(); + rebuildRiggedAttachments(); } //----------------------------------------------------------------------------- diff -r 6f4836fb74ce indra/newview/llvoavatar.h --- a/indra/newview/llvoavatar.h Sat Apr 14 22:23:24 2012 -0400 +++ b/indra/newview/llvoavatar.h Tue Apr 24 18:25:35 2012 -0500 @@ -195,6 +195,8 @@ virtual F32 getPixelArea() const; virtual LLPolyMesh* getHeadMesh(); virtual LLPolyMesh* getUpperBodyMesh(); + virtual LLPolyMesh* getMesh(S32 which); + virtual LLVector3d getPosGlobalFromAgent(const LLVector3 &position); virtual LLVector3 getPosAgentFromGlobal(const LLVector3d &position); virtual void updateVisualParams(); diff -r 6f4836fb74ce indra/newview/llvovolume.cpp --- a/indra/newview/llvovolume.cpp Sat Apr 14 22:23:24 2012 -0400 +++ b/indra/newview/llvovolume.cpp Tue Apr 24 18:25:35 2012 -0500 @@ -76,6 +76,8 @@ #include "llviewershadermgr.h" #include "llvoavatar.h" #include "llvocache.h" +#include "lldeformerworker.h" + const S32 MIN_QUIET_FRAMES_COALESCE = 30; const F32 FORCE_SIMPLE_RENDER_AREA = 512.f; @@ -3811,7 +3813,7 @@ LLVector4a& v = vol_face.mPositions[j]; LLVector4a t; LLVector4a dst; - bind_shape_matrix.affineTransform(v, t); + bind_shape_matrix.affineTransform(v, t); // shouldn't this matrix be premultiplied into the matrix palette? this is expensive. final_mat.affineTransform(t, dst); pos[j] = dst; } @@ -3848,6 +3850,377 @@ } } + +LLDeformedVolume* LLVOVolume::getDeformedVolume() +{ + if (!mDeformedVolume) + { + // doesn't exist yet - make it + mDeformedVolume = new LLDeformedVolume; + } + + return mDeformedVolume; +} + +static LLFastTimer::DeclareTimer FTM_SKIN_RIGGED_DEFORM("Deform"); + + +LLDeformedVolume::deform_table_t* LLDeformedVolume::getDeformTable(LLVolume* source, LLVOAvatar* avatar, + const LLMeshSkinInfo* skin, S32 face, + LLPointer drawable) +{ + // see if we can find this deform table in the cache + LLDeformTableCacheIndex cache_index(source->getParams().getSculptID(), face, source->getVolumeFace(face).mNumVertices); + + deform_cache_t::iterator i = mDeformCache.find(cache_index); + + if (i != mDeformCache.end()) + { + return &(i->second); + } + + // doesn't exist in cache, must create + // send a request to the deformer worker. + + LLPointer request = new LLDeformerWorker::Request; + + // pack the request + request->mDeformedVolume = this; + request->mDrawable = drawable; + request->mMeshID = source->getParams().getSculptID(); + request->mFace = face; + request->mVertexCount = source->getVolumeFace(face).mNumVertices; + + // before we do any more work, see if this request is already on the queue + if (LLDeformerWorker::alreadyQueued(request)) + { + // no more to be done here, return NULL + return NULL; + } + + + // we copy this data now so that when we do the work in + // the second thread, there's no chance it will be clobbered + + // fill volume vertex positions + const LLVolumeFace& source_face = source->getVolumeFace(face); + request->mVolumePositions.resize(source_face.mNumVertices); + for (S32 i = 0; i < source_face.mNumVertices; i++) + { + request->mVolumePositions[i] = source_face.mPositions[i]; + } + + // fill avatar vertex positions + S32 MORPH_MESHES[] = { LLVOAvatarDefines::MESH_ID_HEAD, + LLVOAvatarDefines::MESH_ID_UPPER_BODY, + LLVOAvatarDefines::MESH_ID_LOWER_BODY }; + + + S32 num_meshes = sizeof(MORPH_MESHES)/sizeof(S32); + request->mAvatarIndices.resize(num_meshes); + request->mAvatarPositions.resize(num_meshes); + + for (S32 j = 0; j < num_meshes; j++) + { + S32 mesh_id = MORPH_MESHES[j]; + + LLPolyMesh* avatar_mesh = avatar->getMesh(mesh_id); + + LLPolyFace* faces = avatar_mesh->getFaces(); + + + // store avatar indices (LLPolyFaces are triangles) + S32 num_triangles = avatar_mesh->getNumFaces(); + request->mAvatarIndices[j].resize(num_triangles * 3); + for (S32 k = 0; k < num_triangles; k++) + { + // indices for triangle corners + S32 index0 = faces[k][0]; + S32 index1 = faces[k][1]; + S32 index2 = faces[k][2]; + + request->mAvatarIndices[j][k * 3 + 0] = index0; + request->mAvatarIndices[j][k * 3 + 1] = index1; + request->mAvatarIndices[j][k * 3 + 2] = index2; + } + + // store avatar vertices + const LLVector3* base_positions = avatar_mesh->getBaseCoords(); + S32 num_vertices = avatar_mesh->getNumVertices(); + request->mAvatarPositions[j].resize(num_vertices); + for (S32 k = 0; k < num_vertices; k++) + { + request->mAvatarPositions[j][k] = base_positions[k]; + } + } + + + // scale and matrix constants + + // maps volume space to object (avatar) space + LLMatrix4a bind_shape_matrix_a; + request->mBindShapeMatrix.loadu(skin->mBindShapeMatrix); + // skeleton scaling factor - assumes all joints have same scale and that the scale is uniform in XYZ (currently true) + request->mScale = powf(skin->mInvBindMatrix[0].determinant(), 1.0f / 3.0f); + + + // submit request to deformer worker + LLDeformerWorker::submitRequest(request); + + return NULL; +} + + + +void LLDeformedVolume::computeDeformTable(LLPointer request) +{ + // see if this table is already in the cache. if so, return (no work needed) + LLDeformTableCacheIndex cache_index(request->mMeshID, request->mFace, request->mVertexCount); + + deform_cache_t::iterator i = mDeformCache.find(cache_index); + + if (i != mDeformCache.end()) + { + return; + } + + // tables are fairly small, we keep them around in case the user is switching between meshes for poorman's animation + // one entry per face per LOD per sculpt_ID + U32 MAX_CACHE_SIZE = 20; + + if (mDeformCache.size() > MAX_CACHE_SIZE) + { + // this table will very infrequently fill - so make space in simple way: + // delete element at random + U32 random = ll_rand(MAX_CACHE_SIZE-1); + + deform_cache_t::iterator iterator = mDeformCache.begin(); + + for (int i = 0; i < random; i++) + { + iterator++; + } + + mDeformCache.erase(iterator); + } + + + deform_table_t& deform_table = mDeformCache[cache_index]; + + // table is a mapping from verts on the volume to verts on the avatar mesh. + // currently we take closest point - but may want to modify this in favor + // verts with similar skin weights and/or normals (so verts on the left leg + // don't move with the right leg by accident.) + + + // build mapping + deform_table.resize(request->mVertexCount); + + + // these are the meshes we follow + S32 MORPH_MESHES[] = { LLVOAvatarDefines::MESH_ID_HEAD, LLVOAvatarDefines::MESH_ID_UPPER_BODY, LLVOAvatarDefines::MESH_ID_LOWER_BODY }; + + // for each vertex in the volume, find the closest point on the avatar mesh(es) + for (S32 i = 0; i < request->mVertexCount; i++) + { + + LLVector4a source_position = request->mVolumePositions[i]; + LLVector4a source_position_transformed; + request->mBindShapeMatrix.affineTransform(source_position, source_position_transformed); + source_position_transformed.mul(request->mScale); + + // position to get closest to + LLVector3 target(source_position_transformed.getF32ptr()); + + F32 closest_distance_squared = F32_MAX; + S32 closest_mesh; + // corners of the triangle containing the closest point; + S32 closest_vertex[LL_DEFORMER_WEIGHT_COUNT]; + // weights of those corners (barycentric coordinates) + F32 closest_vertex_weights[LL_DEFORMER_WEIGHT_COUNT]; + + for (S32 k = 0; k < sizeof(MORPH_MESHES)/sizeof(S32); k++) + { + S32 mesh_id = MORPH_MESHES[k]; + + + // LLPolyFaces are triangles + for (S32 j = 0; j < request->mAvatarIndices[k].size(); j += 3) + { + // indices for triangle corners + S32 index0 = request->mAvatarIndices[k][j+0]; + S32 index1 = request->mAvatarIndices[k][j+1]; + S32 index2 = request->mAvatarIndices[k][j+2]; + + // positions of triangle corners + LLVector3 vert0 = request->mAvatarPositions[k][index0]; + LLVector3 vert1 = request->mAvatarPositions[k][index1]; + LLVector3 vert2 = request->mAvatarPositions[k][index2]; + + // barycentric coord of closest point + F32 bary_a; + F32 bary_b; + + F32 this_distance_squared = LLTriangleClosestPoint(vert0, vert1, vert2, target, bary_a, bary_b); + + if (this_distance_squared < closest_distance_squared) + { + // record corner indices of this triangle + closest_vertex[0] = index0; + closest_vertex[1] = index1; + closest_vertex[2] = index2; + + // compute weights from barycentric coordinates + closest_vertex_weights[0] = 1.0f - bary_a - bary_b; + closest_vertex_weights[1] = bary_a; + closest_vertex_weights[2] = bary_b; + + // and closest mesh + closest_mesh = mesh_id; + + + closest_distance_squared = this_distance_squared; + } + + + } + + } + + + // create entry for found vertices + for (S32 k = 0; k < LL_DEFORMER_WEIGHT_COUNT; k++) + { + deform_table[i].mMesh[k] = (U8)closest_mesh; + deform_table[i].mVertex[k] = closest_vertex[k]; + deform_table[i].mWeight[k] = closest_vertex_weights[k]; + } + } + + return; +} + + + +BOOL LLDeformedVolume::deform(LLVolume* source, LLVOAvatar* avatar, + const LLMeshSkinInfo* skin, S32 face, LLPointer drawable) +{ + LLFastTimer t(FTM_SKIN_RIGGED_DEFORM); + + // get the deform table + deform_table_t* deform_table = getDeformTable(source, avatar, skin, face, drawable); + + // if it's not available, fail + if (!deform_table) + { + return FALSE; + } + + + // first copy the source volume + bool copy = false; + if (source->getNumVolumeFaces() != getNumVolumeFaces()) + { + copy = true; + } + + for (S32 i = 0; i < source->getNumVolumeFaces() && !copy; ++i) + { + const LLVolumeFace& src_face = source->getVolumeFace(i); + const LLVolumeFace& dst_face = getVolumeFace(i); + + if (src_face.mNumIndices != dst_face.mNumIndices || + src_face.mNumVertices != dst_face.mNumVertices) + { + copy = true; + } + } + + if (copy) + { + copyVolumeFaces(source); + } + + + + const LLVolumeFace& source_face = source->getVolumeFace(face); + const LLVolumeFace& destination_face = getVolumeFace(face); + + + // transforms volume space to avatar space + LLMatrix4a bind_shape_matrix_a; + bind_shape_matrix_a.loadu(skin->mBindShapeMatrix); + + // transforms back again + LLMatrix4 bind_shape_matrix_inverse = skin->mBindShapeMatrix; + bind_shape_matrix_inverse.invert_real(); + LLMatrix4a bind_shape_matrix_inverse_a; + bind_shape_matrix_inverse_a.loadu(bind_shape_matrix_inverse); + + // skeleton scaling factor - assumes all joints have same scale and that the scale is uniform in XYZ (currently true) + F32 scale = powf(skin->mInvBindMatrix[0].determinant(), 1.0f / 3.0f); + + + + for (S32 i = 0; i < source_face.mNumVertices; i++) + { + LLVector4a weighted_morph_delta; + weighted_morph_delta.clear(); + + F32 total_weight = 0; + + for (S32 j = 0; j < LL_DEFORMER_WEIGHT_COUNT; j++) + { + U16 closest_vertex = (*deform_table)[i].mVertex[j]; + S32 closest_mesh = (*deform_table)[i].mMesh[j]; + F32 closest_weight = (*deform_table)[i].mWeight[j]; + + // find the current mesh + LLPolyMesh* avatar_mesh = avatar->getMesh(closest_mesh); + // and get positions for both original and morphed vertices + const LLVector3* base_positions = avatar_mesh->getBaseCoords(); + const LLVector4* morph_positions = avatar_mesh->getCoords(); + + + // find the movement of this vertex due to morphing + LLVector4a base_position; + base_position.load3(base_positions[closest_vertex].mV); + LLVector4a morph_position; + morph_position.load3(morph_positions[closest_vertex].mV); + LLVector4a morph_delta; + morph_delta.setSub(morph_position, base_position); + + // weight this delta and add to total + morph_delta.mul(closest_weight); + weighted_morph_delta.add(morph_delta); + total_weight += closest_weight; + } + + // normalize + weighted_morph_delta.mul(1.0f/total_weight); + + // source position mapped to avatar space + LLVector4a source_position = source_face.mPositions[i]; + LLVector4a source_position_transformed; + bind_shape_matrix_a.affineTransform(source_position, source_position_transformed); + source_position_transformed.mul(scale); + + // modify by movement delta + LLVector4a destination_position_transformed; + destination_position_transformed.setAdd(source_position_transformed, weighted_morph_delta); + + // map back to volume space + destination_position_transformed.mul(1.0f / scale); + LLVector4a destination_position; + bind_shape_matrix_inverse_a.affineTransform(destination_position_transformed, destination_position); + + destination_face.mPositions[i] = destination_position; + } + + + return TRUE; // success! +} + U32 LLVOVolume::getPartitionType() const { if (isHUDAttachment()) diff -r 6f4836fb74ce indra/newview/llvovolume.h --- a/indra/newview/llvovolume.h Sat Apr 14 22:23:24 2012 -0400 +++ b/indra/newview/llvovolume.h Tue Apr 24 18:25:35 2012 -0500 @@ -34,6 +34,7 @@ #include "m3math.h" // LLMatrix3 #include "m4math.h" // LLMatrix4 #include +#include "lldeformerworker.h" class LLViewerTextureAnim; class LLDrawPool; @@ -62,6 +63,72 @@ void update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, const LLVolume* src_volume); }; + + +const S32 LL_DEFORMER_WEIGHT_COUNT = 3; + + +class LLDeformedVolume : public LLVolume +{ +public: + LLDeformedVolume() + : LLVolume(LLVolumeParams(), 0.f) + { + } + + // used as a key for the deform table cache + class LLDeformTableCacheIndex + { + public: + LLDeformTableCacheIndex(const LLUUID& id, S32 face, S32 vertex_count) : + mID(id), mFace(face), mVertexCount(vertex_count) {} + + LLUUID mID; + S32 mFace; + S32 mVertexCount; + + bool operator<(const LLDeformTableCacheIndex& lhs) const + { + if (mID < lhs.mID) + return TRUE; + if (mID > lhs.mID) + return FALSE; + if (mFace < lhs.mFace) + return TRUE; + if (mFace > lhs.mFace) + return FALSE; + if (mVertexCount < lhs.mVertexCount) + return TRUE; + + return FALSE; + } + }; + + + // these are entries in the deformer table. + // they map vertices on the volume mesh to + // vertices on the avatar mesh(es). + struct LLDeformMap + { + U8 mMesh[LL_DEFORMER_WEIGHT_COUNT]; + U16 mVertex[LL_DEFORMER_WEIGHT_COUNT]; + F32 mWeight[LL_DEFORMER_WEIGHT_COUNT]; + }; + + typedef std::vector deform_table_t; + typedef std::map deform_cache_t; + deform_cache_t mDeformCache; + + BOOL deform(LLVolume* source, LLVOAvatar* avatar, const LLMeshSkinInfo* skin, S32 face, LLPointer drawable); + + deform_table_t* getDeformTable(LLVolume* source, LLVOAvatar* avatar, const LLMeshSkinInfo* skin, + S32 face, LLPointer drawable); + void computeDeformTable(LLPointer request); + +}; + + + // Base class for implementations of the volume - Primitive, Flexible Object, etc. class LLVolumeInterface { @@ -316,6 +383,9 @@ //clear out rigged volume and revert back to non-rigged state for picking/LOD/distance updates void clearRiggedVolume(); + + // deformed volume (rigged attachments follow avatar morph shape changes + LLDeformedVolume* getDeformedVolume(); protected: S32 computeLODDetail(F32 distance, F32 radius); @@ -360,6 +430,7 @@ S32 mMDCImplCount; LLPointer mRiggedVolume; + LLPointer mDeformedVolume; // statics public: diff -r 6f4836fb74ce indra/newview/skins/default/xui/en/floater_model_preview.xml --- a/indra/newview/skins/default/xui/en/floater_model_preview.xml Sat Apr 14 22:23:24 2012 -0400 +++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml Tue Apr 24 18:25:35 2012 -0500 @@ -1162,6 +1162,13 @@ label_text.text_color="White" name="upload_joints" top_pad="15"/> +