Closed 8Observer8 closed 4 months ago
In theory, all you need to do to get the final point is store the xf from the pushTransform and in the draw function multiply it with the vector you need in final coordinates:
b2Transform.MultiplyVec2(xf, pointIn, pointOut)
At least, that's what's been done in the original: https://github.com/erincatto/box2d/blob/c6cc3646d1701ab3c0750ef397d2d68fc6dbcff2/src/dynamics/b2_world.cpp#L1095
Local scale transformation to form a line or segment
I will try to understand this problem by explaining how the code works. I think the problem is in the order in which the transformations are applied. I will use the original example from the topic: https://plnkr.co/edit/zTthXbGVITITERkL
Focus on the debug-drawer.js
file. This method is automatically called every frame:
DrawSolidPolygon(vertices, vertexCount, color) {
mat4.mul(this.projViewMatrix, this.projMatrix, this.viewMatrix);
gl.uniform3f(this.uColorLocation, color.r, color.g, color.b);
this.drawLine(vertices[0], vertices[1]);
this.drawLine(vertices[1], vertices[2]);
this.drawLine(vertices[2], vertices[3]);
this.drawLine(vertices[3], vertices[0]);
}
I need to draw four lines to create a rectangle. In order to draw a line, I have to transform the square which is in the VBO using matrix operations. I create a vector using start and end points in the this.drawLine
method:
this.tempVec[0] = this.toX - this.fromX;
this.tempVec[1] = this.toY - this.fromY;
I can find the length of this vector using the glMatrix library:
this.length = vec3.length(this.tempVec);
I have a predefined this.lineWidth = 4;
variangle and I can create a scale vector:
this.scale[0] = this.length;
this.scale[1] = this.lineWidth;
this.scale[2] = 1;
I use this vector to scale the square to make a line:
mat4.fromRotationTranslationScale(this.modelMatrix, this.rotation, this.position, this.scale); // Create a model matrix that keep: scale, rotation, and position
mat4.mul(this.mvpMatrix, this.projViewMatrix, this.modelMatrix); // Multiply the projection view matrix (orthographic projection and camera) by the model matrix to create the model view projection matrix
gl.uniformMatrix4fv(this.uMvpMatrixLocation, false, this.mvpMatrix); // Move calculated matrix to vertex shader
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); // Draw a line (converted square to line)
...
I will continue my explanation later...
Local position transformation to set a center of line or segment
this.centerX
and this.centerY
are calculated like this inside of this.drawLine
:
drawLine(pointA, pointB) {
this.fromX = pointA.x * this.pixelsPerMeter;
this.fromY = pointA.y * this.pixelsPerMeter;
this.toX = pointB.x * this.pixelsPerMeter;
this.toY = pointB.y * this.pixelsPerMeter;
if (this.fromX > this.toX) {
this.centerX = this.toX + Math.abs(this.fromX - this.toX) / 2;
}
else {
this.centerX = this.fromX + Math.abs(this.toX - this.fromX) / 2;
}
if (this.fromY > this.toY) {
this.centerY = this.toY + Math.abs(this.fromY - this.toY) / 2;
}
else {
this.centerY = this.fromY + Math.abs(this.toY - this.fromY) / 2;
}
Local rotation transformation for a line or segment
This code uses a begin and end of a line to calculate a local rotation of a line:
quat.rotationTo(this.rotation, this.unitX, this.tempVec);
Amount of transformations
Local transformations to form a line and to set position and rotation of a line:
Two global transformations:
PushTransform(xf) {
// Translation
this.xf[0] = xf.p.x * this.pixelsPerMeter;
this.xf[1] = xf.p.y * this.pixelsPerMeter;
// Rotation
this.radianOffset = xf.q.s;
}
We have five transformations.
Original example from the topic on two sandboxes:
Now you can see this order of transformations in the example above:
vec3.add(this.position, this.position, this.xf);
quat.rotateZ(this.rotation, this.rotation, this.radianOffset);
mat4.fromRotationTranslationScale(this.modelMatrix, this.rotation,
this.position, this.scale);
mat4.mul(this.mvpMatrix, this.projViewMatrix, this.modelMatrix);
gl.uniformMatrix4fv(this.uMvpMatrixLocation, false, this.mvpMatrix);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
Before of this code I create a quaternion (this.rotation
) to rotate a horizontal line to 0 or to 90. I add this.radianOffset
to this.rotation
here:
quat.rotateZ(this.rotation, this.rotation, this.radianOffset);
The final transformation (this.mvpMatrix
or this.modelMatrix
) is applied in order: 1) scale 2) rotation 3) translation. I should apply this.radianOffset
after: 1) scale, 2) rotation to 0 or to 90 and 3) translation this.position
. I replaced this line mat4.fromRotationTranslationScale(this.modelMatrix, this.rotation, this.position, this.scale);
to
// Like above
vec3.add(this.position, this.position, this.xf);
quat.rotateZ(this.rotation, this.rotation, this.radianOffset);
mat4.identity(this.modelMatrix);
mat4.translate(this.modelMatrix, this.modelMatrix, this.position);
mat4.fromQuat(this.quatMat, this.rotation);
mat4.mul(this.modelMatrix, this.modelMatrix, this.quatMat);
mat4.scale(this.modelMatrix, this.modelMatrix, this.scale);
mat4.mul(this.mvpMatrix, this.projViewMatrix, this.modelMatrix);
gl.uniformMatrix4fv(this.uMvpMatrixLocation, false, this.mvpMatrix);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
Like in the documentation which describes how can I replace mat4.fromRotationTranslationScale
:
After this I have the same example but I can apply transformations separately:
You can comment this piece of code:
// Like above
vec3.add(this.position, this.position, this.xf);
quat.rotateZ(this.rotation, this.rotation, this.radianOffset);
mat4.identity(this.modelMatrix);
mat4.translate(this.modelMatrix, this.modelMatrix, this.position);
mat4.fromQuat(this.quatMat, this.rotation);
mat4.mul(this.modelMatrix, this.modelMatrix, this.quatMat);
mat4.scale(this.modelMatrix, this.modelMatrix, this.scale);
mat4.mul(this.mvpMatrix, this.projViewMatrix, this.modelMatrix);
gl.uniformMatrix4fv(this.uMvpMatrixLocation, false, this.mvpMatrix);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
And add this code:
mat4.identity(this.modelMatrix);
mat4.translate(this.modelMatrix, this.modelMatrix, this.xf);
mat4.fromQuat(this.quatMat, this.radianOffset);
mat4.mul(this.modelMatrix, this.modelMatrix, this.quatMat);
mat4.translate(this.modelMatrix, this.modelMatrix, this.position);
mat4.fromQuat(this.quatMat, this.rotation);
mat4.mul(this.modelMatrix, this.modelMatrix, this.quatMat);
mat4.scale(this.modelMatrix, this.modelMatrix, this.scale);
mat4.mul(this.mvpMatrix, this.projViewMatrix, this.modelMatrix);
gl.uniformMatrix4fv(this.uMvpMatrixLocation, false, this.mvpMatrix);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
But I see nothing and I don't understand why because everything looks right:
mat4.identity(this.modelMatrix);
mat4.translate(this.modelMatrix, this.modelMatrix, this.xf);
mat4.fromQuat(this.quatMat, this.radianOffset);
mat4.mul(this.modelMatrix, this.modelMatrix, this.quatMat);
mat4.translate(this.modelMatrix, this.modelMatrix, this.position);
mat4.fromQuat(this.quatMat, this.rotation);
mat4.mul(this.modelMatrix, this.modelMatrix, this.quatMat);
mat4.scale(this.modelMatrix, this.modelMatrix, this.scale);
mat4.mul(this.mvpMatrix, this.projViewMatrix, this.modelMatrix);
gl.uniformMatrix4fv(this.uMvpMatrixLocation, false, this.mvpMatrix);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
I found a problem in this line:
mat4.fromQuat(this.quatMat, this.radianOffset);
The second argument of mat4.fromQuat must be a quaternion (not angle in radians). I don't have error message because it is JavaScript.
I am very close to a solution. I have a slight angle offset between the object and the borders of the colliders:
I have a slight angle offset between the object and the borders of the colliders
I think it is a problem with box2d-core
. Because I set angle = 40
for boxBody, but here:
PushTransform(xf) {
// Translation
this.xf[0] = xf.p.x * this.pixelsPerMeter;
this.xf[1] = xf.p.y * this.pixelsPerMeter;
// Rotation
this.radianOffset = xf.q.s;
console.log(this.radianOffset * 180 / Math.PI);
}
I print angle and it is equal to 36.
I created this demo in pure WebGL 1.0, glMatrix, Tiled, FreeTexturePacker and box2d/core: https://8observer8.github.io/webgl10-js/mario-2d-jumps-box2dcore-webgl-js/ to show how I draw colliders with sprites.
I think there are no plans to implement drawing with direct coordinates of vertices without save()
and restore()
line in Box2D-WASM.
I will try to fix this issue later. At the moment I have no ideas. Can someone fix: https://plnkr.co/edit/zTthXbGVITITERkL (another sandbox: https://playcode.io/1211513)