Problem with Unit highlighting

Hi Guys,

i got a Problem with highlighting a Unit.

This is my Code:


float radius = 0;
Vector3f extent = new Vector3f();
((BoundingBox) activeToMove.getWorldBound()).getExtent(extent);
if (extent.x >= extent.z)
{
    radius = extent.x;
}
else
{
   radius = extent.z;
}
float delta = FastMath.PI / 12;
Vector3f vec = null;
Vector3f[] points = new Vector3f[24];
ColorRGBA[] colors = new ColorRGBA[24];

for (int i1 = 0; i1 < 24; i1++)
{
   Vector3f rayOrigin = (new Vector3f(activeToMove.getWorldTranslation().x + radius
   * FastMath.cos(delta * i1), 1000, activeToMove.getWorldTranslation().z + radius* FastMath.sin(delta * i1)));
   Vector3f rayDirection = (new Vector3f(activeToMove.getWorldTranslation().x + radius
   * FastMath.cos(delta * i1), 99,activeToMove.getWorldTranslation().z+radius   * FastMath.sin(delta * i1)));
   Ray ray = new Ray(rayOrigin, rayDirection);
   picker.getTerrainIntersection(ray, vec);
   points[i1] = vec.add(0, 0.05f, 0);
   colors[i1] = new ColorRGBA(0, 1, 0, 1);
}


This Code is pretty much copied out of this Thread http://jmonkeyengine.com/forum/index.php?topic=4084.msg36324#msg36324 and a bit modyfied to match to my program. My Probelm is the casted Ray to get the terrainlocation for the linepoint seems not to intersect my terrain and i dont know why. My Terrain is an instance of terrainpage, but the BresenhamTerrainPicker sould handle this. So, what did i wrong? Any idea?
In my understanding the rayOrigin vector is somewhere above the activeToMove Node. The rayDirection vector has the same coordiantes except the y coordiante. So the direction vector should be somewhere directly under the origin. So the ray should point vertically on the terrain, right? A Ray is endless and all objects in a specific node and its children, which the ray intersects, will be return, right? So why is the returned Vector of "picker.getTerrainIntersection(ray, vec);" null?

Thanks for the help

Edregol

isn't rayDirection local? so it should be 0,-1,0 to point downwards

Here's how I modified that loop to work with my terrain:



for (int j = 0; j < 24; j++) {
   vec =
      new Vector3f(node.getWorldTranslation().x + radius * FastMath.cos(delta * j),
      terrainBlock.getHeight(node.getWorldTranslation().x + radius * FastMath.cos(delta * j), node.getWorldTranslation().z + radius * FastMath.sin(delta * j)),
      node.getWorldTranslation().z + radius * FastMath.sin(delta * j));

   points[j] = vec.add(0, 0.05f, 0);
   colors[j] = new ColorRGBA(0,1,0,1);
}



I modified Y in the vector to be relative to the height of the terrain.  node is the object being selected.  This is not the most efficient, but you get the idea.

Hope that helps
evilangelist

isn't rayDirection local? so it should be 0,-1,0 to point downwards

Crap your right. Thanks for mentioning that.


Here's how I modified that loop to work with my terrain:


for (int j = 0; j < 24; j++) {
   vec =
      new Vector3f(node.getWorldTranslation().x + radius * FastMath.cos(delta * j),
      terrainBlock.getHeight(node.getWorldTranslation().x + radius * FastMath.cos(delta * j), node.getWorldTranslation().z + radius * FastMath.sin(delta * j)),
      node.getWorldTranslation().z + radius * FastMath.sin(delta * j));

   points[j] = vec.add(0, 0.05f, 0);
   colors[j] = new ColorRGBA(0,1,0,1);
}



I modified Y in the vector to be relative to the height of the terrain.  node is the object being selected.  This is not the most efficient, but you get the idea.

Hope that helps
evilangelist


Nice approach and so easy, I was thinking a bit too complex, once again :)

Thanks for the help

Edregol

Unfortunaltey both suggestions don't work for me.



Changeing the direction of the ray to (0,-1,0) results in an returned null vector also.



Using this Code:


for (int j = 0; j < 24; j++) {
   vec =
      new Vector3f(node.getWorldTranslation().x + radius * FastMath.cos(delta * j),
      terrainBlock.getHeight(node.getWorldTranslation().x + radius * FastMath.cos(delta * j), node.getWorldTranslation().z + radius * FastMath.sin(delta * j)),
      node.getWorldTranslation().z + radius * FastMath.sin(delta * j));

   points[j] = vec.add(0, 0.05f, 0);
   colors[j] = new ColorRGBA(0,1,0,1);
}


