Creating a new Forester plugin

I am trying to make the forester in a more JME way
the basic idea is this: (if the photo will work)

any remarks, critisizm, complaints, compliments, or encouragement are apreciated. (as well as any help)

4 Likes

Note: (since the image didn’t work)
user code —> my code —> trees generated
my code using supporting classes.
JME helping me a ton with geometries and such.

Steps for posting an image:

  1. go to imgur.com
  2. uploud your image there.
  3. come here and click the little image link
  4. paste in the URL from your imgur picture.

could i join?

I think before he left androlo posted “BioMonkey” that was an enhanced version of the forester. It was still not a plugin, but I think there were more features.
I think I have it somewhere on my Hard drive, I’ll dig around and post it.

i have that too here https://www.dropbox.com/s/oqx0f1ditb64f5y/Workbench.rar.
extracted AtmosphereManager, its quite nice with those color transitions :slight_smile:
didnt like that terrain generation and grass/trees positioning by terrain height (do not know maybe there is a way to pass density with a texture)
also forests wasnt dense enough :smiley: i just wanted to make very dense forests, but was running out of mem.
also if everything was seperate in biomonkey it would be quite usefull.

I am making some progress, though my current path will encounter a problem when creating a large amounts of trees.
is there any way to combine geometries into one? or at lest combine spatials?
this isn’t the only problem, but i already have a tid bit of code that is a little glitchy, just look:

The tree here is slightly rotated, i don’t know why, maybe it is the tilt of the terrain below. the code is simply suposed to make the tree be at ground level, where ever “ground level” is. but sadly it isn’t working right now.
here is the code of my forester thing, i know not all the functionality is there, but i am doing one thing at a time:

[java]package ForesterRemake;

import com.jme3.math.Vector2f;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.terrain.geomipmap.TerrainQuad;
import java.util.Random;

/**
*

  • @author Joel - School
    */
    public class Forester {
    public TerrainQuad terrain;
    public Node rootNode;
    public Forester(TerrainQuad terrain, Node rootNode)
    {
    this.terrain = terrain;
    this.rootNode = rootNode;
    }

    /**

    • float = the density of the trees
    • Spatial = the model of the tree being created
    • boolean = whether or not the trees are solid
    • Vector2f = the size of the terrain (x and z accordingly)
      */
      public void CreateTrees(float Density, Spatial model, boolean solid, Vector2f terrainSize)
      {
      Random rand = new Random();
      Spatial tempModel = model.clone();
      int x = rand.nextInt((int) terrainSize.x);
      int z = rand.nextInt((int) terrainSize.y);
      int y = (int) terrain.getHeight(new Vector2f(x,z));
      tempModel.setLocalTranslation(x,y,z);
      rootNode.attachChild(tempModel);

    }

    public Node updateRootNode()
    {
    return rootNode;
    }
    }[/java]

if u want lots of tress try this lib: Dropbox - Error
just on firs time set world.generate(true);

ITS WORKING!!!
i got it to work, see:

i did it using this new code:

[java]import com.jme3.bullet.PhysicsSpace;
import com.jme3.bullet.collision.shapes.CollisionShape;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.bullet.util.CollisionShapeFactory;
import com.jme3.math.Vector2f;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.terrain.Terrain;
import com.jme3.terrain.geomipmap.TerrainQuad;
import java.util.Random;

