(WIP) Grass Generator

For the last several days, I’ve been toying with grass generation. At first I wanted to try billboards and quads and such, but I really didn’t like how it looked. It felt too obviously fake and ugly. So after some research, I decided on a different, less-used approach. Here’s a few pictures:

How it works:
I made a simple, 6-tri grass-blade model and generated LODs in jMP. The lowest one is a single triangle, the second is a few, and the highest is all 6. The GrassControl creates grass chunks and distributes the blades (rotated and scaled randomly) across the surface of the TerrainQuad. Each chunk node is run through the batch optimizer, and given a LodControl. The pictures were taken running on an aging HD6670 (which is performing oddly slow today).

I’ll post the source (it’s just one class, one model, and a custom material def + shaders thus far) when I’m a little less tired. My next features include different types/heights/colors of grass, flowers, etc.

2 Likes

@vinexgames
This looks really interesting! Could you maybe provide the code, I’ve been trying to get nice grass into my game for a couple of days now, but I didn’t really get what I wanted…

@mathiasj said: @vinexgames This looks really interesting! Could you maybe provide the code, I've been trying to get nice grass into my game for a couple of days now, but I didn't really get what I wanted...

There was grass in this… with source code, if you follow the whole thread: http://hub.jmonkeyengine.org/forum/topic/isosurface-demo-dev-blog-experiment/

…may be different than what you are looking for, I guess.

1 Like

@mathiasj I actually haven’t had a lot of time to work on this since school started. At this point the code has kinda fallen into disarray. I can, however, explain the process I used, as well as the process I intended to use to get different kinds of grass and stuff.
The biggest pitfall of this generator is the actual generation of the grass. I would highly recommend porting it to another thread with app.enqueue(). Here’s how I did it:

Pass in the TerrainQuad, and decide on a grass chunk size. (Larger = faulty LOD but less often rebuilding, smaller = looks better, at the cost of frequent regeneration)
For each tile on the terrain quad, place a Node with a GrassControl.
GrassControl’s update looked like this:
distance = distance from tile to camera.
if distance > lod range, cull the grass blades and count a timer. If the tile has been culled for over 15 seconds, remove it and set a regenerate tag.
Else if the tile is close enough to the camera, regenerate the blades if needed and render the blades.

As far as generation goes, I used density maps. For each tile, add x*y blades. For each blade, read the density map and check if it shouldn’t be there.

I planned to add other types of foliage by creating similarly low detail models and giving them a similar density map. You could also manually set their direct density as a constant, giving an even distribution all over the terrain.

1 Like

@vinexgames
Thanks, you already helped me a lot! But I really have problems with the framerate; when I render just some grass it already is at about 20 fps… And my graphic card is pretty good, so that can’t be the issue. I created the model in Blender from a cube and textured it. I don’t use LoD because when I generate it in the SDK, triangles are removed that make the model look really odd (and there is no difference between LoD 1 and LoD 2 because the model has got so few triangles…). But LoD can’t really be the problem because in your screenshot there are more than a million vertices and in my scene there are only ca 100,000 but I just get FPS around 20. My graphics card is the R9 270X. Another problem is the removing of the grass tiles because once I detach them, the Control doesn’t get updated anymore so they don’t get re-attached once I am in the range again. And what do you mean by the grass chunck size?
How can there be such a difference in the performance even if I use less vertices and have the better graphics card?

EDIT: I just saw that you just have about 100 objects, whilst I have more than 4000 objects in the scene…? How did you do that or am I doing something totally wrong? ^^

This is the code I use so far:

[java]
package mygame;

import com.jme3.app.SimpleApplication;
import com.jme3.bounding.BoundingBox;
import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.terrain.geomipmap.TerrainQuad;
import java.util.ArrayList;