Lets me get a bit further, but it doesnt look like it should do.



The highlight marker floats over the ground and on a much higher altitude than it schould be.

My code is nearly the same as is posted previousely:


activeToMove = pd.getTargetMesh().getParent();

                  float radius = 0;
                  Vector3f extent = new Vector3f();
                  ((BoundingBox) activeToMove.getWorldBound()).getExtent(extent);
                  if (extent.x >= extent.z)
                  {
                     radius = extent.x;
                  }
                  else
                  {
                     radius = extent.z;
                  }
                  float delta = FastMath.PI / 12;
                  Vector3f vec = null;

                  Vector3f[] points = new Vector3f[24];
                  ColorRGBA[] colors = new ColorRGBA[24];

                  for (int j = 0; j < 24; j++) {
                     vec =
                        new Vector3f(activeToMove.getWorldTranslation().x + radius * FastMath.cos(delta * j),
                        terrain.getHeight(activeToMove.getWorldTranslation().x + radius * FastMath.cos(delta * j), activeToMove.getWorldTranslation().z + radius * FastMath.sin(delta * j)),
                        activeToMove.getWorldTranslation().z + radius * FastMath.sin(delta * j));

                     points[j] = vec.add(0, 0.05f, 0);
                     colors[j] = new ColorRGBA(0,1,0,1);
                  }

                  selectionShape = new Line("selection " + activeToMove.getName(), points, null, colors, null);
                  selectionShape.setMode(Line.Mode.Loop);
                  selectionShape.setLineWidth(3);

                  selectionShape.updateRenderState();
                  activeToMove.attachChild(selectionShape);


Any Idea? thanks for the help

Edregol

i think terrainBlock.getHeight() returns just the height of the terrain, it doesn't take the local translation of the terrainblock into account.

try:

terrainBlock.getHeight(…) + terrainBlock.getLocalTranslation().y;

Core-Dump said:

i think terrainBlock.getHeight() returns just the height of the terrain, it doesn't take the local translation of the terrainblock into account.
try:
terrainBlock.getHeight(..) + terrainBlock.getLocalTranslation().y;

I dont know if this is relevant, but I'm using terrainpage instead of terrainblock. Apart from that adding  "+ terrainPage.getLocalTranslation().y" doesn't change anything. Maybe the problem is cause by the node activeToMove? It's a self written class which extends Node, the orginial node functionality is untouched. A Spatial is attached to it, in this case its a box. The box got a BoundingBox and is updated while instantiating it. I dont see any problem there but maybe one of you?

Edregol

can you make a simple runnable test case to reproduce the problem ?

I think core-dump is along the right track and your problem is related to how you are translating your unit node.  How do you create the box node?  And how do you initially position it?

Core-Dump said:

can you make a simple runnable test case to reproduce the problem ?

Could be difficult because my whole code is a mess.^^ But I'll give it a try.
evilangelist said:

I think core-dump is along the right track and your problem is related to how you are translating your unit node.  How do you create the box node?  And how do you initially position it?

Until then, I could post some code snippets which describe how I position my nodes and stuff.

Lets start.

This codesnippet is executed int the initGame Method. The UnitManager contains a Hashmap which stores all Units. Unit is a self written class which extends Node.


Box box = new Box("Player1", new Vector3f(0, 0, 0), new Vector3f(10, 10, 10));
      box.setModelBound(new BoundingBox());
      box.updateModelBound();
      UnitManager.getUnitMap().put("Player1", new Unit(50, 10, 10, box));



Because I create two units at the startup, I've written this "for" segment. We're still in InitGame. It gets all units, stored in the Hashmap and places them onto the terrain. This code seems to work to me.


int distance = 0;
      Node model = null;
      for (String i : UnitManager.getUnitMap().keySet())
      {

         model = UnitManager.getUnitMap().get(i);
         rootNode.attachChild(model);
         model.setLocalTranslation(new Vector3f(100, 0, distance));
         distance += 20;
         float characterMinHeight = terrain.getHeight(model.getLocalTranslation())
               + ((BoundingBox) model.getChild(0).getWorldBound()).yExtent;
         model.getLocalTranslation().y = characterMinHeight;
      }


Here the unit is demermined which was clicked and the selectionshape is set. Executed in the update Method.


