Dynamic/Animated Objects

Hi All,



I’m going to need some dynamic objects soon - by which I mean things like arrows that dynamically point between two objects, and move as the two objects move (so they need to resize, rotate, etc).



I’m thinking the best way to do that is going to be to just generate and update the vertices and mesh as required (I already create some custom meshes but I only create them once, I don’t modify them once created) but I was wondering if there was any advice on what the best way to go about that is. Do I need to regenerate the geometry from the vertex mesh each time or is there a better way? Is it “expensive” to be modifying meshes all the time or is it not too bad for performance? In most cases I will just be changing the position of the vertices, but it’s possible I may need to add new segments as well…



Thanks,

Z

Right, I’ve been trying to make sense of this but I’m struggling a little and I can’t see any relevant information in the tutorials.



So lets say I have the following code:

[java]

private static final IntBuffer indexes = BufferUtils.createIntBuffer(new int[] {

0,1,2,

0,2,1,

});





Transform t = start.getGlow().getWorldTransform();

v1 = t . transformVector(s1, null);

v2 = t . transformVector(s2, null);

v3 = t . transformVector(s3, null);

target = end.getGlow().getWorldTransform().transformVector(s3, null);



seeking = true;



float[] colorArray = new float[34];

int index=0;

for (int i=0;i<2;i++) {

colorArray[index++]=start.getColor().r;

colorArray[index++]=start.getColor().g;

colorArray[index++]=start.getColor().b;

colorArray[index++]=ALPHA
start.getColor().a;

}

colorArray[index++]=end.getColor().r;

colorArray[index++]=end.getColor().g;

colorArray[index++]=end.getColor().b;

colorArray[index++]=ALPHA*end.getColor().a;



mesh.setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(v1,v2,v3));

mesh.setBuffer(Type.Index, 3, indexes);

mesh.setBuffer(Type.Color, 4, colorArray);

mesh.updateBound();



Geometry geo = new Geometry(“Card Link”, mesh);

geo.setMaterial(MaterialCache.getMaterial(assetManager, start.getColor()));

geo.setQueueBucket(Bucket.Transparent);

[/java]



This creates the mesh, and the geometry (its untested so far but its similar to other custom mesh creation I’ve done that works).



Now, I have the point v3 that occupies position 6, 7 & 8 in the vertices float buffer. If I move that point what do I do? If I am moving all 3 points does that change anything?



Is all I need to do setDynamic (or maybe setStreamed?) in the mesh and then modify the contents of the float buffer and reset it?



Do I reset the whole buffer each time or just modify the contents? I notice that the buffer seems to have put methods to append on the end but nothing to overwrite existing values. Have I missed something there?



How about updateBound, is that needed each time?



Will the geometry automatically pick up these changes or do I need to recreate that too?



Sorry for all the questions!



Thanks,

Z

Thats the best way, yeah. If you can do it with bones and animation I guess that would be the easiest way to do that “visually”.

No-one? :frowning:

Ok, this is what I ended up with. It seems to work and performance seems good so far. If there is any way to get the updatePositions method more efficient though I’d like to hear it as currently I need to create a new FloatBuffer every update…



[java]

package net.herodex.client.framework.card.effects;



import com.jme3.asset.AssetManager;

import com.jme3.math.ColorRGBA;

import com.jme3.math.Quaternion;

import com.jme3.math.Vector3f;

import com.jme3.renderer.queue.RenderQueue.Bucket;

import com.jme3.scene.Geometry;

import com.jme3.scene.Mesh;

import com.jme3.scene.VertexBuffer.Type;

import com.jme3.util.BufferUtils;

import java.util.ArrayList;

import java.util.List;

import net.herodex.client.framework.card.CardInstance;



