[SOLVED] Obtaining TerrainQuads from Terrains

Hi Monkeys!

Maybe I’m just a little bit confused after hours of reading postings of grass-systems. I checked out a variety of different solutions from this forum. But now I got stuck on this Thread (Grassy Banana). At the end of the thread, there is a short example on how to use this plugin. That works perfectly if I “code” the terrain. But how could I gain the same result with a terrain created in the SDKs terrain-editor?

More accuarate:

  1. made a scene and saved it as example.j3o
  2. added a terrain with the build in terrain-editor
  3. loaded the terrain-spatial using: Spatial terrain = assetManager.loadModel(“Scenes/example.j3o”);
  4. added the terrain to the rootNode.

Now my problem is, that I need to parse a terrain-quad to the grass-system, but I’m not able to find a solution how to get a specific terrain-quad out of the terrain-spatial. Seems somehow logical, but that doesn’t help much :smile: Could someone please give me a shove in the right direction?! No need for a complete solution, just a hint to reformat my brain :smile:

Greetz Tankwart

I don’t understand why you want to find a TerrainQuad out of the TerrainSpatial. Maybe I’m misunderstanding you but the only way to have GrassyBanana work in the sdk is to create it “manually” and then save your node that hold both of them as a j3o file. After that you can open it in the sdk, but unfortunately there is a bug in the GrassArea serialization so as it is right now you can’t use it in the sdk, I’ll try to investigate this issue.

The only way to create it in the sdk would be to make it pure control or make a wizard which is really really time consuming.

What do you mean by that ?

Hi stormrage!

Thank you for your informations about your plugin!

In your sample-code, there is the line “grassArea = new GrassArea(terrain, 8, assetManager, 75);” As far as I understood, the “terrain”-variable is an instance of TerrainQuad. So I would have to “give in” a TerrainQuad, where the grass should appear. I hope I got this right, but maybe I’m completely wrong. That was why I wanted to obtain a specific TerrainQuad out of my terrain. I thought there would be a function like: terrainNode.getTerrainQuadAt(x,y), where the result can be given to your sample-code, which leads to “growing” grass on this particular TerrainQuad.

Possible, but I don’t think that it is your fault! My english isn’t the best. Sorry for that.

That won’t be a problem on my side. At least not for now. My goal would be reached, when I press play and there appears grass in the scene. I’m not in the state of developing something, I just tinker around an want to understand the different solutions. In this case, I think, I’m on the wrong way. Maybe I’m thinking too simple.

I thought, that I have to give in the terrain (or part(s) of it) to your plugin, so it can figure out which position the grass could be placed. That’s the reason why I wanted to obtain a TerrainQuad from the terrain.

I think the greatest problem now is my lack of english-knowledge. :smile: Maybe it would be easier to paste my actual code here, so someone could say: “You are an idiot! Trys this and you are fine.” :smile:

Here the stripped down and not working code:

package mygame.appstates;

import com.Stomrage.grassybanana.GrassArea.GrassArea;
import com.Stomrage.grassybanana.GrassArea.GrassArea.ColorChannel;
import com.Stomrage.grassybanana.GrassArea.GrassArea.DensityMap;
import com.Stomrage.grassybanana.GrassArea.GrassAreaControl;
import com.jme3.app.Application;
import com.jme3.app.SimpleApplication;
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import com.jme3.asset.AssetManager;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.renderer.Camera;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import java.util.logging.Level;
import java.util.logging.Logger;
import mygame.Main;

public class Terrain extends AbstractAppState {

    private SimpleApplication app;
    private Node rootNode;
    private AssetManager assetManager;
    private BulletAppState bulletAppState;   

    @Override
    public void initialize(AppStateManager stateManager, Application application) {

        super.initialize(stateManager, application);

        app = (SimpleApplication) application;
        rootNode = app.getRootNode();
        assetManager = app.getAssetManager();
        bulletAppState = Main.getBulletAppState();

        init();

        grassInit();

    }

    @Override
    public void update(float tpf) {
    }
    
    Spatial terrain;