/**
*

  • @author Joel - School
    */
    public class Forester {
    public TerrainQuad terrain;
    public Node foresterNode = new Node();
    public BulletAppState forPhy;

    public Forester(AppStateManager state)
    {
    forPhy = new BulletAppState();
    forPhy.setThreadingType(BulletAppState.ThreadingType.PARALLEL);
    state.attach(forPhy);
    forPhy.getPhysicsSpace().setAccuracy(1f / 30f);
    }

    public void CreateTrees(float Density, Spatial model, boolean solid, Vector2f terrainSize, TerrainQuad terrain)
    {
    Random rand = new Random();

     this.terrain = terrain;
     Vector2f terrainSizeScale100th = terrainSize.divide(Density * 0.01f);
     int numTrees = (int) (terrainSizeScale100th.x + terrainSizeScale100th.y);
     int i = 0;
     while (i < numTrees)
     {
     Spatial tempModel = model.clone();
     int x = rand.nextInt((int) terrainSize.x * 2) - (int) terrainSize.y;
     int z = rand.nextInt((int) terrainSize.y * 2) - (int) terrainSize.y;
     int y = (int) terrain.getHeight(new Vector2f(x,z)) - 100;
     tempModel.setLocalTranslation(x,y,z);
     foresterNode.attachChild(tempModel);
     if (solid)
     {
     CollisionShape tempModelShape =
             CollisionShapeFactory.createMeshShape((Node) tempModel);
     RigidBodyControl tempModelControl = new RigidBodyControl(tempModelShape, 0);
     tempModel.addControl(tempModelControl);
     forPhy.getPhysicsSpace().add(tempModelControl);
     }
     i++;
     }  
    

    }

    public void CreateRandomHeightTrees(float Density, Spatial model, boolean solid, Vector2f terrainSize, TerrainQuad terrain, int varying)
    {
    Random rand = new Random();

     model.scale(1f, 0.1f + rand.nextInt(varying) / 10, 1f);
     
     this.terrain = terrain;
     Vector2f terrainSizeScale100th = terrainSize.divide(Density * 0.01f);
     int numTrees = (int) (terrainSizeScale100th.x + terrainSizeScale100th.y);
     int i = 0;
     while (i < numTrees)
     {
     Spatial tempModel = model.clone();
     int x = rand.nextInt((int) terrainSize.x * 2) - (int) terrainSize.y;
     int z = rand.nextInt((int) terrainSize.y * 2) - (int) terrainSize.y;
     int y = (int) terrain.getHeight(new Vector2f(x,z)) - 100;
     tempModel.setLocalTranslation(x,y,z);
     foresterNode.attachChild(tempModel);
     if (solid)
     {
     CollisionShape tempModelShape =
             CollisionShapeFactory.createMeshShape((Node) tempModel);
     RigidBodyControl tempModelControl = new RigidBodyControl(tempModelShape, 0);
     tempModel.addControl(tempModelControl);
     forPhy.getPhysicsSpace().add(tempModel);
     }
     i++;
     }  
    

    }

    public Node updateRoot(Node root)
    {
    root.attachChild(foresterNode);
    return root;
    }
    public AppStateManager updatePhy (AppStateManager phy)
    {
    phy.attach(forPhy);
    return phy;
    }
    }[/java]
    please list any suggestions for further improvement (i am expecting a long list, but at least i’ve got trees) :smiley:

2 Likes

I got functional grass as well, only comes in quad form though. i will add more functionality soon.
<a http://i.imgur.com/HfR4tFB.jpg
for some reason, the image icon for the reply thing is missing, so i had to settle for a link.

and your grass without lod?
im working currently on grass control, but with some lod
also trees/objects also should come in patches so u can batch them
going to find an article on geom pagin

1 Like
<cite>@eraslt said:</cite> and your grass without lod? im working currently on grass control, but with some lod also trees/objects also should come in patches so u can batch them going to find an article on geom pagin
I am going to add some lod now, thanks.

On a different note: I want to hopefully make this a plugin within 1-5 months, But i want to make sure my plugin is useful first.

By the way, just because me and @eraslt seem to be the only ones posting on here, it doesn’t mean you can’t post. I would appreciate any comments you have that are relevant to this, including: ideas for improvement, step-by-step process on how to make a plugin, +other things relevant to this topic.

(You = anybody reading this)

Here is the “forester.class” kind of code:
[java]package ForesterRemake;

import com.jme3.app.state.AppStateManager;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.PhysicsSpace;
import com.jme3.bullet.collision.shapes.CollisionShape;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.bullet.util.CollisionShapeFactory;
import com.jme3.material.Material;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.renderer.queue.RenderQueue.Bucket;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Box;
import com.jme3.terrain.Terrain;
import com.jme3.terrain.geomipmap.TerrainQuad;
import java.util.Random;
import com.jme3.scene.shape.Quad;

