8 Skinning

8.4 Markers and point attachments

In addition to controlling the positions of mesh vertices, a SkinMeshBody can also be used to control the positions of dynamic point components, including markers and other points which can be attached to the skin body. For both markers and attached points, any applied forces are propagated back onto the skin body’s master bodies, using the principle of virtual work. This allows skin bodies to be fully incorporated into a dynamic model.

Markers and point attachments can be created even if the SkinMeshBody does not have a mesh, a fact that can be used in situations where a mesh is unnecessary, such as when employing skinning techniques for muscle wrapping (Section 8.7).

8.4.1 Markers

Markers attached to a skin body are instances of SkinMarker, and are contained in the body’s subcomponent list markers (analogous to the markers list for FEM models). Markers can be created and maintained using the following SkinMeshBody methods:

  // create markers:
  SkinMarker addMarker (Point3d pos)
  SkinMarker addMarker (String name, Point3d pos)
  SkinMarker addMarker (
     String name, Point3d pos, VectorNd weights)
  // remove markers:
  boolean removeMarker (SkinMarker mkr)
  void clearMarkers();
  // access the marker list:
  PointList<SkinMarker> markers()

The addMarker methods each create and return a SkinMarker which is added to the skin body’s list of markers. The marker’s initial position is specified by pos, while the second and third methods also allow a name to be specified. Connections between the marker and the master bodies are created in the same way as for mesh vertices, with the connection weights either being determined by the skin body’s weighting function (as returned by getWeightingFunction()), or explicitly specified by the argument weights (third method).

Once created, markers can be removed individually or all together by the removeMarker() and clearMarkers() methods. The entire marker list can be accessed on a read-only basis by the method markers().

8.4.2 Point attachments

In addition to markers, applications can also attach any regular Point component (including particles and FEM nodes) to a skin body by using one of its createPointAttachment methods:

  PointSkinAttachment createPointAttachment (Point pnt)
  PointSkinAttachment createPointAttachment (Point pnt, VectorNd weights)

Both of these create a PointSkinAttachment that connects the point pnt to the master bodies in the same way as for mesh vertices and markers, with the connection weights either being determined by the skin body’s weighting function or explicitly specified by the argument weights in the second method.

Once created, the point attachment must also be added to the underlying MechModel, as illustrated by the following code fragment:

  MechModel mech;
  SkinMeshBody skinBody;
  Point pnt;
  // ... initialize ...
  PointSkinAttacment a = skinBody.createPointAttachment(pnt);
  mech.addAttachment (a);

8.4.3 Example: skinning rigid bodies and FEM models

Figure 8.4: AllBodySkinning model as first loaded into ArtiSynth (left), and after the simulation is run and the model has fallen under gravity (right). The skin mesh is rendered in cyan using only its edges.

An example of skinning a mesh over both rigid bodies and FEM models is given by the demo model
artisynth.demos.tutorial.AllBodySkinning. It consists of a skin mesh placed around a tubular FEM model connected to rigid bodies connected at each end, and a marker attached to the mesh tip. The code for the build() method is given below:

1    public void build (String[] args) {
2       MechModel mech = new MechModel ("mech");
3       addModel (mech);
4
5       // size and density parameters
6       double len = 1.0;
7       double rad = 0.15;
8       double density = 1000.0;
9
10       // create a tubular FEM model, and rotate it so it lies along the x axis
11       FemModel3d fem = FemFactory.createHexTube (
12          null, len, rad/3, rad, 8, 8, 2);
13       fem.transformGeometry (new RigidTransform3d (0, 0, 0, 0, Math.PI/2, 0));
14       mech.addModel (fem);
15
16       // create two rigid body blocks
17       RigidBody block0 =
18          RigidBody.createBox ("block0", len/2, 2*rad, 2*rad, density);
19       block0.setPose (new RigidTransform3d (-3*len/4, 0, 0));
20       mech.addRigidBody (block0);
21       block0.setDynamic (false);
22
23       RigidBody block1 =
24          RigidBody.createBox ("block1", len/2, 2*rad, 2*rad, density);
25       block1.setPose (new RigidTransform3d (3*len/4, 0, 0));
26       mech.addRigidBody (block1);
27
28       // attach the blocks to each end of the FEM model
29       for (FemNode3d n : fem.getNodes()) {
30          if (Math.abs(n.getPosition().x-len/2) < EPS) {
31             mech.attachPoint (n, block1);
32          }
33          if (Math.abs(n.getPosition().x+len/2) < EPS) {
34             mech.attachPoint (n, block0);
35          }
36       }
37       fem.setMaterial (new LinearMaterial (500000.0, 0.49));
38
39       // create base mesh to be skinned
40       PolygonalMesh mesh =
41          MeshFactory.createRoundedCylinder (
42             /*r=*/0.4, 2*len, /*nslices=*/16, /*nsegs=*/15, /*flatbotton=*/false);
43       // rotate mesh so its long axis lies along the x axis
44       mesh.transform (new RigidTransform3d (0, 0, 0, 0, Math.PI/2, 0));
45
46       // create the skinBody, with the FEM model and blocks as master bodies
47       SkinMeshBody skinBody = new SkinMeshBody ("skin", mesh);
48       skinBody.addMasterBody (fem);
49       skinBody.addMasterBody (block0);
50       skinBody.addMasterBody (block1);
51       skinBody.computeAllVertexConnections();
52       mech.addMeshBody (skinBody);
53
54       // add a marker point to the end of the skin mesh
55       SkinMarker marker =
56          skinBody.addMarker ("marker", new Point3d(1.4, 0.000, 0.000));
57
58       // set up rendering properties
59       RenderProps.setFaceStyle (skinBody, FaceStyle.NONE);
60       RenderProps.setDrawEdges (skinBody, true);
61       RenderProps.setLineColor (skinBody, Color.CYAN);
62       fem.setSurfaceRendering (FemModel.SurfaceRender.Shaded);
63       RenderProps.setFaceColor (fem, new Color (0.5f, 0.5f, 1f));
64       RenderProps.setSphericalPoints (marker, 0.05, Color.RED);
65    }

