Height Field Collision Issue

Report any bugs here and we'll post fixes

Moderators: Sascha Willems, Thomas

Re: Height Field Collision Issue

Postby Esharc » Wed Jan 26, 2022 5:49 am

Yes, if the height field is large enough, then the ai vehicle does not get those bumps anymore. So it does seem to be a viable solution to use the procedural mesh.

The trace message that I got:
Fix compound contact for pair: ndShapeCompound ndShapeStaticProceduralMesh

But I was mistaken, the assert actually happens in ndContactSolver::CompoundContactsDiscrete() and not ndContactSolver::CalculateContactsDiscrete(). So I just moved the trace message to that function to get the pair that needs to be implemented.
Esharc
 
Posts: 120
Joined: Tue Jan 10, 2017 5:23 am
Location: South Africa

Re: Height Field Collision Issue

Postby Julio Jerez » Wed Jan 26, 2022 6:45 am

Ah good in both counts.

If you tell me where you put the assert, a can make the correction.

It should be eassy because procedural shape are just static meshes, so it is probably on filter check that need to be added before it gets to that point.

On the ai drive on a mesh, it sound like you have a solution.

What I suggest, is that you look at the way the height field shape does it.

Copy that code and use it as the start point for your system.

The one on the sdk is complex because has to deal with different styles of triangulation of the hight field but you do not has to follow that.

Only last thing, in the video I saw you use a star shape style of triangulation, that works, but is is not better than just using a regular like a triangle strip.

Here is one justification.
Say you want to approximate a real rolling terrain with a height field.
Them you want to measure how good the approximation it.
One way to do it is by summing the square distance of a bunch of points over the entire surface and calculate media and the deviation.

Obviously at every hiehtfiled the distance is zero, but if you take points at the center of each triangle then you will find that the points that fall in triangles who share a star edge are closer to the real surface therefore the error square will be much smaller.
But the points in triangles that do not share that vertex are farther away and the square error will be dominated by those.

While if the triangulation was a strip, the error will be somewhere in between, and the square error will be much smaller.
Basically the error distribution is more even using a regular strip than using an alternate fan.
Julio Jerez
Moderator
Moderator
 
Posts: 12249
Joined: Sun Sep 14, 2003 2:18 pm
Location: Los Angeles

Re: Height Field Collision Issue

Postby Esharc » Wed Jan 26, 2022 7:02 am

The assert happens in ndContactSolver::CompoundContactsDiscrete() at line 2614 and the trace message that is printed is "Fix compound contact for pair: ndShapeCompound ndShapeStaticProceduralMesh"
Esharc
 
Posts: 120
Joined: Tue Jan 10, 2017 5:23 am
Location: South Africa

Re: Height Field Collision Issue

Postby Julio Jerez » Wed Jan 26, 2022 12:42 pm

ah yes I now see it, I am fixing it.

on the procedural mesh, I set the compound demo to test the procedural mesh.
you can look at file.

....\applications\ndSandbox\toolbox\ndMakeProceduralStaticMap.cpp
line 186

