Skip to content

Commit

Permalink
[rmodels] Consistent DrawBillboardPro with DrawTexturePro (#4132)
Browse files Browse the repository at this point in the history
* [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>
  • Loading branch information
bohonghuang and github-actions[bot] authored Jul 7, 2024
1 parent b613032 commit 6dd2a0e
Show file tree
Hide file tree
Showing 7 changed files with 70 additions and 102 deletions.
12 changes: 7 additions & 5 deletions examples/models/models_billboard.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,12 @@ int main(void)
// NOTE: Billboard locked on axis-Y
Vector3 billUp = { 0.0f, 1.0f, 0.0f };

// Set the height of the rotating billboard to 1.0 with the aspect ratio fixed
Vector2 size = { source.width / source.height, 1.0f };

// Rotate around origin
// Here we choose to rotate around the image center
// NOTE: (-1, 1) is the range where origin.x, origin.y is inside the texture
Vector2 rotateOrigin = { 0.0f };
Vector2 origin = Vector2Scale(size, 0.5f);

// Distance is needed for the correct billboard draw order
// Larger distance (further away from the camera) should be drawn prior to smaller distance.
Expand Down Expand Up @@ -84,11 +86,11 @@ int main(void)
if (distanceStatic > distanceRotating)
{
DrawBillboard(camera, bill, billPositionStatic, 2.0f, WHITE);
DrawBillboardPro(camera, bill, source, billPositionRotating, billUp, (Vector2) {1.0f, 1.0f}, rotateOrigin, rotation, WHITE);
DrawBillboardPro(camera, bill, source, billPositionRotating, billUp, size, origin, rotation, WHITE);
}
else
{
DrawBillboardPro(camera, bill, source, billPositionRotating, billUp, (Vector2) {1.0f, 1.0f}, rotateOrigin, rotation, WHITE);
DrawBillboardPro(camera, bill, source, billPositionRotating, billUp, size, origin, rotation, WHITE);
DrawBillboard(camera, bill, billPositionStatic, 2.0f, WHITE);
}

Expand All @@ -108,4 +110,4 @@ int main(void)
//--------------------------------------------------------------------------------------

return 0;
}
}
2 changes: 1 addition & 1 deletion parser/output/raylib_api.json
Original file line number Diff line number Diff line change
Expand Up @@ -10400,7 +10400,7 @@
},
{
"type": "float",
"name": "size"
"name": "scale"
},
{
"type": "Color",
Expand Down
2 changes: 1 addition & 1 deletion parser/output/raylib_api.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7230,7 +7230,7 @@ return {
{type = "Camera", name = "camera"},
{type = "Texture2D", name = "texture"},
{type = "Vector3", name = "position"},
{type = "float", name = "size"},
{type = "float", name = "scale"},
{type = "Color", name = "tint"}
}
},
Expand Down
2 changes: 1 addition & 1 deletion parser/output/raylib_api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3968,7 +3968,7 @@ Function 466: DrawBillboard() (5 input parameters)
Param[1]: camera (type: Camera)
Param[2]: texture (type: Texture2D)
Param[3]: position (type: Vector3)
Param[4]: size (type: float)
Param[4]: scale (type: float)
Param[5]: tint (type: Color)
Function 467: DrawBillboardRec() (6 input parameters)
Name: DrawBillboardRec
Expand Down
2 changes: 1 addition & 1 deletion parser/output/raylib_api.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2645,7 +2645,7 @@
<Param type="Camera" name="camera" desc="" />
<Param type="Texture2D" name="texture" desc="" />
<Param type="Vector3" name="position" desc="" />
<Param type="float" name="size" desc="" />
<Param type="float" name="scale" desc="" />
<Param type="Color" name="tint" desc="" />
</Function>
<Function name="DrawBillboardRec" retType="void" paramCount="6" desc="Draw a billboard texture defined by source">
Expand Down
2 changes: 1 addition & 1 deletion src/raylib.h
Original file line number Diff line number Diff line change
Expand Up @@ -1546,7 +1546,7 @@ RLAPI void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, floa
RLAPI void DrawModelWires(Model model, Vector3 position, float scale, Color tint); // Draw a model wires (with texture if set)
RLAPI void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model wires (with texture if set) with extended parameters
RLAPI void DrawBoundingBox(BoundingBox box, Color color); // Draw bounding box (wires)
RLAPI void DrawBillboard(Camera camera, Texture2D texture, Vector3 position, float size, Color tint); // Draw a billboard texture
RLAPI void DrawBillboard(Camera camera, Texture2D texture, Vector3 position, float scale, Color tint); // Draw a billboard texture
RLAPI void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector2 size, Color tint); // Draw a billboard texture defined by source
RLAPI void DrawBillboardPro(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector3 up, Vector2 size, Vector2 origin, float rotation, Color tint); // Draw a billboard texture defined by source and rotation

Expand Down
150 changes: 58 additions & 92 deletions src/rmodels.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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 };

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

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);
// Flip the content of the billboard while maintaining the counterclockwise edge rendering order
if (size.x < 0.0f)
{
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;
}

if (rotation != 0.0f)
// 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++)
{
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));
points[i] = Vector3Subtract(points[i], origin3D);
if (rotation != 0.0) points[i] = Vector3RotateByAxisAngle(points[i], forward, rotation * DEG2RAD);
points[i] = Vector3Add(points[i], position);
}

// Translate points to the draw center (position)
topLeft = Vector3Add(topLeft, position);
topRight = Vector3Add(topRight, position);
bottomRight = Vector3Add(bottomRight, position);
bottomLeft = Vector3Add(bottomLeft, 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)
{
// 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
rlColor4ub(tint.r, tint.g, tint.b, tint.a);
for (int i = 0; i < 4; i++)
{
// 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);
}

Expand Down

0 comments on commit 6dd2a0e

Please sign in to comment.