# Lemur Gems #4 : Simple Mesh Deformation

In this Lemur Gem I will show an example of using the DMesh class to deform a standard Mesh on the fly. Lemur Gem #3 showed how to easily turn regular 3D scene objects into clickable things. I will extend that to turn those boxes into swaying beams.

Some random introductions…

First meet MBox. MBox behaves similarly to a JME box except it can be ‘split’ along any of the major axes to provide simple subdivision. I replace the old Box initialization code with MBox so that there are more triangles to morph.

Next there is DMesh and Deformation. A DMesh is a proper JME Mesh extension that wraps any JME Mesh and modifies it with a specified Deformation function. The deformation function is passed each the position and normal Vector3fs for each vertex and can modify them as desired.

A Deformations utility class provides some default deformation functions. (Just two right now.) Of these, I will be using the Cylindrical deformation in this demo. The Cylindrical deformation conceptually warps space around some origin at some radius. For example, a flat plane/grid could be warped to a specific curve by placing the origin somewhere off the plane and providing an appropriate radius for the curve.

I think it’s probably best if seen. Explanations are hard without a picture.

In the last demo, I created 5 regular JME Box meshes. In this one I create 5 MBox meshes as follows:

``````    // MBox is like a JME box except that it can be split along
// the three axes.  In this case, we have no splits in x or z
// and 5 splits in y.  This means that each long side will be
MBox b = new MBox(0.5f, 3, 0.5f, 0, 5, 0);
``````

To that a deformation is applied as follows:

``````    // Create a deformation function from the standard
// built-in deformations.
// The "cylindrical" deformation warps space such that
//    cylindrical( int majorAxis, int minorAxis,
//                 float start, float limit )
// The start and limit control the range of the effect along the major
// axis.
final Vector3f curveOrigin = new Vector3f(3, -3, 0) ;
final Cylindrical cylDeform = Deformations.cylindrical( 1, 0, curveOrigin, radius, 0, 0 );

// DMesh takes any source mesh and applies a deformation function producing
// new mesh data.  The function parameters can be updated later to animate them.
final DMesh mesh = new DMesh(b, cylDeform);
``````

…and that DMesh is passed to the Geometry instead of the box in gem 3’s version. After that we have a slightly different mouse listener setup:

``````    MouseEventControl.addListenersToSpatial(geom,
new DefaultMouseListener() {

private boolean dragging;
private float xLast;
private float limit = 0;

@Override
public void mouseButtonEvent( MouseButtonEvent event, Spatial target, Spatial capture ) {
event.setConsumed();

if( event.isPressed() ) {
xLast = event.getX();
dragging = true;
} else {
dragging = false;
mesh.createCollisionData();
geom.updateModelBound();
}
}

@Override
public void mouseMoved( MouseMotionEvent event, Spatial target, Spatial capture ) {
if( !dragging ) {
return;
}
event.setConsumed();

float xDelta = event.getX() - xLast;
xLast = event.getX();

// The limit sets the 'range' of the effect from origin.
// So a limit of 6 would be the maximum because our boxes are
// only 6 units long and origin is at the base.
// However, limit starts to have a small effect at 5 or so.
// At that point we switch to moving the curve radius in.
limit += xDelta / 100;

// When the limit it 7 or more then the curve radius becomes
// 1.0.  Anything much smaller than that and the object starts
// wrapping back upon itself.  So we'll hard-clamp the max
// of limit to 7.
if( limit > 7 ) {
limit = 7;
}

if( limit > 5 ) {
// Shift the curve origin closer to the box base...
// ie: a tighter curve
// A radius smaller than 1 starts to wrap back upon
// itself.
curveOrigin.x = 3 - (limit - 5);
} else {
// Reset the curve origin and radius back to normal.
curveOrigin.x = 3;
}
cylDeform.setLimit(Math.min(6, limit));
mesh.updateMesh();
}

@Override
public void mouseEntered( MouseMotionEvent event, Spatial target, Spatial capture ) {
Material m = ((Geometry)target).getMaterial();
m.setColor("Color", ColorRGBA.Yellow);
}

@Override
public void mouseExited( MouseMotionEvent event, Spatial target, Spatial capture ) {
Material m = ((Geometry)target).getMaterial();
m.setColor("Color", ColorRGBA.Blue);
}
});
``````

Here we’ve kept the existing enter/exit behavior of setting the mesh yellow/blue respectively. To it we’ve added button down and up detection to setup dragging. The x delta from one dragged mouse move to the next is tracked and used to adjust the Cylindrical deformations parameters. The Cylindrical deformation is a kind of complicated morph but it makes cool results so I used it. However, it consequently has a lot of parameters.

This is the exact same deformation I use to curve the animated pages of my 3D Book UIs.

And a video…

12 Likes

Awesome!

1 Like

Very nice!

1 Like