the code conditionally generates two kinds of grids.
Code: Select all
ndBodyKinematic* BuildProceduralMap(ndDemoEntityManager* const scene, ndInt32 grids, ndFloat32 gridSize, ndFloat32 perturbation)
{
#if 1


make a grid and add all the face to a collision tree.

if the #if is set to zero, them generate a virtual grid, no data, just a function that makes the grid

if you sync and you set that define to 0 them you can look at function

Code: Select all
   virtual void GetCollidingFaces(const ndVector& minBox, const ndVector& maxBox, ndArray<ndVector>& vertex, ndArray<ndInt32>& faceList, ndArray<ndInt32>& faceMaterial, ndArray<ndInt32>& indexListList) const


on the same file, and that will give you an idea for how to implement your callback.
Julio Jerez
Moderator
Moderator
 
Posts: 12249
Joined: Sun Sep 14, 2003 2:18 pm
Location: Los Angeles

Re: Height Field Collision Issue

Postby Julio Jerez » Wed Jan 26, 2022 1:50 pm

ok that drive method is in now.

if you sync and you check compound demo, then you will have a sample of how to resolve the problem.

you should implement these functions as a minimum.
Code: Select all
   virtual void DebugShape(const ndMatrix& matrix, ndShapeDebugNotify& debugCallback) const;
   virtual ndFloat32 RayCast(ndRayCastNotify& callback, const ndVector& localP0, const ndVector& localP1, ndFloat32 maxT, const ndBody* const body, ndContactPoint& contactOut) const;
   virtual void GetCollidingFaces(ndPolygonMeshDesc* const data) const;
   D_COLLISION_API void Save(const ndLoadSaveBase::ndSaveDescriptor& desc) const;


if you want save and load serialization, then you must also add
D_CLASS_REFLECTION(ndRegularProceduralGrid); and implement
Code: Select all
ndShapeStaticProceduralMesh(const ndLoadSaveBase::ndLoadDescriptor& desc);
void Save(const ndLoadSaveBase::ndSaveDescriptor& desc) const;


and that will work with the engine as any other first-class shape.
Julio Jerez
Moderator
Moderator
 
Posts: 12249
Joined: Sun Sep 14, 2003 2:18 pm
Location: Los Angeles

Re: Height Field Collision Issue

Postby Esharc » Thu Jan 27, 2022 9:16 am

I unfortunately have some bad news. I have added the static procedural mesh as you suggested, and used the same code as in your example for the GetCollidingFaces, and the issue still happens with the vehicle popping up in the air.

I will try and recreate the issue in the sandbox application. Hopefully it is reproducible.
Esharc
 
Posts: 120
Joined: Tue Jan 10, 2017 5:23 am
Location: South Africa

Re: Height Field Collision Issue

Postby Julio Jerez » Thu Jan 27, 2022 11:37 am

Ok,
If the first test using a lager grid hightfiekd passes, and this on fail.
The there most be a bug in the way the edge manifold is constructed for procedural mesh.

My guess is that it is still using the legacy method that was used in 3.xx.

Later I will compare the grids from a hight field and a procedural height field and if they are different, I made the changes.
I am almost positive that is the problem.
Julio Jerez
Moderator
Moderator
 
Posts: 12249
Joined: Sun Sep 14, 2003 2:18 pm
Location: Los Angeles

Re: Height Field Collision Issue

Postby Julio Jerez » Thu Jan 27, 2022 1:47 pm

yes, that's the problem. you do not have to do anything.
I will fix it late today. and you just need to sync.

Edit:
It should be fix now.
Julio Jerez
Moderator
Moderator
 
Posts: 12249
Joined: Sun Sep 14, 2003 2:18 pm
Location: Los Angeles

Re: Height Field Collision Issue

Postby Esharc » Fri Jan 28, 2022 3:05 am

Hi Julio,

It does not seem to be fixed.
https://youtu.be/6Y-tGygBqCU
https://youtu.be/AVTiGLBU-dk

In the one video I have the procedural mesh grid size set to 2 and the other one set to 8. I have also slowed the AI vehicle down so that the force that is experienced when hitting the bump is not so violent. It seems that it happens less when the grid size is increased. There is currently no height data being read in, so essentially it is just a flat plane that the vehicle is moving on. The debug visuals uses the same GetCollidingFaces() function call to draw the grid.

I am going to try and recreate it in the Newton Sandbox today.
Esharc
 
Posts: 120
Joined: Tue Jan 10, 2017 5:23 am
Location: South Africa

Re: Height Field Collision Issue

Postby Julio Jerez » Fri Jan 28, 2022 8:00 am

I am assuming you are passing an index list in the callback where al interior edges are part of the same minfold..
By this I mean when the car reach the border of a tile, you are not making two vertex list, and then making the index list but the triangles at the edge of two different tiles do not share the same edge. Because it seems something like that is happening.

Ok try recreating in the sand box, there had to be something else we are missing.
Julio Jerez
Moderator
Moderator
 
Posts: 12249
Joined: Sun Sep 14, 2003 2:18 pm
Location: Los Angeles

Re: Height Field Collision Issue

Postby Esharc » Mon Jan 31, 2022 9:57 am

Hi Julio,

Yes, the code for the GetCollidingFaces is the same in my implementation of the ndShapeStaticProceduralMesh as what you have in the ndRegularProceduralGrid.

Sorry for the delay in response. I tried recreating the issue in the Sandbox application, but it worked perfectly there. So I then decided to simplify the issue on my side, and make the exact same test as I have in the Sandbox as our unit tests for the engine. And I got the same issue there, the only difference now being the shape of the AI vehicle.

In you Sandbox I am using a ndShapeBox, and in my physics engine test I am using a ndShapeCompound shape. I then decided to try it with a ndShapeBox, and it worked fine on my side. So I now want to attempt to get the exact shape into the Sandbox and see if it happens there as well.

Here is the code that I have added to push the box around in the Sandbox
Code: Select all
/* Copyright (c) <2003-2021> <Newton Game Dynamics>
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely
*/

#include "ndSandboxStdafx.h"
#include "ndSkyBox.h"
#include "ndTargaToOpenGl.h"
#include "ndDemoMesh.h"
#include "ndDemoCamera.h"
#include "ndPhysicsUtils.h"
#include "ndPhysicsWorld.h"
#include "ndMakeStaticMap.h"
#include "ndCompoundScene.h"
#include "ndDemoEntityManager.h"
#include "ndDemoInstanceEntity.h"
#include "ndHeightFieldPrimitive.h"
#include "ndMakeProceduralStaticMap.h"

class CAiController : public ndModel
{
public:
   CAiController(ndDemoEntityManager* pScene, ndBodyDynamic* pBody)
      : m_pScene(pScene)
      , m_pAiBody(pBody)
      , m_dDesiredSpeed(4.1667)      // 15 kilometers per hour (9.3 miles per hour)
      , m_dCurrentSpeed(0.0)
      , m_dSpeedProportional(3000.0)
      , m_dIntegral(0.0)
      , m_dIntegralGain(3000.0)
      , m_dDerivativeGain(600.0)
      , m_dPreviousError(0.0)
      , m_dCombinedMaximumForce()
   {
      m_dCombinedMaximumForce = pBody->GetMassMatrix().m_w * ndFloat32(6.0);      // Mass * Maximum acceleration of 6m/s/s
   }

protected:
   virtual void Update(ndWorld* const, ndFloat32 timestep) override
   {
      if (m_pAiBody->GetPosition().m_y < 1.5f)
      {
         ndMatrix mMatrix(m_pAiBody->GetRotation(), ndVector(ndFloat32(0.0), ndFloat32(0.0), ndFloat32(0.0), ndFloat32(1.0)));
         ndVector vForward = mMatrix.TransformVector(ndVector(ndFloat32(0.0), ndFloat32(0.0), ndFloat32(1.0), ndFloat32(0.0)));
         ndVector vVelocity = m_pAiBody->GetVelocity();
         m_dCurrentSpeed = (vForward.DotProduct(vVelocity)).GetScalar();
         ndFloat32 dSpeedDifference = m_dDesiredSpeed - m_dCurrentSpeed;
         ndFloat32 dForce = _UpdatePIDForDriveForces(dSpeedDifference, timestep);
         dForce = dClamp(dForce, -m_dCombinedMaximumForce, m_dCombinedMaximumForce);
         ndVector vForce(ndFloat32(0.0), ndFloat32(0.0), dForce, ndFloat32(0.0));
         ndVector vOffset(ndFloat32(0.0), ndFloat32(0.0), ndFloat32(0.0), ndFloat32(1.0));
         ndVector vOffsetLS = mMatrix.TransformVector(vOffset);      // Offset in local space
         ndVector vForceLS = mMatrix.TransformVector(vForce);      // Force in local space
         m_pAiBody->SetForce(m_pAiBody->GetForce() + vForceLS);
         m_pAiBody->SetTorque(m_pAiBody->GetTorque() + (vOffsetLS.CrossProduct(vForceLS)));
         _ApplyLateralForces(timestep);
      }

      // Get the camera to follow the vehicle
      ndVector origin(m_pAiBody->GetPosition());
      ndQuaternion rot(dYawMatrix(180.0f * ndDegreeToRad));
      origin.m_x += 10.0f;
      m_pScene->SetCameraMatrix(rot, origin);
   }

private:
   ndFloat32 _UpdatePIDForDriveForces(ndFloat32 dError, ndFloat32 dTimestep)
   {
      ndFloat32 dProportional = m_dSpeedProportional * dError;
      m_dIntegral = m_dIntegral + m_dIntegralGain * dError * dTimestep;

      // Make sure our integral remains inside reasonable bounds relative to the maximum force we are supposed to use
      m_dIntegral = dClamp(m_dIntegral, -m_dCombinedMaximumForce, m_dCombinedMaximumForce);

      // Reset integral so that we don't overshoot stopping, and don't move slightly when our speed should be zero.
      if (abs(m_dDesiredSpeed) < 0.01 && abs(m_dCurrentSpeed) < 0.01)
         m_dIntegral = 0;

      ndFloat32 dDerivative = m_dDerivativeGain * (dError - m_dPreviousError) / dTimestep;
      ndFloat32 dOutput = m_dIntegral + dDerivative + dProportional;
      m_dPreviousError = dError;
      return dOutput;
   }
   void _ApplyLateralForces(ndFloat32 dTimestep)
   {
      ndMatrix mMatrix(m_pAiBody->GetRotation(), ndVector(ndFloat32(0.0), ndFloat32(0.0), ndFloat32(0.0), ndFloat32(1.0)));
      ndVector vForward = mMatrix.TransformVector(ndVector(ndFloat32(0.0), ndFloat32(0.0), ndFloat32(1.0), ndFloat32(0.0)));
      ndVector vUp = mMatrix.TransformVector(ndVector(ndFloat32(0.0), ndFloat32(1.0), ndFloat32(0.0), ndFloat32(0.0)));
      // Get some info about our current velocity
      ndVector vVelocity = m_pAiBody->GetVelocity();
      ndFloat32 dVelocity = vVelocity.DotProduct(vVelocity).GetScalar();
      // Work out lateral force to stop sliding (all in world space)
      ndVector vVelocitySideways = vVelocity - vForward.Scale(vVelocity.DotProduct(vForward).GetScalar()) - vUp.Scale(vVelocity.DotProduct(vUp).GetScalar());
      ndVector vVelocityUp = vVelocity - vForward.Scale(vVelocity.DotProduct(vForward).GetScalar()) - vVelocitySideways;
      ndVector vSidewaysMomentum = vVelocitySideways.Scale(m_pAiBody->GetMassMatrix().m_w);
      ndVector vVerticalMomentum = vVelocityUp.Scale(m_pAiBody->GetMassMatrix().m_w);
      m_pAiBody->ApplyImpulsePair(vSidewaysMomentum.Scale(-0.8), ndVector(0.0), dTimestep);      // 0.8 = momentum cancelling factor

      // Work out steering forces
      ndFloat32 dSteering = 0.0;               // Keep the vehicle travelling straight
      ndFloat32 dMaxTorque = 50000;
      ndFloat32 dDesiredAngularSpeedFactor = 1.0;
      ndFloat32 dDesiredAngularSpeed = dDesiredAngularSpeedFactor * dVelocity * dSteering / (ndPi);   // Steering is a value from -pi to pi

      if (m_dCurrentSpeed < 0)   // Flip desired angular speed for reversing
         dDesiredAngularSpeed *= -1;

      ndFloat32 dAngularSpeed = vUp.DotProduct(m_pAiBody->GetOmega()).GetScalar();            // Component of the angular velocity about the up vector
      ndFloat32 dAngularSpeedDifference = dDesiredAngularSpeed - dAngularSpeed;
      dAngularSpeedDifference = dClamp(dAngularSpeedDifference, -0.3, 0.3);
      dAngularSpeedDifference = dAngularSpeedDifference / 0.3;               // Normalise to between -1 and 1;
      ndFloat32 dTorque = dAngularSpeedDifference * dMaxTorque;
      ndVector vYAxisMoment = ndVector(ndFloat32(0.0), dTorque, ndFloat32(0.0), ndFloat32(0.0));                        // Vehicle space torque
      ndVector vWorldMoment = mMatrix.TransformVector(vYAxisMoment);               // Get the torque to world space
      m_pAiBody->SetTorque(m_pAiBody->GetTorque() + vWorldMoment);
   }

   ndDemoEntityManager* m_pScene;
   ndBodyDynamic* m_pAiBody;
   ndFloat32 m_dDesiredSpeed;
   ndFloat32 m_dCurrentSpeed;
   ndFloat32 m_dSpeedProportional;
   ndFloat32 m_dIntegral;
   ndFloat32 m_dIntegralGain;
   ndFloat32 m_dDerivativeGain;
   ndFloat32 m_dPreviousError;
   ndFloat32 m_dCombinedMaximumForce;
};

static ndBodyDynamic* AddRigidBody(ndDemoEntityManager* const scene, const ndMatrix& matrix, const ndShapeInstance& shape, ndDemoInstanceEntity* const rootEntity, ndFloat32 mass)
{
   ndBodyDynamic* const pBody = new ndBodyDynamic();
   ndDemoEntity* const pEntity = new ndDemoEntity(matrix, rootEntity);
   pBody->SetNotifyCallback(new ndDemoEntityNotify(scene, pEntity));
   pBody->SetMatrix(matrix);
   pBody->SetCollisionShape(shape);
   pBody->SetMassMatrix(mass, shape);
   CAiController* pController = new CAiController(scene, pBody);
   

   ndWorld* const world = scene->GetWorld();
   world->AddModel(pController);
   world->AddBody(pBody);
   return pBody;
}

static void AddAiVehicle(ndDemoEntityManager* const scene)
{
   ndShapeInstance simpleBoxInstance(new ndShapeBox(1.0, 0.5, 2.0));
   ndDemoMeshIntance* const boxGeometry = new ndDemoMeshIntance("AiVehicle", scene->GetShaderCache(), &simpleBoxInstance, "earthmap.tga", "earthmap.tga", "earthmap.tga");
   ndDemoInstanceEntity* const boxEntity = new ndDemoInstanceEntity(boxGeometry);
   scene->AddEntity(boxEntity);
   ndMatrix mBodyMatrix = dGetIdentityMatrix();
   mBodyMatrix.m_posit = ndVector(0.0f, 5.0f, 0.0f, 1.0f);
   AddRigidBody(scene, mBodyMatrix, simpleBoxInstance, boxEntity, 1000.0);
}

void ndBasicAiDemo(ndDemoEntityManager* const scene)
{
   BuildProceduralMap(scene, 500, 2.0f, 0.0f);
   AddAiVehicle(scene);
}
Esharc
 
Posts: 120
Joined: Tue Jan 10, 2017 5:23 am
Location: South Africa

Re: Height Field Collision Issue

Postby Julio Jerez » Mon Jan 31, 2022 11:30 am

Oh that reveal some hints as to what could be.
Julio Jerez
Moderator
Moderator
 
Posts: 12249
Joined: Sun Sep 14, 2003 2:18 pm
Location: Los Angeles

Re: Height Field Collision Issue

Postby Esharc » Tue Feb 01, 2022 4:23 am

I can now confirm that it is the shape that is causing the problem. Here is the test that I created using a compound shape

Code: Select all
/* Copyright (c) <2003-2021> <Newton Game Dynamics>
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely
*/

#include "ndSandboxStdafx.h"
#include "ndSkyBox.h"
#include "ndTargaToOpenGl.h"
#include "ndDemoMesh.h"
#include "ndDemoCamera.h"
#include "ndPhysicsUtils.h"
#include "ndPhysicsWorld.h"
#include "ndMakeStaticMap.h"
#include "ndCompoundScene.h"
#include "ndDemoEntityManager.h"
#include "ndDemoInstanceEntity.h"
#include "ndHeightFieldPrimitive.h"
#include "ndMakeProceduralStaticMap.h"

class CAiController : public ndModel
{
public:
   CAiController(ndDemoEntityManager* pScene, ndBodyDynamic* pBody)
      : m_pScene(pScene)
      , m_pAiBody(pBody)
      , m_dDesiredSpeed(4.1667)      // 15 kilometers per hour (9.3 miles per hour)
      , m_dCurrentSpeed(0.0)
      , m_dSpeedProportional(3000.0)
      , m_dIntegral(0.0)
      , m_dIntegralGain(3000.0)
      , m_dDerivativeGain(600.0)
      , m_dPreviousError(0.0)
      , m_dCombinedMaximumForce()
   {
      m_dCombinedMaximumForce = pBody->GetMassMatrix().m_w * ndFloat32(6.0);      // Mass * Maximum acceleration of 6m/s/s
   }

protected:
   virtual void Update(ndWorld* const, ndFloat32 timestep) override
   {
      if (m_pAiBody->GetPosition().m_y < 1.5f)
      {
         ndMatrix mMatrix(m_pAiBody->GetRotation(), ndVector(ndFloat32(0.0), ndFloat32(0.0), ndFloat32(0.0), ndFloat32(1.0)));
         ndVector vForward = mMatrix.TransformVector(ndVector(ndFloat32(0.0), ndFloat32(0.0), ndFloat32(1.0), ndFloat32(0.0)));
         ndVector vVelocity = m_pAiBody->GetVelocity();
         m_dCurrentSpeed = (vForward.DotProduct(vVelocity)).GetScalar();
         ndFloat32 dSpeedDifference = m_dDesiredSpeed - m_dCurrentSpeed;
         ndFloat32 dForce = _UpdatePIDForDriveForces(dSpeedDifference, timestep);
         dForce = dClamp(dForce, -m_dCombinedMaximumForce, m_dCombinedMaximumForce);
         ndVector vForce(ndFloat32(0.0), ndFloat32(0.0), dForce, ndFloat32(0.0));
         ndVector vOffset(ndFloat32(0.0), ndFloat32(0.0), ndFloat32(0.0), ndFloat32(1.0));
         ndVector vOffsetLS = mMatrix.TransformVector(vOffset);      // Offset in local space
         ndVector vForceLS = mMatrix.TransformVector(vForce);      // Force in local space
         m_pAiBody->SetForce(m_pAiBody->GetForce() + vForceLS);
         m_pAiBody->SetTorque(m_pAiBody->GetTorque() + (vOffsetLS.CrossProduct(vForceLS)));
         _ApplyLateralForces(timestep);
      }

      // Get the camera to follow the vehicle
      ndVector origin(m_pAiBody->GetPosition());
      ndQuaternion rot(dYawMatrix(180.0f * ndDegreeToRad));
      origin.m_x += 10.0f;
      m_pScene->SetCameraMatrix(rot, origin);
   }

private:
   ndFloat32 _UpdatePIDForDriveForces(ndFloat32 dError, ndFloat32 dTimestep)
   {
      ndFloat32 dProportional = m_dSpeedProportional * dError;
      m_dIntegral = m_dIntegral + m_dIntegralGain * dError * dTimestep;

      // Make sure our integral remains inside reasonable bounds relative to the maximum force we are supposed to use
      m_dIntegral = dClamp(m_dIntegral, -m_dCombinedMaximumForce, m_dCombinedMaximumForce);

      // Reset integral so that we don't overshoot stopping, and don't move slightly when our speed should be zero.
      if (abs(m_dDesiredSpeed) < 0.01 && abs(m_dCurrentSpeed) < 0.01)
         m_dIntegral = 0;

      ndFloat32 dDerivative = m_dDerivativeGain * (dError - m_dPreviousError) / dTimestep;
      ndFloat32 dOutput = m_dIntegral + dDerivative + dProportional;
      m_dPreviousError = dError;
      return dOutput;
   }
   void _ApplyLateralForces(ndFloat32 dTimestep)
   {
      ndMatrix mMatrix(m_pAiBody->GetRotation(), ndVector(ndFloat32(0.0), ndFloat32(0.0), ndFloat32(0.0), ndFloat32(1.0)));
      ndVector vForward = mMatrix.TransformVector(ndVector(ndFloat32(0.0), ndFloat32(0.0), ndFloat32(1.0), ndFloat32(0.0)));
      vForward.m_w = ndFloat32(0.0);
      ndVector vUp = mMatrix.TransformVector(ndVector(ndFloat32(0.0), ndFloat32(1.0), ndFloat32(0.0), ndFloat32(0.0)));
      vUp.m_w = ndFloat32(0.0);
      // Get some info about our current velocity
      ndVector vVelocity = m_pAiBody->GetVelocity();
      ndFloat32 dVelocity = vVelocity.DotProduct(vVelocity).GetScalar();
      // Work out lateral force to stop sliding (all in world space)
      ndVector vVelocitySideways = vVelocity - vForward.Scale(vVelocity.DotProduct(vForward).GetScalar()) - vUp.Scale(vVelocity.DotProduct(vUp).GetScalar());
      ndVector vVelocityUp = vVelocity - vForward.Scale(vVelocity.DotProduct(vForward).GetScalar()) - vVelocitySideways;
      ndVector vSidewaysMomentum = vVelocitySideways.Scale(m_pAiBody->GetMassMatrix().m_w);
      ndVector vVerticalMomentum = vVelocityUp.Scale(m_pAiBody->GetMassMatrix().m_w);
      m_pAiBody->ApplyImpulsePair(ndVector::m_negOne * vSidewaysMomentum * 0.8, ndVector(0.0), dTimestep);      // 0.8 = momentum cancelling factor
      m_pAiBody->ApplyImpulsePair(ndVector::m_negOne * vVerticalMomentum * 0.0, ndVector(0.0), dTimestep);

      // Work out steering forces
      ndFloat32 dSteering = 0.0;               // Keep the vehicle travelling straight
      ndFloat32 dMaxTorque = 50000;
      ndFloat32 dDesiredAngularSpeedFactor = 1.0;
      ndFloat32 dDesiredAngularSpeed = dDesiredAngularSpeedFactor * dVelocity * dSteering / (ndPi);   // Steering is a value from -pi to pi

      if (m_dCurrentSpeed < 0)   // Flip desired angular speed for reversing
         dDesiredAngularSpeed *= -1;

      ndFloat32 dAngularSpeed = vUp.DotProduct(m_pAiBody->GetOmega()).GetScalar();            // Component of the angular velocity about the up vector
      ndFloat32 dAngularSpeedDifference = dDesiredAngularSpeed - dAngularSpeed;
      dAngularSpeedDifference = dClamp(dAngularSpeedDifference, ndFloat32(-0.3), ndFloat32(0.3));
      dAngularSpeedDifference = dAngularSpeedDifference / ndFloat32(0.3);               // Normalise to between -1 and 1;
      ndFloat32 dTorque = dAngularSpeedDifference * dMaxTorque;
      ndVector vYAxisMoment = ndVector(ndFloat32(0.0), dTorque, ndFloat32(0.0), ndFloat32(0.0));                        // Vehicle space torque
      ndVector vWorldMoment = mMatrix.TransformVector(vYAxisMoment);               // Get the torque to world space
      m_pAiBody->SetTorque(m_pAiBody->GetTorque() + vWorldMoment);
   }

   ndDemoEntityManager* m_pScene;
   ndBodyDynamic* m_pAiBody;
   ndFloat32 m_dDesiredSpeed;
   ndFloat32 m_dCurrentSpeed;
   ndFloat32 m_dSpeedProportional;
   ndFloat32 m_dIntegral;
   ndFloat32 m_dIntegralGain;
   ndFloat32 m_dDerivativeGain;
   ndFloat32 m_dPreviousError;
   ndFloat32 m_dCombinedMaximumForce;
};

static ndBodyDynamic* AddRigidBody(ndDemoEntityManager* const scene, const ndMatrix& matrix, const ndShapeInstance& shape, ndDemoInstanceEntity* const rootEntity, ndFloat32 mass)
{
   ndBodyDynamic* const pBody = new ndBodyDynamic();
   ndDemoEntity* const pEntity = new ndDemoEntity(matrix, rootEntity);
   pBody->SetNotifyCallback(new ndDemoEntityNotify(scene, pEntity));
   pBody->SetMatrix(matrix);
   pBody->SetCollisionShape(shape);
   pBody->SetMassMatrix(mass, shape);
   CAiController* pController = new CAiController(scene, pBody);
   

   ndWorld* const world = scene->GetWorld();
   world->AddModel(pController);
   world->AddBody(pBody);
   return pBody;
}

ndShapeInstance CreateCompondCollision()
{
   ndFloat32 convexHullPoints[336] = { 2.64974, 0.903322, -0.766362, 2.64974, 0.903322, -0.0842047, 0.677557, 0.903322, 0.662591, -0.596817, 0.903322, 0.662591, -2.77764, 0.903321, 0.0424131,
                        -2.77764, 0.903321, -0.766362, -1.56248, 0.903322, -0.766362, -0.784835, 0.903322, -0.766362, 1.46171, 0.903322, -0.766362, 2.22016, 0.903322, -0.766362,
                        -2.77764, -0.918637, -0.766362, -2.77764, -0.918637, 0.0424131, -0.596817, -0.918637, 0.662591, 0.677557, -0.918636, 0.662591, 2.64974, -0.918636, -0.0842047,
                        -1.56248, -0.918637, -0.766362, -0.784834, -0.918637, -0.766362, 1.46171, -0.918636, -0.766362, 2.22016, -0.918636, -0.766362, 2.64974, -0.918636, -0.766362,
                        2.22016, -0.654416, -0.766362, 2.22016, -0.918636, -0.766362, 2.64974, -0.918636, -0.766362, 2.22016, 0.661676, -0.766362, 2.22016, 0.903322, -0.766362,
                        2.64974, 0.903322, -0.766362, -2.77764, 0.903321, -0.766362, -2.77764, 0.903321, 0.0424131, -2.77764, -0.918637, 0.0424131, -2.77764, -0.918637, -0.766362,
                        -2.77764, 0.903321, 0.0424131, -0.596817, 0.903322, 0.662591, -0.596817, -0.918637, 0.662591, -2.77764, -0.918637, 0.0424131, 2.64974, 0.903322, -0.0842047,
                        2.64974, 0.903322, -0.766362, 2.64974, -0.918636, -0.766362, 2.64974, -0.918636, -0.0842047, 0.677557, 0.903322, 0.662591, 2.64974, 0.903322, -0.0842047,
                        2.64974, -0.918636, -0.0842047, 0.677557, -0.918636, 0.662591, -2.77764, -0.918637, -0.766362, -1.56248, -0.918637, -0.766362, -1.56248, -0.654416, -0.766362,
                        -2.77764, 0.903321, -0.766362, -1.56248, 0.661676, -0.766362, -1.56248, 0.903322, -0.766362, -0.784834, -0.654416, -0.766362, -0.784835, 0.661676, -0.766362,
                        -0.784834, -0.918637, -0.766362, 1.46171, -0.918636, -0.766362,   1.46171, -0.654416, -0.766362, 1.46171, 0.661676, -0.766362, -0.784835, 0.903322, -0.766362,
                        1.46171, 0.903322, -0.766362, -0.784834, -0.654416, -0.766362, -1.56248, -0.654416, -0.766362, -1.17366, -0.654417, -1.15761, -1.56248, -0.654416, -0.766362,
                        -1.56248, -0.918637, -0.766362, -1.17366, -0.918637, -1.15761, -1.17366, -0.654417, -1.15761, -1.56248, -0.918637, -0.766362, -0.784834, -0.918637, -0.766362,
                        -1.17366, -0.918637, -1.15761, -0.784834, -0.918637, -0.766362, -0.784834, -0.654416, -0.766362, -1.17366, -0.654417, -1.15761, -1.17366, -0.918637, -1.15761,
                        -1.56248, 0.661676, -0.766362, -0.784835, 0.661676, -0.766362, -1.17366, 0.661676, -1.15761, -0.784835, 0.661676, -0.766362, -0.784835, 0.903322, -0.766362,
                        -1.17366, 0.903322, -1.15761, -1.17366, 0.661676, -1.15761, -0.784835, 0.903322, -0.766362, -1.56248, 0.903322, -0.766362, -1.17366, 0.903322, -1.15761,
                        -1.56248, 0.903322, -0.766362, -1.56248, 0.661676, -0.766362, -1.17366, 0.661676, -1.15761, -1.17366, 0.903322, -1.15761, 2.22016, -0.654416, -0.766362,
                        1.46171, -0.654416, -0.766362, 1.84093, -0.654416, -1.15761, 1.46171, -0.654416, -0.766362, 1.46171, -0.918636, -0.766362, 1.84093, -0.918636, -1.15761,
                        1.84093, -0.654416, -1.15761, 1.46171, -0.918636, -0.766362, 2.22016, -0.918636, -0.766362, 1.84093, -0.918636, -1.15761, 2.22016, -0.918636, -0.766362,
                        2.22016, -0.654416, -0.766362, 1.84093, -0.654416, -1.15761, 1.84093, -0.918636, -1.15761, 1.46171, 0.661676, -0.766362, 2.22016, 0.661676, -0.766362,
                        1.84093, 0.661676, -1.15761, 2.22016, 0.661676, -0.766362, 2.22016, 0.903322, -0.766362, 1.84093, 0.903322, -1.15761, 1.84093, 0.661676, -1.15761,
                        2.22016, 0.903322, -0.766362, 1.46171, 0.903322, -0.766362, 1.84093, 0.903322, -1.15761, 1.46171, 0.903322, -0.766362, 1.46171, 0.661676, -0.766362,
                        1.84093, 0.661676, -1.15761, 1.84093, 0.903322, -1.15761 };

   ndMatrix mParentInv = dGetIdentityMatrix();
   mParentInv.m_posit = ndVector(ndFloat32(0.0), ndFloat32(-1.0), ndFloat32(0.0), ndFloat32(1.0));
   ndMatrix mChildWorld = dGetIdentityMatrix();
   mChildWorld.m_front = ndVector(ndFloat32(0.0), ndFloat32(0.0), ndFloat32(1.0), ndFloat32(0.0));
   mChildWorld.m_up = ndVector(ndFloat32(1.0), ndFloat32(0.0), ndFloat32(0.0), ndFloat32(0.0));
   mChildWorld.m_right = ndVector(ndFloat32(0.0), ndFloat32(1.0), ndFloat32(0.0), ndFloat32(0.0));
   mChildWorld.m_posit = ndVector(ndFloat32(0.0), ndFloat32(1.158), ndFloat32(-0.508), ndFloat32(1.0));
   ndMatrix mChildLocal = mChildWorld * mParentInv;
   ndShapeInstance shapeInstance(new ndShapeCompound());
   shapeInstance.GetShape()->GetAsShapeCompound()->BeginAddRemove();
   ndShapeInstance childShapeInstance(new ndShapeConvexHull(336 / 3, sizeof(ndFloat32) * 3, 0.0, &convexHullPoints[0]));
   childShapeInstance.SetLocalMatrix(mChildLocal);
   shapeInstance.GetShape()->GetAsShapeCompound()->AddCollision(&childShapeInstance);
   shapeInstance.GetShape()->GetAsShapeCompound()->EndAddRemove();
   return shapeInstance;
}

ndShapeInstance CreateBoxCollision()
{
   return ndShapeInstance(new ndShapeBox(1.0, 0.5, 2.0));
}

static void AddAiVehicle(ndDemoEntityManager* const scene)
{
   //ndShapeInstance shapeInstance = CreateBoxCollision();
   ndShapeInstance shapeInstance = CreateCompondCollision();
   
   ndDemoMeshIntance* const aiGeometry = new ndDemoMeshIntance("AiVehicle", scene->GetShaderCache(), &shapeInstance, "earthmap.tga", "earthmap.tga", "earthmap.tga");
   ndDemoInstanceEntity* const aiEntity = new ndDemoInstanceEntity(aiGeometry);
   scene->AddEntity(aiEntity);
   ndMatrix mBodyMatrix = dGetIdentityMatrix();
   mBodyMatrix.m_posit = ndVector(0.0f, 5.0f, 0.0f, 1.0f);
   AddRigidBody(scene, mBodyMatrix, shapeInstance, aiEntity, 1000.0);
}

void ndBasicAiDemo(ndDemoEntityManager* const scene)
{
   BuildProceduralMap(scene, 120, 2.0f, 0.0f);
   AddAiVehicle(scene);
}


I am now going to try and add wheels to the AI vehicle and see how that works
Esharc
 
Posts: 120
Joined: Tue Jan 10, 2017 5:23 am
Location: South Africa

Re: Height Field Collision Issue

Postby Julio Jerez » Tue Feb 01, 2022 9:45 am

ok I paste that demo into the sand box so that we have the same common ground.
Julio Jerez
Moderator
Moderator
 
Posts: 12249
Joined: Sun Sep 14, 2003 2:18 pm
Location: Los Angeles

Re: Height Field Collision Issue

Postby Julio Jerez » Tue Feb 01, 2022 10:39 am

I see that there is one large compound move over ground, but it seem to have only one part.

I was expecting a compound with more than one part, but this seem to be one large hull that moves scraping the ground.

I will see is this happen when using convex hull instead, in the spirit to see if it is a bug or a consequence of the contact pruner.

however, to me having a box like that scraping the floor seem like a poor model for a vehicle.

the contact will be all over the place, I think a better representation would be a compound like this.
Untitled.png
Untitled.png (1.89 KiB) Viewed 9948 times


that will give you precise contact positions. and since you ai logic deal with contacts anywhere, them this should not affect your ai. and will fix the edge bump problem.

Also knowing when the contacts are, you can make post contact decisions.
but for that you would has to wrap the vehicle in a ndModel, and this will give you a call with all the data that you need before is goes to the solver. you can rectify contact, if there are in a bad location or deep penetration of bad normal.

but the first thing I would do is add those spheres as the contact collectors.
Julio Jerez
Moderator
Moderator
 
Posts: 12249
Joined: Sun Sep 14, 2003 2:18 pm
Location: Los Angeles

PreviousNext

Return to Bugs and Fixes

Who is online

Users browsing this forum: No registered users and 21 guests