Here's what I ended up doing incase anyone else stumbled onto this.
This implementation will only work on rectangular shapes. Since those are stored in the same order in the shape every time. Im sure there's a way to figure out what the vertices map to in a custom shape though as well, but I don't know it.
After you perform the raycast, and retrieve the fixture information from box2d, perform your own a line-segment hit test on each of the edges in the fixture, first rotate the lines by the angle of the b2Body.
To perform this raytest, create a linesegment represented from your player to the point recieved from the raycast. If you like you can extrapolate it a tiny tiny bit more, just to make sure it connects.
Create 4 line segments from the fixture such that:
/**
* Creates a linesegment between points A and B and offsets them by a point
* @param offset
* @param a
* @param b
*/
FROM_VECTORS_AND_OFFSET: function( offset, a, b ) {
var pointA = new b2Vec2(offset.x + a.x, offset.y + a.y);
var pointB = new b2Vec2(offset.x + b.x, offset.y + b.y);
return new LineSegment(pointA, pointB);
},
/**
* Creates an array lines from a b2Body
* @param {Box2D.Dynamics.b2Body} aBody
* @return {Array} An array of lines
*/
FROM_BODY: function( aBody ) {
var bodyPos = aBody.GetPosition();
var shape = aBody.GetFixtureList().GetShape();
var lineA = FROM_VECTORS_AND_OFFSET( bodyPos, shape.m_vertices[0], shape.m_vertices[1] );
var lineB = FROM_VECTORS_AND_OFFSET( bodyPos, shape.m_vertices[1], shape.m_vertices[2] );
var lineC = FROM_VECTORS_AND_OFFSET( bodyPos, shape.m_vertices[2], shape.m_vertices[3] );
var lineD = FROM_VECTORS_AND_OFFSET( bodyPos, shape.m_vertices[3], shape.m_vertices[0] );
return [lineA, lineB, lineC, lineD];
},
Rotate each line by the angle of the body such that:
rotateLinesegment: function(pointA, pointB, angle) {
// Find the mid-point between A and B - i make a copy of pointA just to be safe
var center = pointA.Copy();
center.Add( pointB );
center.Multiply( 0.5 );
var na = pointA.Copy();
na.Subtract(center);
var nb = pointB.Copy();
nb.Subtract(center);
// Rotate each component in the vector by angle
var rotateVector = function( v, angle ) {
var s = Math.sin(angle);
var c = Math.cos(angle);
var nx = c * v.x - s * v.y;
var ny = s * v.x + c * v.y;
v.x = nx;
v.y = ny;
};
// Rotate, then untranslate it back to center
rotateVector(na, angle);
na.Add( center );
// Rotate, then untranslate it back to center
rotateVector(nb, angle);
nb.Add( center );
pointA = na;
pointB = nb;
return {pointA: pointA, pointB: pointB}
}
Perform a line-line intersection on each line.
/**
* Intersects two lines
* @param line1
* @param line2
* @return Array of points where the lines intersect
*/
INTERSECT_LINES: function( line1, line2 ) {
function intersectLineLine(a1, a2, b1, b2) {
var result = [];
var ua_t = (b2.x - b1.x) * (a1.y - b1.y) - (b2.y - b1.y) * (a1.x - b1.x);
var ub_t = (a2.x - a1.x) * (a1.y - b1.y) - (a2.y - a1.y) * (a1.x - b1.x);
var u_b = (b2.y - b1.y) * (a2.x - a1.x) - (b2.x - b1.x) * (a2.y - a1.y);
if (u_b != 0) {
var ua = ua_t / u_b;
var ub = ub_t / u_b;
// See if T is between 0.0 - 1.0
if (0 <= ua && ua <= 1 && 0 <= ub && ub <= 1) {
var x = a1.x + ua * (a2.x - a1.x);
var y = a1.y + ua * (a2.y - a1.y);
var point = new Box2D.Common.Math.b2Vec2(x, y);
result.push(point);
} else { // Lines are coincident... count anyway?
}
} else {
}
return result;
}
return intersectLineLine(line1._a, line1._b, line2._a, line2._b);
}
// Somewhere after creating the lines in the raycast call
for(var i = 0; i < lines.length; i++) {
var aLine = lines[i];
aLine.rotate( otherBody.GetAngle() );
var result = INTERSECT_LINES( playerToTracer, aLine );
if(result) {
contactLine = aLine
}
}
After all these steps, you will have the information about which line you collided with in the fixture.