Using Hinge Joints for Wheels

This example uses Newton custom hinge joints. It is based on our Box2D Smart Pascal chassis that you can view in motion here. We leave you to experiment with ramps. For success with the steps below, you need to have installed GLScene and GLSceneLCL_NGD.

It is handy to give the ground a height of 1 and Position.Y of -1. This brings the top of the ground to Y = -0.5, and means that a cube with default side length 1 or a cylinder of default radius 0.5 (added at the origin by default) will be resting on the ground.

  1. Start a new form-based application in Lazarus. Save all, saving the project as Wheels and the unit as uWheels.
  2. Copy newton.dll and dJointLibrary.dll from GLSceneLCL\external to the project folder.
  3. Select menu item Project > Project Inspector... and add GLSceneLCL_NGD as a new requirement.
  4. Add to the form from the GLScene tab a GLScene, GLSceneViewer, GLCadencer and GLNGDManager.
  5. Drag the bottom right corner of the form and GLSceneViewer to give the scene a reasonable size.
  6. Double click on the GLScene1 icon to obtain the GLScene Editor. Click on Scene objects then on the top right icon to add a camera. Add also to Scene objects two cubes, four cylinders and a light source.
  7. Use the Object Inspector to change the names of the cubes to Ground and Chassis. Change the names of the cylinders to RearLeft, RearRight, FrontLeft and FrontRight.
  8. Use the Object Inspector to change the properties of Ground. Change its CubeDepth and CubeWidth to 20 and its Position.Y to -1. Click on the Behaviours ellipsis, then on the + icon of the pop-up and add a NGD Static behaviour. Select the created behaviour and select GLNGDManager1 as its manager.
  9. Use the Object Inspector to change the properties of Chassis. Change its CubeDepth 1.5, CubeHeight to 0.4 and CubeWidth to 2.5. Change its Position.X to 0.7. Click on the Behaviours ellipsis, click on the + icon of the pop-up and add a NGD Dynamic behaviour. Select the created behaviour and select GLNGDManager1 as its manager.
  10. Use the Object Inspector to change the properties of the cylinders. For each, change its Height property to 0.2 and its PitchAngle to 90. Click on the Behaviours ellipsis, click on the + icon of the pop-up and add a NGD Dynamic behaviour. Select the created behaviour and select GLNGDManager1 as its manager and Torque.Z to -0.5 (for a four-wheel drive). Change Position.X for the front wheels to 1.4, Position.Z for the left wheels to -1 and for the front wheels to 1.
  11. Use the Object Inspector to add four joints. Select nj_CustomHinge for the JointType of each and set the MaxAngle for CustomHingeOptions to 720. Select Chassis for the ParentObject of each and a different cylinder for the ChildObject of each. Set the CustomHingeOption PinDirection.Z of each to 1. Set the CustomHingeOption PivotPoints of each joint to be the same as the Position of the ChildObject cylinder, so that, for example, for the joint to the left rear wheel PivotPoint.X and PivotPoint.Y are 0 and PivotPoint.Z is -1.
  12. Use the Object Inspector to change Y and Z coordinates of the camera's position to 3 and 15, respectively. Change the X, Y, and Z coordinates of the light source position to -10, 5 and 2, respectively.
  13. Set the Camera property of the GLSceneViewer1 to GLCamera1 and the Scene property of GLCadencer1 to GLScene1.
    Our text view of the form is now as follows
    object Form1: TForm1
      Left = 607
      Height = 521
      Top = 182
      Width = 721
      Caption = 'Form1'
      ClientHeight = 521
      ClientWidth = 721
      OnCreate = FormCreate
      LCLVersion = '1.6.0.4'
      object GLSceneViewer1: TGLSceneViewer
        Left = 32
        Height = 402
        Top = 112
        Width = 644
        Camera = GLCamera1
        FieldOfView = 152.06169128418
        TabOrder = 0
      end
      object GLScene1: TGLScene
        left = 40
        top = 23
        object GLCamera1: TGLCamera
          TagFloat = 0
          DepthOfView = 100
          FocalLength = 50
          TargetObject = Ground
          Position.Coordinates = {
            0000000000004040000070410000803F
          }
        end
        object Ground: TGLCube
          TagFloat = 0
          PitchAngle = 0
          Position.Coordinates = {
            00000000000080BF000000000000803F
          }
          RollAngle = 0
          TurnAngle = 0
          BehavioursData = {
            0458434F4C02010201060C54474C4E47445374617469630200060A4E47442053
            746174696302000201060D474C4E47444D616E61676572310800090500000000
            000AD7A3F83F0600
          }
          CubeSize = {
            0000A0410000803F0000A041
          }
        end
        object GLLightSource1: TGLLightSource
          TagFloat = 0
          ConstAttenuation = 1
          LinearAttenuation = 0
          QuadraticAttenuation = 0
          Position.Coordinates = {
            000020C10000A040000000400000803F
          }
          SpotCutOff = 180
          SpotExponent = 0
        end
        object RearLeft: TGLCylinder
          TagFloat = 0
          Direction.Coordinates = {
            000000000000803F0000000000000000
          }
          PitchAngle = 90
          Position.Coordinates = {
            0000000000000000000080BF0000803F
          }
          RollAngle = 0
          TurnAngle = 0
          Up.Coordinates = {
            0000000000000000FFFF7FBF00000000
          }
          BottomRadius = 0.5
          Height = 0.200000002980232
          TopRadius = 0.5
          BehavioursData = {
            0458434F4C02010201060D54474C4E474444796E616D69630200060B4E474420
            44796E616D696302000201060D474C4E47444D616E6167657231080009050000
            0000000AD7A3F83F0600020109050000000000CDCCCCFB3F0500000000000000
            80FF3F0905000000000000000000000200080200090000000000000000000000
            BF000000000200090000000000000000000000000000803F020008
          }
        end
        object Chassis: TGLCube
          TagFloat = 0
          PitchAngle = 0
          Position.Coordinates = {
            3333333F00000000000000000000803F
          }
          RollAngle = 0
          TurnAngle = 0
          BehavioursData = {
            0458434F4C02010201060D54474C4E474444796E616D69630200060B4E474420
            44796E616D696302000201060D474C4E47444D616E6167657231080009050000
            0000000AD7A3F83F0600020109050000000000CDCCCCFB3F0500000000000000
            80FF3F0905000000000000000000000200080200080200090000000000000000
            000000000000803F020008
          }
          CubeSize = {
            00002040CDCCCC3E0000C03F
          }
        end
        object RearRight: TGLCylinder
          TagFloat = 0
          Direction.Coordinates = {
            000000000000803F0000000000000000
          }
          PitchAngle = 90
          Position.Coordinates = {
            00000000000000000000803F0000803F
          }
          RollAngle = 0
          TurnAngle = 0
          Up.Coordinates = {
            0000000000000000000080BF00000000
          }
          BottomRadius = 0.5
          Height = 0.200000002980232
          TopRadius = 0.5
          BehavioursData = {
            0458434F4C02010201060D54474C4E474444796E616D69630200060B4E474420
            44796E616D696302000201060D474C4E47444D616E6167657231080009050000
            0000000AD7A3F83F0600020109050000000000CDCCCCFB3F0500000000000000
            80FF3F0905000000000000000000000200080200090000000000000000000000
            BF000000000200090000000000000000000000000000803F020008
          }
        end
        object FrontLeft: TGLCylinder
          TagFloat = 0
          Direction.Coordinates = {
            000000000000803F0000000000000000
          }
          PitchAngle = 90
          Position.Coordinates = {
            3333B33F00000000000080BF0000803F
          }
          RollAngle = 0
          TurnAngle = 0
          Up.Coordinates = {
            0000000000000000000080BF00000000
          }
          BottomRadius = 0.5
          Height = 0.200000002980232
          TopRadius = 0.5
          BehavioursData = {
            0458434F4C02010201060D54474C4E474444796E616D69630200060B4E474444
            796E616D69633302000201060D474C4E47444D616E6167657231080009050000
            0000000AD7A3F83F0600020109050000000000CDCCCCFB3F0500000000000000
            80FF3F0905000000000000000000000200080200090000000000000000000000
            BF000000000200090000000000000000000000000000803F020008
          }
        end
        object FrontRight: TGLCylinder
          TagFloat = 0
          Direction.Coordinates = {
            000000000000803F0000000000000000
          }
          PitchAngle = 90
          Position.Coordinates = {
            3333B33F000000000000803F0000803F
          }
          RollAngle = 0
          TurnAngle = 0
          Up.Coordinates = {
            0000000000000000000080BF00000000
          }
          BottomRadius = 0.5
          Height = 0.200000002980232
          TopRadius = 0.5
          BehavioursData = {
            0458434F4C02010201060D54474C4E474444796E616D69630200060B4E474444
            796E616D69633402000201060D474C4E47444D616E6167657231080009050000
            0000000AD7A3F83F0600020109050000000000CDCCCCFB3F0500000000000000
            80FF3F0905000000000000000000000200080200090000000000000000000000
            BF000000000200090000000000000000000000000000803F020008
          }
        end
      end
      object GLCadencer1: TGLCadencer
        Scene = GLScene1
        MaxDeltaTime = 0
        MinDeltaTime = 0
        FixedDeltaTime = 0
        OnProgress = GLCadencer1Progress
        left = 96
        top = 23
      end
      object GLNGDManager1: TGLNGDManager
        NewtonSurfaceItem = <>
        NewtonSurfacePair = <>
        NewtonJoint = <    
          item
            CustomHingeOptions.PivotPoint.Coordinates = {
              0000000000000000000080BF0000803F
            }
            CustomHingeOptions.PinDirection.Coordinates = {
              00000000000000000000803F00000000
            }
            CustomHingeOptions.MinAngle = 0
            CustomHingeOptions.MaxAngle = 720
            JointType = nj_CustomHinge
            ParentObject = Chassis
            ChildObject = RearLeft
          end    
          item
            CustomHingeOptions.PivotPoint.Coordinates = {
              00000000000000000000803F0000803F
            }
            CustomHingeOptions.PinDirection.Coordinates = {
              00000000000000000000803F00000000
            }
            CustomHingeOptions.MinAngle = 0
            CustomHingeOptions.MaxAngle = 720
            JointType = nj_CustomHinge
            ParentObject = Chassis
            ChildObject = RearRight
          end    
          item
            CustomHingeOptions.PivotPoint.Coordinates = {
              3333B33F00000000000080BF0000803F
            }
            CustomHingeOptions.PinDirection.Coordinates = {
              00000000000000000000803F00000000
            }
            CustomHingeOptions.MinAngle = 0
            CustomHingeOptions.MaxAngle = 720
            JointType = nj_CustomHinge
            ParentObject = Chassis
            ChildObject = FrontLeft
          end    
          item
            CustomHingeOptions.PivotPoint.Coordinates = {
              3333B33F000000000000803F0000803F
            }
            CustomHingeOptions.PinDirection.Coordinates = {
              00000000000000000000803F00000000
            }
            CustomHingeOptions.MinAngle = 0
            CustomHingeOptions.MaxAngle = 720
            JointType = nj_CustomHinge
            ParentObject = Chassis
            ChildObject = FrontRight
          end>
        left = 152
        top = 23
      end
    end    
    
  14. Remove the redundant GLBaseClasses from the uses clause.
  15. Double click in the OnProgress event of GLCadencer1 and paste into the created procedure this line:
      GLNGDManager1.Step(deltaTime);
    
  16. Execute the application.
    When executing the application, you may receive an error message that WGL_ARB_pbuffer support is required. Check the box with caption Ignore this exception type and press the button to continue. If a wheel buckles, check that its Direction.Y is 1 and Direction.X and Direction.Z are 0. Up.Z should be -1 and Up.X and Up.Y should be 0. The joint type must be nj_CustomHinge and you must have set the properties for CustomHingeOptions (and not, for example HingeOptions).
    The chassis should move to the right for two revolutions of each wheel.

You can provide a climbable ramp with a cube that has a NGD static behaviour, CubeDepth = 4, CubeHeight = 0.1, CubeWidth = 2, RollAngle = 8, Position.X = 4, Position.Y = -0.5 and Position.Z = 8.

You can make the chassis turn left by applying a force to the right wheel. This can be done in the Object Inspector or in code with a line such as TGLNGDDynamic(FrontRight.Behaviours[0]).Force.X := 5;

Programming - a skill for life!

How to use Newton joints in GLScene