Recent

Author Topic: 3D Rotating Cube  (Read 1220 times)

Gigatron

  • Sr. Member
  • ****
  • Posts: 334
  • Amiga Rulez !!
3D Rotating Cube
« on: April 07, 2025, 03:03:14 pm »
Hi,

Here is a simple BGRA wireframe 3d rotating cube, you can play with this to improve it;
All rotation forumula are from WWW .
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls,
  9.   BGRAVirtualScreen, BGRABitmap, BGRABitmapTypes;
  10.  
  11.  
  12. const
  13.   CubeSize = 600;
  14.  
  15. type
  16.   TVec3 = record
  17.     x, y, z: Single;
  18.   end;
  19.  
  20.  
  21.   { TForm1 }
  22.  
  23.   TForm1 = class(TForm)
  24.     BGRAVirtualScreen1: TBGRAVirtualScreen;
  25.     Timer1: TTimer;
  26.     procedure BGRAVirtualScreen1Redraw(Sender: TObject; Bitmap: TBGRABitmap);
  27.     procedure FormCreate(Sender: TObject);
  28.     procedure Timer1Timer(Sender: TObject);
  29.   private
  30.  
  31.   public
  32.  
  33.   end;
  34.  
  35. var
  36.   Form1: TForm1;
  37.  
  38.  AngleX, AngleY, AngleZ: Single;
  39.   CubePoints: array[0..7] of TVec3 = (
  40.     (x:-1; y:-1; z:-1), (x:1;  y:-1; z:-1),
  41.     (x:1; y:1; z:-1),   (x:-1; y:1;  z:-1),
  42.     (x:-1; y:-1; z:1),  (x:1;  y:-1; z:1),
  43.     (x:1; y:1; z:1),    (x:-1; y:1;  z:1)
  44.   );
  45.  
  46. implementation
  47.  
  48. {$R *.lfm}
  49.  
  50. procedure DrawCube(Bitmap: TBGRABitmap; AngleX, AngleY, AngleZ: Single);
  51. const
  52.   Edges: array[0..11, 0..1] of Integer = (
  53.     (0,1), (1,2), (2,3), (3,0),
  54.     (4,5), (5,6), (6,7), (7,4),
  55.     (0,4), (1,5), (2,6), (3,7)
  56.   );
  57. var
  58.   i: Integer;
  59.   Rotated: array[0..7] of TVec3;
  60.   Projected: array[0..7] of TPoint;
  61.   cx, cy: Integer;
  62.   x1, y1, z1: Single;
  63.   p1, p2: TPoint;
  64. begin
  65.   cx := Bitmap.Width div 2;
  66.   cy := Bitmap.Height div 2;
  67.  
  68.   for i := 0 to 7 do
  69.   begin
  70.     x1 := CubePoints[i].x;
  71.     y1 := CubePoints[i].y;
  72.     z1 := CubePoints[i].z;
  73.  
  74.     // Rotation X
  75.     Rotated[i].y := y1 * cos(AngleX) - z1 * sin(AngleX);
  76.     Rotated[i].z := y1 * sin(AngleX) + z1 * cos(AngleX);
  77.     y1 := Rotated[i].y;
  78.     z1 := Rotated[i].z;
  79.  
  80.     // Rotation Y
  81.     Rotated[i].x := x1 * cos(AngleY) + z1 * sin(AngleY);
  82.     Rotated[i].z := -x1 * sin(AngleY) + z1 * cos(AngleY);
  83.     x1 := Rotated[i].x;
  84.     z1 := Rotated[i].z;
  85.  
  86.     // Rotation Z
  87.     Rotated[i].x := x1 * cos(AngleZ) - y1 * sin(AngleZ);
  88.     Rotated[i].y := x1 * sin(AngleZ) + y1 * cos(AngleZ);
  89.  
  90.     // Projection
  91.     Projected[i].X := Round(cx + (Rotated[i].x * CubeSize) / (Rotated[i].z + 5));
  92.     Projected[i].Y := Round(cy + (Rotated[i].y * CubeSize) / (Rotated[i].z + 5));
  93.   end;
  94.  
  95.   for i := 0 to 11 do
  96.   begin
  97.     p1 := Projected[Edges[i][0]];
  98.     p2 := Projected[Edges[i][1]];
  99.    // Bitmap.DrawLineAntialias(p1.X, p1.Y, p2.X, p2.Y, BGRA(255, 255, 255), 6);
  100.     Bitmap.DrawLine(p1.X, p1.Y, p2.X, p2.Y, BGRA(255, 255, 255),false);
  101.     Bitmap.DrawLine(4+p2.X, 4+p2.Y, p1.X+4, p1.Y+4, BGRA(255, 0, 0),false);
  102.   end;
  103. end;
  104.  
  105. { TForm1 }
  106.  
  107. procedure TForm1.FormCreate(Sender: TObject);
  108. begin
  109.   Timer1.Enabled := True;
  110.   Timer1.Interval := 15*6; // Amiga500 68000 vector rotation speed in 1986 !
  111. end;
  112.  
  113. procedure TForm1.BGRAVirtualScreen1Redraw(Sender: TObject; Bitmap: TBGRABitmap);
  114. begin
  115.   DrawCube(Bitmap, AngleX, AngleY, AngleZ);
  116. end;
  117.  
  118. procedure TForm1.Timer1Timer(Sender: TObject);
  119. begin
  120.   AngleX += 0.020;
  121.   AngleY += 0.030;
  122.   AngleZ += 0.010;
  123.   BGRAVirtualScreen1.RedrawBitmap;
  124. end;
  125.  
  126. end.
  127.  
