SkinT.cc 7.71 KB
Newer Older
Jan Möbius's avatar
Jan Möbius committed
1
2
3
#ifdef USE_OPENMP
#include <omp.h>
#endif
Dirk Wilden's avatar
Dirk Wilden committed
4
5
6
7
8
#define SKINT_C

//-----------------------------------------------------------------------------

template<typename MeshT>
9
10
SkinT<MeshT>::SkinT(SkeletonT<PointT> *_skeleton, MeshT *_mesh, int _objectID) :
  BaseSkin(_objectID),
Dirk Wilden's avatar
Dirk Wilden committed
11
12
  skeleton_(_skeleton),
  mesh_(_mesh),
13
  objectId_(_objectID),
14
15
  lastmethod_(M_LBS),
  weightsComputed_(false)
16

Dirk Wilden's avatar
Dirk Wilden committed
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
{
}

//-----------------------------------------------------------------------------

template<typename MeshT>
SkinT<MeshT>::~SkinT()
{

}

//-----------------------------------------------------------------------------

template<typename MeshT>
typename SkinT<MeshT>::Skeleton* SkinT<MeshT>::skeleton()
{
  return skeleton_;
}

//-----------------------------------------------------------------------------

/**
 * @brief Attach the given mesh as skin to this skeleton
 *
 * The mesh will be equipped with two properties. One holding the original vertex coordinates, a second
 * holding the per vertex weights for the joints.
 * The bone weights are derived from the segment information stored with the bones and in a vertex based
 * mesh property called "Face Segment".
 */
template<typename MeshT>
void SkinT<MeshT>::attachSkin()
{
  // create the skins properties
  OpenMesh::VPropHandleT<DefaultPose> propDefaultPose;
  OpenMesh::VPropHandleT<SkinWeights> propWeights;

  //make sure properties are there
  if( !mesh_->get_property_handle(propDefaultPose, DEFAULTPOSE_PROP) )
    mesh_->add_property(propDefaultPose, DEFAULTPOSE_PROP);

  if (! mesh_->get_property_handle(propWeights, SKIN_WEIGHTS_PROP))
    mesh_->add_property(propWeights, SKIN_WEIGHTS_PROP);

  // backup the default pose
  for(typename MeshT::VertexIter it = mesh_->vertices_begin(); it != mesh_->vertices_end(); ++it)
    mesh_->property(propDefaultPose, it) = DefaultPose(mesh_->point(it), mesh_->normal(it));
}

//-----------------------------------------------------------------------------

template<typename MeshT>
void SkinT<MeshT>::deformSkin()
{
  deformSkin(lastAnimationHandle_, lastmethod_);
}

//-----------------------------------------------------------------------------

/**
 * @brief Deforms the given skin to the pose defined by the skeleton
 *
 * This method will only work properly if the mesh has been prepared to be a skin by a call of
 * SkeletonT::PrepareSkin.
 *
 * Two algorithms are implemented:
Jan Möbius's avatar
Jan Möbius committed
82
83
 * - Linear Blend Skinning (LBS) or Skeletal Space Deformation (SSD), e.g. see \cite LCF00 "[2]", \cite RLN06 "[3]"
 * - Spherical Blend Skinning (SBS), see \cite KZ05 "[1]"
Dirk Wilden's avatar
Dirk Wilden committed
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
 *
 * @par Linear Blend Skinning
 * Every vertex will be transformed by every joint with a non-null weight. First
 * of all the vertex is transformed into the coordinate system of the joint, then it is transformed back
 * to the global coordinate system of the pose given by the active frame in the given animation.
 *
 * @par
 * Since the mesh is given in the default pose the default pose joints are used to calculate the vertices
 * position relative to the joint. Then they are transformed back into global coordinates using the joints
 * in the current pose.
 *
 * @par
 * The global matrices of the default pose and animation are not updated by this method, make sure to
 * update them before deforming the skin or the results will be unexpected.
 *
 * \f[ v_{pose} = M_{pose} \cdot M^{-1}_{default} \cdot v_{default} \f]
 *
 *
 * @param _hAni The animation frame to be used as target
 * @param _method deformation method to be used @see BaseSkin.hh
 */
