Collision System, How Does It Work?
by Robert Fritzen · in Torque 3D Professional · 07/27/2014 (10:18 am) · 20 replies
So, I've got what should hopefully be a simple question today.
I need to know how to make the collision system work for a new object I'm working on. The object inherits from the item class, and it has a specified position & scale.
I've already got the rendering of the cube where collision should be happening working, I just need to know which methods to declare to work with it.
I also need to know how to exclude certain objects from being included in this item's collision, preferably by typemask and object fields.
Thanks!
I need to know how to make the collision system work for a new object I'm working on. The object inherits from the item class, and it has a specified position & scale.
I've already got the rendering of the cube where collision should be happening working, I just need to know which methods to declare to work with it.
I also need to know how to exclude certain objects from being included in this item's collision, preferably by typemask and object fields.
Thanks!
About the author
Illinois Grad. Retired T3D Developer / Pack Dev.
#2
07/29/2014 (6:30 am)
Essentially, I'm working on a object to mirror the old Tribes 2 ForceField class. So far, The collision works for a single point, where the object appears in the editor, I'm just trying to get the collision to work across the whole field.
#3
You can look at the Box or Mesh Collider compoinents, as well as the supporting collisionBehavior class.
Dan's been doing some cleanup with interfaces and I'll be doing an update to those soon, but those components basically do ONLY collisions and nothing else, so they'd be a pretty decent starting point.
Since I had to drudge through this junk to get that far, if you have any particular questions about how the collisions process, I may have an idea, so feel free to ask :D
07/29/2014 (9:43 am)
While it's not really currently documented and it's slightly flakey in some cases, I'd suggest nabbing the Entity/Component experimental branch I set up.You can look at the Box or Mesh Collider compoinents, as well as the supporting collisionBehavior class.
Dan's been doing some cleanup with interfaces and I'll be doing an update to those soon, but those components basically do ONLY collisions and nothing else, so they'd be a pretty decent starting point.
Since I had to drudge through this junk to get that far, if you have any particular questions about how the collisions process, I may have an idea, so feel free to ask :D
#4
1) void buildConvex(const Box3F& box, Convex* convex): This is responsible for creating a "box" to resemble the collision area of the shape, ideally, you establish the boundaries of the shape that are "collidable" in this method.
2) bool buildPolyList(PolyListContext context, AbstractPolyList *plist, const Box3F &box, const SphereF &sphere): This method is responsible for actually declaring the points that objects "collide" with. For cube shapes and variants, it uses a vector of planes to establish collision along the boundaries.
3) bool castRay(const Point3F &start, const Point3F &end, RayInfo *info): This method handles raycasting for collision, IE: from a point on the object to the target object in question.
4) void queueCollision(SceneObject *obj, const VectorF &vec): This method is the last point before the "collision" event is actually triggered. Ideally, this is where I would filter out the stuff I don't want to actually collide with the field.
So, do I have it right there? Note, I haven't actually tested this logic since I haven't coded two of the above mentioned methods yet, but I'm just in the research phase of my little project here and I want to make sure I'm on the right track.
07/29/2014 (6:45 pm)
So correct me if I'm wrong here, I'm just trying to establish a level of what I'll need to do to accomplish this task. If I'm messing something up in the explanation or if I have something unneeded, or I'm missing something, please tell me what I need to add to this list, and how it's needed for collision support.1) void buildConvex(const Box3F& box, Convex* convex): This is responsible for creating a "box" to resemble the collision area of the shape, ideally, you establish the boundaries of the shape that are "collidable" in this method.
2) bool buildPolyList(PolyListContext context, AbstractPolyList *plist, const Box3F &box, const SphereF &sphere): This method is responsible for actually declaring the points that objects "collide" with. For cube shapes and variants, it uses a vector of planes to establish collision along the boundaries.
3) bool castRay(const Point3F &start, const Point3F &end, RayInfo *info): This method handles raycasting for collision, IE: from a point on the object to the target object in question.
4) void queueCollision(SceneObject *obj, const VectorF &vec): This method is the last point before the "collision" event is actually triggered. Ideally, this is where I would filter out the stuff I don't want to actually collide with the field.
So, do I have it right there? Note, I haven't actually tested this logic since I haven't coded two of the above mentioned methods yet, but I'm just in the research phase of my little project here and I want to make sure I'm on the right track.
#5
07/29/2014 (7:41 pm)
Roughly, AFAIR. Also buildConvex doesn't have to be a box, but in your case it will be. For building a force field, you should check out the GroundPlane class - it's pretty easy to modify it to not return an infinite plane (in fact, that's exactly what happens if you do a navigation query - check out the code inside the PLC_Navigation if).
#6
The general flow is the same in the rest of torque(and the function names should MOSTLY be the same), but it should give you some insight to the active/reactive sides of the collision code.
Naturally, your code will be a little different, but the setup should carry between pretty well.
pastebin.com/kYT59HMc
Also, like Dan said, your collisions don't have to be a box but they should store out to a convex either way. Even the polysoup collisions pretty much just build a linked list of convexes that store one poly each.
As long as whatever collision data you're using stores into your convex, you should be golden.
07/30/2014 (1:19 pm)
This is from the documentation I was typing up today for the box collider component.The general flow is the same in the rest of torque(and the function names should MOSTLY be the same), but it should give you some insight to the active/reactive sides of the collision code.
Naturally, your code will be a little different, but the setup should carry between pretty well.
pastebin.com/kYT59HMc
Also, like Dan said, your collisions don't have to be a box but they should store out to a convex either way. Even the polysoup collisions pretty much just build a linked list of convexes that store one poly each.
As long as whatever collision data you're using stores into your convex, you should be golden.
#7
I should note that the class now directly inherits from SceneObject, instead of Item.
All, I need to expand this functioning just a little bit to do some tests to see if the object is to "collide" with the field or if it can pass through without colliding. These "test" fields are saved on the object.
08/01/2014 (9:00 am)
Alright, so I've made some changes to my code, and now I can actually get the thing to render and collide. But some console inspection with some debug echoes is showing that only buildConvex and onCollision are being called. I should note that the class now directly inherits from SceneObject, instead of Item.
All, I need to expand this functioning just a little bit to do some tests to see if the object is to "collide" with the field or if it can pass through without colliding. These "test" fields are saved on the object.
#8
I've successfully created my ForceField object that bases from ShapeBase, and the field itself can now collide with everything, which is good, mostly. The only problem is that for my needs, I need to be able to filter out objects from the collision process based on typemasks and some fields stored on the object.
Now, if I'm wrong (and I really hope I am here), collision works on each object individually, and interactable collision (IE: when an object touches my object, I can work with them) is not supported. So, the last thing I want to figure out here, is how can I omit certain objects from the collision process based on these ideas.
08/10/2014 (7:36 am)
Alright, so some more research into the engine suggests I may be in for quite a headache here in the near future.I've successfully created my ForceField object that bases from ShapeBase, and the field itself can now collide with everything, which is good, mostly. The only problem is that for my needs, I need to be able to filter out objects from the collision process based on typemasks and some fields stored on the object.
Now, if I'm wrong (and I really hope I am here), collision works on each object individually, and interactable collision (IE: when an object touches my object, I can work with them) is not supported. So, the last thing I want to figure out here, is how can I omit certain objects from the collision process based on these ideas.
#9
08/10/2014 (9:53 am)
ShapeBase has a queueCollision() method that could be made virtual (or ShapeBase could be extended a little) - it appears that you could opt objects out of the collision test queue there, but I haven't really tested it.
#10
08/11/2014 (6:49 pm)
I think to work with per-team forcefields you'd have to modify ShapeBase/Player collision. The forcefield itself, I think, won't be able to access the collider's information, the collider has to access the forcefield.
#11
@Dan: I was hoping that wasn't the case, but it looks like that's how the system was made (which is silly IMO). This kind of takes away the idea of filtering out by typemask without needing to edit numerous files.
EDIT: So, I managed to get the ShapeBase to fork off queueCollision to the ForceField, but ignoring the item still forces a collision.
08/13/2014 (8:22 am)
@Richard: Doesn't look like that worked.@Dan: I was hoping that wasn't the case, but it looks like that's how the system was made (which is silly IMO). This kind of takes away the idea of filtering out by typemask without needing to edit numerous files.
EDIT: So, I managed to get the ShapeBase to fork off queueCollision to the ForceField, but ignoring the item still forces a collision.
#12
08/13/2014 (8:49 am)
Just add the functionality to ShapeBase - I know, that sucks. But it does mean that every ShapeBase-derived object will have awesome collision filtering!
#13
Which then cancels out the ShapeBase call in favor of the FF call. The rest of it works like this:
Unfortunately, the check is being set off which should hit that return statement, but the collision is still occurring.
08/13/2014 (8:55 am)
What I managed to get so far is along the lines of the following:if(dStrcmp(obj->getClassName(), "ForceField") == 0) {
//Fork this off to the ForceField
static_cast<ForceField *>(obj)->queueCollision(static_cast<ForceField *>(obj), this, vec);
return;
}Which then cancels out the ShapeBase call in favor of the FF call. The rest of it works like this:
void ForceField::queueCollision(ForceField *me, ShapeBase *obj, const VectorF &vec) {
.
.
.
bool passOrNot; //<--- Set by tests of the FF settings and that of obj
if(passOrNot) {
//Pass
return;
}
else {
//Mirrored code from queueCollision in ShapeBase without the FF check.
}
}Unfortunately, the check is being set off which should hit that return statement, but the collision is still occurring.
#14
08/13/2014 (9:12 am)
I'll bet the other object is still queuing the collision - maybe this really does need to be farther up the chain....
#15
So, apparently I'm in for a very big nightmare task of rewriting a small, but extremely important class called the CollisionList. Turns out that some wise guy had a wonderful (sarc) idea of making a list class with only add() and clear() operations, and not one single remove() command. Furthermore, the same person who made this class also made it use a static array, making list compaction of deleted elements... well.. "fun".
At this point in time, I'm either looking at a complete re-write of that class to implement a Vector<> of collision elements, or somehow biting the bullet with a list compaction headache method as well as a remove() method.
08/22/2014 (11:45 am)
Oh dear lord Richard, you're right.. And it gets even better...So, apparently I'm in for a very big nightmare task of rewriting a small, but extremely important class called the CollisionList. Turns out that some wise guy had a wonderful (sarc) idea of making a list class with only add() and clear() operations, and not one single remove() command. Furthermore, the same person who made this class also made it use a static array, making list compaction of deleted elements... well.. "fun".
At this point in time, I'm either looking at a complete re-write of that class to implement a Vector<> of collision elements, or somehow biting the bullet with a list compaction headache method as well as a remove() method.
#16
08/22/2014 (12:35 pm)
You could track down everywhere anything gets added to the list and try intercepting it there - or override CollisionList::add() to allow you to pass some way of pre-testing the add (maybe a functor or lambda or something).
#17
08/22/2014 (5:20 pm)
Isn't list compaction as simple as a for loop? I haven't worked with CollisionList for ages :/...
#18
I'm definitely going to investigate this manner much more closely to see if there's a way to do it easier before just going the Vector<> route, but then again, that might have some extra benefits. We will see.
08/22/2014 (8:01 pm)
You'd think it would be, but I can't seem to properly recall the way to do it, without needing to dig deeper.I'm definitely going to investigate this manner much more closely to see if there's a way to do it easier before just going the Vector<> route, but then again, that might have some extra benefits. We will see.
#19
08/22/2014 (8:35 pm)
Something like:void remove(const U32 i) {
if (i < mCount) {
for (U32 j = i, mCount--; j < mCount; j++) {
mCollision[j] = mCollision[j+1];
}
}
}EDIT: turns out I'm dumb. Fixed.
#20
We all have to remember that these containers were hand-crafted at a time when the STL was a very poor choice for game development. And that the STL is a "best general implementation" intended to be as fast as possible while being as generally useful as possible - so it isn't tuned for games, but it's not as dog slow as it used to be.
08/30/2014 (8:24 am)
You know, Torque's containers are "incomplete" - It would be nice to convert to STL containers, but there would be some testing (and maybe tweaking) that would need to be done to ensure that the standard containers aren't hurting performance. For 90% of all cases I'm sure they wouldn't, but in some inner loop somewhere they might be doing some checking that eats cycles.We all have to remember that these containers were hand-crafted at a time when the STL was a very poor choice for game development. And that the STL is a "best general implementation" intended to be as fast as possible while being as generally useful as possible - so it isn't tuned for games, but it's not as dog slow as it used to be.
Torque Owner Daniel Buckmaster
T3D Steering Committee
Also, this probably hasn't dated much.