/**

*

  • @author Tim

    */

    public class AnchorGlowLink {



    private static final float CARD_WIDTH = 3.2f;

    private static final float INNER_HEIGHT = 0.1f;

    private static final float OUTER_HEIGHT = 0.2f;

    private static final float INNER_WIDTH = 0.2f;

    private static final float OUTER_WIDTH = 0.2f;

    private static final float BUMP_HEIGHT = 0.05f;



    private static final float ALPHA = 1f;



    private static final Vector3f V3_END = new Vector3f(-CARD_WIDTH/2, 0, 0);



    private static final Vector3f[] ANCHOR = new Vector3f[] {

    new Vector3f(-CARD_WIDTH/2, 0, OUTER_HEIGHT),

    new Vector3f(-CARD_WIDTH/2+INNER_WIDTH, 0, INNER_HEIGHT),

    new Vector3f(-CARD_WIDTH/2+INNER_WIDTH, 0, -INNER_HEIGHT),

    new Vector3f(-CARD_WIDTH/2, 0, -OUTER_HEIGHT),

    new Vector3f(-CARD_WIDTH/2-OUTER_WIDTH, 0, -INNER_HEIGHT),

    new Vector3f(-CARD_WIDTH/2-OUTER_WIDTH, 0, INNER_HEIGHT),



    new Vector3f(-CARD_WIDTH/2,-BUMP_HEIGHT, INNER_HEIGHT),

    new Vector3f(-CARD_WIDTH/2,-BUMP_HEIGHT, -INNER_HEIGHT),

    };



    private static final int[] TRIANGLES = new int[] {

    // Link

    -1, 4, 5,

    -1, 5, 4,

    -1, 5, 0,

    -1, 0, 1,

    -1, 3, 4,

    -1, 2, 3,



    // Anchor

    6,2,0,

    6,2,1,

    6,7,2,

    6,4,7,

    6,5,4,

    6,0,5,

    7,3,2,

    7,4,3,

    };



    private final List<CardInstance> starts;

    private CardInstance end;



    private final ColorRGBA color;



    private final Geometry spatial;

    private Mesh mesh = new Mesh();

    private Vector3f[] points;





    public AnchorGlowLink(AssetManager assetManager, ColorRGBA color, CardInstance end, List<CardInstance> starts) {

    this.starts = new ArrayList<CardInstance>(starts);

    this.end = end;

    this.color = color;



    regenerateMesh();



    mesh.setDynamic();



    Geometry geo = new Geometry("Card Link", mesh);

    geo.setMaterial(MaterialCache.getAdditiveMaterial(assetManager));

    geo.setQueueBucket(Bucket.Transparent);



    spatial = geo;

    }



    public void addStart(CardInstance start) {

    starts.add(start);

    regenerateMesh();

    }



    public void removeStart(CardInstance start) {

    if (starts.remove(start))

    regenerateMesh();

    }



    public void setEnd(CardInstance end) {

    if (end == this.end)

    return;



    starts.add(this.end);

    this.end = end;

    starts.remove(end);



    regenerateMesh();

    }



    private void regenerateMesh() {



    points = new Vector3f[1+starts.size()ANCHOR.length];

    int[] triangles = new int[starts.size()TRIANGLES.length];

    float[] colors = new float[4+starts.size()ANCHOR.length4];



    int pointsIndex=0;

    int colorIndex=0;

    int trianglesIndex=0;



    // Do not use Transform as we need translation and rotation but not scale

    Vector3f t = end.getSpatial().getWorldTranslation();

    Quaternion q = end.getSpatial().getWorldRotation();



    points[pointsIndex++] = q.mult(V3_END).addLocal(t);



    colors[colorIndex++]=color.r;

    colors[colorIndex++]=color.g;

    colors[colorIndex++]=color.b;

    colors[colorIndex++]=ALPHA
    color.a;



    for (int i=0;i<starts.size();i++) {



    // Do triangles first so that pointsIndex is pointing to where the

    // first of these points will be

    for (int j=0;j<TRIANGLES.length;j++) {

    int k = TRIANGLES[j];

    if (k == -1)

    k = 0;

    else

    k = pointsIndex+k;

    triangles[trianglesIndex++] = k;

    }



    // Do not use Transform as we need translation and rotation but not scale

    t = starts.get(i).getSpatial().getWorldTranslation();

    q = starts.get(i).getSpatial().getWorldRotation();

    for (int j=0;j<ANCHOR.length;j++) {

    points[pointsIndex++] = q.mult(ANCHOR[j]).addLocal(t);



    colors[colorIndex++]=color.r;

    colors[colorIndex++]=color.g;

    colors[colorIndex++]=color.b;

    colors[colorIndex++]=ALPHA
    color.a;

    }

    }



    mesh.setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(points));

    mesh.setBuffer(Type.Index, 3, BufferUtils.createIntBuffer(triangles));

    mesh.setBuffer(Type.Color, 4, BufferUtils.createFloatBuffer(colors));

    mesh.updateBound();



    }



    private void updatePositions() {



    int pointsIndex=0;



    // Do not use Transform as we need translation and rotation but not scale

    Vector3f t = end.getSpatial().getWorldTranslation();

    Quaternion q = end.getSpatial().getWorldRotation();



    points[pointsIndex] = q.mult(V3_END, points[pointsIndex++]).addLocal(t);



    for (int i=0;i<starts.size();i++) {



    // Do not use Transform as we need translation and rotation but not scale

    t = starts.get(i).getSpatial().getWorldTranslation();

    q = starts.get(i).getSpatial().getWorldRotation();

    for (int j=0;j<ANCHOR.length;j++) {

    points[pointsIndex] = q.mult(ANCHOR[j], points[pointsIndex++]).addLocal(t);

    }

    }



    mesh.setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(points));

    mesh.updateBound();



    }





    public List<CardInstance> getStarts() {

    return starts;

    }



    public CardInstance getEnd() {

    return end;

    }



    public void update(float tpf) {

    updatePositions();

    }



    public Geometry getSpatial() {

    return spatial;

    }



    public boolean containsStart(CardInstance card) {

    return starts.contains(card);

    }



    public void removeEnd() {

    if (starts.isEmpty())

    end = null;

    else

    end = starts.get(starts.size()-1);

    starts.remove(starts.size()-1);

    }



    }

    [/java]