Hello JME community. I am new here as I have only been learning JME for a short while.
I am trying to create a simple application that fires a laser. I read in some of the past forum posts and learned that TrailMesh was a good way to go. From there, I borrowed form the HelloIntersection class and came up with the following code:
import jmetest.renderer.TestBoxColor;
import com.jme.app.SimpleGame;
import com.jme.bounding.BoundingSphere;
import com.jme.image.Texture;
import com.jme.input.KeyInput;
import com.jme.input.action.InputActionEvent;
import com.jme.input.action.KeyInputAction;
import com.jme.math.Vector3f;
import com.jme.renderer.Renderer;
import com.jme.scene.Controller;
import com.jme.scene.TriMesh;
import com.jme.scene.Spatial.CullHint;
import com.jme.scene.Spatial.LightCombineMode;
import com.jme.scene.shape.Sphere;
import com.jme.scene.state.BlendState;
import com.jme.scene.state.CullState;
import com.jme.scene.state.TextureState;
import com.jme.scene.state.ZBufferState;
import com.jme.util.TextureManager;
import com.jme.util.Timer;
import com.jmex.effects.TrailMesh;
public class TestLasers extends SimpleGame
{
private TrailMesh theTrailMesh;
private Vector3f tangent = new Vector3f();
protected void simpleInitGame()
{
display.setTitle("LaserTest");
cam.getLocation().set(new Vector3f(140, 140, 140));
cam.lookAt(new Vector3f(), Vector3f.UNIT_Y);
// Create the trail
theTrailMesh = new TrailMesh("TrailMesh", 100);
theTrailMesh.setUpdateSpeed(60.0f);
theTrailMesh.setFacingMode(TrailMesh.FacingMode.Billboard);
theTrailMesh.setUpdateMode(TrailMesh.UpdateMode.Step);
// Try out some additive blending etc
theTrailMesh.setRenderQueueMode(Renderer.QUEUE_TRANSPARENT);
theTrailMesh.setCullHint(CullHint.Never);
TextureState ts = display.getRenderer().createTextureState();
ts.setEnabled(true);
Texture t1 = TextureManager.loadTexture(
TestBoxColor.class.getClassLoader().getResource(
"jmetest/data/texture/trail.png"),
Texture.MinificationFilter.Trilinear,
Texture.MagnificationFilter.Bilinear);
ts.setTexture(t1);
theTrailMesh.setRenderState(ts);
BlendState bs = display.getRenderer().createBlendState();
bs.setBlendEnabled(true);
bs.setSourceFunction(BlendState.SourceFunction.SourceAlpha);
bs.setDestinationFunction(BlendState.DestinationFunction.One);
bs.setTestEnabled(true);
theTrailMesh.setRenderState(bs);
ZBufferState zs = display.getRenderer().createZBufferState();
zs.setWritable(false);
theTrailMesh.setRenderState(zs);
CullState cs = display.getRenderer().createCullState();
cs.setCullFace(CullState.Face.None);
cs.setEnabled(true);
theTrailMesh.setRenderState(cs);
rootNode.attachChild(theTrailMesh);
input.addAction(new FireLaser(), "firelaser", KeyInput.KEY_F, false);
// No lighting for clarity
rootNode.setLightCombineMode(LightCombineMode.Off);
rootNode.setRenderQueueMode(com.jme.renderer.Renderer.QUEUE_OPAQUE);
}
public static void main(String[] args) {
TestLasers app = new TestLasers();
app.setConfigShowMode(ConfigShowMode.AlwaysShow);
app.start();
}
class FireLaser extends KeyInputAction {
int numBullets;
public void performAction(InputActionEvent evt) {
TrailMesh newLaser = new TrailMesh("newLaser" + numBullets++, 5);
newLaser.setUpdateSpeed(60.0f);
newLaser.setFacingMode(TrailMesh.FacingMode.Billboard);
newLaser.setUpdateMode(TrailMesh.UpdateMode.Step);
// Try out some additive blending etc
newLaser.setRenderQueueMode(Renderer.QUEUE_TRANSPARENT);
newLaser.setCullHint(CullHint.Never);
/**
* The question is, why isn't the commented code below needed to
* keep the lasers colored? (i.e. Not white)
*/
// TextureState ts = display.getRenderer().createTextureState();
// ts.setEnabled(true);
// Texture t1 = TextureManager.loadTexture(
// TestBoxColor.class.getClassLoader().getResource(
// "jmetest/data/texture/trail.png"),
// Texture.MinificationFilter.Trilinear,
// Texture.MagnificationFilter.Bilinear);
// ts.setTexture(t1);
// trailMesh.setRenderState(ts);
//
// BlendState bs = display.getRenderer().createBlendState();
// bs.setBlendEnabled(true);
// bs.setSourceFunction(BlendState.SourceFunction.SourceAlpha);
// bs.setDestinationFunction(BlendState.DestinationFunction.One);
// bs.setTestEnabled(true);
// trailMesh.setRenderState(bs);
//
// ZBufferState zs = display.getRenderer().createZBufferState();
// zs.setWritable(false);
// trailMesh.setRenderState(zs);
//
// CullState cs = display.getRenderer().createCullState();
// cs.setCullFace(CullState.Face.None);
// cs.setEnabled(true);
// trailMesh.setRenderState(cs);
rootNode.attachChild(newLaser);
Sphere bullet = new Sphere("bullet" + numBullets++, 8, 8, 0.001f);
bullet.setModelBound(new BoundingSphere());
bullet.updateModelBound();
/** Move bullet to the camera location */
bullet.setLocalTranslation(new Vector3f(15,1,1));
/**
* Update the new world location for the bullet before I add a
* controller
*/
bullet.updateGeometricState(0, true);
/**
* Add a movement controller to the bullet going in the camera's
* direction
*/
bullet.addController(new LaserMover(bullet, newLaser, new Vector3f(new Vector3f(1,1,1))));
rootNode.attachChild(bullet);
bullet.updateRenderState();
}
}
class LaserMover extends Controller
{
private static final long serialVersionUID = 1L;
/** Bullet that's moving */
TriMesh bullet;
TrailMesh laser;
/** Direction of bullet **/
Vector3f direction;
/** speed of bullet */
float speed = 1000;
/** Seconds it will last before going away */
float lifeTime = 10;
LaserMover(TriMesh bullet, TrailMesh trailMesh, Vector3f direction) {
this.bullet = bullet;
this.laser = trailMesh;
this.direction = direction;
this.direction.normalizeLocal();
}
public void update(float time)
{
lifeTime -= time;
/** If life is gone, remove it **/
if (lifeTime < 0)
{
rootNode.detachChild(laser);
rootNode.detachChild(bullet);
bullet.removeController(this);
laser.removeController(this);
return;
}
/** Move bullet */
Vector3f bulletPos = bullet.getLocalTranslation();
bulletPos.addLocal(direction.mult(time * speed));
bullet.setLocalTranslation(bulletPos);
laser.setLocalTranslation(direction.mult(time * speed));
laser.updateGeometricState(0.0f, true);
Vector3f tg = new Vector3f();
// Create a spin for tangent mode
tg.set(direction.mult(time * speed));
tg.normalizeLocal();
// Setup width
float width = 1.0f;
laser.setTrailFront(bullet.getWorldTranslation(), tangent,
width, Timer.getTimer().getTimePerFrame());
laser.update(cam.getLocation());
}
}
}
NOTE: when you run this code, press 'f' to fire the laser.
Notice the block of commented code in FireLaser.performAction(). Originally, I had this code running and I didn't have any similar code in simpleInitGame(). Unfortunately, when this was so, nothing worked. When I comment this code out and leave the code in simpleInitGame(), I get a yellow laser.
I don't understand why this is the case. Can someone explain why running this code only once <in simpleInitGame()> forces all lasers to show up properly? At first I thought I had to apply this TextureState to all laser objects but from my code I don't see how I'm applying it at all right now...
Any help is greatly appreciated.
OldMonk