From c059ece2a4292581d40e17257f8f9f2eec9f3b3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20=E2=9D=A4=EF=B8=8F?= Date: Thu, 13 Nov 2025 17:10:13 -0500 Subject: [PATCH] [examples] Added: directional billboard (#5351) * Added models directional billboard example * add killbot texture * various fixes and formatting tweaks * corrected stdlib --- .../models/models_directional_billboard.c | 116 ++++++++++++++++++ .../models/models_directional_billboard.png | Bin 0 -> 20612 bytes examples/models/resources/skillbot.png | Bin 0 -> 2241 bytes 3 files changed, 116 insertions(+) create mode 100644 examples/models/models_directional_billboard.c create mode 100644 examples/models/models_directional_billboard.png create mode 100644 examples/models/resources/skillbot.png diff --git a/examples/models/models_directional_billboard.c b/examples/models/models_directional_billboard.c new file mode 100644 index 000000000..fe1b33b2d --- /dev/null +++ b/examples/models/models_directional_billboard.c @@ -0,0 +1,116 @@ +/******************************************************************************************* +* +* raylib [models] example - directional billboard +* +* Example complexity rating: [★★☆☆] 2/4 +* +* Example originally created with raylib 5.6-dev, last time updated with raylib 5.6 +* +* Example contributed by Robin (@RobinsAviary) and reviewed by Ramon Santamaria (@raysan5) +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2025-2025 Robin (@RobinsAviary) +* Killbot art by patvanmackelberg https://opengameart.org/content/killbot-8-directional under CC0 +* +********************************************************************************************/ + +#include "raylib.h" +#include "raymath.h" +#include + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [models] example - directional billboard"); + + // Set up the camera + Camera camera = { 0 }; + camera.position = (Vector3){ 2.0f, 1.0f, 2.0f }; // Starting position + camera.target = (Vector3){ 0.0f, 0.5f, 0.0f }; // Target position + camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Up vector + camera.fovy = 45.0f; // FOV + camera.projection = CAMERA_PERSPECTIVE; // Projection type (Standard 3D perspective) + + // Load billboard texture + Texture skillbot = LoadTexture("resources/skillbot.png"); + + // Timer to update animation + float anim_timer = 0.0f; + // Animation frame + unsigned int anim = 0; + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + UpdateCamera(&camera, CAMERA_ORBITAL); + + // Update timer with delta time + anim_timer += GetFrameTime(); + + // Update frame index after a certain amount of time (half a second) + if (anim_timer > 0.5f) + { + anim_timer = 0.0f; + anim += 1; + } + + // Reset frame index to zero on overflow + if (anim >= 4) anim = 0; + + // Find the current direction frame based on the camera position to the billboard object + float dir = (float)floor(((Vector2Angle((Vector2){ 2.0f, 0.0f }, (Vector2){ camera.position.x, camera.position.z })/PI)*4.0f) + 0.25f); + + // Correct frame index if angle is negative + if (dir < 0.0f) + { + dir = 8.0f - (float)abs((int)dir); + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + BeginMode3D(camera); + + DrawGrid(10, 1.0f); + + // Draw billboard pointing straight up to the sky, rotated relative to the camera and offset from the bottom + DrawBillboardPro(camera, skillbot, (Rectangle){ 0.0f + (anim*24.0f), 0.0f + (dir*24.0f), 24.0f, 24.0f }, Vector3Zero(), (Vector3){ 0.0f, 1.0f, 0.0f }, Vector2One(), (Vector2){ 0.5f, 0.0f }, 0, WHITE); + + EndMode3D(); + + // Render various variables for reference + DrawText(TextFormat("animation: %d", anim), 10, 10, 20, DARKGRAY); + DrawText(TextFormat("direction frame: %.0f", dir), 10, 40, 20, DARKGRAY); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + // Unload billboard texture + UnloadTexture(skillbot); + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/models/models_directional_billboard.png b/examples/models/models_directional_billboard.png new file mode 100644 index 0000000000000000000000000000000000000000..cbbe11065eef52f7389e697660af68e119443e5d GIT binary patch literal 20612 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 X_q4(ZPw&Im1#zD9eC8f==WqKT!Md5^ literal 0 HcmV?d00001 diff --git a/examples/models/resources/skillbot.png b/examples/models/resources/skillbot.png new file mode 100644 index 0000000000000000000000000000000000000000..537f1338367ec575c714a6e858200eb07bc7ec1b GIT binary patch literal 2241 zcmZuy3piBk8vfUst6_{=Xe6VW=;As#xtk@_l+47Wh=vf+j>d$_WsH=ggc9w_F*(H3 zg{fnwBA4pm!PZTwV`E$@Lkdy0X3SYr?dR-sp7TG?x4!rL{_p?(^{?-H|Mgdn&qgwd zN&*0oS(`k3Q5}Lx6A^}2g_u~q{jYTy7B!T|_Hn^7_PfmKH{hcMs#Fp6y;7-*w95S4u=u;2XqQLGvv zsHoQeq5pSR0MO5qM*vtf#@d!)4T-hgeZOj?R zJ$(n>og8-*`uC4c#?BgQ;eP5RXR|#PE3w=RSJ&F!RLLKVj0FkoLYr82zMS zu=bHz>8UjMD)NdLE;A7B(Q2qn7H|2QSwR(iXbArL;-Km1t6oNf@!Blk)Sm;0ju#eY z4>@^1oOco%KAKMytGi*Uu%Tf`M`MM6apkw@W#}YPj)QD@f`nO=S4R@6%gn#SCUifCu8#Y1ChnkJ-XB6byVwUbb4T zP-sqcg?s5$CM-a?|9oMP@Y8 z)iL}fjJ@(;o5+qvgdNDo$>%}JV=CAmlGoo^HCoUJh~Da*q%Q5}loopJ26Ke)b93=;P+pU|w~jo<;7We58$aOV2qYX@ z6GRVnFmXsP@w%=VqMJI%7gbIl2|JVm&H1$*wH#_KdZ8%8|FELS#KriE8_vf4Fe(3wUM;8yE)z9h{cs3l!sbDpIw<>abiQPWv3a5n zs|HX7eQmLAQ@UXGwIR!*g20lTcYkC!G;f+e`xJFVTApW3TxL`J66D0FdtmZRL)CGf9Y_nMbrA0R}BuCHSG=%sd z+FbU96^0H}CI!wn3CAb$2~60^nl)J+eBLWT(H3$4ECFO!$DKR&CV2##O1Hp7UZBC;g+8Y^XGm=TDO22U~3w&Q){;i9?!~H&Jk|+7YVuz4sjcfueUApjYpL ztP-F8;q;Xjx7>E5nHwrc<3*<_#4Fj+TF$$YJo`$X1CGZ0gI?{f+~Qe^@TZw)e!g zVF4{}ln%tYWb*RrVb#s<9BCpxgkJyjL;q<cW}3DXB^K zrr1oo(S|<8f?6ehP~*%bY7&jo*DBDHiM(iQ8k5dmQ*~rCBEbwX5_ZvF%hr60@VCyWY0?immF$$@28^ JsB#Yx{TuKsjBNk_ literal 0 HcmV?d00001