diff -r 6b887df85f95 indra/llmath/m4math.cpp --- a/indra/llmath/m4math.cpp Mon Oct 31 16:08:55 2011 -0700 +++ b/indra/llmath/m4math.cpp Sat Dec 31 21:15:11 2011 -0600 @@ -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 6b887df85f95 indra/llmath/m4math.h --- a/indra/llmath/m4math.h Mon Oct 31 16:08:55 2011 -0700 +++ b/indra/llmath/m4math.h Sat Dec 31 21:15:11 2011 -0600 @@ -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 6b887df85f95 indra/newview/lldrawpoolavatar.cpp --- a/indra/newview/lldrawpoolavatar.cpp Mon Oct 31 16:08:55 2011 -0700 +++ b/indra/newview/lldrawpoolavatar.cpp Sat Dec 31 21:15:11 2011 -0600 @@ -1291,7 +1291,9 @@ } } -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) @@ -1344,9 +1346,12 @@ m.m[4], m.m[5], m.m[6], m.m[8], m.m[9], m.m[10] }; - LLMatrix3 mat_normal(mat3); + LLMatrix3 mat_normal(mat3); + + LLDeformedVolume* deformed_volume = vobj->getDeformedVolume(); + deformed_volume->deform(volume, avatar, skin, face->getTEOffset()); - face->getGeometryVolume(*volume, face->getTEOffset(), mat_vert, mat_normal, offset, true); + face->getGeometryVolume(*deformed_volume, face->getTEOffset(), mat_vert, mat_normal, offset, true); } if (sShaderLevel <= 0 && face->mLastSkinTime < avatar->getLastSkinTime()) @@ -1424,6 +1429,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); @@ -1486,7 +1495,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); stop_glerror(); diff -r 6b887df85f95 indra/newview/lldrawpoolavatar.h --- a/indra/newview/lldrawpoolavatar.h Mon Oct 31 16:08:55 2011 -0700 +++ b/indra/newview/lldrawpoolavatar.h Sat Dec 31 21:15:11 2011 -0600 @@ -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 renderRigged(LLVOAvatar* avatar, U32 type, bool glow = false); void renderRiggedSimple(LLVOAvatar* avatar); diff -r 6b887df85f95 indra/newview/llpolymesh.h --- a/indra/newview/llpolymesh.h Mon Oct 31 16:08:55 2011 -0700 +++ b/indra/newview/llpolymesh.h Sat Dec 31 21:15:11 2011 -0600 @@ -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 6b887df85f95 indra/newview/llvoavatar.cpp --- a/indra/newview/llvoavatar.cpp Mon Oct 31 16:08:55 2011 -0700 +++ b/indra/newview/llvoavatar.cpp Sat Dec 31 21:15:11 2011 -0600 @@ -5187,6 +5187,10 @@ return mMeshLOD[MESH_ID_UPPER_BODY]->mMeshParts[0]->getMesh(); } +LLPolyMesh* LLVOAvatar::getMesh(S32 which) +{ + return mMeshLOD[which]->mMeshParts[0]->getMesh(); +} //----------------------------------------------------------------------------- // LLVOAvatar::getPosGlobalFromAgent() @@ -5715,9 +5719,10 @@ mLastSkeletonSerialNum = mSkeletonSerialNum; mRoot.updateWorldMatrixChildren(); } - + dirtyMesh(); updateHeadOffset(); + rebuildRiggedAttachments(); } //----------------------------------------------------------------------------- diff -r 6b887df85f95 indra/newview/llvoavatar.h --- a/indra/newview/llvoavatar.h Mon Oct 31 16:08:55 2011 -0700 +++ b/indra/newview/llvoavatar.h Sat Dec 31 21:15:11 2011 -0600 @@ -192,6 +192,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 6b887df85f95 indra/newview/llvovolume.cpp --- a/indra/newview/llvovolume.cpp Mon Oct 31 16:08:55 2011 -0700 +++ b/indra/newview/llvovolume.cpp Sat Dec 31 21:15:11 2011 -0600 @@ -3811,7 +3811,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 +3848,220 @@ } } + +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) +{ + 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 + + + // tables are fairly small, we keep them around in case the user is switching between meshes for poorman's animation + U32 MAX_CACHE_SIZE = 20; + + if (mDeformCache.size() > MAX_CACHE_SIZE) + { + // 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 is favor + // verts with similar skin weights and/or normals (so verts on the left leg + // don't move with the right leg by accident.) + + const LLVolumeFace& source_face = source->getVolumeFace(face); + + + + // build mapping + deform_table.resize(source_face.mNumVertices); + + // maps volume space to object (avatar) space + LLMatrix4a bind_shape_matrix_a; + bind_shape_matrix_a.loadu(skin->mBindShapeMatrix); + // skeleton scaling factor - lots of assumptions in this, but necessary for efficiency + F32 scale = powf(skin->mInvBindMatrix[0].determinant(), 1.0f / 3.0f); + + // 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 vertex on the avatar mesh(es) + for (S32 i = 0; i < source_face.mNumVertices; i++) + { + 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); + + F32 closest_distance; + S32 closest_vertex; + S32 closest_mesh; + + BOOL first_time = TRUE; + + for (S32 k = 0; k < sizeof(MORPH_MESHES)/sizeof(S32); k++) + { + S32 mesh_id = MORPH_MESHES[k]; + + LLPolyMesh* avatar_mesh = avatar->getMesh(mesh_id); + + const LLVector3* base_positions = avatar_mesh->getBaseCoords(); + + for (S32 j = 0; j < avatar_mesh->getNumVertices(); j++) + { + LLVector4a base_position; + base_position.load3(base_positions[j].mV); + + LLVector4a delta; + delta.setSub(source_position_transformed, base_position); + + F32 distance = delta.getLength3().getF32(); + + if (first_time || // first time through loop is always closest + (distance < closest_distance)) + { + closest_distance = distance; + closest_vertex = j; + closest_mesh = mesh_id; + first_time = FALSE; + } + } + } + // create entry for found vertex + deform_table[i].mVertex = closest_vertex; + deform_table[i].mMesh = (U8)closest_mesh; + } + + + return deform_table; +} + + + +void LLDeformedVolume::deform(LLVolume* source, LLVOAvatar* avatar, const LLMeshSkinInfo* skin, S32 face) +{ + LLFastTimer t(FTM_SKIN_RIGGED_DEFORM); + + 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 - lots of assumptions in this, but necessary for efficiency + F32 scale = powf(skin->mInvBindMatrix[0].determinant(), 1.0f / 3.0f); + + + // get the deform table + LLDeformTableCacheIndex cache_index(source->getParams().getSculptID(), face, source->getVolumeFace(face).mNumVertices); + deform_table_t& deform_table = getDeformTable(source, avatar, skin, face); + + for (S32 i = 0; i < source_face.mNumVertices; i++) + { + U16 closest_vertex = deform_table[i].mVertex; + S32 closest_mesh = deform_table[i].mMesh; + + // 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); + + // 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, 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; + } + +} + U32 LLVOVolume::getPartitionType() const { if (isHUDAttachment()) diff -r 6b887df85f95 indra/newview/llvovolume.h --- a/indra/newview/llvovolume.h Mon Oct 31 16:08:55 2011 -0700 +++ b/indra/newview/llvovolume.h Sat Dec 31 21:15:11 2011 -0600 @@ -62,6 +62,68 @@ void update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, const LLVolume* src_volume); }; + + + + +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; + U16 mVertex; + }; + + typedef std::vector deform_table_t; + typedef std::map deform_cache_t; + deform_cache_t mDeformCache; + + void deform(LLVolume* source, LLVOAvatar* avatar, const LLMeshSkinInfo* skin, S32 face); + +private: + deform_table_t& getDeformTable(LLVolume* source, LLVOAvatar* avatar, const LLMeshSkinInfo* skin, S32 face); +}; + + + // Base class for implementations of the volume - Primitive, Flexible Object, etc. class LLVolumeInterface { @@ -315,6 +377,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); @@ -359,6 +424,7 @@ S32 mMDCImplCount; LLPointer mRiggedVolume; + LLPointer mDeformedVolume; // statics public: