10 Skinning

10.2 Creating a skin mesh

As mentioned above, skin meshes within ArtiSynth are implemented using the SkinMeshBody component. Applications typically create a skin mesh in code according to the following steps:

  1. 1.

    Create an instance of SkinMeshBody and assign its underlying mesh, usually within the constructor;

  2. 2.

    Add references to the required master bodies;

  3. 3.

    Compute the master body connections

This is illustrated by the following example:

  MechModel mech;
  PolygonalMesh mesh;
  // ... initialize mesh ...
  // create the body with an underlying mesh:
  SkinMeshBody skinMesh = new SkinMeshBody(mesh);
  // add references to the master bodies:
  skinMesh.addMasterBody (rigidBody1);
  skinMesh.addMasterBody (rigidBody2);
  skinMesh.addMasterBody (femModel1);
  // compute the weighted connections for each vertex:
  // add to the MechModel
  mech.addMeshBody (skinMesh)

Master body references are added using addMasterBody(). When all the master bodies have been added, the method
computeAllVertexConnections() computes the weighted connections to each vertex. The connection weights w_{k} for each vertex are determined by a weighting function, based on the distances d_{k} between the vertex and each master body. The default weighting function is inverse-square weighting, which first computes a set of raw weights w_{k}^{*} according to

w_{k}^{*}=\frac{d_{\text{min}}^{2}}{d_{k}^{2}}, (10.8)

where d_{\text{min}}\equiv\min(d_{j}) is the minimum master body distance, and then normalizes these to determine w_{k}:

w_{k}=\frac{w^{*}_{k}}{\sum_{j}w^{*}_{j}}. (10.9)

Other weighting functions can be specified, as described in Section 10.3.

SkinMeshBody provides the following set of methods to set and query its master body configuration:

  void addMasterBody (ModelComponent body)    // add a master body
  int numMasterBodies()                       // query number of master bodies
  boolean hasMasterBody (ModelComponent body) // query master body presence
  ModelComponent getMasterBody (int idx)      // get a master body by index
  RigidTransform3d getBasePose (Frame frame)  // query base pose for frame
  void setBasePose (Frame frame, RigidTransform3d T) // set base pose for frame

When a Frame master body is added using addMasterBody(), its initial, or base, pose (corresponding to {\bf R}_{0} and {\bf p}_{F0} in Section 10.1) is set from its current pose. If necessary, applications can later query and reset the base pose using the methods getBasePose() and setBasePose().

Internally, each vertex is connected to the master bodies by a PointSkinAttachment, which contains a list of
PointSkinAttachment.SkinConnection components describing each master connection. Applications can obtain the PointSkinAttachment for each vertex using the method

  PointSkinAttachment getVertexAttachment (int vidx)

where vidx is the vertex index, which must be in the range 0 to numv-1, with numv the number of vertices as returned by numVertices(). Methods also exist to query and set each vertex’s base (i.e., initial) position {\bf p}_{0}:

  Point3d getVertexBasePosition (int vidx)
  void setVertexBasePosition (int vidx, Point3d pos)

10.2.1 Example: skinning over rigid bodies

Figure 10.3: RigidBodySkinning model loaded into ArtiSynth (left), then run with excitation set to 0.7 (middle). The right image shows the result of changing the frameBlending property from LINEAR to DUAL_QUATERNION_LINEAR


An example of skinning a mesh over two rigid bodies is given by artisynth.demos.tutorial.RigidBodySkinning. It consists of a SkinMeshBody placed around two rigid bodies connected by a hinge joint to form a toy “arm”, with a Muscle added to move the lower body with respect to the upper. The code for the build() method is given below:

