[rmodels] Consistent DrawBillboardPro with DrawTexturePro (#4132)

* [rmodels] Re-implement `DrawBillboardPro`

* [rmodels] Add comments to `DrawBillboardPro`

* [rmodels] Make `DrawBillboardPro` consistent with `DrawTexturePro`

* Update raylib_api.* by CI

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
bohonghuang
2024-07-08 02:27:51 +08:00
committed by GitHub
parent b61303244c
commit 6dd2a0e645
7 changed files with 71 additions and 103 deletions

View File

@@ -3638,11 +3638,11 @@ void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float
}
// Draw a billboard
void DrawBillboard(Camera camera, Texture2D texture, Vector3 position, float size, Color tint)
void DrawBillboard(Camera camera, Texture2D texture, Vector3 position, float scale, Color tint)
{
Rectangle source = { 0.0f, 0.0f, (float)texture.width, (float)texture.height };
DrawBillboardRec(camera, texture, source, position, (Vector2){ size, size }, tint);
DrawBillboardRec(camera, texture, source, position, (Vector2) { scale*fabsf((float)source.width/source.height), scale }, tint);
}
// Draw a billboard (part of a texture defined by a rectangle)
@@ -3651,116 +3651,82 @@ void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle source, Vector
// NOTE: Billboard locked on axis-Y
Vector3 up = { 0.0f, 1.0f, 0.0f };
DrawBillboardPro(camera, texture, source, position, up, size, Vector2Zero(), 0.0f, tint);
DrawBillboardPro(camera, texture, source, position, up, size, Vector2Scale(size, 0.5), 0.0f, tint);
}
// Draw a billboard with additional parameters
// NOTE: Size defines the destination rectangle size, stretching the source texture as required
void DrawBillboardPro(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector3 up, Vector2 size, Vector2 origin, float rotation, Color tint)
{
// NOTE: Billboard size will maintain source rectangle aspect ratio, size will represent billboard width
Vector2 sizeRatio = { size.x*fabsf((float)source.width/source.height), size.y };
// Compute the up vector and the right vector
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 };
right = Vector3Scale(right, size.x);
up = Vector3Scale(up, size.y);
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);
if (rotation != 0.0f)
// Flip the content of the billboard while maintaining the counterclockwise edge rendering order
if (size.x < 0.0f)
{
float sinRotation = sinf(rotation*DEG2RAD);
float cosRotation = cosf(rotation*DEG2RAD);
// NOTE: (-1, 1) is the range where origin.x, origin.y is inside the texture
float rotateAboutX = sizeRatio.x*origin.x/2;
float rotateAboutY = sizeRatio.y*origin.y/2;
float xtvalue, ytvalue;
float rotatedX, rotatedY;
xtvalue = Vector3DotProduct(right, topLeft) - rotateAboutX; // Project points to x and y coordinates on the billboard plane
ytvalue = Vector3DotProduct(up, topLeft) - rotateAboutY;
rotatedX = xtvalue*cosRotation - ytvalue*sinRotation + rotateAboutX; // Rotate about the point origin
rotatedY = xtvalue*sinRotation + ytvalue*cosRotation + rotateAboutY;
topLeft = Vector3Add(Vector3Scale(up, rotatedY), Vector3Scale(right, rotatedX)); // Translate back to cartesian coordinates
xtvalue = Vector3DotProduct(right, topRight) - rotateAboutX;
ytvalue = Vector3DotProduct(up, topRight) - rotateAboutY;
rotatedX = xtvalue*cosRotation - ytvalue*sinRotation + rotateAboutX;
rotatedY = xtvalue*sinRotation + ytvalue*cosRotation + rotateAboutY;
topRight = Vector3Add(Vector3Scale(up, rotatedY), Vector3Scale(right, rotatedX));
xtvalue = Vector3DotProduct(right, bottomRight) - rotateAboutX;
ytvalue = Vector3DotProduct(up, bottomRight) - rotateAboutY;
rotatedX = xtvalue*cosRotation - ytvalue*sinRotation + rotateAboutX;
rotatedY = xtvalue*sinRotation + ytvalue*cosRotation + rotateAboutY;
bottomRight = Vector3Add(Vector3Scale(up, rotatedY), Vector3Scale(right, rotatedX));
xtvalue = Vector3DotProduct(right, bottomLeft)-rotateAboutX;
ytvalue = Vector3DotProduct(up, bottomLeft)-rotateAboutY;
rotatedX = xtvalue*cosRotation - ytvalue*sinRotation + rotateAboutX;
rotatedY = xtvalue*sinRotation + ytvalue*cosRotation + rotateAboutY;
bottomLeft = Vector3Add(Vector3Scale(up, rotatedY), Vector3Scale(right, rotatedX));
source.x += size.x;
source.width *= -1.0;
right = Vector3Negate(right);
origin.x *= -1.0f;
}
if (size.y < 0.0f)
{
source.y += size.y;
source.height *= -1.0;
up = Vector3Negate(up);
origin.y *= -1.0f;
}
// Translate points to the draw center (position)
topLeft = Vector3Add(topLeft, position);
topRight = Vector3Add(topRight, position);
bottomRight = Vector3Add(bottomRight, position);
bottomLeft = Vector3Add(bottomLeft, position);
// Draw the texture region described by source on the following rectangle in 3D space:
//
// size.x <--.
// 3 ^---------------------------+ 2 \ rotation
// | | /
// | |
// | origin.x position |
// up |.............. | size.y
// | . |
// | . origin.y |
// | . |
// 0 +---------------------------> 1
// right
Vector3 forward;
if (rotation != 0.0) forward = Vector3CrossProduct(right, up);
Vector3 origin3D = Vector3Add(Vector3Scale(Vector3Normalize(right), origin.x), Vector3Scale(Vector3Normalize(up), origin.y));
Vector3 points[4];
points[0] = Vector3Zero();
points[1] = right;
points[2] = Vector3Add(up, right);
points[3] = up;
for (int i = 0; i < 4; i++)
{
points[i] = Vector3Subtract(points[i], origin3D);
if (rotation != 0.0) points[i] = Vector3RotateByAxisAngle(points[i], forward, rotation * DEG2RAD);
points[i] = Vector3Add(points[i], position);
}
Vector2 texcoords[4];
texcoords[0] = (Vector2) { (float)source.x/texture.width, (float)(source.y + source.height)/texture.height };
texcoords[1] = (Vector2) { (float)(source.x + source.width)/texture.width, (float)(source.y + source.height)/texture.height };
texcoords[2] = (Vector2) { (float)(source.x + source.width)/texture.width, (float)source.y/texture.height };
texcoords[3] = (Vector2) { (float)source.x/texture.width, (float)source.y/texture.height };
rlSetTexture(texture.id);
rlBegin(RL_QUADS);
rlColor4ub(tint.r, tint.g, tint.b, tint.a);
if (sizeRatio.x*sizeRatio.y >= 0.0f)
for (int i = 0; i < 4; i++)
{
// 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);
}
else
{
// Reverse vertex order if the size has only one negative dimension
rlTexCoord2f((float)(source.x + source.width)/texture.width, (float)source.y/texture.height);
rlVertex3f(topRight.x, topRight.y, topRight.z);
rlTexCoord2f((float)(source.x + source.width)/texture.width, (float)(source.y + source.height)/texture.height);
rlVertex3f(bottomRight.x, bottomRight.y, bottomRight.z);
rlTexCoord2f((float)source.x/texture.width, (float)(source.y + source.height)/texture.height);
rlVertex3f(bottomLeft.x, bottomLeft.y, bottomLeft.z);
rlTexCoord2f((float)source.x/texture.width, (float)source.y/texture.height);
rlVertex3f(topLeft.x, topLeft.y, topLeft.z);
rlTexCoord2f(texcoords[i].x, texcoords[i].y);
rlVertex3f(points[i].x, points[i].y, points[i].z);
}
rlEnd();
rlSetTexture(0);
}