@richtea
Thats still a little unclear. Do you mean it didn’t compile?
Or compiled but didn’t work as expected. If so how? (Looking at your example though i wouldnt expect it to work as netAccelarationForBoid seems to be exclusively the reverse of the position, ignoring all your distance calculations).
I think we may need to see more of the code as well. I have too many questions as it stands
Thank you richtea for reaching out!
Heres the vanilla code:
Main.java
package mygame;
import com.jme3.app.SimpleApplication;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.renderer.RenderManager;
import com.jme3.scene.Mesh;
import com.jme3.scene.shape.Box;
/**
* This is the Main Class of your Game. You should only do initialization here.
*/
public class Main extends SimpleApplication {
private Flock flock;
private final int boidCount = 1;
public static void main(String[] args) {
Main app = new Main();
app.start();
}
@Override
public void simpleInitApp() {
Mesh mesh = new Box(0.01f, 0.01f, 0.03f); // the geometric model of one boid. Here a cube of size=(x:0.01,y:0.01,z:0.03)
Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); // the surface material for the geometric boid model.
mat.setColor("Color", ColorRGBA.Green);
// instantiation of the flock
flock = new Flock(rootNode, boidCount, mesh, mat );
// camera rotation is controlled via mouse movement while the position is controlled via wasd-keys
flyCam.setEnabled(true);
flyCam.setMoveSpeed(20);
}
@Override
public void simpleUpdate(float tpf) {
flock.update(tpf); // called once per frame
}
@Override
public void simpleRender(RenderManager rm) {
// add here custom rendering stuff if needed
}
}
Flock.java
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package mygame;
import com.jme3.math.FastMath;
import com.jme3.material.Material;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.Node;
import com.jme3.scene.instancing.InstancedNode;
import java.util.ArrayList;
import java.util.List;
/**
* This class controls and manages all boids within a flock (swarm)
*/
public class Flock {
private Material boidMaterial;
private Mesh boidMesh;
private Node scene;
private InstancedNode instancedNode;
private List<Boid> boids;
/**
*
* @param scene a reference to the root node of the scene graph (e. g.
* rootNode from SimpleApplication).
* @param boidCount number of boids to create.
* @param boidMesh reference mesh (geometric model) which should be used for
* a single boid.
* @param boidMaterial the material controls the visual appearance (e. g.
* color or reflective behavior) of the surface of the boid model.
*/
public Flock(Node scene, int boidCount, Mesh boidMesh, Material boidMaterial) {
this.boidMesh = boidMesh;
this.boidMaterial = boidMaterial;
this.scene = scene;
this.boidMaterial.setBoolean("UseInstancing", true);
this.instancedNode = new InstancedNode("instanced_node");
this.scene.attachChild(instancedNode);
boids = createBoids(boidCount);
instancedNode.instance();
}
/**
* The update method should be called once per frame
*
* @param dtime determines the elapsed time in seconds (floating-point)
* between two consecutive frames
*/
public void update(float dtime) {
for (Boid boid : boids) {
// netAccelaration should be a linear combination of
// separation,
// alignment,
// cohesion, and
// further forces..
// accelaration=boid.position.negate()) means that there is a permanent acceleration towards the origin of the coordinate system (0,0,0) which decreases if the distance of the boid to origin decreases.
Vector3f netAccelarationForBoid = boid.position.negate();
boid.update(netAccelarationForBoid, dtime);
}
}
/**
* Creates a list of Boid objects and adds corresponding instanced models
* (based on boidMesh) to the scene graph
*
* @param boidCount The number of boids to create
* @return A list of Boid objects. For each object a corresponding instanced
* geometry is added to the scene graph (Boid.geometry)
*/
private List<Boid> createBoids(int boidCount) {
List<Boid> boidList = new ArrayList<Boid>();
for (int i = 0; i < boidCount; i++) {
Boid newBoid = new Boid(createInstance());
boidList.add(newBoid);
}
return boidList;
}
/**
* Creates an instanced copy of boidMesh using boidMaterial with individual
* geometric transform
*
* @return The instanced geometry attached to the scene graph
*/
private Geometry createInstance() {
Geometry geometry = new Geometry("boid", boidMesh);
geometry.setMaterial(boidMaterial);
instancedNode.attachChild(geometry);
return geometry;
}
}
Boid.java
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package mygame;
import com.jme3.math.FastMath;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
/**
* Boid represents one individual boid in the flock.
* It's motion is integrated within the update method, which should be called once per frame.
*/
public class Boid {
public static float spawnVolumeSize = 5.0f;
public Vector3f position;
public Vector3f velocity;
private Geometry geometry;
/**
* The constructor instantiates a Boid a random position p within -spawnVolumeSize/2 <= p <= spawnVolumeSize/2.
* The initial velocity is set to random 3D-vector with a magnitude of one.
* @param geom corresponds to a geometry object within the scene graph and has to exist.
*/
public Boid(Geometry geom) {
this.geometry = geom;
velocity = new Vector3f();
position = new Vector3f();
position.x = (FastMath.nextRandomFloat() -0.5f) * spawnVolumeSize;
position.y = (FastMath.nextRandomFloat() -0.5f) * spawnVolumeSize;
position.z = (FastMath.nextRandomFloat() -0.5f) * spawnVolumeSize;
velocity.x = (FastMath.nextRandomFloat() -0.5f);
velocity.y = (FastMath.nextRandomFloat() -0.5f);
velocity.z = (FastMath.nextRandomFloat() -0.5f);
velocity.normalizeLocal();
}
/**
* update calculates the new position of the Boid based on its current position and velocity influenced by accelaration. update should be called once per frame
* @param accelaration The net accelaration of all forces that influence the boid
* @param dtime The elapsed time in seconds between two consecutive frames
*/
public void update(Vector3f accelaration, float dtime)
{
//integrate velocity: v = v + a*dt; keep in mind: [m/s^2 * s = m/s]
//integrate position: p = p + v*dt; keep in mind: [m/s * s = m]
velocity = velocity.add(accelaration.mult(dtime));
position = position.add(velocity.mult(dtime));
//update scene instance
geometry.setLocalTranslation(position);
geometry.lookAt(position.add(velocity), Vector3f.UNIT_Y);
}
}
MODIFIED CODE
Only Flock.java been modified
Idea 1
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package mygame;
import com.jme3.math.FastMath;
import com.jme3.material.Material;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.Node;
import com.jme3.scene.instancing.InstancedNode;
import java.util.ArrayList;
import java.util.List;
/**
* This class controls and manages all boids within a flock (swarm)
*
*/
public class Flock {
private Material boidMaterial;
private Mesh boidMesh;
private Node scene;
private InstancedNode instancedNode;
private List<Boid> boids;
/**
*
* @param scene a reference to the root node of the scene graph (e. g.
* rootNode from SimpleApplication).
* @param boidCount number of boids to create.
* @param boidMesh reference mesh (geometric model) which should be used for
* a single boid.
* @param boidMaterial the material controls the visual appearance (e. g.
* color or reflective behavior) of the surface of the boid model.
*/
public Flock(Node scene, int boidCount, Mesh boidMesh, Material boidMaterial) {
this.boidMesh = boidMesh;
this.boidMaterial = boidMaterial;
this.scene = scene;
this.boidMaterial.setBoolean("UseInstancing", true);
this.instancedNode = new InstancedNode("instanced_node");
this.scene.attachChild(instancedNode);
boids = createBoids(boidCount);
instancedNode.instance();
}
/**
* The update method should be called once per frame
*
* @param dtime determines the elapsed time in seconds (floating-point)
* between two consecutive frames
*/
public void update(float dtime) {
for (Boid boid : boids) {
// netAccelaration should be a linear combination of
// separation,
// alignment,
// cohesion, and
// further forces..
// accelaration=boid.position.negate()) means that there is a permanent acceleration towards the origin of the coordinate system (0,0,0) which decreases if the distance of the boid to origin decreases.
Vector3f netAccelarationForBoid = boid.position.add(boid.position.x, boid.position.y,boid.position.z).negate(); // accelaration=boid.position.negate()) means that there is a permanent acceleration towards the origin of the coordinate system (0,0,0) which decreases if the distance of the boid to origin decreases.
float lengthOfAccel = netAccelarationForBoid.add(netAccelarationForBoid).length();
Vector3f Vector1 = boid.position.add(boid.position.x, boid.position.y,boid.position.z);
float lengthOfVec1 = Vector1.add(Vector1).length();
Vector3f Vector2 = boid.position.add(boid.position.x, boid.position.y,boid.position.z);
float lengthOfVec2 = Vector2.add(Vector2).length();
float DistanceBetweenVec1AndAccel = Vector1.distance(netAccelarationForBoid);
float DistanceBetweenAccelAndVec1 = netAccelarationForBoid.distance(Vector1);
float radiant1 = boid.position.angleBetween(netAccelarationForBoid);
float radiant2 = boid.position.angleBetween(Vector2);
float radiantResult = radiant1 - radiant2;
Vector3f accelInterpol = netAccelarationForBoid.interpolateLocal(netAccelarationForBoid, Vector1, 0.03f);
if(boid != boids && DistanceBetweenVec1AndAccel <= radiantResult && DistanceBetweenAccelAndVec1 <= radiantResult){
boid.position.subtract(netAccelarationForBoid, Vector2);
boid.position.add(boid.position, boid.velocity);
boid.position.add(boid.position, accelInterpol);
}
boid.update(netAccelarationForBoid, dtime);
}
}
/**
* Creates a list of Boid objects and adds corresponding instanced models
* (based on boidMesh) to the scene graph
*
* @param boidCount The number of boids to create
* @return A list of Boid objects. For each object a corresponding instanced
* geometry is added to the scene graph (Boid.geometry)
*/
private List<Boid> createBoids(int boidCount) {
List<Boid> boidList = new ArrayList<Boid>();
for (int i = 0; i < boidCount; i++) {
Boid newBoid = new Boid(createInstance());
boidList.add(newBoid);
}
return boidList;
}
/**
* Creates an instanced copy of boidMesh using boidMaterial with individual
* geometric transform
*
* @return The instanced geometry attached to the scene graph
*/
private Geometry createInstance() {
Geometry geometry = new Geometry("boid", boidMesh);
geometry.setMaterial(boidMaterial);
instancedNode.attachChild(geometry);
return geometry;
}
}
Idea 2
Here I tried to rewrite the code on processing.org
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package mygame;
import com.jme3.math.FastMath;
import com.jme3.material.Material;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.Node;
import com.jme3.scene.instancing.InstancedNode;
import java.util.ArrayList;
import java.util.List;
/**
* This class controls and manages all boids within a flock (swarm)
*
*/
public class Flock {
private Material boidMaterial;
private Mesh boidMesh;
private Node scene;
private InstancedNode instancedNode;
private List<Boid> boids;
/**
*
* @param scene a reference to the root node of the scene graph (e. g.
* rootNode from SimpleApplication).
* @param boidCount number of boids to create.
* @param boidMesh reference mesh (geometric model) which should be used for
* a single boid.
* @param boidMaterial the material controls the visual appearance (e. g.
* color or reflective behavior) of the surface of the boid model.
*/
public Flock(Node scene, int boidCount, Mesh boidMesh, Material boidMaterial) {
this.boidMesh = boidMesh;
this.boidMaterial = boidMaterial;
this.scene = scene;
this.boidMaterial.setBoolean("UseInstancing", true);
this.instancedNode = new InstancedNode("instanced_node");
this.scene.attachChild(instancedNode);
boids = createBoids(boidCount);
instancedNode.instance();
}
/**
* The update method should be called once per frame
*
* @param dtime determines the elapsed time in seconds (floating-point)
* between two consecutive frames
*/
public void update(float dtime) {
for (Boid boid : boids) {
// netAccelaration should be a linear combination of
// separation,
// alignment,
// cohesion, and
// further forces..
// accelaration=boid.position.negate()) means that there is a permanent acceleration towards the origin of the coordinate system (0,0,0) which decreases if the distance of the boid to origin decreases.
Vector3f netAccelarationForBoid = boid.position.add(boid.position.x, boid.position.y,boid.position.z).negate();
Vector3f BoidPositionVector = boid.position.add(boid.position.x, boid.position.y,boid.position.z);
float lengthOfBoidPositionVector = BoidPositionVector.length();
float subtractionOfBoidPosVectorAndAccelVector = BoidPositionVector.subtract(netAccelarationForBoid).length();
float distanceBetweenVector1AndNetAccel = BoidPositionVector.distance(netAccelarationForBoid);
float radiant = boid.position.angleBetween(netAccelarationForBoid);
if(boid != boids && subtractionOfBoidPosVectorAndAccelVector < radiant){
Vector3f diff = boid.position.subtract(netAccelarationForBoid, BoidPositionVector);
}
boid.update(netAccelarationForBoid, dtime);
}
}
/**
* Creates a list of Boid objects and adds corresponding instanced models
* (based on boidMesh) to the scene graph
*
* @param boidCount The number of boids to create
* @return A list of Boid objects. For each object a corresponding instanced
* geometry is added to the scene graph (Boid.geometry)
*/
private List<Boid> createBoids(int boidCount) {
List<Boid> boidList = new ArrayList<Boid>();
for (int i = 0; i < boidCount; i++) {
Boid newBoid = new Boid(createInstance());
boidList.add(newBoid);
}
return boidList;
}
/**
* Creates an instanced copy of boidMesh using boidMaterial with individual
* geometric transform
*
* @return The instanced geometry attached to the scene graph
*/
private Geometry createInstance() {
Geometry geometry = new Geometry("boid", boidMesh);
geometry.setMaterial(boidMaterial);
instancedNode.attachChild(geometry);
return geometry;
}
}
Idea 3
Here I tried to rewrite the code on processing.org
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package mygame;
import static mygame.Boid.spawnVolumeSize;
import com.jme3.math.FastMath;
import com.jme3.material.Material;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.Node;
import com.jme3.scene.instancing.InstancedNode;
import java.util.ArrayList;
import java.util.List;
/**
* This class controls and manages all boids within a flock (swarm)
*
*/
public class Flock {
private Material boidMaterial;
private Mesh boidMesh;
private Node scene;
private InstancedNode instancedNode;
private List<Boid> boids;
private Vector3f position;
private Vector3f velocity;
/**
*
* @param scene a reference to the root node of the scene graph (e. g.
* rootNode from SimpleApplication).
* @param boidCount number of boids to create.
* @param boidMesh reference mesh (geometric model) which should be used for
* a single boid.
* @param boidMaterial the material controls the visual appearance (e. g.
* color or reflective behavior) of the surface of the boid model.
*/
public Flock(Node scene, int boidCount, Mesh boidMesh, Material boidMaterial) {
this.boidMesh = boidMesh;
this.boidMaterial = boidMaterial;
this.scene = scene;
this.boidMaterial.setBoolean("UseInstancing", true);
this.instancedNode = new InstancedNode("instanced_node");
this.scene.attachChild(instancedNode);
position = new Vector3f();
position.x = (FastMath.nextRandomFloat() - 0.5f) * spawnVolumeSize;
position.y = (FastMath.nextRandomFloat() - 0.5f) * spawnVolumeSize;
position.z = (FastMath.nextRandomFloat() - 0.5f) * spawnVolumeSize;
boids = createBoids(boidCount);
instancedNode.instance();
}
Vector3f seek(Vector3f target) {
float maxspeed = 50.5f;
for (Boid boid : boids) {
Vector3f desired = boid.position.subtract(target, position);
desired.normalize();
desired.mult(maxspeed);
boid.position.subtract(desired, velocity);
//System.out.println("test = " + steer);
}
return target;
}
Vector3f separation(Vector3f v) {
float maxspeed = 2.5f;
float prefSeperation = 2.0f;
Vector3f steer = new Vector3f(0, 0, 0);
int boidsCountNearby = 0;
for (Boid other : boids) {
Vector3f boidVector = other.position.add(6, 6, 4);
float distanceVector = other.position.distance(boidVector);
//System.out.println("testDISTANCE =" + distanceVector);
//System.out.println("testPREF =" + prefSeperation);
//System.out.println("testDISVECTOR =" + distanceVector);
if ((distanceVector > 0) && (distanceVector > prefSeperation)) {
Vector3f difference = other.position.add(other.position.x, other.position.y, other.position.z).subtract(position.x, position.y, position.z);
Vector3f normalizedDiff = boidVector.normalize();
boidVector.divide(distanceVector);
steer.add(difference);
boidsCountNearby++;
//System.out.println("testBOIDCOUNT =" + boidsCountNearby);
}
}
if (boidsCountNearby > 0) {
steer.divide((float) boidsCountNearby);
}
if (steer.length() > 0) {
steer.normalize();
steer.subtract(velocity);
steer.mult(maxspeed);
}
return steer;
}
Vector3f cohesion(Vector3f v) {
float neighbordist = 2;
Vector3f sum = new Vector3f(0, 0, 0);
int count = 0;
for (Boid other : boids) {
Vector3f boidVector = other.position.add(20, 50, 40);
float distanceVector = other.position.distance(boidVector);
if ((distanceVector > 0) && (distanceVector > neighbordist)) {
sum.add(other.position);
count++;
}
}
if (count > 0) {
sum.divide(count);
return seek(sum);
} else
{
return new Vector3f(0, 0, 0);
}
}
/**
* The update method should be called once per frame
*
* @param dtime determines the elapsed time in seconds (floating-point)
* between two consecutive frames
*/
public void update(float dtime) {
for (Boid boid : boids) {
// netAccelaration should be a linear combination of
// separation,
// alignment,
// cohesion, and
// further forces..
// accelaration=boid.position.negate()) means that there is a permanent acceleration towards the origin of the coordinate system (0,0,0) which decreases if the distance of the boid to origin decreases.
Vector3f netAccelarationForBoid = boid.position.negate();
separation(netAccelarationForBoid);
cohesion(netAccelarationForBoid);
boid.update(netAccelarationForBoid, dtime);
}
}
/**
* Creates a list of Boid objects and adds corresponding instanced models
* (based on boidMesh) to the scene graph
*
* @param boidCount The number of boids to create
* @return A list of Boid objects. For each object a corresponding instanced
* geometry is added to the scene graph (Boid.geometry)
*/
private List<Boid> createBoids(int boidCount) {
List<Boid> boidList = new ArrayList<Boid>();
for (int i = 0; i < boidCount; i++) {
Boid newBoid = new Boid(createInstance());
boidList.add(newBoid);
}
return boidList;
}
/**
* Creates an instanced copy of boidMesh using boidMaterial with individual
* geometric transform
*
* @return The instanced geometry attached to the scene graph
*/
private Geometry createInstance() {
Geometry geometry = new Geometry("boid", boidMesh);
geometry.setMaterial(boidMaterial);
instancedNode.attachChild(geometry);
return geometry;
}
}
@xuan Oh, thanks alot! I was overworked and didnt realized that.
I will look into Processing after my Boid task! It looks interesting!