Simulating 3D ball physics is one of those tasks that sounds difficult but is actually fairly simple in C++ IDE. In this post, you’ll learn how to simulate 3D balls in a cube, using OpenGL in C++ Builder; adding physics to a 3D object to make it more realistic, and creating 3D animations with OpenGL.
Table of Contents
Using C++ Builder for animations, simulations and games is not difficult
C++ Builder makes it relatively easy to build these kind of simple simulations and games. C++ is a fast programming language that you can use to develop fully-native simulations and games which operate with maximum performance. With C++ Builder, you can directly create your own 3D objects and you can animate them at runtime in real-time. The Viewport3D (TViewportd3D) component in C++ Builder FireMonkey projects is very effective at displaying many basic 3D Objects like geometric planes, cubes, spheres, cones, 3D ellipses and much more. Please see this post about Working With 3D In Modern Windows C++ Development for creating these 3D objects. You also might find it useful to visit this post: How To Simulate Realistic Physical Effects in C++ which goes into a related topic for 2d objects.
We can also easily load your 3D objects into Viewport3D by using Model3D (TModel3D).
We can also use OpenGL or Direct3D libraries or some other 3rd party 3D Engines. In this post we will see how we can simulate ball physics in 3D.
What is OpenGL?
OpenGL (Open Graphics Library) is a 3D library for development of graphical programs. It is a cross-language, cross-platform Application Programming Interface (API) for rendering 2D and 3D vector graphics. The OpenGL API is typically used to interact with CPU and mostly with GPU to achieve hardware accelerated rendering.
Creating an example OpenGL application in C++
C++ Builder supports to develop very low level graphics (here low term means very fast and native codes for 3D graphics) with specific C++ codes or with OpenGL. If you want to simulate something with OpenGL or if you want to develop a game with OpenGL, we should setup openGL we need a loop that loops on Application Idle. In this loop we should apply physics and and we should draw or update our 3D objects.
In this example we will have balls in a cube and this balls will attract with the boundaries of this cube and with each other. Let’s start with creating a new VCL application in C++ Builder.
How to set up an OpenGL example header file in C++ Builder
First switch to Unit1.h from the tabs below the IDE to modify header. We should include two main OpenGL headers (gl.h and glu.h) as below,
1 2 3 4 |
#include <gl\gl.h> #include <gl\glu.h> |
As we described above, we need to declare IdleLoop(), Setup_OpenGL(), Create_Objects3D(), Draw_Objects3D(), Apply_Physics3D() methods as below.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class TForm1 : public TForm { public: // User declarations __fastcall TForm1(TComponent* Owner); void __fastcall IdleLoop(TObject*, bool&); bool __fastcall Setup_OpenGL(); void __fastcall Create_Objects3D(); void __fastcall Draw_Objects3D(); void __fastcall Apply_Physics3D(); }; |
We need to define number of balls, we need to add some private and public declarations for OpenGL settings and for our objects, here is the Unit1.h Header of full example,
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 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
//--------------------------------------------------------------------------- #ifndef OpenGL_Ball_Physics3D_Unit1H #define OpenGL_Ball_Physics3D_Unit1H //--------------------------------------------------------------------------- #include <Classes.hpp> #include <Controls.hpp> #include <StdCtrls.hpp> #include <Forms.hpp> #include <ExtCtrls.hpp> #include <gl\gl.h> #include <gl\glu.h> //--------------------------------------------------------------------------- #define NB 4 // NUMBER of BALLS+1, 0 for the CUBE class TForm1 : public TForm { __published: // IDE-managed Components void __fastcall FormResize(TObject *Sender); void __fastcall FormPaint(TObject *Sender); void __fastcall FormDestroy(TObject *Sender); void __fastcall FormCreate(TObject *Sender); void __fastcall FormKeyDown(TObject *Sender, WORD &Key, TShiftState Shift); void __fastcall FormMouseMove(TObject *Sender, TShiftState Shift, int X, int Y); void __fastcall FormMouseUp(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y); void __fastcall FormMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y); private: // User declarations HDC hdc; HGLRC hrc; int PixelFormat; GLuint startoflist; GLfloat W, H; GLuint texture1, texture2, texture3; public: // User declarations bool rotate_by_mouse; float LX,LY; float ox[NB],oy[NB],oz[NB], vx[NB],vy[NB],vz[NB]; float ballRadius[NB]; GLUquadricObj *bob[NB]; __fastcall TForm1(TComponent* Owner); void __fastcall IdleLoop(TObject*, bool&); bool __fastcall Setup_OpenGL(); void __fastcall Generate_Cube3D(int i, float cubeSize, float R, float G, float B); void __fastcall Generate_Ball3D(int i, float ballRadius, float R, float G, float B); void __fastcall Create_Objects3D(); void __fastcall Draw_Objects3D(); void __fastcall Apply_Physics3D(); }; //--------------------------------------------------------------------------- extern PACKAGE TForm1 *Form1; //--------------------------------------------------------------------------- #endif |
How to create an OpenGL example CPP File in C++ Builder
Now let’s back to Unit1.cpp by using tabs below,
Let’s summarize how our OpenGL app will work, this is the whole main part of our app in summary. We need IdleLoop() that runs Apply_Physics3D() and Draw_Objects3D(). When form is created we should setup OpenGL(). If this OpenGL set it up successful, then we should run Create_Objects3D() and we should also set to our IdleLoop() method to application ( Application->OnIdle = IdleLoop; ). Thus, our application should be 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 39 |
//--------------------------------------------------------------------------- #include <vcl.h> #pragma hdrstop #include "OpenGL_Ball_Physics3D_Unit1.h" //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; //--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //--------------------------------------------------------------------------- void __fastcall TForm1::IdleLoop(TObject*, bool& done) { done = false; // APPLY 3D PHYSICS Apply_Physics3D(); // DRAW 3D OBJECTS Draw_Objects3D(); } //--------------------------------------------------------------------------- void __fastcall TForm1::FormCreate(TObject *Sender) { if ( Setup_OpenGL() ) { // Create 3D OBJECTS Create_Objects3D(); // SET LOOP TO APP IDLE EVENT Application->OnIdle = IdleLoop; } } |
Here, if our app sets OpenGL it creates object and defines IdleLoop method as Application->IOnIdle(). This loop will calculate physical environment and it will draw objects every loop.
OpenGL pixel formats, lightings and textures
Our Setup_OpenGL() method will setup Pixel Format then it will create a Context with this Pixel Format and then it will set some OpenGL settings, Lightings, Textures etc. Here is our example 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 39 40 41 42 43 44 45 46 47 48 |
bool __fastcall TForm1::Setup_OpenGL() { hdc = GetDC(Handle); // SETUP PixelFormatDescriptor PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), 1, PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, PFD_TYPE_RGBA, 24, 0,0,0,0,0,0, 0,0, 0,0,0,0,0, 32, 0, 0, PFD_MAIN_PLANE, 0, 0,0, }; PixelFormat = ChoosePixelFormat(hdc, &pfd); SetPixelFormat(hdc, PixelFormat, &pfd); // CREATE CONTEXT hrc = wglCreateContext(hdc); if(hrc == NULL) { ShowMessage("hrc is NULL"); return false; } if(wglMakeCurrent(hdc, hrc) == false){ ShowMessage("Could not MakeCurrent"); return false; } glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); W = ClientWidth; H = ClientHeight; // SETUP OPENGL LIGHTINGS , Setup_Lightings(); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_COLOR_MATERIAL); glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE); glShadeModel(GL_SMOOTH); // SETUP OPENGL TEXTURES , Setup_Textures(); // ... return true; } |
Creating and drawing OpenGL objects in C++ Builder
We can create cube by creating a Cylinder – gluCylinder() – with 4 slices and 1 section. In our Generate_Cube3D() method we have object number, cube size and R,G,B values. Here is our Generate_Cube3D() method that draws OpenGL cube 3D.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
//--------------------------------------------------------------------------- void __fastcall TForm1::Generate_Cube3D(int i, float cubeSize, float R, float G, float B) { glBindTexture(GL_TEXTURE_2D, texture1); bob[i] = gluNewQuadric(); gluQuadricTexture(bob[i], GL_TRUE); gluQuadricDrawStyle(bob[i], GLU_LINE); gluQuadricNormals(bob[i], GLU_SMOOTH); glNewList(startoflist, GL_COMPILE); glColor3f(R,G,B); gluCylinder(bob[i], cubeSize, cubeSize, cubeSize*sqrt(2), 4, 1); glEndList(); } |
We can create our balls by creating a Sphare – gluSphere(). In our Generate_Ball3D() method we have object number, ball radius and R,G,B values. Here is our Generate_Ball3D() method that draws OpenGL ball 3D.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
void __fastcall TForm1::Generate_Ball3D(int i, float ballRadius, float R, float G, float B) { bob[i] = gluNewQuadric(); gluQuadricTexture(bob[i], GL_TRUE); gluQuadricDrawStyle(bob[i], GLU_FILL); gluQuadricNormals(bob[i], GLU_SMOOTH); glNewList(startoflist+i, GL_COMPILE); glColor3f(R, G, B); gluSphere(bob[i], ballRadius, 16, 9); glEndList(); } |
Making a Create_Objects3D() C++ method
Now lets define our Create_Objects3D() method with these two methods above. In this method, first we should define position, velocity and radius parameters of our balls randomly and then we should generate our 3D Cube and our Balls in OpenGL Matrix with two methods above. Here is our method 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 |
void __fastcall TForm1::Create_Objects3D() { startoflist = glGenLists(4); Generate_Cube3D(0, 200, 0.8f, 0.9f, 0.8f); srand(time(0)); for(int i=1; i<NB; i++) // setting random ball parameters { ballRadius[i] = 5+rand()%10; vx[i]= rand()%5; vy[i]= rand()%5; vz[i]= rand()%5; ox[i]= rand()%100-50; oy[i]= rand()%100-50; oz[i]= rand()%100-50; } Generate_Ball3D(1, ballRadius[1], 0.8f, 0.9f, 0.8f); Generate_Ball3D(2, ballRadius[2], 0.8f, 0.8f, 0.0f); Generate_Ball3D(3, ballRadius[3], 0.0f, 0.8f, 0.8f); } |
Now we can draw objects to OpenGL matrix as in our Draw_Objects3D() method 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 |
void __fastcall TForm1::Draw_Objects3D() { // Clear GL Buffer glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glRotatef(1, 0.1, -0.2, 0.1); // ROTATE ALL OBJECTS // SETUP MATRIX glPushMatrix(); // ADD CUBE glPushMatrix(); glColor3f(0.8f, 0.9f, 0.8f); glTranslatef(0, 0, -141); glCallList(startoflist); glPopMatrix(); // ADD BALLS for(int i=1; i<NB; i++) { glPushMatrix(); glTranslatef(ox[i], oy[i], oz[i]); glCallList(startoflist+i); glPopMatrix(); } glPopMatrix(); glFlush(); SwapBuffers(hdc); // Double Buffering for Smooth View } |
How to apply simulated physics to 3D objects in C++
Remember that we do calculations to apply physics in our IdleLoop(). Thus we should define this Apply_Physics3D() method 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 39 |
void __fastcall TForm1::Apply_Physics3D() { float a,b,c; // APPLY 3D PHYSICS TO BALLS for(int i=1; i<NB; i++) { // set new position with velocity ox[i] += vx[i]; oy[i] += vy[i]; oz[i] += vz[i]; //check boundary collusions if(ox[i]>100 || ox[i]<-100) { vx[i]*=-1; ox[i]+=vx[i]; } if(oy[i]>100 || oy[i]<-100) { vy[i]*=-1; oy[i]+=vy[i]; } if(oz[i]>100 || oz[i]<-100) { vz[i]*=-1; oz[i]+=vz[i]; } // check ball collusions for(int j=1; j<NB; j++) { if (i!=j) { if(fabs(ox[i]-ox[j])<ballRadius[i]+ballRadius[j] && fabs(oy[i]-oy[j])<ballRadius[i]+ballRadius[j] && fabs(oz[i]-oz[j])<ballRadius[i]+ballRadius[j]) { // add momentum equations here, here we will do simple tricks a=vx[i]; b=vy[i]; c=vz[i]; vx[i]=vx[j]; vy[i]=vy[j]; vz[i]=vz[j]; vx[j]=a; vy[j]=b; vz[j]=c; } } } } } |
In this method we check cube boundaries and if there is collusion we reverse the velocity vector by multiplying with -1. Here we also check ball collusions with each other then if there is collusion we change velocity vectors of balls.
Setting some methods of the form
We need these OnPaint(), OnResize() and OnDestroy() procedures to apply changes in our OpenGL.
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 |
void __fastcall TForm1::FormPaint(TObject *Sender) { Draw_Objects3D(); } //--------------------------------------------------------------------------- void __fastcall TForm1::FormResize(TObject *Sender) { GLfloat nRange = 300.0; W = ClientWidth; H = ClientHeight; if(H == 0) H = 1; glViewport(0, 0, W, H); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if (W <= H) glOrtho (-nRange, nRange, -nRange*H/W, nRange*H/W, -nRange, nRange); else glOrtho (-nRange*W/H, nRange*W/H, -nRange, nRange, -nRange, nRange); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } //--------------------------------------------------------------------------- void __fastcall TForm1::FormDestroy(TObject *Sender) { for(int i=0; i<=3; i++) gluDeleteQuadric(bob[i]); wglMakeCurrent(NULL, NULL); wglDeleteContext(hrc); } |
How to add mouse and keyboard interactions to an OpenGL display in C++
Finally if we can simply rotate our OpenGL environment by mouse or by keyboard as given example 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 |
void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { rotate_by_mouse=true; } //--------------------------------------------------------------------------- void __fastcall TForm1::FormMouseUp(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { rotate_by_mouse=false; } //--------------------------------------------------------------------------- void __fastcall TForm1::FormMouseMove(TObject *Sender, TShiftState Shift, int X, int Y) { if(rotate_by_mouse) glRotatef(-2, X-LX, Y-LY, 0.0); LX = X; LY = Y; } //--------------------------------------------------------------------------- void __fastcall TForm1::FormKeyDown(TObject *Sender, WORD &Key, TShiftState Shift) { if(Key == VK_UP) glRotatef(-5, 1.0, 0.0, 0.0); if(Key == VK_DOWN) glRotatef(5, 1.0, 0.0, 0.0); if(Key == VK_LEFT) glRotatef(-5, 0.0, 1.0, 0.0); if(Key == VK_RIGHT) glRotatef(5, 0.0, 1.0, 0.0); } |
Here is our example C+ OpenGL Application running
Now you can run example and you will see that balls are moving and pinging at the borders and with each other. When this app is running our IdleLoop() will keep calculating physical conditions and formulas and it will draw new positions of all OpenGL objects.
Here is the example output with 3 balls,
If you have errors about variables or procedures please check full Unit1.h header above.
Here is the full Unit1.cpp example.
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 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 |
//--------------------------------------------------------------------------- #include <vcl.h> #pragma hdrstop #include "OpenGL_Ball_Physics3D_Unit1.h" //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; //--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //--------------------------------------------------------------------------- void __fastcall TForm1::IdleLoop(TObject*, bool& done) { done = false; // APPLY 3D PHYSICS Apply_Physics3D(); // DRAW 3D OBJECTS Draw_Objects3D(); } //--------------------------------------------------------------------------- void __fastcall TForm1::FormCreate(TObject *Sender) { if ( Setup_OpenGL() ) { // Create 3D OBJECTS Create_Objects3D(); // SET LOOP TO APP IDLE EVENT Application->OnIdle = IdleLoop; } } //--------------------------------------------------------------------------- bool __fastcall TForm1::Setup_OpenGL() { hdc = GetDC(Handle); // SETUP PixelFormatDescriptor PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), 1, PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, PFD_TYPE_RGBA, 24, 0,0,0,0,0,0, 0,0, 0,0,0,0,0, 32, 0, 0, PFD_MAIN_PLANE, 0, 0,0, }; PixelFormat = ChoosePixelFormat(hdc, &pfd); SetPixelFormat(hdc, PixelFormat, &pfd); // CREATE CONTEXT hrc = wglCreateContext(hdc); if(hrc == NULL) { ShowMessage("hrc is NULL"); return false; } if(wglMakeCurrent(hdc, hrc) == false){ ShowMessage("Could not MakeCurrent"); return false; } glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); W = ClientWidth; H = ClientHeight; // SETUP OPENGL LIGHTINGS , Setup_Lightings(); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_COLOR_MATERIAL); glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE); glShadeModel(GL_SMOOTH); // SETUP OPENGL TEXTURES , Setup_Textures(); // ... return true; } //--------------------------------------------------------------------------- void __fastcall TForm1::FormResize(TObject *Sender) { GLfloat nRange = 300.0; W = ClientWidth; H = ClientHeight; if(H == 0) H = 1; glViewport(0, 0, W, H); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if (W <= H) glOrtho (-nRange, nRange, -nRange*H/W, nRange*H/W, -nRange, nRange); else glOrtho (-nRange*W/H, nRange*W/H, -nRange, nRange, -nRange, nRange); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } //--------------------------------------------------------------------------- void __fastcall TForm1::FormDestroy(TObject *Sender) { for(int i=0; i<=3; i++) gluDeleteQuadric(bob[i]); wglMakeCurrent(NULL, NULL); wglDeleteContext(hrc); } //--------------------------------------------------------------------------- void __fastcall TForm1::Generate_Cube3D(int i, float cubeSize, float R, float G, float B) { glBindTexture(GL_TEXTURE_2D, texture1); bob[i] = gluNewQuadric(); gluQuadricTexture(bob[i], GL_TRUE); gluQuadricDrawStyle(bob[i], GLU_LINE); gluQuadricNormals(bob[i], GLU_SMOOTH); glNewList(startoflist, GL_COMPILE); glColor3f(R,G,B); gluCylinder(bob[i], cubeSize, cubeSize, cubeSize*sqrt(2), 4, 1); glEndList(); } //--------------------------------------------------------------------------- void __fastcall TForm1::Generate_Ball3D(int i, float ballRadius, float R, float G, float B) { bob[i] = gluNewQuadric(); gluQuadricTexture(bob[i], GL_TRUE); gluQuadricDrawStyle(bob[i], GLU_FILL); gluQuadricNormals(bob[i], GLU_SMOOTH); glNewList(startoflist+i, GL_COMPILE); glColor3f(R, G, B); gluSphere(bob[i], ballRadius, 16, 9); glEndList(); } //--------------------------------------------------------------------------- void __fastcall TForm1::Create_Objects3D() { startoflist = glGenLists(4); Generate_Cube3D(0, 200, 0.8f, 0.9f, 0.8f); srand(time(0)); for(int i=1; i<NB; i++) // setting random ball parameters { ballRadius[i] = 5+rand()%10; vx[i]= rand()%5; vy[i]= rand()%5; vz[i]= rand()%5; ox[i]= rand()%100-50; oy[i]= rand()%100-50; oz[i]= rand()%100-50; } Generate_Ball3D(1, ballRadius[1], 0.8f, 0.9f, 0.8f); Generate_Ball3D(2, ballRadius[2], 0.8f, 0.8f, 0.0f); Generate_Ball3D(3, ballRadius[3], 0.0f, 0.8f, 0.8f); } //--------------------------------------------------------------------------- void __fastcall TForm1::Draw_Objects3D() { // Clear GL Buffer glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glRotatef(1, 0.1, -0.2, 0.1); // ROTATE ALL OBJECTS // SETUP MATRIX glPushMatrix(); // ADD CUBE glPushMatrix(); glColor3f(0.8f, 0.9f, 0.8f); glTranslatef(0, 0, -141); glCallList(startoflist); glPopMatrix(); // ADD BALLS for(int i=1; i<NB; i++) { glPushMatrix(); glTranslatef(ox[i], oy[i], oz[i]); glCallList(startoflist+i); glPopMatrix(); } glPopMatrix(); glFlush(); SwapBuffers(hdc); // Double Buffering for Smooth View } //--------------------------------------------------------------------------- void __fastcall TForm1::Apply_Physics3D() { float a,b,c; // APPLY 3D PHYSICS TO BALLS for(int i=1; i<NB; i++) { ox[i] += vx[i]; oy[i] += vy[i]; oz[i] += vz[i]; //check boundary collusions if(ox[i]>100 || ox[i]<-100) { vx[i]*=-1; ox[i]+=vx[i]; } if(oy[i]>100 || oy[i]<-100) { vy[i]*=-1; oy[i]+=vy[i]; } if(oz[i]>100 || oz[i]<-100) { vz[i]*=-1; oz[i]+=vz[i]; } // check ball collusions for(int j=1; j<NB; j++) { if (i!=j) { if(fabs(ox[i]-ox[j])<ballRadius[i]+ballRadius[j] && fabs(oy[i]-oy[j])<ballRadius[i]+ballRadius[j] && fabs(oz[i]-oz[j])<ballRadius[i]+ballRadius[j]) { // add momentum equations here, here we will do simple tricks a=vx[i]; b=vy[i]; c=vz[i]; vx[i]=vx[j]; vy[i]=vy[j]; vz[i]=vz[j]; vx[j]=a; vy[j]=b; vz[j]=c; } } } } } //--------------------------------------------------------------------------- void __fastcall TForm1::FormPaint(TObject *Sender) { Draw_Objects3D(); } //--------------------------------------------------------------------------- void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { rotate_by_mouse=true; } //--------------------------------------------------------------------------- void __fastcall TForm1::FormMouseUp(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { rotate_by_mouse=false; } //--------------------------------------------------------------------------- void __fastcall TForm1::FormMouseMove(TObject *Sender, TShiftState Shift, int X, int Y) { if(rotate_by_mouse) glRotatef(-2, X-LX, Y-LY, 0.0); LX = X; LY = Y; } //--------------------------------------------------------------------------- void __fastcall TForm1::FormKeyDown(TObject *Sender, WORD &Key, TShiftState Shift) { if(Key == VK_UP) glRotatef(-5, 1.0, 0.0, 0.0); if(Key == VK_DOWN) glRotatef(5, 1.0, 0.0, 0.0); if(Key == VK_LEFT) glRotatef(-5, 0.0, 1.0, 0.0); if(Key == VK_RIGHT) glRotatef(5, 0.0, 1.0, 0.0); } |
As you see C++ Builder is very capable to build your own OpenGL apps from the scratch with modern methods.
C++ Builder is the easiest and fastest C and C++ IDE for building simple or professional applications on the Windows, MacOS, iOS & Android operating systems. There is a free C++ Builder Community Edition for students, beginners, and startups; it can be downloaded from here. For professional developers, we have Professional, Architect, or Enterprise versions of C++ Builder and there is a trial version you can download from here.