if (MouseInput.get().isButtonDown(0))
      {
         Ray mouseRay = getRay();
            pr.clear();
            rootNode.findPick(mouseRay, pr);
            for (int i = 0; i < pr.getNumber(); i++)
            {
               PickData pd = pr.getPickData(i);
               if (pd.getTargetMesh().getName().startsWith("Play"))
               {
                  activeToMove = pd.getTargetMesh().getParent();

                  float radius = 0;
                  Vector3f extent = new Vector3f();
                  ((BoundingBox) activeToMove.getWorldBound()).getExtent(extent);
                  if (extent.x >= extent.z)
                  {
                     radius = extent.x;
                  }
                  else
                  {
                     radius = extent.z;
                  }
                  float delta = FastMath.PI / 12;
                  Vector3f vec = null;

                  Vector3f[] points = new Vector3f[24];
                  ColorRGBA[] colors = new ColorRGBA[24];

                  for (int j = 0; j < 24; j++) {
                     vec =
                        new Vector3f(activeToMove.getWorldTranslation().x + radius * FastMath.cos(delta * j),
                        terrain.getHeight(activeToMove.getWorldTranslation().x + radius * FastMath.cos(delta * j), activeToMove.getWorldTranslation().z + radius * FastMath.sin(delta * j))+ terrain.getWorldTranslation().y,
                        activeToMove.getWorldTranslation().z + radius * FastMath.sin(delta * j));

                     points[j] = vec.add(0, 0.05f, 0);
                     colors[j] = new ColorRGBA(0,1,0,1);
                  }

                  selectionShape = new Line("selection " + activeToMove.getName(), points, null, colors, null);
                  selectionShape.setMode(Line.Mode.Loop);
                  selectionShape.setLineWidth(3);

                  selectionShape.updateRenderState();
                  activeToMove.attachChild(selectionShape);
               }
[...]



If a unit is selected and the right mousebutton is pressed it'll be moved. MovementManger contains a Hashmap also. This Hashmap is used to store the unit and its desired location. Yes i know this could be solved a bit better, but this was the first thing which came to my mind :)
Picker is an instance of BresenhamTerrainPicker. The if part which includes"needsToMove = MovementManager.moveUnits(tpf);" issues the unit movement.
This code is executed in the updateMethod.


if (MouseInput.get().isButtonDown(1))
      {
         Ray mouseRay = getRay();
         pr.clear();
         rootNode.findPick(mouseRay, pr);
         Vector3f loc = new Vector3f();
         for (int i = 0; i < pr.getNumber(); i++)
         {
         if (activeToMove.getName().startsWith("Play"))
         {
            
            picker.getTerrainIntersection(mouseRay, loc);
            MovementManager.getMoveUnitMap().put(activeToMove.getName(), new UnitToMove((Unit) activeToMove, loc));
            needsToMove = true;
         }
      }
if (needsToMove)
      {
         needsToMove = MovementManager.moveUnits(tpf);
      }



This is the code from the MovementManager class, which handles the unit movement. Evertime a unit needs to be moved. the unit and its desired loaction will be putted into the hashmap. If the unit reaches its location, the approprate entry in the hasmap will be deleted.


public static boolean moveUnits(float tpf)
   {
      Collection keys = new ArrayList(MoveUnitMap.keySet());
      if (MoveUnitMap.size() > 0)
      {
         for (Iterator iterator = keys.iterator(); iterator.hasNext();)
         {
            String key = (String) iterator.next();
            UnitToMove UTM = MoveUnitMap.get(key);
            UTM.getUnit().setMoving(true);
            Vector3f loc = UTM.getLoc();
            Vector3f unit2Destination = new Vector3f();
            unit2Destination = loc.subtract(UTM.getUnit().getLocalTranslation());
            logger.info("Moving " + UTM.getUnit().getName() + " to " + loc + ". now: "
                  + UTM.getUnit().getLocalTranslation());
            UTM.getUnit().setLocalTranslation(UTM.getUnit().getLocalTranslation().add(unit2Destination.mult(tpf)));
            checkReached(loc, key, UTM.getUnit());
         }
         return true;
      }
      else
      {
         return false;
      }
   }

   private static void checkReached(Vector3f loc, String Field, Unit unit)
   {
      float eps = 3f;
      float x = loc.x - unit.getWorldTranslation().x;
      float y = loc.y - unit.getWorldTranslation().y;
      float z = loc.z - unit.getWorldTranslation().z;
      if (x < eps && y < eps && z < eps)
      {
         unit.setMoving(false);
         MoveUnitMap.remove(Field);
      }
   }



The whole code i've posted here is working to me, but maybe there is some logical fault in it.
I hope someone understands what i've done there. :)

Thanks
Edregol

why dont you use a Projection Texture



for example:


ProjectedTextureUtil.updateProjectedTexture( m_selectionMarkerTexture, 20.0f, 1.0f, 1.0f, 10.0f, m_selectionMarkerModel.getLocalTranslation(), pos, Vector3f.UNIT_XYZ );

Hm well, I could give it a try.



Thanks

Well the code is running without any errors but i dont see any projected textures.



I've created the texture, attached the texture to the texturestate and the texturestate is attached to the terrain:


TextureState ts = display.getRenderer().createTextureState();
      projectedTexture = TextureManager.loadTexture( Game.class.getClassLoader().getResource(
      "jmetest/data/texture/halo.jpg" ), Texture.MinificationFilter.Trilinear, Texture.MagnificationFilter.Bilinear );
      ts.setTexture( projectedTexture, 0 );
      projectedTexture.setMatrix(new Matrix4f());
      projectedTexture.setWrap(Texture.WrapMode.BorderClamp );
      projectedTexture.setEnvironmentalMapMode( Texture.EnvironmentalMapMode.EyeLinear );
      projectedTexture.setApply( Texture.ApplyMode.Combine );
      projectedTexture.setCombineFuncRGB( Texture.CombinerFunctionRGB.Add );
      projectedTexture.setCombineSrc0RGB( Texture.CombinerSource.CurrentTexture );
      projectedTexture.setCombineOp0RGB( Texture.CombinerOperandRGB.SourceColor );
      projectedTexture.setCombineSrc1RGB( Texture.CombinerSource.Previous );
      projectedTexture.setCombineOp1RGB( Texture.CombinerOperandRGB.SourceColor );
      projectedTexture.setCombineScaleRGB( Texture.CombinerScale.One );
        terrain.setRenderState(ts);


Seems right to me.

Do you know how to calculate the pos vector?


ProjectedTextureUtil.updateProjectedTexture( projectedTexture, 20.0f, 1.0f, 1.0f, 1000.0f, activeToMove.getLocalTranslation(), [i][b]pos[/b][/i], Vector3f.UNIT_Y );


I guess, the pos vector needs to be a local vector. My activeToMove is an instance of Node and is close to the terrain on which the texture needs to be projected. Maybe this is a problem?. if I set pos to (0,-1,0) nothing happens. Even if i set the y value 6th parameter to something like 500 no texture will be projected to the terrain.

The TestProjectedTexture.java from jmetest doesn't help at all.

Any idea?

Thanks Edregol

Hi there,

recently i found some time to work on this again.

I was thinking my problem occured because my activeToMove model doesn't look at the ground. In this case the texture would be projected somewhere far away. So I attached a node to my activeToMove. Its postition a bit above the model and  looking on the ground. But still no projected texture. Here is the code:


Node projNode = new Node("projNode");
                  activeToMove.attachChild(projNode);
                  Vector3f projLoc = new Vector3f();
                  projLoc.x = activeToMove.getLocalTranslation().x;
                  projLoc.z = activeToMove.getLocalTranslation().z;
                  projLoc.y = 500;
                  projNode.setLocalTranslation(projLoc);
                  Vector3f projLookLoc = new Vector3f();
                  projLookLoc.x = activeToMove.getLocalTranslation().x;
                  projLookLoc.z = activeToMove.getLocalTranslation().z;
                  projLookLoc.y = -500;
                  projNode.lookAt(projLookLoc, Vector3f.UNIT_Y);
                  ProjectedTextureUtil.updateProjectedTexture(projectedTexture, 20.0f, 1.0f, 1.0f, 1000.0f,
                        activeToMove.getLocalTranslation(), projLookLoc, Vector3f.UNIT_Y);

Hi there,

solved the problem with this code. Found it somewhere in this forum, dont remember where :slight_smile:



Vector3f loc = getTerrainPick(getTerrainRay(activeSelection.getLocalTranslation()));
Vector3f newLoc = loc.clone();
                  newLoc.y += 20f;
                  Vector3f groundLoc = loc.clone();
                  groundLoc.y = terrain.getHeight(groundLoc);
                  groundLoc.z -= 10;
                  ProjectedTextureUtil.updateProjectedTexture(projectedTexture1, 30.0f, 1.0f, 1.0f, 1000.0f,
                        newLoc, groundLoc, new Vector3f(0, -1, 0));



greetings Edregol