include/vehicle2/PxVehicleParams.h

File members: include/vehicle2/PxVehicleParams.h

// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//  * Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
//  * Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
//  * Neither the name of NVIDIA CORPORATION nor the names of its
//    contributors may be used to endorse or promote products derived
//    from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Copyright (c) 2008-2024 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.

#pragma once
#include "foundation/PxFoundation.h"
#include "foundation/PxAssert.h"
#include "foundation/PxMemory.h"
#include "foundation/PxVec3.h"
#include "foundation/PxMat33.h"

#include "PxVehicleLimits.h"

class OmniPvdWriter;

#if !PX_DOXYGEN
namespace physx
{
class PxConvexMesh;
class PxScene;
namespace vehicle2
{
#endif

struct PxVehicleAxleDescription
{
    PX_FORCE_INLINE void setToDefault()
    {
        PxMemZero(this, sizeof(PxVehicleAxleDescription));
    }

    void addAxle(const PxU32 nbWheelsOnAxle, const PxU32* const wheelIdsOnAxle)
    {
        PX_ASSERT((nbWheels + nbWheelsOnAxle) < PxVehicleLimits::eMAX_NB_WHEELS);
        PX_ASSERT(nbAxles < PxVehicleLimits::eMAX_NB_AXLES);
        nbWheelsPerAxle[nbAxles] = nbWheelsOnAxle;
        axleToWheelIds[nbAxles] = nbWheels;
        for (PxU32 i = 0; i < nbWheelsOnAxle; i++)
        {
            wheelIdsInAxleOrder[nbWheels + i] = wheelIdsOnAxle[i];
        }
        nbWheels += nbWheelsOnAxle;
        nbAxles++;
    }

    PX_FORCE_INLINE PxU32 getNbAxles() const
    {
        return nbAxles;
    }

    PX_FORCE_INLINE PxU32 getNbWheelsOnAxle(const PxU32 i) const
    {
        return nbWheelsPerAxle[i];
    }

    PX_FORCE_INLINE PxU32 getWheelOnAxle(const PxU32 j, const PxU32 i) const
    {
        return wheelIdsInAxleOrder[axleToWheelIds[i] + j];
    }

    PX_FORCE_INLINE PxU32 getNbWheels() const
    {
        return nbWheels;
    }

    PX_FORCE_INLINE PxU32 getAxle(const PxU32 wheelId) const
    {
        for (PxU32 i = 0; i < getNbAxles(); i++)
        {
            for (PxU32 j = 0; j < getNbWheelsOnAxle(i); j++)
            {
                if (getWheelOnAxle(j, i) == wheelId)
                    return i;
            }
        }
        return 0xffffffff;
    }

    PX_FORCE_INLINE bool isValid() const
    {
        PX_CHECK_AND_RETURN_VAL(nbAxles > 0, "PxVehicleAxleDescription.nbAxles must be greater than zero", false);
        PX_CHECK_AND_RETURN_VAL(nbWheels > 0, "PxVehicleAxleDescription.nbWheels must be greater than zero", false);
        return true;
    }

    PxU32 nbAxles;
    PxU32 nbWheelsPerAxle[PxVehicleLimits::eMAX_NB_AXLES];
    PxU32 axleToWheelIds[PxVehicleLimits::eMAX_NB_AXLES];

    PxU32 wheelIdsInAxleOrder[PxVehicleLimits::eMAX_NB_WHEELS];
    PxU32 nbWheels;

    PX_COMPILE_TIME_ASSERT(PxVehicleLimits::eMAX_NB_AXLES == PxVehicleLimits::eMAX_NB_WHEELS);
    // It should be possible to support cases where each wheel is controlled individually and thus
    // having a wheel per axle for up to the max wheel count.
};

struct PxVehicleAxes
{
    enum Enum
    {
        ePosX = 0,
        eNegX,
        ePosY,
        eNegY,
        ePosZ,
        eNegZ,
        eMAX_NB_AXES
    };
};

struct PxVehicleFrame
{
    PxVehicleAxes::Enum lngAxis;
    PxVehicleAxes::Enum latAxis;
    PxVehicleAxes::Enum vrtAxis;

    PX_FORCE_INLINE void setToDefault()
    {
        lngAxis = PxVehicleAxes::ePosX;
        latAxis = PxVehicleAxes::ePosY;
        vrtAxis = PxVehicleAxes::ePosZ;
    }

