In a 3D Application Development, 3D objects can be displayed with 2D projection methods by using 2D / 3D mathematical calculations drawings. That may be hard to code and needs much coding skills to display them. We can use OpenGL and DirectX with their 3D functions/commands to display them. We can also use and port a 3D Engine SDK. In C++ Builder, 3D objects can be easily displayed by using a Viewport3D component. We can change their shape, position, color, materials, etc. on design time or runtime. We can add Texture Materials or Light Materials to them. We can group them by using Dummy objects. In this post, we explain what a Viewport3D is and how to draw a very parametric 3D Torus in C++ Builder.
Table of Contents
What is Viewport3D?
Viewport3D (TViewport3D) is a 3D component to display 3D objects in its space. TViewport3D implements IViewport3D methods to describe how a 3D object is seen. It is really simple and nice to develop some small 3D games or add some 3D features to our applications. Viewport3D is a component that shows the viewport of the camera or default view. We can arrange its position, Width, and Height on any place of our form or we can use it in client view to do full screen. We can use viewport3D without a 3D Camera, it uses its design camera view. If we want to use a camera and want to see the view from the view of that camera, we must set its Use Design Camera property to false from its properties. For more details about how to use Viewport3D, 3D objects, and lights, materials, and camera, here are some example posts that we released before,
- Modern Windows “Hello World” 3D Example in C++ Builder
- Learn About Working With 3D In Modern Windows C++ Development
- Quickly Learn About Working With 3D Windows Components In C++ Development
- Learn To Quickly Create Specific 3D Objects In Modern C++ Applications For Windows
How To Draw A Torus In 3D?
To create a torus in 3D, first, we should calculate the position of 2 points (P0, P1) in the inner circle (with R2 radius and Alpha angle), then we can rotate these points in the outer circle (with R1 and Beta angle) to find P2, P3 points. Then, we can create a plane with these 4 points, which means there will be two triangle faces on this plane. Here is a schematic of how we can draw torus.
How To Create A Custom Torus In 3D Mesh?
Let’s create a simple application that draws a custom torus as below,
We should add these components below from the Palette window on the right side;
- a Button (Button1)
- a Viewport3D (Viewport3D1) component to our form,
- a TDummy object (Dummy1),
- a TLightMaterialSource (LightMaterialSource1),
- and a TLight (Light1) and a TCamera (Camera1).
We can create a new TMesh in C++ as shown below.
1 2 3 4 |
TMesh mesh=new TMesh(Viewport3D1); mesh->Parent = Viewportd3D1; |
We can set some properties of TMesh object like so:
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 |
Parent = ParentObj; Width=1; Height=1; Depth=1; Position->X=0; Position->Y=0; Position->Z=0; Scale->X=1; Scale->Y=1; Scale->Z=1; RotationAngle->X=0; RotationAngle->Y=0; RotationAngle->Z=0; RotationCenter->X=0; RotationCenter->Y=0; RotationCenter->Z=0; WrapMode=TMeshWrapMode::Original; TwoSide=true; Visible=true; Opacity=1.0; |
Here we use 4 and 6 magic numbers to create VertexBuffer
and IndexBuffer
.
1 2 3 4 |
mesh->Data->VertexBuffer->Length=4*(numofplanes); mesh->Data->IndexBuffer->Length= 6*(numofplanes); |
Mesh Data has 4 parameters, these are P0, P1, P2, P3 nodes;
1 2 3 4 5 6 |
mesh->Data->VertexBuffer->Vertices[NP+0] = P0; mesh->Data->VertexBuffer->Vertices[NP+1] = P1; mesh->Data->VertexBuffer->Vertices[NP+2] = P2; mesh->Data->VertexBuffer->Vertices[NP+3] = P3; |
and Index Buffer has 6 params, because 4 nodes creates 2 faces and we can define each of them by 3 points.
1 2 3 4 5 6 7 8 9 |
mesh->Data->IndexBuffer->Indices[NI+0] =NP+0; mesh->Data->IndexBuffer->Indices[NI+1] =NP+2; mesh->Data->IndexBuffer->Indices[NI+2] =NP+1; mesh->Data->IndexBuffer->Indices[NI+3] =NP+0; mesh->Data->IndexBuffer->Indices[NI+4] =NP+3; mesh->Data->IndexBuffer->Indices[NI+5] =NP+2; |
This article explains more: https://vulkan-tutorial.com/Vertex_buffers/Index_buffer
How To Create A Custom Torus Class with TCustomMesh?
First, let’s create a new C++ Builder Multi-Device (FireMonkey) Application, and add let’S add some global variables as below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#include <fmx.h> #pragma hdrstop #include "Custom3DObjects_Unit1.h" //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.fmx" TForm1 *Form1; float LX,LY; bool mouse_rotation; using namespace std; //--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } |
In modern C++, we can create a custom torus by using a TCustomMesh class as its parent class.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class TTorus : public TCustomMesh { private: TPoint3D P0,P1,P2,P3; int NP=0, NI=0; int numofsteps=360; public: __fastcall virtual TTorus(TComponent* AOwner, TFmxObject *ParentObj, double R1, double R2, int InnerSeg, int OuterSeg, double innerDeg, double outerDeg, TMaterialSource *MT); }; |
A Torus can be drawn with rotating points in two radius (R1 and R2). R1 radius is the radius of the torus and R2 is the radius of its pipe. So we will have 2 loops for the beta angles in R1 and alpha angles in R2;
Here above, the last TTorus
(…) method is the constructor of this class. Now, we can define this constructor at the outside of this class. We can create a Torus object in its parametric constructor 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 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 |
__fastcall TTorus::TTorus(TComponent* AOwner, TFmxObject *ParentObj, double R1, double R2, int InnerSeg, int OuterSeg, double innerDeg, double outerDeg, TMaterialSource *MT) : TCustomMesh(AOwner) { Parent = ParentObj; Width=1; Height=1; Depth=1; Position->X=0; Position->Y=0; Position->Z=0; Scale->X=1; Scale->Y=1; Scale->Z=1; RotationAngle->X=0; RotationAngle->Y=0; RotationAngle->Z=0; RotationCenter->X=0; RotationCenter->Y=0; RotationCenter->Z=0; WrapMode=TMeshWrapMode::Original; TwoSide=true; Visible=true; Opacity=1.0; float alfa_inc = M_PI/InnerSeg; float beta_inc = M_PI/OuterSeg; int num_of_planes = 4*((outerDeg/beta_inc)*(innerDeg/alfa_inc)); Data->Clear(); Data->VertexBuffer->Length = 4*(num_of_planes); Data->IndexBuffer->Length = 6*(num_of_planes); for(float beta=0; beta<outerDeg; beta += beta_inc) { for( float alfa=0; alfa<innerDeg; alfa+= alfa_inc ) { P0.X = (R1 + R2*sin(alfa))*cos(beta); P0.Y = (0 + R2*cos(alfa)); P0.Z = (R1 + R2*sin(alfa))*sin(beta) ; P1.X = (R1 + R2*sin(alfa+alfa_inc))*cos(beta); P1.Y = (0 + R2*cos(alfa+alfa_inc)); P1.Z = (R1 + R2*sin(alfa+alfa_inc))*sin(beta) ; P2.X = (R1 + R2*sin(alfa+alfa_inc))*cos(beta+beta_inc); P2.Y = (0 + R2*cos(alfa+alfa_inc)); P2.Z = (R1 + R2*sin(alfa+alfa_inc))*sin(beta+beta_inc) ; P3.X = (R1 + R2*sin(alfa))*cos(beta+beta_inc); P3.Y = (0 + R2*cos(alfa)); P3.Z = (R1 + R2*sin(alfa))*sin(beta+beta_inc) ; Data->VertexBuffer->Vertices[NP+0] = P0; Data->VertexBuffer->Vertices[NP+1] = P1; Data->VertexBuffer->Vertices[NP+2] = P2; Data->VertexBuffer->Vertices[NP+3] = P3; Data->VertexBuffer->TexCoord0[NP+0] = PointF(0, (P0.Y+35)/45); Data->VertexBuffer->TexCoord0[NP+1] = PointF(0, (P0.Y+35)/45); Data->VertexBuffer->TexCoord0[NP+2] = PointF(0, (P0.Y+35)/45); Data->VertexBuffer->TexCoord0[NP+3] = PointF(0, (P0.Y+35)/45); //mesh->Data->Normals="....."; Data->IndexBuffer->Indices[NI+0] = NP+0; Data->IndexBuffer->Indices[NI+1] = NP+2; Data->IndexBuffer->Indices[NI+2] = NP+1; Data->IndexBuffer->Indices[NI+3] = NP+0; Data->IndexBuffer->Indices[NI+4] = NP+3; Data->IndexBuffer->Indices[NI+5] = NP+2; NP+=4; NI+=6; } } MaterialSource=MT; Data->CalcFaceNormals(true); Repaint(); } |
Finally, we can create a custom torus object with a one click as below.
1 2 3 4 5 6 7 8 |
TTorus *torus; void __fastcall TForm1::Button1Click(TObject *Sender) { torus = new TTorus(Form1, Dummy1, 5, 2, 36, 36, 2*M_PI, M_PI, LightMaterialSource1); } |
We can use OnMouseMove
, OnMouseUp
, OnMouseDown
events of Viewport3D to rotate this 3D object by mouse. Just select your ViewPort3D, go to the Events tab of the Object Inspector, and double click each of them to create their definitions. You can add your codes inside them. Here is a simple version.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
void __fastcall TForm1::Viewport3D1MouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, float X, float Y) { mouse_rotation=true; } //--------------------------------------------------------------------------- void __fastcall TForm1::Viewport3D1MouseUp(TObject *Sender, TMouseButton Button, TShiftState Shift, float X, float Y) { mouse_rotation=false; LX=X; LY=Y; } //--------------------------------------------------------------------------- void __fastcall TForm1::Viewport3D1MouseMove(TObject *Sender, TShiftState Shift, float X, float Y) { if(mouse_rotation) { Dummy1->RotationAngle->X=(Y-LY)*0.4; Dummy1->RotationAngle->Y=(LX-X)*0.4; } } |
As a result, if you run your code and click to button, it will create your own ViewPort3D object on your Dummy object as below.
Note that this is an educational example, you can improve and optimize this code to make it faster and better.
C++ Builder is the easiest and fastest C and C++ compiler and IDE for building simple or professional applications on the Windows operating system. It is also easy for beginners to learn with its wide range of samples, tutorials, help files, and LSP support for code. RAD Studio’s C++ Builder version comes with the award-winning VCL framework for high-performance native Windows apps and the powerful FireMonkey (FMX) framework for UIs.
There is a free C++ Builder Community Edition for students, beginners, and startups; it can be downloaded from here. For professional developers, there are Professional, Architect, or Enterprise versions of C++ Builder and there is a trial version you can download from here.