Hey there,
I implemented a Ribbon class that is a shape in the shape of the shape of a Ribbon
TestClass (modified TestBezierCurve):
/*
* Copyright (c) 2003-2006 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package jmetest.curve;
import java.util.ArrayList;
import java.util.List;
import com.jme.app.SimpleGame;
import com.jme.bounding.BoundingSphere;
import com.jme.curve.BezierCurve;
import com.jme.curve.CurveController;
import com.jme.image.Texture;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.scene.Controller;
import com.jme.scene.TriMesh;
import com.jme.scene.shape.Box;
import com.jme.scene.shape.Ribbon;
import com.jme.scene.state.TextureState;
import com.jme.scene.state.ZBufferState;
import com.jme.util.TextureManager;
import com.jme.util.geom.BufferUtils;
/**
* <code>TestBezierCurve</code>
*
* @author Mark Powell
* @version $Id: TestBezierCurve.java,v 1.19 2006/05/11 19:39:48 nca Exp $
*/
public class TestRibbon extends SimpleGame {
private Vector3f up = new Vector3f(0, 1, 0);
public static void main(String[] args) {
TestRibbon app = new TestRibbon();
app.setDialogBehaviour(ALWAYS_SHOW_PROPS_DIALOG);
app.start();
}
/*
* (non-Javadoc)
*
* @see com.jme.app.SimpleGame#initGame()
*/
protected void simpleInitGame() {
lightState.setEnabled(false); // by default for this demo
display.setTitle("Bezier Curve Test");
// create control Points
Vector3f[] points = new Vector3f[4];
points[0] = new Vector3f(-4, 0, 0);
points[1] = new Vector3f(-2, 3, 2);
points[2] = new Vector3f(2, -3, -2);
points[3] = new Vector3f(4, 0, 0);
BezierCurve curve = new BezierCurve("Curve", points);
ColorRGBA[] colors = new ColorRGBA[4];
colors[0] = new ColorRGBA(0, 1, 0, 1);
colors[1] = new ColorRGBA(1, 0, 0, 1);
colors[2] = new ColorRGBA(1, 1, 0, 1);
colors[3] = new ColorRGBA(0, 0, 1, 1);
curve.setColorBuffer(0, BufferUtils.createFloatBuffer(colors));
List<Vector3f> vecs = new ArrayList<Vector3f>();
vecs.clear();
for (float j = 0.0f; j < 1.0f; j += 1.0f / 256) {
vecs.add(curve.getPoint(j));
} // for
Ribbon ribbon = new Ribbon(vecs.toArray(new Vector3f[vecs.size()]), 0.1f, new Vector3f(0, 0, 1));
ribbon.setSolidColor(ColorRGBA.blue.clone());
Vector3f min = new Vector3f(-0.1f, -0.1f, -0.1f);
Vector3f max = new Vector3f(0.1f, 0.1f, 0.1f);
ZBufferState buf = display.getRenderer().createZBufferState();
buf.setEnabled(true);
buf.setFunction(ZBufferState.CF_LEQUAL);
TriMesh t = new Box("Control 1", min, max);
t.setModelBound(new BoundingSphere());
t.updateModelBound();
t.setLocalTranslation(new Vector3f(points[0]));
TriMesh t2 = new Box("Control 2", min, max);
t2.setModelBound(new BoundingSphere());
t2.updateModelBound();
t2.setLocalTranslation(new Vector3f(points[1]));
TriMesh t3 = new Box("Control 3", min, max);
t3.setModelBound(new BoundingSphere());
t3.updateModelBound();
t3.setLocalTranslation(new Vector3f(points[2]));
TriMesh t4 = new Box("Control 4", min, max);
t4.setModelBound(new BoundingSphere());
t4.updateModelBound();
t4.setLocalTranslation(new Vector3f(points[3]));
TriMesh box = new Box("Controlled Box", min.mult(5), max.mult(5));
box.setModelBound(new BoundingSphere());
box.updateModelBound();
box.setLocalTranslation(new Vector3f(points[0]));
CurveController cc = new CurveController(curve, box);
box.addController(cc);
cc.setRepeatType(Controller.RT_CYCLE);
cc.setUpVector(up);
cc.setSpeed(0.5f);
TextureState ts = display.getRenderer().createTextureState();
ts.setEnabled(true);
ts.setTexture(TextureManager.loadTexture(TestRibbon.class.getClassLoader().getResource(
"jmetest/data/images/Monkey.jpg"), Texture.MM_LINEAR, Texture.FM_LINEAR));
box.setRenderState(ts);
rootNode.setRenderState(buf);
rootNode.attachChild(t);
rootNode.attachChild(t2);
rootNode.attachChild(t3);
rootNode.attachChild(t4);
rootNode.attachChild(box);
rootNode.attachChild(ribbon);
}
}
Ribbon.class:
import java.nio.FloatBuffer;
import com.jme.math.Vector3f;
import com.jme.scene.TexCoords;
import com.jme.scene.TriMesh;
import com.jme.util.geom.BufferUtils;
public class Ribbon extends TriMesh {
private static final long serialVersionUID = 1L;
private Vector3f[] points;
private float width;
private Vector3f normal;
public Ribbon(Vector3f[] points, float width, Vector3f normal) {
this.points = points;
this.width = width;
this.normal = normal;
create();
} // Ribbon
private void create() {
Vector3f[] vertices = new Vector3f[(points.length - 1) * 4];
Vector3f nextVec, cur, dist;
float numSections = (points.length - 1) * 2;
for (int i = 0; i < points.length - 1; i++) {
cur = points[i];
nextVec = points[i + 1];
dist = nextVec.subtract(cur).normalizeLocal().crossLocal(normal).normalizeLocal().multLocal(width / 2);
vertices[4 * i] = cur.add(dist);
vertices[4 * i + 1] = cur.add(dist.negate());
vertices[4 * i + 2] = nextVec.add(dist);
vertices[4 * i + 3] = nextVec.add(dist.negate());
} // for
int[] indices = new int[vertices.length];
Vector3f[] normals = new Vector3f[vertices.length];
for (int i = 0; i < indices.length; i++) {
indices[i] = i;
normals[i] = normal;
} // for
this.setMode(Mode.Strip);
this.setVertexBuffer(BufferUtils.createFloatBuffer(vertices));
this.setIndexBuffer(BufferUtils.createIntBuffer(indices));
this.setNormalBuffer(BufferUtils.createFloatBuffer(normals));
if (getTextureCoords(0) == null) {
FloatBuffer tex = BufferUtils.createVector2Buffer(vertices.length);
this.setTextureCoords(new TexCoords(tex, 2), 0);
for (int i = 0; i < numSections; i++) {
tex.put((float)i / numSections).put(0);
tex.put((float)i / numSections).put(1);
} // for
} // if
} // create
} // Ribbon
} // Ribbon
I use it in my project for exactly such a thing as the testcase does. I draw a ribbon along a Curve.
The neat thing is that, differentiating it from a Line, has no limit on width and it uses TriangleStrips -> the 'line segments' are connected as well.
Maybe someone is up for setting the TextureCoordinates so one can apply a texture to it (make a laser out of it or a rocket trail, ...)
so long,
Andy