Trip to Europe...  20 days

TBMan

  • Sr. Member
  • ****
  • Posts: 281
Re: 3D Rotating Cube
« Reply #1 on: April 07, 2025, 06:22:00 pm »
I'm not a math wiz so before I start tinkering with your code, are the angles in radians?
I love programming.

Some things I've done using PTCgraph:

NFL Retro Football (almost finished):
https://www.youtube.com/watch?v=78mTtsd7ppk


Solitaire games:
https://www.youtube.com/watch?v=zmtxI7FdWuQ&list=PLa4BPpFl34iVhFwX1JZwVm3vE5ay_i3R2

Handoko

  • Hero Member
  • *****
  • Posts: 5487
  • My goal: build my own game engine using Lazarus
Re: 3D Rotating Cube
« Reply #2 on: April 07, 2025, 06:40:15 pm »
I'm not a math wiz so before I start tinkering with your code, are the angles in radians?

Radians. The DrawCube procedure uses sin and cos functions, and their parameters are in radians:
https://www.freepascal.org/docs-html/rtl/system/sin.html

TBMan

  • Sr. Member
  • ****
  • Posts: 281
Re: 3D Rotating Cube
« Reply #3 on: April 07, 2025, 09:13:24 pm »
I'm not a math wiz so before I start tinkering with your code, are the angles in radians?

Radians. The DrawCube procedure uses sin and cos functions, and their parameters are in radians:
https://www.freepascal.org/docs-html/rtl/system/sin.html

I knew that. LOL.
I love programming.

Some things I've done using PTCgraph:

NFL Retro Football (almost finished):
https://www.youtube.com/watch?v=78mTtsd7ppk


Solitaire games:
https://www.youtube.com/watch?v=zmtxI7FdWuQ&list=PLa4BPpFl34iVhFwX1JZwVm3vE5ay_i3R2

Gigatron

  • Sr. Member
  • ****
  • Posts: 334
  • Amiga Rulez !!
Re: 3D Rotating Cube
« Reply #4 on: April 07, 2025, 09:16:22 pm »
I'm not a math wiz so before I start tinkering with your code, are the angles in radians?