    PX_FORCE_INLINE PxMat33 getFrame() const
    {
        const PxVec3 basisDirs[6] = { PxVec3(1,0,0), PxVec3(-1,0,0), PxVec3(0,1,0), PxVec3(0,-1,0), PxVec3(0,0,1), PxVec3(0,0,-1) };
        const PxMat33 mat33(basisDirs[lngAxis], basisDirs[latAxis], basisDirs[vrtAxis]);
        return mat33;
    }

    PX_FORCE_INLINE PxVec3 getLngAxis() const
    {
        const PxVec3 basisDirs[6] = { PxVec3(1,0,0), PxVec3(-1,0,0), PxVec3(0,1,0), PxVec3(0,-1,0), PxVec3(0,0,1), PxVec3(0,0,-1) };
        return basisDirs[lngAxis];
    }

    PX_FORCE_INLINE PxVec3 getLatAxis() const
    {
        const PxVec3 basisDirs[6] = { PxVec3(1,0,0), PxVec3(-1,0,0), PxVec3(0,1,0), PxVec3(0,-1,0), PxVec3(0,0,1), PxVec3(0,0,-1) };
        return basisDirs[latAxis];
    }

    PX_FORCE_INLINE PxVec3 getVrtAxis() const
    {
        const PxVec3 basisDirs[6] = { PxVec3(1,0,0), PxVec3(-1,0,0), PxVec3(0,1,0), PxVec3(0,-1,0), PxVec3(0,0,1), PxVec3(0,0,-1) };
        return basisDirs[vrtAxis];
    }

    PX_FORCE_INLINE bool isValid() const
    {
        PX_CHECK_AND_RETURN_VAL(lngAxis < PxVehicleAxes::eMAX_NB_AXES, "PxVehicleFrame.lngAxis is invalid", false);
        PX_CHECK_AND_RETURN_VAL(latAxis < PxVehicleAxes::eMAX_NB_AXES, "PxVehicleFrame.latAxis is invalid", false);
        PX_CHECK_AND_RETURN_VAL(vrtAxis < PxVehicleAxes::eMAX_NB_AXES, "PxVehicleFrame.vrtAxis is invalid", false);
        const PxMat33 frame = getFrame();
        const PxQuat quat(frame);
        PX_CHECK_AND_RETURN_VAL(quat.isFinite() && quat.isUnit() && quat.isSane(), "PxVehicleFrame is not a legal frame", false);
        return true;
    }
};

struct PxVehicleScale
{
    PxReal scale;

    PX_FORCE_INLINE void setToDefault()
    {
        scale = 1.0f;
    }

    PX_FORCE_INLINE bool isValid() const
    {
        PX_CHECK_AND_RETURN_VAL(scale > 0.0f, "PxVehicleScale.scale must be greater than zero", false);
        return true;
    }
};

template<typename T>
struct PxVehicleArrayData
{
    enum DataFormat
    {
        eARRAY_OF_STRUCTS = 0,
        eARRAY_OF_POINTERS
    };

    PX_FORCE_INLINE void setData(T* data)
    {
        arrayOfStructs = data;
        dataFormat = eARRAY_OF_STRUCTS;
    }

    PX_FORCE_INLINE void setData(T*const* data)
    {
        arrayOfPointers= data;
        dataFormat = eARRAY_OF_POINTERS;
    }

    PX_FORCE_INLINE PxVehicleArrayData()
    {
    }

    PX_FORCE_INLINE explicit PxVehicleArrayData(T* data)
    {
        setData(data);
    }

    PX_FORCE_INLINE explicit PxVehicleArrayData(T*const* data)
    {
        setData(data);
    }

    PX_FORCE_INLINE T& getData(PxU32 index)
    {
        if (dataFormat == eARRAY_OF_STRUCTS)
            return arrayOfStructs[index];
        else
            return *arrayOfPointers[index];
    }

    PX_FORCE_INLINE T& operator[](PxU32 index)
    {
        return getData(index);
    }

    PX_FORCE_INLINE const T& getData(PxU32 index) const
    {
        if (dataFormat == eARRAY_OF_STRUCTS)
            return arrayOfStructs[index];
        else
            return *arrayOfPointers[index];
    }

    PX_FORCE_INLINE const T& operator[](PxU32 index) const
    {
        return getData(index);
    }

    PX_FORCE_INLINE void setEmpty()
    {
        arrayOfStructs = NULL;
    }

    PX_FORCE_INLINE bool isEmpty() const
    {
        return (arrayOfStructs == NULL);
    }

    PX_FORCE_INLINE const PxVehicleArrayData<const T>& getConst() const
    {
        return reinterpret_cast<const PxVehicleArrayData<const T>&>(*this);
    }

