Do you want to learn how to make your C++ app simulate physics of objects in 3D without using any 3D engine? In this post we will explain how we can simulate ball physics 2D in a simple way. Let’s assume that we look from a X-Y view, and Y is the height that means Y=0 is the ground. How we can simulate a ball physics in a given ball velocity, and gravity in that environment?
C++ Builder is a great compiler and C++ IDE with FireMonkey and VCL frameworks. It has compilers for Win32, Win64, Android and iOS. C++Builder has both CLANG Enhanced C/C++ Compiler and a Borland C/C++ Compiler. It also features a modern, high-productivity RAD Studio IDE, debugger tools, and enterprise connectivity for to accelerate cross-platform UI development. We can develop GUI based applications easily, as it comes with the award-winning VCL framework for high-performance native Windows apps and the powerful FireMonkey (FMX) framework for cross-platform UIs. There is a free C++ Builder Community Edition that can be used by students, beginners and startups with limitations.
Let’s assume that we have a ball at bx and by coordinate with a given Vx and Vy velocity. Now we want to see what happens in every milliseconds under the gravitational environment.
Table of Contents
What is a TSphere and how can we use it?
The TSphere is is used in Viewport3D component, it is a 3D sphere, a class implements a 3D sphere shape built on a 3D wireframe, that can be placed on a 3D FireMonkey form. TSphere is a visual object that can be added from the Tool Palette. To change the color or add texture to the sphere, use the MaterialSource property. Set SubdivisionsAxes and SubdivisionsHeight to specify how smooth the sphere’s surfaces are.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
Sphere1->BeginUpdate(); Sphere1->Width=10; Sphere1->Height=10; Sphere1->Depth=10; Sphere1->SubDivisionsHeight=10; Sphere1->SubDivisionsWidth=10; Sphere1->Position->X=0; Sphere1->Position->Y=0; Sphere1->Position->Z=0; Sphere1->Rotation->X=0; Sphere1->Rotation->Y=0; Sphere1->Rotation->Z=0; Sphere1->MaterialSource = LightMaterialSource1; //MaterialSources should be defined or dragged on to form Sphere1->EndUpdate(); |
Here is a short example C++ app showing how to use TSphere
Let’s create a new Multi-Device C++ Builder FireMonkey Application and save all project and unit files to a folder. Now we should create a ball class, g gravity
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
//--------------------------------------------------------------------------- #include <fmx.h> #include <time.h> #pragma hdrstop #include "Ball_Physics_3D_Unit1.h" //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.fmx" TForm1 *Form1; class Tball { public: TCustomMesh *mesh; float x,y,z; float vx,vy,vz; float ax, ay,az; float R; Tball() // Constructor { }; ~Tball() //Deconstructor { } }; #define NUMBER_OF_BALLS 10 Tball ball[NUMBER_OF_BALLS]; float t = 0; // time float g = 9.81; // gravity (m/s'2) //--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } |
Let’s create new balls and lets define their radius, position and velocity randomly.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
//--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { srand(time(0)); for(int i=0; i<NUMBER_OF_BALLS; i++) { ball[i].mesh = new TSphere(Viewport3D1); // new ball ball[i].mesh->Parent = Viewport3D1; // we need to define its parent ball[i].mesh->MaterialSource = LightMaterialSource1; // lets define its material source ball[i].R = 0.5+(rand()%10)/10.0; ball[i].x = (rand()%100)/10.0-5; ball[i].y = (rand()%100)/10.0+5; ball[i].z = (rand()%100)/1.00-5; ball[i].vx = (rand()%100)/10.0-5; ball[i].vy = (rand()%100)/10.0-5; ball[i].vz = (rand()%100)/10.0-5; ball[i].mesh->Width = 2*ball[i].R; ball[i].mesh->Height = 2*ball[i].R; ball[i].mesh->Depth = 2*ball[i].R; } } |
Simulating ball physics in your own C++ app
We can simulate ball physics in time fractions, we will use dt variable to see our simulation in that time frictions. To simulate ball physics on the ground in 3D we should detect ball position in Y direction, if this Y position smaller than ball radius that means ball hits the ground so we should change the velocity vector direction by multiplying -1, if we want to add some energy loss on the ground, we can multiply with between 0.0 to -1 (i.e. 0.9).
We can add a Timer (TTimer) component, drag this component to your form and set its Interval property to between 10 to 35, that means each interval will simulate our dt fraction of time in that interval time. If you are new to C++ Builder and you don’t know how to Timer component, we had a previous post that explains well all about Timer component. Please read this post below.
Double click to Timer component to create OnTimer() event and modify lines inside as below,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
void __fastcall TForm1::Timer1Timer(TObject *Sender) { float dt =0.05; t+=dt; for(int i=0; i<NUMBER_OF_BALLS; i++) { if(ball[i].y<=ball[i].R) // if ball smash ground { ball[i].vy = -0.9*ball[i].vy; ball[i].vx = 0.9*ball[i].vx; ball[i].vz = 0.9*ball[i].vz; } else ball[i].vy -= g*dt; // velocity speeds up with gravitational acceleration in a time period //ball[i].mesh->BeginUpdate(); ball[i].x += ball[i].vx*dt; ball[i].y += ball[i].vy*dt; ball[i].z += ball[i].vz*dt; ball[i].mesh->Position->X = ball[i].x; ball[i].mesh->Position->Y = -1*ball[i].y; ball[i].mesh->Position->Z = ball[i].z; //ball[i].mesh->EndUpdate(); } UnicodeString ustr; ustr.printf(L"t=%8.3f Ball0 vx:%8.3fm/s vy:%8.3fm/s vz:%8.3fm/s", t, ball[0].vx, ball[0].vy, ball[0].vz); Label1->Text = ustr; } |
What does the C++ app example do?
This Timer event sets position of balls in their velocity in each interval. Also, it checks Y position of each ball and if it hits the ground sets its new velocity with a small velocity loss. We added a line to printout the position of first ball[0] parameters. You can remove this to make it faster and smooth.
Finally, when we close the form, we should free all mesh objects that we allocated with the new command, as below,
1 2 3 4 5 6 7 8 9 |
void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action) { for(int i=0; i<NUMBER_OF_BALLS; i++) { ball[i].mesh->Free(); } } |
Now you can simulate many ball objects, or you can add more detailed physical or engineering formulas to simulate other objects.