Radians. The DrawCube procedure uses sin and cos functions, and their parameters are in radians:
https://www.freepascal.org/docs-html/rtl/system/sin.html

Thank you @Handoko for the answer;
Trip to Europe...  20 days

d2010

  • Full Member
  • ***
  • Posts: 230
Re: 3D Rotating Cube
« Reply #5 on: April 10, 2025, 03:23:12 am »
I'm not a math wiz so before I start tinkering with your code, are the angles in radians?
Here is My-Demo.

TBMan

  • Sr. Member
  • ****
  • Posts: 281
Re: 3D Rotating Cube
« Reply #6 on: April 10, 2025, 04:11:39 am »
Here's my spin on this (pun intended) with ptcgraph. Check the initgraph to make sure its compatible with your PC. 2 video pages
are being used.

Code: Pascal  [Select][+][-]
  1. program cube1;
  2. uses windows, ptcgraph,ptccrt,math;
  3. const
  4.   CubeSize = 600;
  5.  
  6. type
  7.   TVec3 = record
  8.     x, y, z: Single;
  9.   end;
  10.  
  11.   tpoint = record
  12.     x,y:single;
  13.   end;
  14.  
  15.   VideoPages = (Page0, Page1);
  16.  
  17. var
  18.  gdriver,gmode:smallint;
  19.  Page: VideoPages = Page0;
  20.  AngleX, AngleY, AngleZ: Single;
  21.   CubePoints: array[0..7] of TVec3 = (
  22.     (x:-1; y:-1; z:-1), (x:1;  y:-1; z:-1),
  23.     (x:1; y:1; z:-1),   (x:-1; y:1;  z:-1),
  24.     (x:-1; y:-1; z:1),  (x:1;  y:-1; z:1),
  25.     (x:1; y:1; z:1),    (x:-1; y:1;  z:1)
  26.   );
  27.  
  28.  
  29. procedure Nextactivepage;
  30.   begin
  31.     if page = page0 then page := page1
  32.     else
  33.       page := page0;
  34.       SetactivePage(Ord(Page));
  35.   end;
  36.  
  37. procedure NextVisualPage;
  38.   begin
  39.     SetVisualpage(Ord(Page));
  40.   end;
  41.  
  42. procedure frametick(frames:integer);
  43. var
  44.   scount,ecount:qword;
  45. begin
  46.    scount := GetTickCount64;
  47.   repeat
  48.     ecount := GetTickCount64;
  49.   until ecount > (scount+(64*15)/frames);
  50. end;
  51.  
  52.  
  53.  
  54. procedure DrawCube(AngleX, AngleY, AngleZ: Single);
  55. const
  56.   Edges: array[0..11, 0..1] of Integer = (
  57.     (0,1), (1,2), (2,3), (3,0),
  58.     (4,5), (5,6), (6,7), (7,4),
  59.     (0,4), (1,5), (2,6), (3,7)
  60.   );
  61. var
  62.   i: Integer;
  63.   Rotated: array[0..7] of TVec3;
  64.   Projected: array[0..7] of TPoint;
  65.   cx, cy: Integer;
  66.   x1, y1, z1: Single;
  67.   p1, p2: TPoint;
  68. begin
  69.   cx := 210;
  70.   cy := 210;
  71.  
  72.   for i := 0 to 7 do
  73.   begin
  74.     x1 := CubePoints[i].x;
  75.     y1 := CubePoints[i].y;
  76.     z1 := CubePoints[i].z;
  77.  
  78.     // Rotation X
  79.     Rotated[i].y := y1 * cos(AngleX) - z1 * sin(AngleX);
  80.     Rotated[i].z := y1 * sin(AngleX) + z1 * cos(AngleX);
  81.     y1 := Rotated[i].y;
  82.     z1 := Rotated[i].z;
  83.  
  84.     // Rotation Y
  85.     Rotated[i].x := x1 * cos(AngleY) + z1 * sin(AngleY);
  86.     Rotated[i].z := -x1 * sin(AngleY) + z1 * cos(AngleY);
  87.     x1 := Rotated[i].x;
  88.     z1 := Rotated[i].z;
  89.  
  90.     // Rotation Z
  91.     Rotated[i].x := x1 * cos(AngleZ) - y1 * sin(AngleZ);
  92.     Rotated[i].y := x1 * sin(AngleZ) + y1 * cos(AngleZ);
  93.  
  94.     // Projection
  95.     Projected[i].X := Round(cx + (Rotated[i].x * CubeSize) / (Rotated[i].z + 5));
  96.     Projected[i].Y := Round(cy + (Rotated[i].y * CubeSize) / (Rotated[i].z + 5));
  97.   end;
  98.  
  99.   for i := 0 to 11 do
  100.   begin
  101.     p1 := Projected[Edges[i][0]];
  102.     p2 := Projected[Edges[i][1]];
  103.  
  104.     Line(round(p1.X),round( p1.Y), round(p2.X), round(p2.Y));
  105. //    Line(round(p2.X)+4, round(p2.Y)+4, round(p1.X)+4,round( p1.Y)+4);
  106.   end;
  107. end;
  108.  
  109. var
  110.   j:integer;
  111.   s:single;
  112.  
  113.   Ydegrees,
  114.   xdegrees,zdegrees:integer;
  115. begin
  116.  gdriver := vesa;
  117.  Gmode := installusermode(800, 600, 256, 2, 8000, 6000);
  118.  WindowTitle := '3d Cube';
  119.  initgraph(gdriver, gmode, '');
  120.  setviewport(100,50,600,500,true);
  121.   xdegrees := 90;
  122.   zdegrees := 100;
  123.   ydegrees := 45;
  124.  
  125. repeat
  126.     nextactivepage;
  127.     clearviewport;
  128.     DrawCube(angleX, AngleY, angleZ);
  129.     nextvisualpage;
  130.     inc(zdegrees);
  131.     if zdegrees >360 then zdegrees := 1;
  132.     inc(xdegrees);
  133.     if xdegrees > 360 then xdegrees := 1;
  134.     dec(ydegrees);
  135.     if ydegrees <0 then ydegrees := 359;
  136.     angleX := degTorad(xdegrees);
  137.     angleZ := degToRad(Zdegrees);
  138.     angley := degtorad(ydegrees);
  139.     frametick(960);
  140. until keypressed;
  141.  
  142. end.
  143.                                
  144.  
  145.  
  146.  
