Package com.l2client.util

Source Code of com.l2client.util.GrassLayerUtil

package com.l2client.util;

import java.nio.ByteBuffer;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;

import jme3tools.optimize.GeometryBatchFactory;

import com.jme3.asset.AssetManager;
import com.jme3.asset.TextureKey;
import com.jme3.bounding.BoundingBox;
import com.jme3.bounding.BoundingSphere;
import com.jme3.bounding.BoundingVolume;
import com.jme3.collision.CollisionResults;
import com.jme3.material.MatParam;
import com.jme3.material.Material;
import com.jme3.material.RenderState;
import com.jme3.material.RenderState.FaceCullMode;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Ray;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Quad;
import com.jme3.shader.VarType;
import com.jme3.texture.Image;
import com.jme3.texture.Texture;

/**
* TODO description of working of the utility
*
*/
public class GrassLayerUtil {

  /**
   * Creates a patch field of grass quads distributed on the y up surface of
   * the given spatial
   *
   * @param spatial
   *            Spatial with a geometry attached on which the grass should be
   *            planted on
   * @param assetManager
   *            Reference to the jme asset manager for loading of grass
   *            texture
   * @param texturePath
   *            Path to the grass image ressource
   * @param patchScaleVariation
   *            Patches will be scaled randomly by a factor between
   *            1/patchScaleVariation and patchScaleVariation
   * @param patchWidth
   *            Width of an unscaled patch quad
   * @param patchHeight
   *            Height of an unscaled patch quad
   * @param inc
   *            Space between quads
   * @param fadeEnd
   *            At what distance from the camera no patches should be visible
   *            anymore, when 0 disabled
   * @param fadeRange
   *            At what distance from the fadeEnd fading should start
   * @param minIntensity
   *         Minimum intensity of texture color to be a valid placement position from 0-1.0
   * @param channelId
   *         Which texture channel should be used to check for placement
   * @return
   */
  public static Node createPatchField(Spatial spatial,
      AssetManager assetManager, String texturePath,
      float patchScaleVariation, float patchWidth, float patchHeight,
      float inc, float fadeEnd, float fadeRange, float minIntensity,
      int channelId, int clusters) {
    Node spatNode = (Node) spatial;
    Node grassLayer = new Node("grass_" + spatial.getName());
    grassLayer.setModelBound(new BoundingBox());

    Texture tex = assetManager.loadTexture(texturePath);
    Material faceMat = new Material(assetManager,
        "/com/l2client/materials/LightingGrass.j3md");
    faceMat.getAdditionalRenderState().setBlendMode(
        RenderState.BlendMode.Alpha);
    faceMat.getAdditionalRenderState().setFaceCullMode(FaceCullMode.Off);
    faceMat.getAdditionalRenderState().setDepthWrite(true);
    faceMat.getAdditionalRenderState().setDepthTest(true);
    faceMat.setTransparent(true);
    faceMat.setTextureParam("DiffuseMap", VarType.Texture2D, tex);
    faceMat.setFloat("AlphaDiscardThreshold", 0.3f);
    // faceMat.setTextureParam("AlphaMap", VarType.Texture2D,tex);
    faceMat.setBoolean("UseAlpha", true);
    if (fadeEnd > 0f) {
      faceMat.setFloat("FadeEnd", fadeEnd);// 300f);
      faceMat.setFloat("FadeRange", fadeRange); // 50f);
      faceMat.setBoolean("FadeEnabled", true);
    }
    faceMat.setBoolean("Swaying", true);
    faceMat.setVector3("SwayData", new Vector3f(1.0f, 0.5f, 300f));// frequency,
                                    // variation,
                                    // third?
    faceMat.setVector2("Wind", new Vector2f(1f, 1f));

    Geometry terrain = null;

    if (spatial instanceof Geometry) {
      terrain = (Geometry) spatial;
    } else {
      for (Spatial currentSpatial : spatNode.getChildren()) {
        if (currentSpatial instanceof Geometry) {
          terrain = (Geometry) currentSpatial;
          break;
        }
      }
    }

    if (terrain == null || spatNode.getChildren().isEmpty()) {
      Logger.getLogger(GrassLayerUtil.class.getName()).log(Level.SEVERE,
          "Could not find terrain object.", new Exception());
      System.exit(0);
    }

    // Generate grass uniformly with random offset.
    float terrainWidth = 1f * 256; // get width length of terrain(assuming
                    // its a square)
    BoundingVolume bounds = ((Spatial) terrain).getWorldBound();
    if (BoundingVolume.Type.AABB.equals(bounds.getType())) {
      BoundingBox bb = ((BoundingBox) bounds);
      terrainWidth = Math.max(bb.getXExtent(), bb.getZExtent());
      terrainWidth *= 2f;
    } else if (BoundingVolume.Type.Sphere.equals(bounds.getType())) {
      terrainWidth = ((BoundingSphere) bounds).getRadius();
      terrainWidth *= 2f;
    }
    Vector3f centre = bounds.getCenter(); // get the centr location of the
                        // terrain
    Vector2f grassPatchRandomOffset = new Vector2f().zero();
    Vector3f candidateGrassPatchLocation = new Vector3f();

    Random rand = new Random();
    Ray ray = new Ray(Vector3f.ZERO, Vector3f.UNIT_Y.mult(-1f));
    CollisionResults results = new CollisionResults();
    float ax, az;
    for (float x = centre.x - terrainWidth / 2 + inc; x < centre.x
        + terrainWidth / 2 - inc; x += inc) {
      for (float z = centre.z - terrainWidth / 2 + inc; z < centre.z
          + terrainWidth / 2 - inc; z += inc) {
        grassPatchRandomOffset.set(inc, inc);
        grassPatchRandomOffset.multLocal(rand.nextFloat()); // make the
                                  // off set
                                  // length a
                                  // random
                                  // distance
                                  // smaller
                                  // than the
                                  // increment
                                  // size
        grassPatchRandomOffset
            .rotateAroundOrigin(
                (float) (((int) (rand.nextFloat() * 359)) * (Math.PI / 180)),
                true); // rotate the offset by a random angle

        ax = x + grassPatchRandomOffset.x;
        az = z + grassPatchRandomOffset.y;
        ray.setOrigin(new Vector3f(ax, centre.y + terrainWidth, az));
        terrain.collideWith(ray, results);

        if (results.size() <= 0)
          continue;

        try {
          if (results.size() > 0) {

            candidateGrassPatchLocation.set(ax, results
                .getCollision(0).getContactPoint().y, az);
            results.clear();

            if (isGrassLayer(candidateGrassPatchLocation, terrain,
                minIntensity, terrainWidth, channelId)) {
              // this will be in world coords, but we want it to
              // be in local
              candidateGrassPatchLocation.subtractLocal(terrain
                  .getWorldTranslation());
              grassLayer.attachChild(createGrassPatch(
                  candidateGrassPatchLocation, faceMat,
                  patchScaleVariation, patchWidth,
                  patchHeight, rand.nextFloat()));
            }
          }
        } catch (Exception e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }

      }
    }

    GeometryBatchFactory.optimize(grassLayer);
    grassLayer.updateGeometricState();
   
//    DistanceLodControl c = new DistanceLodControl();
//    (grassLayer).getChild(0).addControl(c);
//    c.setDistTolerance(250f);

    return (Node) grassLayer;
  }