A MechModel is first created and length and density parameters are defined (lines 2-8). Then a tubular FEM model is created, by using the FemFactory method createHexTube() and transforming the result by rotating it by 90 degrees about the y axis (lines 10-14). Two rigid body blocks are then created (lines 16-26) and attached to the ends of the FEM model by finding and attaching the left and rightmost nodes (lines 28-24). The model is anchored to ground by setting the left block to be non-dynamic (line 21).

The mesh to be skinned is a rounded cylinder, created using the MeshFactory method createRoundedCylinder() and rotating the result by 90 degrees about the y axis (lines 39-44). This is then used to create the skin body itself, to which both rigid bodies and the FEM model are added as master bodies and the vertex connections are computed using a call to computeAllVertexConnections() (lines 46-52). A marker is then added to the tip of the skin body, using the addMarker() method (lines 54-56). Finally render properties are set (lines 58-64): The mesh is made transparent by drawing only its edges in cyan; the FEM model surface mesh is rendered in blue-grey, and the tip marker is drawn as a red sphere.

To run this example in ArtiSynth, select All demos > tutorial > AllBodySkinning from the Models menu. The model should load and initially appear as in Figure 8.4 (left). When running the simulation, the FEM and the rightmost rigid body fall under gravity, causing the skin mesh to deform. The pull tool can then be used to move things around by applying forces to the master bodies or the skin mesh itself.

8.4.4 Mesh-based markers and attachments

For the markers and point attachments described above, the connections to the underlying master bodies are created in the same manner as connections for individual mesh vertices. This means that the resulting markers and attached points move independently of the mesh vertices, as though they were vertices in their own right.

An advantage to this is that such markers and attachments can be created even if the SkinMeshBody does not even have a mesh, as noted above. However, a disadvantage is that such markers will not remain tightly connected to vertex-based features (such as the faces of a PolygonalMesh or the line segments of a PolylineMesh). For example, consider a marker defined by

  SkinMarker mkr = skinBody.addMarker (pos);

where pos is a point that is initially located on a face of the body’s mesh. As the master bodies move and the mesh deforms, the resulting marker may not remain strictly on the face. In many cases, this may not be problematic or the deviation may be too small to matter. However, if it is desirable for markers or point attachments to be tightly bound to mesh features, they can instead be created with the following methods:

  // create mesh-based markers:
  SkinMarker addMeshMarker (Point3d pos)
  SkinMarker addMeshMarker (String name, Point3d pos)
  // create mesh-based attachments:
  PointSkinAttachment createPointMeshAttachment (Point pnt)

The requested position pos will then be projected onto the nearest mesh feature (e.g., a face for a PolygonalMesh or a line segment for a PolylineMesh), and the resulting position {\bf p} will be defined as a linear combination of the vertex positions {\bf p}_{i} for this feature,

{\bf p}=\sum_{i}\beta_{i}{\bf p}_{i}, (8.10)

where \beta_{i} are the barycentric coordinates of {\bf p} with respect to the feature. The master body connections are then defined by the same linear combination of the connections for each vertex. When the master bodies move, the marker or attached point will move with the feature and remain in the same relative position.

Since SkinMeshBody implements the interface PointAttachable, it provides the general point attachment method

  PointSkinAttachment createPointAttachment (Point pnt)

which allows it to be acted on by agents such as the ArtiSynth marker tool (see the section “Marker tool” in the ArtiSynth User Interface Guide). Whether or not the resulting attachment is a regular attachment or mesh-based is controlled by the skin body’s attachPointsToMesh property, which can be adjusted in the GUI or in code using the property’s accessor methods:

  boolean getAttachPointsToMesh()
  void setAttachPointsToMesh (boolean enable)