I love programming.

Some things I've done using PTCgraph:

NFL Retro Football (almost finished):
https://www.youtube.com/watch?v=78mTtsd7ppk


Solitaire games:
https://www.youtube.com/watch?v=zmtxI7FdWuQ&list=PLa4BPpFl34iVhFwX1JZwVm3vE5ay_i3R2

Gigatron

  • Sr. Member
  • ****
  • Posts: 334
  • Amiga Rulez !!
Re: 3D Rotating Cube
« Reply #7 on: April 10, 2025, 04:27:03 pm »
@d2010 ; @TBMan

Nice and perfect job ;
Trip to Europe...  20 days

TBMan

  • Sr. Member
  • ****
  • Posts: 281
Re: 3D Rotating Cube
« Reply #8 on: April 10, 2025, 06:35:59 pm »
@d2010 ; @TBMan

Nice and perfect job ;

Thanks. The odds of me learning the ins and outs of 3d programming are slim to none due to a no trig or calculus background, but I'd like to learn what to do to construct 3 objects manually as you did in your code. Do you have any recommendations short of college courses?  :D

It looks like the Edges array is the vertices(distances/steps from the centerx and centery?) and the CubePoints seem to be some sort of 2d to 3d translator table.
I love programming.

Some things I've done using PTCgraph:

NFL Retro Football (almost finished):
https://www.youtube.com/watch?v=78mTtsd7ppk


