Nice.
Oh brilliant, I was thinking of making something like that!
Thanks a lot mate!
If you want to make this contribution more visible to prospective users and on par with the jME3 core, please consider making a plugin.
Actually this thing could be candidate to core.
I didn’t look the code in detail but the trailing feature could definitely be a core feature.
@normen, @Sploreg, @Momoko_Fan, @pspeed, what do you think?
Hi @cvlad! I’ve been playing around with this a little and I do like it! I have a little problem with it when using textures though and maybe you can provide me with som answers.
How is the texture coordinates generated? Is the start and end of the mesh supposed to always have the same texture coordinate? The behavior I’m looking for is that a texture is stretched across the whole mesh with no wrapping, is this possible right now? Is there any way for me to know where on the line mesh I am in a shader?
http://i.imgur.com/F3Fb0.png
By stretching an image as the one above using alpha blending one could make a nice trail which will fade out.
@kwando
There should be no wrapping, unless I messed up again (which I actually did when removing points, I fixed that now too, code above is updated). I gave it a shot with your texture, seems to work here:
[java]package mygame;
import com.jme3.app.SimpleApplication;
import com.jme3.input.KeyInput;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.AnalogListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.material.Material;
import com.jme3.material.RenderState.FaceCullMode;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.shape.Box;
public class Main extends SimpleApplication {
Node geom;
public static void main(String[] args) {
Main app = new Main();
app.start();
}
@Override
public void simpleInitApp() {
Box b = new Box(Vector3f.ZERO, 1, 1, 1);
geom = new Node(); //new Geometry(“Box”, b);
Material mat = new Material(assetManager, “Common/MatDefs/Misc/Unshaded.j3md”);
mat.setColor(“Color”, ColorRGBA.Blue);
//geom.setMaterial(mat);
mat.setTexture(“ColorMap”,assetManager.loadTexture(“Textures/F3Fb0.png”));
Geometry trailGeometry = new Geometry();
LineControl line = new LineControl(new LineControl.Algo1CamDirBB(), true);
trailGeometry.addControl(line);
TrailControl trailControl = new TrailControl(line);
geom.addControl(trailControl);
//rootNode.attachChild(trail); // either attach the trail geometry node to the root…
trailGeometry.setIgnoreTransform(true); // or set ignore transform to true. this should be most useful when attaching nodes in the editor
Node test = new Node();
test.attachChild(trailGeometry);
test.setLocalTranslation(new Vector3f(0,2,0)); // without ignore transform this would offset the trail
rootNode.attachChild(test);
mat.getAdditionalRenderState().setFaceCullMode(FaceCullMode.Off);
trailGeometry.setMaterial(mat);
rootNode.attachChild(geom);
initKeys();
}
@Override
public void simpleUpdate(float tpf) {
}
@Override
public void simpleRender(RenderManager rm) {}
private void initKeys() {
inputManager.addMapping(“Left”, new KeyTrigger(KeyInput.KEY_J));
inputManager.addMapping(“Right”, new KeyTrigger(KeyInput.KEY_K));
inputManager.addMapping(“Rotate”, new KeyTrigger(KeyInput.KEY_SPACE),
new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
inputManager.addListener(analogListener, new String[]{“Left”, “Right”, “Rotate”});
}
private AnalogListener analogListener = new AnalogListener() {
public void onAnalog(String name, float value, float tpf) {
if (name.equals(“Rotate”)) {
geom.rotate(0, valuespeed, 0);
}
if (name.equals(“Right”)) {
Vector3f v = geom.getLocalTranslation();
geom.setLocalTranslation(v.x + valuespeed5, v.y, v.z);
}
if (name.equals(“Left”)) {
Vector3f v = geom.getLocalTranslation();
geom.setLocalTranslation(v.x - valuespeed*5, v.y, v.z);
}
}
};
}[/java]
Screenshot:
And here’s a small attempt to explain the idea behind the texture coordinates’ computation:

As you can see texture coordinates range from zero to one, so yeah, there should be no wrapping.
However I added a getter for totalLength, so by pushing that into a shader and multiplying totalLength with the texcoord.x in that shader you can find out how long in your world units (meters?) your line is at that vertex/pixel if you need that for some reason.
@erlend_sh I will try and do that, unless it’s made part of the core anyway. I didn’t know if it was ok for me to spam the contribution center with small contributions like this, since I have a few other things planned too. Or should I make one big plugin with all my stuff in it?
It works if I use thinner lines, so it was probably something fishy with my code…
Thanks for the great explanation, now I know how it is supposed to work
This is a fine contribution, thanks!
I will try and do that, unless it’s made part of the core anyway. I didn’t know if it was ok for me to spam the contribution center with small contributions like this, since I have a few other things planned too. Or should I make one big plugin with all my stuff in it?Even if it has a chance at core it's best to start it off as a plugin anyhow because we need to get to know the code properly first.
As for the "other things planned", well, start by making this plugin. Then you either extend that plugin with more functionality if it's directly related, or you just contribute multiple smaller plugins if they don't have functionality in common.
@nehon I think it is a feature lots of people would use, I can think of many uses for it. So yea I think it could be a core candidate. It’s not that much code so it won’t take long to review it and see how it works and what needs to change. I guess it boils down to: do we fully support it in core, or partially support it as a plugin. Maybe being that we are trying to get a stable (non-beta) 3.0 release out, it can be a plugin for now, then get pulled into core for 3.1.
I was looking for these kind of effects!!! I just tested the sampler code a few minutes ago! very nice!!!
Wow, two things: your voice sounds exactly like @androlo 's! And the other is: gratz for the job man, looks really, really cool, I can also think of some uses of this! ^^
How does it compare vs. the TrailMesh from jME2?
@Momoko_Fan said:
How does it compare vs. the TrailMesh from jME2?
Looking at the code over here: http://code.google.com/p/jmonkeyengine/source/browse/trunk/src/com/jmex/effects/TrailMesh.java?r=4089
Performance of my implementation is probably worse, because of the way I compute texture coordinates.
And because he only allocates memory for the buffers once, though amortized it shouldn't really be a problem with the way I did it.
Well, he uses get(int) on LinkedList though.
As for flexibility, I implemented trails on top of lines, so it should be more flexible.
His trail implementation has FacingMode, UpdateMode and per point adjustable width, which my trail implementation does not have yet, but this could be easily added, since lines already have width per points and swappable billboarding behaviors.
Both our implementations don't cache the axis from one point to another when updating, for which I wrote the methods in the behavior interface, but did not yet change the behaviors' implementations.
Now the question at hand is what to do about the texture coordinates. I opted for leaving the "correct" way in there, but using the dependency injection/strategy pattern as I did with the billboarding behavior should be applicable, too, so if you want to plug in a cheaper way of computing them, that could be possible.
Overall I don't know how much ms these implementations take compared to each other, I can't imagine there being a lot difference, which is why I posted the source as it is.
@cvlad I’m trying to get this working following the mouse pointer while left mouse button is held down. Not sure what I’m missing but any chance you can help me out? Other than the input change and cam move everything is the same from your most recent example. It worked fine before trying to change the input.
[java]
package lineTracer;
import com.jme3.app.SimpleApplication;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.AnalogListener;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.material.Material;
import com.jme3.material.RenderState.FaceCullMode;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.shape.Box;
public class LineTracerTestMain extends SimpleApplication {
Node geom;
public static final Quaternion YAW180 = new Quaternion().fromAngleAxis(FastMath.PI , new Vector3f(0,1,0));
public static void main(String[] args) {
LineTracerTestMain app = new LineTracerTestMain();
app.start();
}
@Override
public void simpleInitApp() {
Box b = new Box(Vector3f.ZERO, 1, 1, 1);
geom = new Node(); //new Geometry("Box", b);
Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
mat.setColor("Color", ColorRGBA.Red);
//geom.setMaterial(mat);
mat.setTexture("ColorMap",assetManager.loadTexture("Textures/lineTracer2.png"));
Geometry trailGeometry = new Geometry();
LineControl line = new LineControl(new LineControl.Algo1CamDirBB(), true);
trailGeometry.addControl(line);
TrailControl trailControl = new TrailControl(line);
geom.addControl(trailControl);
//rootNode.attachChild(trailGeometry); // either attach the trail geometry node to the root…
trailGeometry.setIgnoreTransform(true); // or set ignore transform to true. this should be most useful when attaching nodes in the editor
Node test = new Node();
test.attachChild(trailGeometry);
test.setLocalTranslation(new Vector3f(0,2,0)); // without ignore transform this would offset the trail
rootNode.attachChild(test);
mat.getAdditionalRenderState().setFaceCullMode(FaceCullMode.Off);
trailGeometry.setMaterial(mat);
rootNode.attachChild(geom);
initKeys();
flyCam.setEnabled(false);
cam.setLocation(new Vector3f(0,0,30));
cam.setRotation(YAW180);
}
@Override
public void simpleUpdate(float tpf) {
}
@Override
public void simpleRender(RenderManager rm) {}
private void initKeys() {
inputManager.addMapping("drag", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
inputManager.addListener(analogListener, new String[]{"drag"});
}
private AnalogListener analogListener = new AnalogListener() {
public void onAnalog(String name, float value, float tpf) {
if (name.equals("drag")) {
Vector2f mouseCoords = inputManager.getCursorPosition();
Vector3f location = cam.getWorldCoordinates(mouseCoords, 25).clone();
geom.setLocalTranslation(location);
}
}
};
}
[/java]
Scratch the above. I figured out that I was just setting it to a point too close and found a better way of grabbing a point to move it too. Now my problem is I can’t get rid of the black background around the line and I’m not seeing where its coming from. I’m sure its some sort of rendering setting but I can’t find it. I want the same effect as how the last example looks on the black scene but on any background.
If your texture has alpha in it then you will need to properly setup for transparency by setting the blend mode, putting it in the transparent bucket, etc…
Thanks I’ll give that a try. I just played with the start/end size to get it too look better for now.
@cvlad I love this implementation and I got it to work in a test app. However, my application requires the node to be cloned. When I try to clone the node, I get a null pointer exception! When the function LineControl.cloneForSpatial(spatial) runs, it executes clone.set(this.points,this.halfWidths) when the Mesh of the clone is still null! I tried adding a line of code beforehand that instantiates the mesh for the clone, and that got rid of the exception, but then no trail shows up. What can I do?
Thanks,
Andy
Anyone has succeeded in having trails at a point anywhere else than the model’s (geometry) origin? Every attempt I have made always placed the trail at the origin.
Also, another weird thing is the trail will, 99% of the time, only be visible after I zoom the camera extremely far from the model. So far it only appeared once at normal viewing distance. Once the zooming out made the trail appear I can zoom back in. It will work as it should.
Finally I tried doing it from the scene composer but it seems there are things incompatible with that as I’m getting errors. Either ‘can’t instantiate class’ or other errors (after I tried fixing the instantiation error) It’s actually as Andy above noted.
If @cvlad could visit and review the potential problems and/or fix the issues it would be very much appreciated.
Alternatively, if there is no resolution maybe an official post stating the brokenness of this code and warning users not to use it. (Or, better yet, someone could fix it?)
Goal:
Model with two exhausts where each have a node. By attaching the trails to those nodes I want to have trails seemingly coming out of the exhaust.
EDIT
Ok, it seems I was able to fix the instantiation issue, but for some reason I had to reload the whole model and even wait for a while before it worked. I guess it was kept in memory and that’s why I was continuing to get the message. At least that’s that. Anyway, it’s not displaying, but at least I can go from there… Hopefully I can make this work. Still, if someone has a suggestion, please feel free to help.
ok, i tried this and here are my conclusions :
first of all, about the cloning thing (yeah, i need to clone it too), it’s because the trailcontrol assert that the line control is on the same spatial. And this is not true. In the given example, the linecontrol is attached to a geometry (an immobile geometry) and the trailcontrol is attached to an other geometry which move. It seems that you can attach the trail geometry and the line geometry on the same spatial but you get strange results (the queue of the trail moves too)
If somebody wants to clone it, i found a “simple” way to do that : a delayed creation.
i.e.
[java]
/*
- To change this template, choose Tools | Templates
- and open the template in the editor.
*/
package real.client.controllers.trail;
import com.jme3.asset.AssetManager;
import com.jme3.material.Material;
import com.jme3.material.RenderState.FaceCullMode;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.control.AbstractControl;
import com.jme3.scene.shape.Box;
import kapplication.Initializable;
import real.client.RealClient;
/**
*
-
@author Bubuche
*/
public class DelayedTrailCreation extends AbstractControl implements Initializable<RealClient>, Cloneable
{
private RealClient application;
@Override
protected void controlUpdate(float tpf)
{
createTrail();
spatial.removeControl(this);
}
@Override
protected void controlRender(RenderManager rm, ViewPort vp)
{
}
private void createTrail()
{
AssetManager assetManager = application.getAssetManager();
Box b = new Box(Vector3f.ZERO, 1, 1, 1);
Spatial geom = spatial;
Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
mat.setColor("Color", ColorRGBA.Blue);
//geom.setMaterial(mat);
mat.setTexture(“ColorMap”, assetManager.loadTexture(“Textures/F3Fb0.png”));
Geometry trailGeometry = new Geometry();
LineControl line = new LineControl(new LineControl.Algo1CamDirBB(), true);
trailGeometry.addControl(line);
TrailControl trailControl = new TrailControl(line);
geom.addControl(trailControl);
//rootNode.attachChild(trail); // either attach the trail geometry node to the root…
trailGeometry.setIgnoreTransform(true); // or set ignore transform to true. this should be most useful when attaching nodes in the editor
Node test = new Node();
test.attachChild(trailGeometry);
test.setLocalTranslation(new Vector3f(0, 2, 0)); // without ignore transform this would offset the trail
application.getRootNode().attachChild(test);
mat.getAdditionalRenderState().setFaceCullMode(FaceCullMode.Off);
trailGeometry.setMaterial(mat);
}
@Override
public void init(RealClient application)
{
this.application = application;
}
}
[/java]
This is directly copy past from my program, so you’ll need to clean it. I give it like this cause i want to make sure that people will understand that the delayed controls need to have a reference to the assetmanager, or a way to access it. Why ? Because the only job done by this control is to “copy past” the “example” code. so, you don’t really clone the trail, you create a new instance (but you can clone the instance creator, i.e. this control).
it’s ugly but it works.
I have a way through my “init” method, but you need to find your own. Keep in mind that if you give a reference once (for example you can have 2 constructors for the control and one that take the assetManager as parameter) the reference will be clone when you’ll call the “clone” method (its the default comportement). So, you just need to ensure that your base item (the item you’ll clone over and over again) has a valid reference.
Or you can find an other way, of course
In the code i gave, you still need to add a way to clean things when the trail is over. I’ll investigate on a good way to do this (cause i added a node an a control on it, they’ll stay in the scene if you don’t remove them)
However … the trail itself has a problem (besides the fact that it is opaque) : when you don’t look at it from a side, when you look at it from behind or front, you will not see it, and you’ll even see how “plan” it is. I am not sure why i have this, as for me the geometry that need the control should be used to give a 3d view of the trail in such case, but i have this comportement. So, i am still looking for a way to create 3D trail.
(p.s. i want to achieve a visual effect close to this one http://image.jeuxvideo.com/images/pc/t/i/tiocpc001_m.jpg )