    union
    {
        T* arrayOfStructs;
        T*const* arrayOfPointers;
    };
    PxU8 dataFormat;
};

template<typename T>
struct PxVehicleSizedArrayData : public PxVehicleArrayData<T>
{
    PX_FORCE_INLINE void setDataAndCount(T* data, const PxU32 count)
    {
        PxVehicleArrayData<T>::setData(data);
        size = count;
    }

    PX_FORCE_INLINE void setDataAndCount(T*const* data, const PxU32 count)
    {
        PxVehicleArrayData<T>::setData(data);
        size = count;
    }

    PX_FORCE_INLINE void setEmpty()
    {
        PxVehicleArrayData<T>::setEmpty();
        size = 0;
    }

    PX_FORCE_INLINE bool isEmpty() const
    {
        return ((size == 0) || PxVehicleArrayData<T>::isEmpty());
    }

    PxU32 size;
};

struct PxVehiclePhysXActorUpdateMode
{
    enum Enum
    {
        eAPPLY_VELOCITY = 0,
        eAPPLY_ACCELERATION
    };
};

struct PxVehicleTireSlipParams
{
    PxReal minLatSlipDenominator;

    PxReal minPassiveLongSlipDenominator;

    PxReal minActiveLongSlipDenominator;

    PX_FORCE_INLINE void setToDefault()
    {
        minLatSlipDenominator = 1.0f;
        minActiveLongSlipDenominator = 0.1f;
        minPassiveLongSlipDenominator = 4.0f;
    }

    PX_FORCE_INLINE PxVehicleTireSlipParams transformAndScale(
        const PxVehicleFrame& srcFrame, const PxVehicleFrame& trgFrame, const PxVehicleScale& srcScale, const PxVehicleScale& trgScale) const
    {
        PX_UNUSED(srcFrame);
        PX_UNUSED(trgFrame);
        PxVehicleTireSlipParams p = *this;
        const PxReal scaleRatio = trgScale.scale / srcScale.scale;
        p.minLatSlipDenominator *= scaleRatio;
        p.minPassiveLongSlipDenominator *= scaleRatio;
        p.minActiveLongSlipDenominator *= scaleRatio;
        return p;
    }

    PX_FORCE_INLINE bool isValid() const
    {
        PX_CHECK_AND_RETURN_VAL(minLatSlipDenominator > 0.0f, "PxVehicleTireSlipParams.minLatSlipDenominator must be greater than zero", false);
        PX_CHECK_AND_RETURN_VAL(minPassiveLongSlipDenominator > 0.0f, "PxVehicleTireSlipParams.minPassiveLongSlipDenominator must be greater than zero", false);
        PX_CHECK_AND_RETURN_VAL(minActiveLongSlipDenominator > 0.0f, "PxVehicleTireSlipParams.minActiveLongSlipDenominator must be greater than zero", false);
        return true;
    }
};

struct PxVehicleTireDirectionModes
{
    enum Enum
    {
        eLONGITUDINAL = 0,
        eLATERAL,
        eMAX_NB_PLANAR_DIRECTIONS
    };
};

struct PxVehicleTireAxisStickyParams
{
    PxReal thresholdSpeed;

    PxReal thresholdTime;

    PxReal damping;

    PX_FORCE_INLINE PxVehicleTireAxisStickyParams transformAndScale(
        const PxVehicleFrame& srcFrame, const PxVehicleFrame& trgFrame, const PxVehicleScale& srcScale, const PxVehicleScale& trgScale) const
    {
        PX_UNUSED(srcFrame);
        PX_UNUSED(trgFrame);
        PxVehicleTireAxisStickyParams p = *this;
        const PxReal scaleRatio = trgScale.scale / srcScale.scale;
        p.thresholdSpeed *= scaleRatio;
        return p;
    }

    PX_FORCE_INLINE bool isValid() const
    {
        PX_CHECK_AND_RETURN_VAL(thresholdSpeed >= 0.0f, "PxVehicleTireAxisStickyParams.thresholdSpeed must be greater than or equal to zero", false);
        PX_CHECK_AND_RETURN_VAL(thresholdTime >= 0.0f, "PxVehicleTireAxisStickyParams.thresholdTime must be greater than or equal to zero", false);
        PX_CHECK_AND_RETURN_VAL(damping >= 0.0f, "PxVehicleTireAxisStickyParams.damping must be greater than or equal to zero", false);
        return true;
    }
};

struct PxVehicleTireStickyParams
{
    PxVehicleTireAxisStickyParams stickyParams[PxVehicleTireDirectionModes::eMAX_NB_PLANAR_DIRECTIONS];