template<typename MeshT>
void SkinT<MeshT>::deformSkin(const AnimationHandle &_hAni, Method _method)
{
108
109
110
111
112
113
  // Do not transform skin if skin weights were not computed before
  // as the mesh entirely disappears since all vertices collapse to origin
  if(!weightsComputed_) {
      return;
  }

Dirk Wilden's avatar
Dirk Wilden committed
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
  lastAnimationHandle_ = _hAni;
  lastmethod_          = _method;

  // first get the properties
  OpenMesh::VPropHandleT<DefaultPose> propDefaultPose;
  OpenMesh::VPropHandleT<SkinWeights> propWeights;

  if(!mesh_->get_property_handle(propDefaultPose, DEFAULTPOSE_PROP) ||
     !mesh_->get_property_handle(propWeights, SKIN_WEIGHTS_PROP))
    return; // missing properties

  Pose* pose = skeleton_->pose(_hAni);

  int verticesWithoutWeights = 0;
  
  // for every vertex
  typename MeshT::VertexIter it;
Jan Möbius's avatar
Jan Möbius committed
131
132
133
134
135
136
137
#ifdef USE_OPENMP
  std::vector< OpenMesh::VertexHandle > vhandles; vhandles.clear(); vhandles.reserve(mesh_->n_vertices());
  for(it = mesh_->vertices_begin(); it != mesh_->vertices_end(); ++it){vhandles.push_back(it.handle());}
  int vhcount = (int) vhandles.size();
  #pragma omp parallel for
  for (int vhindex = 0; vhindex < vhcount; vhindex++)
#else
Dirk Wilden's avatar
Dirk Wilden committed
138
  for(it = mesh_->vertices_begin(); it != mesh_->vertices_end(); ++it)
Jan Möbius's avatar
Jan Möbius committed
139
#endif
Dirk Wilden's avatar
Dirk Wilden committed
140
  {
Jan Möbius's avatar
Jan Möbius committed
141
142
143
144
145
146
    #ifdef USE_OPENMP
      const OpenMesh::VertexHandle currentVertexH = vhandles[vhindex];
    #else
      const OpenMesh::VertexHandle currentVertexH = it.handle();
    #endif

Dirk Wilden's avatar
Dirk Wilden committed
147
    // based on its position in the default pose
Jan Möbius's avatar
Jan Möbius committed
148
149
    OpenMesh::Vec3d default_point  = mesh_->property(propDefaultPose, currentVertexH).point,
                    default_normal = mesh_->property(propDefaultPose, currentVertexH).normal;
Dirk Wilden's avatar
Dirk Wilden committed
150
151
152
153
154
155

    OpenMesh::Vec3d point(0, 0, 0), normal(0, 0, 0);	// the new position and normal

    if( _method == M_LBS ) {

      // Linear blend skinning
Jan Möbius's avatar
Jan Möbius committed
156
      SkinWeights &weights = mesh_->property(propWeights, currentVertexH);
Dirk Wilden's avatar
Dirk Wilden committed
157

Jan Möbius's avatar
Jan Möbius committed
158
      #ifndef USE_OPENMP
Dirk Wilden's avatar
Dirk Wilden committed
159
      if (weights.size() == 0)
Jan Möbius's avatar
Jan Möbius committed
160
      {
Dirk Wilden's avatar
Dirk Wilden committed
161
        verticesWithoutWeights++;
Jan Möbius's avatar
Jan Möbius committed
162
163
      }
      #endif
Dirk Wilden's avatar
Dirk Wilden committed
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179

      SkinWeights::iterator it_w;
      for(it_w = weights.begin(); it_w != weights.end(); ++it_w)
      {
        const Matrix &unified = pose->unifiedMatrix(it_w->first);

        point  += it_w->second * unified.transform_point(default_point);
        normal += it_w->second * unified.transform_vector(default_normal);
      }

    }else if( _method == M_DBS ) {
      // Dual quaternion blend skinning

      std::vector<double>         weightVector;
      std::vector<DualQuaternion> dualQuaternions;

Jan Möbius's avatar
Jan Möbius committed
180
      SkinWeights &weights = mesh_->property(propWeights, currentVertexH);
Dirk Wilden's avatar
Dirk Wilden committed
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
      SkinWeights::iterator it_w;

      for(it_w = weights.begin(); it_w != weights.end(); ++it_w){
        weightVector.push_back(    it_w->second );
        dualQuaternions.push_back( pose->unifiedDualQuaternion(it_w->first) );
      }

      DualQuaternion dq = DualQuaternion::interpolate(weightVector, dualQuaternions);

      point  = dq.transform_point(default_point);
      normal = dq.transform_vector(default_normal);

    } else {
      std::cerr << "ERROR: Unknown skinning method!" << std::endl;
    }

Jan Möbius's avatar
Jan Möbius committed
197
198
    mesh_->set_point(currentVertexH, point);
    mesh_->set_normal(currentVertexH, normal);
Dirk Wilden's avatar
Dirk Wilden committed
199
200
  }

Jan Möbius's avatar
Jan Möbius committed
201
202
203
204
  #ifndef USE_OPENMP
    if ( verticesWithoutWeights > 0 )
      std::cerr << "Deform skin: " << verticesWithoutWeights << " vertices without skin weights." << std::endl;
  #endif
Dirk Wilden's avatar
Dirk Wilden committed
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230

  mesh_->update_face_normals();
}

//-----------------------------------------------------------------------------

/**
 * @brief The given mesh will be reset to its default pose and all skin properties are removed
 */
template<typename MeshT>
void SkinT<MeshT>::releaseSkin()
{
  // create the skins properties
  OpenMesh::VPropHandleT<DefaultPose> propDefaultPose;

  // try to restore the default pose
  if(mesh_->get_property_handle(propDefaultPose, DEFAULTPOSE_PROP))
  {
    typename MeshT::VertexIter it;
    for(it = mesh_->vertices_begin(); it != mesh_->vertices_end(); ++it)
    {
      mesh_->set_point(it, mesh_->property(propDefaultPose, it).point);
      mesh_->set_normal(it, mesh_->property(propDefaultPose, it).normal);
    }
    mesh_->remove_property(propDefaultPose);
  }
231
232
233
234

  // Remove the skin weights
  OpenMesh::VPropHandleT<SkinWeights> propSkinWeights;
  mesh_->remove_property(propSkinWeights);
Dirk Wilden's avatar
Dirk Wilden committed
235
236
237
238
}

//-----------------------------------------------------------------------------