1    public void build (String[] args) throws IOException {
2       MechModel mech = new MechModel ("mech");
3       addModel (mech);
5       // set damping parameters for rigid bodies
6       mech.setFrameDamping (10);
7       mech.setRotaryDamping (100.0);
9       // create a toy "arm" conisting of upper and lower rigid bodies connected
10       // by a revolute joint:
11       double len = 2.0;
12       RigidBody upper = addBody (mech, "upper");
13       upper.setPose (new RigidTransform3d (0, 0, len/2));
14       upper.setDynamic (false);  // upper body is fixed
16       RigidBody lower = addBody (mech, "lower");
17       // reposition the lower body"
18       double angle = Math.toRadians(225);
19       double sin = Math.sin(angle);
20       double cos = Math.cos(angle);
21       lower.setPose (new RigidTransform3d (sin*len/2, 0, cos*len/2, 0, angle, 0));
23       // add the revolute joint between the upper and lower bodies:
24       HingeJoint joint =
25          new HingeJoint (lower, upper, new Point3d(), Vector3d.Y_UNIT);
26       joint.setName ("elbow");
27       mech.addBodyConnector (joint);
29       // add two frame markers and a "muscle" to move the lower body
30       FrameMarker mku = mech.addFrameMarker (
31          upper, new Point3d(-len/20, 0, len/2.4));
32       FrameMarker mkl = mech.addFrameMarker (
33          lower, new Point3d(len/20, 0, -len/4));
34       Muscle muscle = new Muscle("muscle");
35       muscle.setMaterial (new SimpleAxialMuscle (1000.0, 0, 2000.0));
36       mech.attachAxialSpring (mku, mkl, muscle);
38       // create an ellipsoidal base mesh for the SkinMeshBody by scaling a
39       // spherical mesh
40       PolygonalMesh mesh = MeshFactory.createSphere (1.0, 12, 12);
41       mesh.scale (1, 1, 2.5);
42       mesh.transform (
43          new RigidTransform3d (-0.6, 0, 0, 0, Math.toRadians(22.5),0));
45       // create the skinMesh, with the upper and lower bodies as master bodies
46       SkinMeshBody skinMesh = new SkinMeshBody (mesh);
47       skinMesh.addMasterBody (upper);
48       skinMesh.addMasterBody (lower);
49       skinMesh.computeAllVertexConnections();
50       mech.addMeshBody (skinMesh);
52       // add a control panel to adjust the muscle excitation and frameBlending
53       ControlPanel panel = new ControlPanel();
54       panel.addWidget (muscle, "excitation");
55       panel.addWidget (skinMesh, "frameBlending");
56       addControlPanel (panel);
58       // set up render properties
59       RenderProps.setFaceStyle (skinMesh, Renderer.FaceStyle.NONE);
60       RenderProps.setDrawEdges (skinMesh, true);
61       RenderProps.setLineColor (skinMesh, Color.CYAN);
62       RenderProps.setSpindleLines (muscle, 0.06, Color.RED);
63       RenderProps.setSphericalPoints (mech, 0.05, Color.WHITE);
64       RenderProps.setFaceColor (joint, Color.BLUE);
65       joint.setShaftLength(len/3);
66       joint.setShaftRadius(0.05);
67       RenderProps.setFaceColor (mech, new Color (0.8f, 0.8f, 0.8f));
68    }

A MechModel is created in the usual way (lines 2-7). To this is added a very simple toy “arm” consisting of an upper and lower body connected by a hinge joint (lines 9-27), with a simple point-to-point muscle attached between frame markers on the upper and lower bodies to provide a means of moving the arm (lines 29-36). Creation of the arm bodies uses an addBody() method which is not shown.

The mesh to be skinned is an ellipsoid, created using the FemFactory method createSphere() to produce a spherical mesh which is then scaled and repositioned (lines 38-43). The skin body itself is then created around this mesh, with the upper and lower bodies assigned as master bodies and the connections computed using computeAllVertexConnections() (lines 45-50).

A control panel is added to allow control over the muscle’s excitation as well as the skin body’s frameBlending property (lines 52-56). Finally, render properties are set (lines 58-67): the skin mesh is made transparent by setting its faceStyle and drawEdges properties to NONE and true, respectively, with cyan colored edges; the muscle is rendered as a red spindle; the joint is drawn as a blue cylinder and the bodies are colored light gray.

To run this example in ArtiSynth, select All demos > tutorial > RigidBodySkinning from the Models menu. The model should load and initially appear as in Figure 10.3 (left). When running the simulation, the arm can be flexed by adjusting the muscle excitation property in the control panel, causing the skin mesh to deform (Figure 10.3, middle). Changing the frameBlending property from its default value of LINEAR to DUAL_QUATERNION_LINEAR causes the mesh deformation to become fatter and less prone to creasing (Figure 10.3, right).