    PX_FORCE_INLINE void setToDefault()
    {
        stickyParams[PxVehicleTireDirectionModes::eLONGITUDINAL].thresholdSpeed = 0.2f;
        stickyParams[PxVehicleTireDirectionModes::eLONGITUDINAL].thresholdTime = 1.0f;
        stickyParams[PxVehicleTireDirectionModes::eLONGITUDINAL].damping = 1.0f;
        stickyParams[PxVehicleTireDirectionModes::eLATERAL].thresholdSpeed = 0.2f;
        stickyParams[PxVehicleTireDirectionModes::eLATERAL].thresholdTime = 1.0f;
        stickyParams[PxVehicleTireDirectionModes::eLATERAL].damping = 0.1f;
    }

    PX_FORCE_INLINE PxVehicleTireStickyParams transformAndScale(
        const PxVehicleFrame& srcFrame, const PxVehicleFrame& trgFrame, const PxVehicleScale& srcScale, const PxVehicleScale& trgScale) const
    {
        PxVehicleTireStickyParams p = *this;
        p.stickyParams[PxVehicleTireDirectionModes::eLONGITUDINAL] =
            stickyParams[PxVehicleTireDirectionModes::eLONGITUDINAL].transformAndScale(srcFrame, trgFrame, srcScale, trgScale);
        p.stickyParams[PxVehicleTireDirectionModes::eLATERAL] =
            stickyParams[PxVehicleTireDirectionModes::eLATERAL].transformAndScale(srcFrame, trgFrame, srcScale, trgScale);
        return p;
    }

    PX_FORCE_INLINE bool isValid() const
    {
        if (!stickyParams[PxVehicleTireDirectionModes::eLONGITUDINAL].isValid())
            return false;
        if (!stickyParams[PxVehicleTireDirectionModes::eLATERAL].isValid())
            return false;
        return true;
    }
};

struct PxVehicleSimulationContextType
{
    enum Enum
    {
        eDEFAULT,
        ePHYSX
    };
};

struct PxVehiclePvdContext
{
public:
    PX_FORCE_INLINE void setToDefault()
    {
        attributeHandles = NULL;
        writer = NULL;
    }

    const struct PxVehiclePvdAttributeHandles* attributeHandles;

    OmniPvdWriter* writer;
};

struct PxVehicleSimulationContext
{
    PxVehicleSimulationContext()
        : type(PxVehicleSimulationContextType::eDEFAULT)
    {}

    PxVec3 gravity;

    PxVehicleFrame frame;
    PxVehicleScale scale;

    //Tire
    PxVehicleTireSlipParams tireSlipParams;
    PxVehicleTireStickyParams tireStickyParams;

    PxReal thresholdForwardSpeedForWheelAngleIntegration;

    PxVehiclePvdContext pvdContext;

protected:
    PxVehicleSimulationContextType::Enum type;

public:
    PX_FORCE_INLINE PxVehicleSimulationContextType::Enum getType() const { return type; }

    PX_FORCE_INLINE void setToDefault()
    {
        frame.setToDefault();
        scale.setToDefault();

        gravity = frame.getVrtAxis() * (-9.81f * scale.scale);

        tireSlipParams.setToDefault();
        tireStickyParams.setToDefault();

        thresholdForwardSpeedForWheelAngleIntegration = 5.0f * scale.scale;

        pvdContext.setToDefault();
    }

    PX_FORCE_INLINE PxVehicleSimulationContext transformAndScale(
        const PxVehicleFrame& srcFrame, const PxVehicleFrame& trgFrame, const PxVehicleScale& srcScale, const PxVehicleScale& trgScale) const
    {
        PxVehicleSimulationContext c = *this;
        const PxReal scaleRatio = trgScale.scale / srcScale.scale;
        c.gravity = trgFrame.getFrame()*srcFrame.getFrame().getTranspose()*c.gravity;
        c.gravity *= scaleRatio;
        c.tireSlipParams = tireSlipParams.transformAndScale(srcFrame, trgFrame, srcScale, trgScale);
        c.tireStickyParams = tireStickyParams.transformAndScale(srcFrame, trgFrame, srcScale, trgScale);
        c.thresholdForwardSpeedForWheelAngleIntegration *= scaleRatio;
        c.frame = trgFrame;
        c.scale = trgScale;
        return c;
    }
};

struct PxVehiclePhysXSimulationContext : public PxVehicleSimulationContext
{
    PxVehiclePhysXSimulationContext()
        : PxVehicleSimulationContext()
    {
        type = PxVehicleSimulationContextType::ePHYSX;
    }

