EDIT:
Examples:
In unity, I simply change the position / scale / rotation in editor. Values for rotation displayed are simply Euler angles like how unity represents them in the editor.
(Notice the Y axis before and after applying a y 290 degree Euler on it)
THIS raylib (wrapping OpenGL calls) replicates the behavior perfectly
void Draw3DBillboardRecUnity(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector2 size, Vector3 eulerAngles, Vector3 scale, Color tint) {
rlPushMatrix();
Vector3 camPos = camera.position;
Vector3 camTar = camera.target;
Vector3 camUp = camera.up;
// get the camera view matrix
Matrix mat = MatrixInvert(MatrixLookAt(camPos, camTar, camUp));
// peel off just the rotation
Quaternion quat = QuaternionFromMatrix(mat);
mat = QuaternionToMatrix(quat);
// apply just the rotation
rlMultMatrixf(MatrixToFloat(mat));
// draw the billboard
float width = size.x / 2;
float height = size.y / 2;
// initial spot
rlScalef(1, 1, 1);
rlTranslatef(width, height, 0);
// null out position experiment
// position = Vector3Zero();
float positionOffsetX = position.x - width * 0.5;
float positionOffsetY = position.y - height * 0.5;
Vector3 positionOffsetVector;
positionOffsetVector.x = positionOffsetX;
positionOffsetVector.y = positionOffsetY;
positionOffsetVector.z = position.z;
Vector3 positionOffsetVectorInverse = Vector3Negate(positionOffsetVector);
// apply scaling first
if (vectorAvailable(scale)) {
rlScalef(scale.x, scale.y, scale.z);
if (vectorAvailable(position))
rlTranslatef(positionOffsetVector.x, positionOffsetVector.y, positionOffsetVector.z);
else
rlTranslatef(-width, -height, 0);
}
// apply rotation
if (rotVectorAvailable(eulerAngles)) {
// prepare yourself
rlTranslatef(positionOffsetVector.x, positionOffsetVector.y, positionOffsetVector.z);
if (eulerAngles.z > 180) {
// eh questionable
rlTranslatef(positionOffsetVector.x, positionOffsetVector.y, positionOffsetVector.z);
rlRotatef(180 - (eulerAngles.z - 180), 0, 0, 1);
rlRotatef(180 - (eulerAngles.z - 180), 0, 0, 1);
rlTranslatef(positionOffsetVectorInverse.x, positionOffsetVectorInverse.y, positionOffsetVectorInverse.z);
} else {
rlTranslatef(positionOffsetVector.x, positionOffsetVector.y, positionOffsetVector.z);
rlRotatef(eulerAngles.z, 0, 0, 1);
rlTranslatef(positionOffsetVectorInverse.x, positionOffsetVectorInverse.y, positionOffsetVectorInverse.z);
}
rlTranslatef(positionOffsetVector.x, positionOffsetVector.y, positionOffsetVector.z);
rlRotatef(eulerAngles.x, -1, 0, 0);
rlTranslatef(positionOffsetVectorInverse.x, positionOffsetVectorInverse.y, positionOffsetVectorInverse.z);
if (eulerAngles.y > 180) {
// greater than 180
rlTranslatef(positionOffsetVector.x, positionOffsetVector.y, positionOffsetVector.z);
rlRotatef(eulerAngles.y, 0, -1, 0);
rlTranslatef(positionOffsetVectorInverse.x, positionOffsetVectorInverse.y, positionOffsetVectorInverse.z);
} else {
// less than 180
rlTranslatef(positionOffsetVectorInverse.x, positionOffsetVectorInverse.y, positionOffsetVectorInverse.z);
rlRotatef(eulerAngles.y, 0, -1, 0);
rlTranslatef(positionOffsetVector.x, positionOffsetVector.y, positionOffsetVector.z);
}
// inverse
rlTranslatef(positionOffsetVectorInverse.x, positionOffsetVectorInverse.y, positionOffsetVectorInverse.z);
}
// apply transformation last
if (vectorAvailable(position)) {
rlTranslatef(positionOffsetVector.x, positionOffsetVector.y, positionOffsetVector.z);
}
Color color = tint;
rlCheckRenderBatchLimit(6);
rlSetTexture(texture.id);
// draw quad
rlBegin(RL_QUADS);
rlColor4ub(tint.r, tint.g, tint.b, tint.a);
rlTexCoord2f((float)source.x / texture.width, (float)(source.y + source.height) / texture.height);
rlVertex3f(-width, -height, 0); // Bottom Left Of The Texture and Quad
rlTexCoord2f((float)(source.x + source.width) / texture.width, (float)(source.y + source.height) / texture.height);
rlVertex3f(+width, -height, 0); // Bottom Right Of The Texture and Quad
rlTexCoord2f((float)(source.x + source.width) / texture.width, (float)source.y / texture.height);
rlVertex3f(+width, +height, 0); // Top Right Of The Texture and Quad
rlTexCoord2f((float)source.x / texture.width, (float)source.y / texture.height);
rlVertex3f(-width, +height, 0); // Top Left Of The Texture and Quad
rlEnd();
rlSetTexture(0);
rlPopMatrix();
}
With this result:
However this code that simply applies the quaternion, does not.
void Draw3DBillboardRecAlmost(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector2 size, Vector3 eulerAngles, Vector3 scale, Color tint) {
Vector2 sizeRatio = { size.x, size.y };
Quaternion tQuat = QuaternionFromEuler(eulerAngles.x*DEG2RAD, eulerAngles.y*-DEG2RAD, eulerAngles.z*-DEG2RAD);
Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up);
Vector3 right = { matView.m0, matView.m4, matView.m8 };
Vector3 up = { matView.m1, matView.m5, matView.m9 };
// rotate both up + right vectors to achieve same effect like unity
up = Vector3RotateByQuaternion(up, tQuat);
right = Vector3RotateByQuaternion(right, tQuat);
Vector3 rightScaled = Vector3Scale(right, sizeRatio.x/2);
Vector3 upScaled = Vector3Scale(up, sizeRatio.y/2);
Vector3 p1 = Vector3Add(rightScaled, upScaled);
Vector3 p2 = Vector3Subtract(rightScaled, upScaled);
Vector3 topLeft = Vector3Scale(p2, -1);
Vector3 topRight = p1;
Vector3 bottomRight = p2;
Vector3 bottomLeft = Vector3Scale(p1, -1);
// Translate points to the draw center (position)
topLeft = Vector3Add(topLeft, position);
topRight = Vector3Add(topRight, position);
bottomRight = Vector3Add(bottomRight, position);
bottomLeft = Vector3Add(bottomLeft, position);
rlCheckRenderBatchLimit(4);
rlSetTexture(texture.id);
rlBegin(RL_QUADS);
rlColor4ub(tint.r, tint.g, tint.b, tint.a);
// Bottom-left corner for texture and quad
rlTexCoord2f((float)source.x/texture.width, (float)source.y/texture.height);
rlVertex3f(topLeft.x, topLeft.y, topLeft.z);
// Top-left corner for texture and quad
rlTexCoord2f((float)source.x/texture.width, (float)(source.y + source.height)/texture.height);
rlVertex3f(bottomLeft.x, bottomLeft.y, bottomLeft.z);
// Top-right corner for texture and quad
rlTexCoord2f((float)(source.x + source.width)/texture.width, (float)(source.y + source.height)/texture.height);
rlVertex3f(bottomRight.x, bottomRight.y, bottomRight.z);
// Bottom-right corner for texture and quad
rlTexCoord2f((float)(source.x + source.width)/texture.width, (float)source.y/texture.height);
rlVertex3f(topRight.x, topRight.y, topRight.z);
rlEnd();
rlSetTexture(0);
}
With this result (notice it missing the translation)
How can I get the below code to work like the above test case?