Solitaire games:
https://www.youtube.com/watch?v=zmtxI7FdWuQ&list=PLa4BPpFl34iVhFwX1JZwVm3vE5ay_i3R2

Gigatron

  • Sr. Member
  • ****
  • Posts: 334
  • Amiga Rulez !!
Re: 3D Rotating Cube
« Reply #9 on: April 10, 2025, 07:03:45 pm »
Hi;



CubePoints defines the 8 corners (vertices) of a cube centered around (0,0,0). Each point is 3D (x, y, z) and goes from -1 to 1.

Simple rotation formulas on each axis (X, Y, Z). You can find the formula on www like me :)

This website help you : https://www.instructables.com/Rotating-Cube-Using-JavaScript/

After rotation perspective projection of  each 3D point into 2D using this line:

Code: Pascal  [Select][+][-]
  1. // Projection
  2.     Projected[i].X := Round(cx + (Rotated[i].x * CubeSize) / (Rotated[i].z + 5));
  3.     Projected[i].Y := Round(cy + (Rotated[i].y * CubeSize) / (Rotated[i].z + 5));

The (z + 5)  (to avoid dividing by zero).

End part is  drawing the edges of the cube using the Edges array, which just connects the right points.

I learn everything I can by reading documents on the www. It takes a lot of time, no TV but learning and making mistakes!! Mistakes are the key to programming :) The mathematical formulas for projection and rotations are available on the net.

Hope this will help;

