[SOLVED] Determining the FOV of a character

Yello !

I’m trying to establish the field of view of characters.
I already implemented the collision between mouse and models, so I know a little bit about collision management.

What I need is this : a first collision test must determine what characters are in a radius from another one : since I would need a circle, the Sphere seems more appropriated.

Then, I will need to use probably a CollisionSegment, to find if nothing obstructs the line of view (it shouldn’t be that different from the mouse clicking on models).

I’ve based the code on the manual and the CollisionRay thing for mouse colliding, and here is what I have :

(_root is the root NodePath of the characters)
Constructor

  _collisionHandlerQueue = new CollisionHandlerQueue();
  _collisionFov          = new CollisionSphere(_root.get_x(), _root.get_y(), _root.get_z(), 1500.f);
  _collisionNode         = new CollisionNode("FovSphere" + data["name"].Value());
  _collisionPath         = NodePath(_collisionNode);
  _collisionNode->add_solid(_collisionFov);
  _collisionTraverser.add_collider(_collisionPath, _collisionHandlerQueue);

Loop

  _collisionFov->set_center(_root.get_pos());
  _collisionTraverser.traverse(_window->get_render());
  cout << "Num of collisions -> " _collisionHandlerQueue->get_num_entries() << endl;
  for(unsigned int i = 1; i<= _collisionHandlerQueue->get_num_entries();i++)
  {
    CollisionEntry* entry = _collisionHandlerQueue->get_entry(i);
    cout<<entry;
  }

There’s a lot (a huge lot) of models around _root, but it doesn’t detect anything. With a radius that huge, I can’t imagine that no collision should be detected.

Could you help me with that one too ?

Hi

Since you are able to do mouse pick on models then you must have done set_collide_mask on those models.

I don’t see calls to
set_from_collide_mask and set_into_collide_mask
in the bit of code you have shown.
You must have done that for the the CollisionRay’s CollisionNode when setting up your mouse picker.

Oh my. I did forgot that line…
On another matter, even if I manually change the collide mask of every children of a node (I had the parent have a mask of 64 and the children of 0), all these children are still detected by the traverser. I easily and cleanly worked around it, but I’d like to know if there’s a way to just ignore them ?

On another collision matter, I tried to implement a CollisionRay and miserably failed. If I set it with set_from_lens, it detects collision, but if I use set_origin and set_direction, I can’t get it to detect any collision.

The CollisionNode is parented to get_render(), and set_origin and set_decision are both the position of NodePath also parented to get_render(). Yet, nothing is detected.

Also, I find different result for rays with origin (0, 1, 0) destination (0, -1, 0) and rays with origin (0, -1, 0) destination (0, 1, 0). Which means that either I’m doing something really bad, or I really understood nothing to rays.
One of those two actually collide with some stuff, and the other one with nothing.

Just to be sure, here is some code again :

  _lofNode   = new CollisionNode("lofRay");
  _lofPath   = _window->get_render().attach_new_node(_lofNode);
  _lofNode->set_from_collide_mask(GeomNode::get_default_collide_mask());
  _lofRay    = new CollisionRay();
  _lofNode->add_solid(_lofRay);
  _lofHandlerQueue = new CollisionHandlerQueue();
  _lofTraverser.add_collider(_lofPath, _lofHandlerQueue);
  _lofRay->set_direction(_root.get_pos());
  _lofRay->set_origin(other->_root.get_pos());

  _lofTraverser.traverse(_window->get_render());

  cout << "Line of sight colliding with " << _lofHandlerQueue->get_num_entries() << endl;
  
  _lofHandlerQueue->sort_entries();

  for (unsigned int i = 0 ; i < _lofHandlerQueue->get_num_entries() ; ++i)
  {
    CollisionEntry* entry = _lofHandlerQueue->get_entry(i);
    NodePath        node  = entry->get_into_node_path();

    if (other->_root.is_ancestor_of(node))
      break ;
    else if (_root.is_ancestor_of(node))
      break ;
    else
    {
      cout << node.get_name() << endl;
      break ;
    }
  }
  return (ret);

EDIT: Alright. As usual, it was something stupid.

So if anyone ever needs to know, this is how to find if one object can see another one :

First, create the collision Ray and parent it to the character from which it will originate. Set the origin to 0, 0, 0.

Then, everytime you want to check if your character can see another one, do this, where _root is the NodePath from which your ray originate, and _other is the NodePath you want to now if _root can see :

  // First, the ray must not be rotated. Since it's a child to your root node, do this :
  LVecBase3 rot = _root.get_hpr();
  _collisionRayPath.set_hpr(-rot.get_x(), -rot.get_y(), -rot.get_z());

// Then, compute the relative position of other from root :
LVector3 dir = _root.get_relative_vector(_other, _other.get_pos() - _root.get_pos());
_collisionRay->set_direction(dir);

// And then, you execute the traverser :
_traverser.traverse(_window->get_render());

// And of course, sort the entries from your queue :
_handlerQueue->sort_entries();

Here you go ! Easy as pie ! If only I didn’t forgot the rotation part I would’ve get some sleep last night !