I just installed beta16. I love the interface changes, I think iterating is definitely nicer than needing a callback, and processing all of the contacts at once is a much better idea than processing them 1 by 1.
Anyways, I encountered something very odd when Newton is set to 2 threads (1 thread works fine). Let's start with some code:
- Code: Select all
body *bodyCast(const NewtonBody *_body)
{
body *ret = (body*)NewtonBodyGetUserData(_body);
assert(ret);
return ret;
}
void contact::reject()
{
if (valid)
{
NewtonContactJointRemoveContact(_joint, _contact);
valid = false;
}
}
contact::contact(const NewtonJoint *_joint, void *_contact):
valid(true),
_joint(_joint),
_contact(_contact),
_material(NewtonContactGetMaterial(_contact)),
faceAttrib(NewtonMaterialGetContactFaceAttribute(_material)),
normalSpeed(NewtonMaterialGetContactNormalSpeed(_material))
{
NewtonMaterialGetContactForce(_material, &force.x);
NewtonMaterialGetContactPositionAndNormal(_material, &position.x, &normal.x);
NewtonMaterialGetContactTangentDirections(_material, &tangent0.x, &tangent1.x);
}
void processContactCB(const NewtonJoint *_joint, float dt, int thread)
{
body *body0 = bodyCast(NewtonJointGetBody0(_joint));
body *body1 = bodyCast(NewtonJointGetBody1(_joint));
const size_t NUM_CONTACTS = NewtonContactJointGetContactCount(_joint);
contactList contacts;
void *_contact = NewtonContactJointGetFirstContact(_joint);
// iterate over each contact, set its defaults, and add it
while (_contact)
{
contact *c = new contact(_joint, _contact);
// set the default material properties
c->elasticity(std::max(body0->material.elasticity, body1->material.elasticity));
c->softness(std::max(body0->material.softness, body1->material.softness));
float sFric = std::min(body0->material.sFric, body1->material.sFric);
float kFric = std::min(body0->material.kFric, body1->material.kFric);
c->frictionCoef(sFric, kFric, 0);
c->frictionCoef(sFric, kFric, 1);
c->frictionState(true, 0);
c->frictionState(true, 1);
contacts.push_back(c);
_contact = NewtonContactJointGetNextContact(_joint, _contact);
}
assert(NUM_CONTACTS == contacts.size());
body0->processContact(*body1, contacts);
body1->processContact(*body0, contacts);
// delete the contacts (don't leak because of new)
contactList::reverse_iterator i = contacts.rbegin();
for (; i != contacts.rend(); ++i)
delete *i;
}
// simple test to try out contact rejection
void body::processContact(body &o, const contactList &contacts)
{
//contacts.front()->reject();
}
Very simply put, if I uncomment the contact rejection in body::processContact(), the assert in processContactCB() gets triggered randomly, usually towards when most of the bodies have settled for the most part (but not always):
NUM_CONTACTS 9
contacts.size() 7
Assertion failed: NUM_CONTACTS == contacts.size()
If I remove the assert, then I get a SEGFAULT, and the debugger points to NewtonWorldForEachJointDo()
Now, given that this only happens when we have more than 1 thread for Newton, I'm guessing I'll need to add a NewtonWorldCriticalSectionLock() when removing a contact - however, is this really what should be done? I thought a critical section lock was only necessary when changing data that doesn't belong to the current callback (like some unrelated body, for example).