    private void init() {
        
        terrain = assetManager.loadModel("Scenes/CarDemo.j3o");
        
        RigidBodyControl terrainControl = new RigidBodyControl(0.0f);

        terrain.addControl(terrainControl);
        terrain.setShadowMode(RenderQueue.ShadowMode.Receive);
        rootNode.attachChild(terrain);
        bulletAppState.getPhysicsSpace().add(terrainControl);

    }
    
    
    private void grassInit() {

        Camera cam = app.getCamera();

        GrassArea grassArea =null;
        
        try {
            //Your TerrainQuad where you want your grass to grow, The minimum holder size, and the Grass draw distance
            grassArea = new GrassArea(terrain, 8, assetManager, 75);
            //A x-tiled atlas texture
            grassArea.setColorTexture(assetManager.loadTexture("Textures/tile_1.png"));
            //A dissolve texture, (a perlin noise is one of the best choice)
            grassArea.setDissolveTexture(assetManager.loadTexture("Textures/noise.png"));
            //A density map for the grass planting
            grassArea.addDensityMap(assetManager.loadTexture("Textures/noise_2.png"));
            //A layer of grass linked to a density map 
            grassArea.addLayer(0f, 0.5f, 2f, ColorChannel.RED_CHANNEL, DensityMap.DENSITY_MAP_1, 2f, 3f);
            grassArea.generate();
        } catch (Exception ex) {
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
        }
        GrassAreaControl grassAreaControl = new GrassAreaControl(cam);
        grassArea.addControl(grassAreaControl);
        rootNode.attachChild(grassArea);


    }
    
}

I got stuck on this line: grassArea = new GrassArea(terrain, 8, assetManager, 75); Cause I’m not able to set the “terrain”-variable correct.

Thank you for investing time in such noob-questions!

Ok I figured it out !

To properly load a TerrainQuad from a j3o file created in the sdk you need to do something like that

        Node loadedNode = (Node) assetManager.loadModel("newScene.j3o");
        TerrainQuad terrain = (TerrainQuad) loadedNode.getChild("terrain-newScene");
        //Where terrain-newScene is the name of you TerrainQuad child

PS : For both of us english isn’t our native langage that’s why we have some difficulties to communicate but it’s fine people always find a way to explain their thought.

1 Like

Wow, that was fast… Thank you! I will try it in the next few minutes and I will give you feedback (and a like, if it works) after that. Thanks alot for your efforts!

EDIT: Once more: Thank you! It now works like a charm! For those who have the same Problem, here the working example:

package mygame.appstates;

import com.Stomrage.grassybanana.GrassArea.GrassArea;
import com.Stomrage.grassybanana.GrassArea.GrassArea.ColorChannel;
import com.Stomrage.grassybanana.GrassArea.GrassArea.DensityMap;
import com.Stomrage.grassybanana.GrassArea.GrassAreaControl;
import com.jme3.app.Application;
import com.jme3.app.SimpleApplication;
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import com.jme3.asset.AssetManager;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.renderer.Camera;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.scene.Node;
import com.jme3.terrain.geomipmap.TerrainQuad;
import java.util.logging.Level;
import java.util.logging.Logger;
import mygame.Main;

public class Terrain extends AbstractAppState {

    private SimpleApplication app;
    private Node rootNode;
    private AssetManager assetManager;
    private BulletAppState bulletAppState;   

    @Override
    public void initialize(AppStateManager stateManager, Application application) {

        super.initialize(stateManager, application);

        app = (SimpleApplication) application;
        rootNode = app.getRootNode();
        assetManager = app.getAssetManager();
        bulletAppState = Main.getBulletAppState();

        init();

        grassInit();

    }

    @Override
    public void update(float tpf) {
    }
    
    Node terrainNode;
    TerrainQuad terrain;

    private void init() {
        
        terrainNode = (Node) assetManager.loadModel("Scenes/CarDemo.j3o");
        terrain = (TerrainQuad) terrainNode.getChild("terrain-CarDemo");
        RigidBodyControl terrainControl = new RigidBodyControl(0.0f);

        terrain.addControl(terrainControl);
        terrain.setShadowMode(RenderQueue.ShadowMode.Receive);
        rootNode.attachChild(terrain);
        bulletAppState.getPhysicsSpace().add(terrainControl);

    }
   
    private void grassInit() {

        Camera cam = app.getCamera();

        GrassArea grassArea =null;
        
        try {
            //Your TerrainQuad where you want your grass to grow, The minimum holder size, and the Grass draw distance
            grassArea = new GrassArea(terrain, 8, assetManager, 75);
            //A x-tiled atlas texture
            grassArea.setColorTexture(assetManager.loadTexture("Textures/grass.jpg"));
            //A dissolve texture, (a perlin noise is one of the best choice)
            grassArea.setDissolveTexture(assetManager.loadTexture("Textures/noise.jpg"));
            //A density map for the grass planting
            grassArea.addDensityMap(assetManager.loadTexture("Textures/density.jpg"));
            //A layer of grass linked to a density map 
            grassArea.addLayer(0f, 0.5f, 2f, ColorChannel.RED_CHANNEL, DensityMap.DENSITY_MAP_1, 2f, 3f);
            grassArea.generate();
        } catch (Exception ex) {
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
        }
        GrassAreaControl grassAreaControl = new GrassAreaControl(cam);
        grassArea.addControl(grassAreaControl);
        rootNode.attachChild(grassArea);


    }
    
}
1 Like