  private static Node createGrassPatch(Vector3f location, Material faceMat,
      float patchScaleVariation, float patchWidth, float patchHeight,
      float rand) {
    Node grassPatch = new Node();
    float selectedSizeVariation = (float) (rand * (patchScaleVariation - (1 / patchScaleVariation)))
        + (1 / patchScaleVariation);
    Quad faceShape = new Quad((patchWidth * selectedSizeVariation),
        patchHeight * selectedSizeVariation, false);
    Geometry face1 = new Geometry("face1", faceShape);
    face1.move(-(patchWidth * selectedSizeVariation) / 2, 0, 0);
    grassPatch.attachChild(face1);

    Geometry face2 = new Geometry("face2", faceShape);
    face2.rotate(new Quaternion().fromAngleAxis(-FastMath.PI / 2,
        new Vector3f(0, 1, 0)));
    face2.move(0, 0, -(patchWidth * selectedSizeVariation) / 2);
    grassPatch.attachChild(face2);

    grassPatch.setCullHint(Spatial.CullHint.Dynamic);
    grassPatch.setQueueBucket(RenderQueue.Bucket.Transparent);

    face1.setMaterial(faceMat);
    face2.setMaterial(faceMat);

    grassPatch.rotate(new Quaternion().fromAngleAxis(
        (((int) (Math.random() * 359)) + 1) * (FastMath.PI / 190),
        new Vector3f(0, 1, 0)));
    grassPatch.setLocalTranslation(location);

    return grassPatch;
  }

  private static boolean isGrassLayer(Vector3f pos, Geometry terrain,
      float intensitiyThreshold, float scaledWidth, int channelId) {
    MatParam matParam = terrain.getMaterial().getParam("AlphaMap");
    if (matParam == null)// try to get the default one..
      matParam = terrain.getMaterial().getParam("DiffuseMap");
    if (matParam == null)
      return false;
    Texture tex = (Texture) matParam.getValue();
    Image image = tex.getImage();
    Vector2f uv = getPointPercentagePosition(terrain, pos, scaledWidth);

    ByteBuffer buf = image.getData(0);
    int width = image.getWidth();
    int height = image.getHeight();

    int x = (int) (uv.x * width);
    int y = (int) (uv.y * height);

    if (((TextureKey) tex.getKey()).isFlipY())
      y = height - y;
    // compute bytes of image
    int bytes = image.getFormat().getBitsPerPixel() / 8;
    int position = (y * width + x) * bytes;// image dependent..
    position += channelId;// move on by channel id offset

    if (position > buf.capacity())
      return false;

    buf.position(position);

    // This is file format dependent! For example I have an image in format
    // BGR8, with only 3 values and inverse order..
    // But we just read the first one here,..
    // TODO make selection of which value a parameter
    if (byte2float(buf.get()) >= intensitiyThreshold) {
      return true;
    } else {
      return false;
    }
  }

  private static Vector2f getPointPercentagePosition(Geometry terrain,
      Vector3f worldLoc, float scaledWidth) {
    Vector2f uv = new Vector2f(worldLoc.x, worldLoc.z);
    uv.subtractLocal(terrain.getWorldTranslation().x,
        terrain.getWorldTranslation().z); // center it on 0,0
    // float scaledSize = terrain.getTerrainSize()*scale;
    uv.addLocal(scaledWidth / 2, scaledWidth / 2); // shift the bottom left
                            // corner up to 0,0
    uv.divideLocal(scaledWidth); // get the location as a percentage

    return uv;
  }

  private static float byte2float(byte b) {
    return ((float) (b & 0xFF)) / 255f;
  }

}
TOP

Related Classes of com.l2client.util.GrassLayerUtil

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.