/**

  • test

  • @author normenhansen
    */
    public class Main extends SimpleApplication {

    private Node terrainNode;
    private ArrayList<Spatial> grassList = new ArrayList<Spatial>();

    public static void main(String[] args) {
    Main app = new Main();
    app.start();
    }

    @Override
    public void simpleInitApp() {
    flyCam.setMoveSpeed(50);

     Spatial scene = assetManager.loadModel("Scenes/newScene.j3o");
     rootNode.attachChild(scene);
    
     terrainNode = (Node) scene;
    
     cam.setLocation(new Vector3f(0, 50, 0));
     cam.lookAt(scene.getLocalTranslation(), Vector3f.UNIT_Y);
    
     initGrass();
     initLight();
    

    }

    private void initGrass() {
    Spatial grass = assetManager.loadModel(“Models/grass.j3o”);
    Spatial grassBend = assetManager.loadModel(“Models/grass.j3o”);

     TerrainQuad tq = (TerrainQuad) terrainNode.getChild("terrain-Scene");
    
     Vector3f extent = ((BoundingBox) terrainNode.getWorldBound()).getExtent(new Vector3f());
     float length = extent.x * 2;
     float numOfGrassForOneSide = 200;
     float squareLength = length / numOfGrassForOneSide;
    
     Vector3f bottomTopCorner = new Vector3f(-squareLength * numOfGrassForOneSide / 2, 0f, squareLength * numOfGrassForOneSide / 2);
     for (int i = 0; i &lt; numOfGrassForOneSide; i++) {
         for (int j = 0; j &lt; numOfGrassForOneSide; j++) {
             boolean bended = Math.random() &gt; .5f;
             Spatial clone;
             if (bended) {
                 clone = grassBend.clone();
             } else {
                 clone = grass.clone();
             }
             clone.setName("Geom");
             float random = (float) Math.random() * 10f;
             float height = (float) Math.random();
             float yangle = (float) Math.random() * 360;
             float xAngle = (float) Math.random() * 30;
             clone.scale(height);
             clone.rotate(xAngle * FastMath.DEG_TO_RAD, yangle * FastMath.DEG_TO_RAD, 0);
             clone.setLocalTranslation(bottomTopCorner.add(squareLength * i + random, 0, -squareLength * j - random));
             clone.addControl(new GrassControl(this));
             rootNode.attachChild(clone);
         }
     }
    

    }

    private void initLight() {
    /**
    * A white, directional light source
    /
    DirectionalLight sun = new DirectionalLight();
    sun.setDirection((new Vector3f(-0.5f, -0.5f, -0.5f)).normalizeLocal());
    sun.setColor(ColorRGBA.White);
    rootNode.addLight(sun);
    /
    *
    * A white ambient light source.
    */
    AmbientLight ambient = new AmbientLight();
    ambient.setColor(ColorRGBA.White.mult(.4f));
    rootNode.addLight(ambient);
    }

    @Override
    public void simpleUpdate(float tpf) {
    }

    @Override
    public void simpleRender(RenderManager rm) {
    //TODO: add render code
    }
    }

/*

  • To change this template, choose Tools | Templates
  • and open the template in the editor.
    */
    package mygame;

import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.control.AbstractControl;

/**
*

  • @author Mathias
    */
    public class GrassControl extends AbstractControl {

    private Spatial grass;
    private Main app;
    private boolean culled;

    public GrassControl(Main app) {
    this.app = app;
    }

    @Override
    public void setSpatial(Spatial s) {
    super.setSpatial(s);
    if (s != null) {
    grass = (Node) s;
    }
    }

    @Override
    protected void controlUpdate(float tpf) {
    float distance = grass.getLocalTranslation().distance(app.getCamera().getLocation());
    if (distance >= 50) {
    if (culled) {
    return;
    }
    grass.setCullHint(Spatial.CullHint.Always);
    culled = true;
    } else {
    if (!culled) {
    return;
    }
    grass.setCullHint(Spatial.CullHint.Never);
    culled = false;
    }
    }

    @Override
    protected void controlRender(RenderManager rm, ViewPort vp) {
    }
    }

[/java]

Thank you!

@mathiasj

Your biggest problems right now stem from using a base lighting material for hundreds upon hundreds of grass blades. I wrote a custom low-cost shader to bypass this problem.
Your next biggest problem (they’re both equally big, really) is the fact that your LODing is done per grass blade. Even for only 200 grass blades, that’s 200 distance calculations per frame. Now remember that from what I can gather you have 200 * 200 grass blades.
Group your grass into “grass chunks”, similarly to how (for lack of a better comparison) Minecraft does its chunks. Then batch the grass chunk using the GeometryBatchOptimizer and apply your GrassControl to that. Now, rather than rendering 200 * 200 grass blades and independently calculating their difference as well as lighting, you’ll have maybe 10 * 10 grass chunks and a much simpler shader. The drawback I failed to pass with this approach is the lack of ability to easily animate your grass blades. A combination of world and object positions could easily be used to pass this, and I’m sure with only a few hours of messing with it I could figure something out, but as it stands that’s the main drawback.

@vinexgames

I'll post the source (it's just one class, one model, and a custom material def + shaders thus far) when I'm a little less tired. My next features include different types/heights/colors of grass, flowers, etc.
so?

@eraslt I’ve been busy with other projects, namely my big main game, and this has been all but abandoned. I will, however, need a good foliage system for this game I’m working on, so when that gets developed I’ll likely share it.
That being said, I gave a pretty good breakdown of how it’s written. It’s an incredibly simple thing to create, and it might be better to build one tailored to your needs instead. Mine was highly customized to the project I was working on at the time and likely wouldn’t have been sufficient or even functional with most other projects. It’s often better to do the projects yourself anyway, as it helps build your knowledge of the engine and how to write working code. I have written my own deferred rendering, shadows, multiple effects, etc. and I’m definitely glad I chose that path.