/**
*

  • @author Joel - School
    */
    public class Forester {

    public TerrainQuad terrain;
    public Node foresterNode = new Node();
    public BulletAppState forPhy;
    Random rand;
    Camera cam = new Camera();
    GrassLayer[] grasses = new GrassLayer[0];

    ;

    public Forester(AppStateManager state, TerrainQuad terrain, Camera cam) {
    this.terrain = terrain;
    forPhy = new BulletAppState();
    forPhy.setThreadingType(BulletAppState.ThreadingType.PARALLEL);
    state.attach(forPhy);
    forPhy.getPhysicsSpace().setAccuracy(1f / 30f);
    this.cam = cam;

     rand = new Random();
    

    }

    public TreeLayer CreateTrees(float Density, Spatial model, boolean solid, Vector2f terrainSize) {
    Vector2f terrainSizeScale100th = terrainSize.divide(Density * 0.01f);
    int numTrees = (int) (terrainSizeScale100th.x + terrainSizeScale100th.y);
    int i = 0;
    while (i < numTrees) {
    Spatial tempModel = model.clone();
    int x = rand.nextInt((int) terrainSize.x * 2) - (int) terrainSize.y;
    int z = rand.nextInt((int) terrainSize.y * 2) - (int) terrainSize.y;
    int y = (int) terrain.getHeight(new Vector2f(x, z)) - 100;
    tempModel.setLocalTranslation(x, y, z);
    foresterNode.attachChild(tempModel);
    if (solid) {
    CollisionShape tempModelShape =
    CollisionShapeFactory.createMeshShape((Node) tempModel);
    RigidBodyControl tempModelControl = new RigidBodyControl(tempModelShape, 0);
    tempModel.addControl(tempModelControl);
    forPhy.getPhysicsSpace().add(tempModelControl);
    }
    i++;
    }
    return new TreeLayer();
    }

    public TreeLayer CreateRandomHeightTrees(float Density, Spatial model, boolean solid, Vector2f terrainSize, int varying) {
    model.scale(1f, 0.1f + rand.nextInt(varying) / 10, 1f);

     this.terrain = terrain;
     Vector2f terrainSizeScale100th = terrainSize.divide(Density * 0.01f);
     int numTrees = (int) (terrainSizeScale100th.x + terrainSizeScale100th.y);
     int i = 0;
     while (i &lt; numTrees) {
         Spatial tempModel = model.clone();
         int x = rand.nextInt((int) terrainSize.x * 2) - (int) terrainSize.y;
         int z = rand.nextInt((int) terrainSize.y * 2) - (int) terrainSize.y;
         int y = (int) terrain.getHeight(new Vector2f(x, z)) - 100;
         tempModel.setLocalTranslation(x, y, z);
         foresterNode.attachChild(tempModel);
         if (solid) {
             CollisionShape tempModelShape =
                     CollisionShapeFactory.createMeshShape((Node) tempModel);
             RigidBodyControl tempModelControl = new RigidBodyControl(tempModelShape, 0);
             tempModel.addControl(tempModelControl);
             forPhy.getPhysicsSpace().add(tempModel);
         }
         i++;
     }
     return new TreeLayer();
    

    }

    public GrassLayer createQuadGrass(Material material, float size, Vector2f terrainSize, float Density) {
    Node Grass = new Node();
    Vector2f terrainSizeScale100th = terrainSize.divide(Density * 0.001f);
    int numTrees = (int) (terrainSizeScale100th.x + terrainSizeScale100th.y);
    int i = 0;
    while (i < numTrees) {
    Box grass = new Box(Vector3f.ZERO, size, size, 0.001f);
    Geometry geom = new Geometry(“Grass”, grass);
    geom.setMaterial(material);
    int x = rand.nextInt((int) terrainSize.x * 2) - (int) terrainSize.y;
    int z = rand.nextInt((int) terrainSize.y * 2) - (int) terrainSize.y;
    int y = (int) terrain.getHeight(new Vector2f(x, z)) - 100;
    geom.setLocalTranslation(x, y + 0.5f, z);
    geom.setQueueBucket(Bucket.Transparent);
    foresterNode.attachChild(geom);
    Box grass2 = new Box(Vector3f.ZERO, size, size, 0.001f);
    Geometry geom2 = new Geometry(“Grass”, grass2);
    geom2.setMaterial(material);
    geom2.setLocalTranslation(x, y + 0.5f, z);
    geom2.setQueueBucket(Bucket.Transparent);
    geom2.rotate(0, 90, 0);
    Node grassNode = new Node();
    grassNode.attachChild(geom2);
    grassNode.attachChild(geom);
    Grass.attachChild(grassNode);
    i++;
    }
    GrassLayer GLayer = new GrassLayer(Grass, cam);
    GrassLayer[] tempMemItems = new GrassLayer[grasses.length + 1];
    int ii = 0;
    while (ii < grasses.length) {
    tempMemItems[ii] = grasses[ii];
    ii++;
    }
    grasses = new GrassLayer[tempMemItems.length];
    ii = 0;
    while (ii < tempMemItems.length) {
    grasses[ii] = tempMemItems[ii];
    ii++;
    }
    grasses[grasses.length - 1] = GLayer;
    return GLayer;
    }

    public Node updateRoot(Node root) {
    root.attachChild(foresterNode);
    return root;
    }

    public AppStateManager updatePhy(AppStateManager phy) {
    phy.attach(forPhy);
    return phy;
    }

    public void update(float tpf) {
    int i = 0;
    while (i < grasses.length)
    {
    grasses[i].update();
    i++;
    }
    }
    }[/java]

Here is the “GrassLayer.class” code (it isn’t functional yet):
[java]package ForesterRemake;

import com.jme3.renderer.Camera;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import java.util.List;

