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
// 6 quads vertically.
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
// the mesh will curve about some 'origin' and radius.
// cylindrical( int majorAxis, int minorAxis,
// Vector3f origin, float radius,
// 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 float radius = 3;
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);
cylDeform.setRadius(curveOrigin.x);
} else {
// Reset the curve origin and radius back to normal.
curveOrigin.x = 3;
cylDeform.setRadius(curveOrigin.x);
}
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.
Here is the full demo class: Google Code Archive - Long-term storage for Google Code Project Hosting.
And a video…