    //Road geometry queries to find the plane under the wheel.
    const PxConvexMesh* physxUnitCylinderSweepMesh;
    const PxScene* physxScene;

    //PhysX actor update
    PxVehiclePhysXActorUpdateMode::Enum physxActorUpdateMode;

    PxReal physxActorWakeCounterResetValue;

    PxReal physxActorWakeCounterThreshold;

    PX_FORCE_INLINE void setToDefault()
    {
        PxVehicleSimulationContext::setToDefault();

        physxUnitCylinderSweepMesh = NULL;
        physxScene = NULL;

        physxActorUpdateMode = PxVehiclePhysXActorUpdateMode::eAPPLY_VELOCITY;

        physxActorWakeCounterResetValue = 20.0f * 0.02f;  // 20 timesteps of size 0.02
        physxActorWakeCounterThreshold = 0.5f * physxActorWakeCounterResetValue;
    }

    PX_FORCE_INLINE PxVehiclePhysXSimulationContext transformAndScale(
        const PxVehicleFrame& srcFrame, const PxVehicleFrame& trgFrame, const PxVehicleScale& srcScale, const PxVehicleScale& trgScale) const
    {
        PxVehiclePhysXSimulationContext r = *this;
        static_cast<PxVehicleSimulationContext&>(r) = PxVehicleSimulationContext::transformAndScale(srcFrame, trgFrame, srcScale, trgScale);
        return r;
    }
};

template <class T, unsigned int NB_ELEMENTS>
class PxVehicleFixedSizeLookupTable
{
public:

    PxVehicleFixedSizeLookupTable()
        : nbDataPairs(0)
    {
    }

    PxVehicleFixedSizeLookupTable(const PxVehicleFixedSizeLookupTable& src)
    {
        PxMemCopy(xVals, src.xVals, sizeof(PxReal)* src.nbDataPairs);
        PxMemCopy(yVals, src.yVals, sizeof(T)*src.nbDataPairs);
        nbDataPairs = src.nbDataPairs;
    }

    ~PxVehicleFixedSizeLookupTable()
    {
    }

    PxVehicleFixedSizeLookupTable& operator=(const PxVehicleFixedSizeLookupTable& src)
    {
        PxMemCopy(xVals, src.xVals, sizeof(PxReal)*src.nbDataPairs);
        PxMemCopy(yVals, src.yVals, sizeof(T)*src.nbDataPairs);
        nbDataPairs = src.nbDataPairs;
        return *this;
    }

    PX_FORCE_INLINE bool addPair(const PxReal x, const T y)
    {
        PX_CHECK_AND_RETURN_VAL(nbDataPairs < NB_ELEMENTS, "PxVehicleFixedSizeLookupTable::addPair() exceeded fixed size capacity", false);
        xVals[nbDataPairs] = x;
        yVals[nbDataPairs] = y;
        nbDataPairs++;
        return true;
    }

    PX_FORCE_INLINE T interpolate(const PxReal x) const
    {
        if (0 == nbDataPairs)
        {
            return T(0);
        }

        if (1 == nbDataPairs || x < xVals[0])
        {
            return yVals[0];
        }

        PxReal x0 = xVals[0];
        T y0 = yVals[0];

        for (PxU32 i = 1; i < nbDataPairs; i++)
        {
            const PxReal x1 = xVals[i];
            const T y1 = yVals[i];

            if ((x >= x0) && (x < x1))
            {
                return (y0 + (y1 - y0) * (x - x0) / (x1 - x0));
            }

            x0 = x1;
            y0 = y1;
        }

        PX_ASSERT(x >= xVals[nbDataPairs - 1]);
        return yVals[nbDataPairs - 1];
    }

    void clear()
    {
        PxMemSet(xVals, 0, NB_ELEMENTS * sizeof(PxReal));
        PxMemSet(yVals, 0, NB_ELEMENTS * sizeof(T));
        nbDataPairs = 0;
    }

    PxReal xVals[NB_ELEMENTS];
    T yVals[NB_ELEMENTS];
    PxU32 nbDataPairs;

    PX_FORCE_INLINE bool isValid() const
    {
        for (PxU32 i = 1; i < nbDataPairs; i++)
        {
            PX_CHECK_AND_RETURN_VAL(xVals[i] > xVals[i - 1], "PxVehicleFixedSizeLookupTable:: xVals[i+1] must be greater than xVals[i]", false);
        }
        return true;
    }
};

#if !PX_DOXYGEN
} // namespace vehicle2
} // namespace physx
#endif