/**
*

  • @author Joel - School
    */
    public class GrassLayer {

    List<Spatial> grasses;
    Spatial[] grassesArray;
    float LOD = 50;
    Camera cam = new Camera();
    public GrassLayer(Node grass, Camera cam)
    {
    grasses = grass.getChildren();
    grassesArray = (Spatial[]) grasses.toArray();
    this.cam = cam;
    }

    public void setDerenderingRange(float range)
    {
    LOD = range;
    }

    public void update()
    {
    int i = 0;
    while (i < grassesArray.length)
    {
    if (grassesArray[i].getLocalTranslation().x > cam.getLocation().x - LOD)
    {
    grassesArray[i].setCullHint(Spatial.CullHint.Always);
    }
    else if (grassesArray[i].getLocalTranslation().y > cam.getLocation().y - LOD)
    {
    grassesArray[i].setCullHint(Spatial.CullHint.Always);
    }
    else if (grassesArray[i].getLocalTranslation().z > cam.getLocation().z - LOD)
    {
    grassesArray[i].setCullHint(Spatial.CullHint.Always);
    }

         if (grassesArray[i].getLocalTranslation().x &lt;= cam.getLocation().x - LOD)
         {
             grassesArray[i].setCullHint(Spatial.CullHint.Never);
         }
         else if (grassesArray[i].getLocalTranslation().y &lt;= cam.getLocation().y - LOD)
         {
             grassesArray[i].setCullHint(Spatial.CullHint.Never);
         }
         else if (grassesArray[i].getLocalTranslation().z &lt;= cam.getLocation().z - LOD)
         {
             grassesArray[i].setCullHint(Spatial.CullHint.Never);
         }
         i++;
     }
    

    }
    }[/java]

PS: the “TreeLayer.class” is currently empty. That’s all i have so far.

<cite>@wabuilderman said:</cite> By the way, just because me and @eraslt seem to be the only ones posting on here, it doesn't mean you can't post. I would appreciate any comments you have that are relevant to this, including: ideas for improvement, step-by-step process on how to make a plugin, +other things relevant to this topic.

(You = anybody reading this)

Only comment I have is, I think this is a very good idea… this component is very important for a lot of game ideas and biomonkey was really looking promising, it’s a really shame things got a bit confrontational at the end. Making a plugin based on that good work and making it JME3-friendly will be very much appreciated by the community I am sure :slight_smile:

thanks monkeychops. by the way, I think that I will be able to make this a plugin soon, not anywhere near as good as the original forester or biomonkey, but at least then it will have a start.

As soon as i get permission, i will be able to make this a plugin, and i even found some good grass control
@vvishmaster YOU RULE!!! this code is much more efficient than i could come up with in a life time. :slight_smile: (the code below is NOT the code i am using, this is just a thank you note)
[java]Long longNumber = 10000000000000000000000000000000000000000000000000000;
if (thanks + longNumber == awsomeJob)
{
System.out.println(“wishmaster, your code is awsome”)
}
[/java]
(a java thank you note)

Just throwing in an idea here without knowing how easy it would be to implement:

Have finding density and type of forrest as an interface or abstract so you could create your own logic to set those.
This would result in:
Density and type of trees based on a texture (tex coordinates mapped to world coordinates, maybe alpha = density, color = tyoe, so an RGBA image could definde types and density of up to 3 tree types).
Using the interface/abstract you could very easily come up with you own ideas to spread the trees.
Something like
[java]
private class myForrestSpread implements ForrestSpread{

    private Vector3f forrestType1Spot = new Vector3f(10f, 0f, 20f);
    private Vector3f forrestType2Spot = new Vector3f(80f, 0f, -30f);
    private Node emptyTree = new Node("Empty");
    
    public float getDensity(Vector3f worldPos) {
        if (worldPos.distance(ForrestType1Spot) &lt; 20f) {
            return 1f - 1f / worldPos.distance(ForrestType1Spot);
        }
        if (worldPos.distance(ForrestType1Spot) &lt; 35f) {
            return 1f - 1f / worldPos.distance(ForrestType1Spot);
        }
        return 0f;
    }
    
    public Node getTreeType(Vector3f worldPos) {
        if (worldPos.distance(ForrestType1Spot) &lt; 20f) {
            return (Node) assetManager.loadModel("Models/Trees/Pine.j3o");
        }
        if (worldPos.distance(ForrestType1Spot) &lt; 35f) {
            return (Node) assetManager.loadModel("Models/Trees/Birch.j3o");
        }
        return emptyTree;
    }
}

[/java]
To give an example what i mean by custom defined code here :wink:

2 Likes

@t0neg0d who was working on a project just like this has now returned after a (involuntary) leave of absence.

You two should definitely talk. If you’re able to collaborate on this that’d be ideal.

2 Likes

i think i will message him, thanks for letting me know. :slight_smile: