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 d19dfec commit b0c9dac
Showing 1 changed file with 29 additions and 100 deletions.
129 changes: 29 additions & 100 deletions src/rmodels.c
Original file line number Diff line number Diff line change
Expand Up @@ -3659,7 +3659,7 @@ void DrawTexturePro3D(Texture2D texture, Rectangle source, Rectangle3D dest, Vec
Vector3 forward;
if (rotation != 0.0) forward = Vector3CrossProduct(dest.right, dest.up);

Vector3 origin3D = Vector3Add(Vector3Scale(Vector3Normalize(dest.up), origin.y), Vector3Scale(Vector3Normalize(dest.right), origin.x));
Vector3 origin3D = Vector3Add(Vector3Scale(Vector3Normalize(dest.right), origin.x), Vector3Scale(Vector3Normalize(dest.up), origin.y));

Vector3 points[4];
points[0] = Vector3Zero();
Expand Down Expand Up @@ -3694,114 +3694,43 @@ 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)
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);
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 b0c9dac

Please sign in to comment.