Skip to content

Commit

Permalink
[rmodels] Re-implement DrawBillboardPro using DrawTexturePro3D
Browse files Browse the repository at this point in the history
  • Loading branch information
bohonghuang committed Jul 6, 2024
1 parent 4b62d7d commit 47671be
Showing 1 changed file with 30 additions and 99 deletions.
129 changes: 30 additions & 99 deletions src/rmodels.c
Original file line number Diff line number Diff line change
Expand Up @@ -3695,114 +3695,45 @@ void DrawTexturePro3D(Texture2D texture, Rectangle source, Rectangle3D dest, Vec
rlSetTexture(0);
}

// 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)
// Create a billboard rectangle in 3D space
static Rectangle3D BillboardRectangle3D(Camera camera, Vector3 position, Vector2 size)
{
// 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 };

Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up);

Vector3 up = { matView.m1, matView.m5, matView.m9 };
Vector3 right = { matView.m0, matView.m4, matView.m8 };
//Vector3 up = { matView.m1, matView.m5, matView.m9 };

Vector3 rightScaled = Vector3Scale(right, sizeRatio.x/2);
Vector3 upScaled = Vector3Scale(up, sizeRatio.y/2);

Vector3 p1 = Vector3Add(rightScaled, upScaled);
Vector3 p2 = Vector3Subtract(rightScaled, upScaled);
return (Rectangle3D) { position, Vector3Scale(up, size.y), Vector3Scale(right, size.x) };
}

Vector3 topLeft = Vector3Scale(p2, -1);
Vector3 topRight = p1;
Vector3 bottomRight = p2;
Vector3 bottomLeft = Vector3Scale(p1, -1);
// Scale a rectangle while keeping its center fixed, allowing for negative scale values to flip the rectangle
static Rectangle RectangleScale(Rectangle rectangle, Vector2 scale)
{
rectangle.position = Vector2Add(rectangle.position, Vector2Scale(Vector2Multiply(rectangle.size, Vector2Subtract(Vector2One(), scale)), 0.5f));
rectangle.size = Vector2Multiply(rectangle.size, scale);
return rectangle;
}

if (rotation != 0.0f)
// 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 };
Rectangle3D dest = BillboardRectangle3D(camera, position, sizeRatio);
dest.up = Vector3Scale(up, sizeRatio.y);
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));
dest.right = Vector3Negate(dest.right);
source = RectangleScale(source, (Vector2) { -1.0f, 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);

rlSetTexture(texture.id);

rlBegin(RL_QUADS);
rlColor4ub(tint.r, tint.g, tint.b, tint.a);

if (sizeRatio.x*sizeRatio.y >= 0.0f)
{
// 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);
}

rlEnd();

rlSetTexture(0);
if (size.y < 0.0f)
{
dest.up = Vector3Negate(dest.up);
source = RectangleScale(source, (Vector2) { 1.0f, -1.0f });
}
Vector2 originUnnormalized = Vector2Multiply(Vector2Scale(Vector2Add(origin, Vector2One()), 0.5f), (Vector2) { fabsf(sizeRatio.x), fabsf(sizeRatio.y) });
DrawTexturePro3D(texture, source, dest, originUnnormalized, rotation, tint);
}

// Draw a bounding box with wires
Expand Down

0 comments on commit 47671be

Please sign in to comment.