Regards

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls,
  9.   BGRAVirtualScreen, BGRABitmap, BGRABitmapTypes, LCLType;
  10.  
  11.  
  12. const
  13.   CubeSize = 600;
  14.  
  15. type
  16.   TVec3 = record
  17.     x, y, z: Single;
  18.   end;
  19.  
  20.  
  21.   { TForm1 }
  22.  
  23.   TForm1 = class(TForm)
  24.     BGRAVirtualScreen1: TBGRAVirtualScreen;
  25.     Timer1: TTimer;
  26.     procedure BGRAVirtualScreen1Redraw(Sender: TObject; Bitmap: TBGRABitmap);
  27.     procedure FormCreate(Sender: TObject);
  28.     procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  29.     procedure Timer1Timer(Sender: TObject);
  30.   private
  31.  
  32.   public
  33.  
  34.   end;
  35.  
  36. var
  37.   Form1: TForm1;
  38.  
  39.  AngleX, AngleY, AngleZ: Single;
  40.   CubePoints: array[0..7] of TVec3 = (
  41.     (x:-1; y:-1; z:-1), (x:1;  y:-1; z:-1),
  42.     (x:1; y:1; z:-1),   (x:-1; y:1;  z:-1),
  43.     (x:-1; y:-1; z:1),  (x:1;  y:-1; z:1),
  44.     (x:1; y:1; z:1),    (x:-1; y:1;  z:1)
  45.   );
  46.  
  47. implementation
  48.  
  49. {$R *.lfm}
  50.  
  51. procedure DrawCube(Bitmap: TBGRABitmap; AngleX, AngleY, AngleZ: Single);
  52. const
  53.   Edges: array[0..11, 0..1] of Integer = (
  54.     (0,1), (1,2), (2,3), (3,0),
  55.     (4,5), (5,6), (6,7), (7,4),
  56.     (0,4), (1,5), (2,6), (3,7)
  57.   );
  58. var
  59.   i: Integer;
  60.   Rotated: array[0..7] of TVec3;
  61.   Projected: array[0..7] of TPoint;
  62.   cx, cy: Integer;
  63.   x1, y1, z1: Single;
  64.   p1, p2: TPoint;
  65.  
  66. begin
  67.   cx := Bitmap.Width div 2;
  68.   cy := Bitmap.Height div 2;
  69.  
  70.   for i := 0 to 7 do
  71.   begin
  72.     x1 := CubePoints[i].x;
  73.     y1 := CubePoints[i].y;
  74.     z1 := CubePoints[i].z;
  75.  
  76.     // Rotation X
  77.     Rotated[i].y := y1 * cos(AngleX) - z1 * sin(AngleX);
  78.     Rotated[i].z := y1 * sin(AngleX) + z1 * cos(AngleX);
  79.     y1 := Rotated[i].y;
  80.     z1 := Rotated[i].z;
  81.  
  82.     // Rotation Y
  83.        Rotated[i].x := x1 * cos(AngleY) + z1 * sin(AngleY);
  84.     Rotated[i].z := -x1 * sin(AngleY) + z1 * cos(AngleY);
  85.     x1 := Rotated[i].x;
  86.     z1 := Rotated[i].z;
  87.  
  88.     // Rotation Z
  89.     Rotated[i].x := x1 * cos(AngleZ) - y1 * sin(AngleZ);
  90.     Rotated[i].y := x1 * sin(AngleZ) + y1 * cos(AngleZ);
  91.  
  92.     // Projection
  93.     Projected[i].X := Round(cx + (Rotated[i].x * CubeSize) / (Rotated[i].z + 5));
  94.     Projected[i].Y := Round(cy + (Rotated[i].y * CubeSize) / (Rotated[i].z + 5));
  95.   end;
  96.  
  97.   for i := 0 to 11 do
  98.   begin
  99.     p1 := Projected[Edges[i][0]];
  100.     p2 := Projected[Edges[i][1]];
  101.    // Bitmap.DrawLineAntialias(p1.X, p1.Y, p2.X, p2.Y, BGRA(255, 255, 255), 6);
  102.     Bitmap.DrawLine(p1.X, p1.Y, p2.X, p2.Y, BGRA(255, 255, 255),false);
  103.     Bitmap.DrawLine(4+p2.X, 4+p2.Y, p1.X+4, p1.Y+4, BGRA(255, 0, 0),false);
  104.   end;
  105. end;
  106.  
  107. { TForm1 }
  108.  
  109. procedure TForm1.FormCreate(Sender: TObject);
  110. begin
  111.   Timer1.Enabled := True;
  112.   Timer1.Interval := 15*2; // Amiga500 68000 vector rotation speed in 1986 !
  113. end;
  114.  
  115. procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState
  116.   );
  117. begin
  118.   case Key of
  119.     VK_LEFT:  AngleY -= 0.03;
  120.     VK_RIGHT: AngleY += 0.03;
  121.     VK_UP:    AngleX -= 0.03;
  122.     VK_DOWN:  AngleX += 0.03;
  123.   end;
  124.   BGRAVirtualScreen1.RedrawBitmap;
  125.  
  126. end;
  127.  
  128. procedure TForm1.BGRAVirtualScreen1Redraw(Sender: TObject; Bitmap: TBGRABitmap);
  129. begin
  130.   DrawCube(Bitmap, AngleX, AngleY, AngleZ);
  131. end;
  132.  
  133. procedure TForm1.Timer1Timer(Sender: TObject);
  134. begin
  135.   //AngleX += 0.020;
  136.   //AngleY += 0.030;
  137.   AngleZ += 0.030;
  138.   BGRAVirtualScreen1.RedrawBitmap;
  139. end;
  140.  
  141. end.
  142.  






« Last Edit: April 10, 2025, 07:20:26 pm by Gigatron »
Trip to Europe...  20 days

TBMan

  • Sr. Member
  • ****
  • Posts: 281
Re: 3D Rotating Cube
« Reply #10 on: April 10, 2025, 07:23:52 pm »
Thank you.
I love programming.

Some things I've done using PTCgraph:

NFL Retro Football (almost finished):
https://www.youtube.com/watch?v=78mTtsd7ppk


Solitaire games:
https://www.youtube.com/watch?v=zmtxI7FdWuQ&list=PLa4BPpFl34iVhFwX1JZwVm3vE5ay_i3R2

 

TinyPortal © 2005-2018