aboutsummaryrefslogtreecommitdiff
path: root/APEX_1.4/common/include/ApexSharedUtils.h
diff options
context:
space:
mode:
authorgit perforce import user <a@b>2016-10-25 12:29:14 -0600
committerSheikh Dawood Abdul Ajees <Sheikh Dawood Abdul Ajees>2016-10-25 18:56:37 -0500
commit3dfe2108cfab31ba3ee5527e217d0d8e99a51162 (patch)
treefa6485c169e50d7415a651bf838f5bcd0fd3bfbd /APEX_1.4/common/include/ApexSharedUtils.h
downloadphysx-3.4-3dfe2108cfab31ba3ee5527e217d0d8e99a51162.tar.xz
physx-3.4-3dfe2108cfab31ba3ee5527e217d0d8e99a51162.zip
Initial commit:
PhysX 3.4.0 Update @ 21294896 APEX 1.4.0 Update @ 21275617 [CL 21300167]
Diffstat (limited to 'APEX_1.4/common/include/ApexSharedUtils.h')
-rw-r--r--APEX_1.4/common/include/ApexSharedUtils.h2364
1 files changed, 2364 insertions, 0 deletions
diff --git a/APEX_1.4/common/include/ApexSharedUtils.h b/APEX_1.4/common/include/ApexSharedUtils.h
new file mode 100644
index 00000000..337f40cb
--- /dev/null
+++ b/APEX_1.4/common/include/ApexSharedUtils.h
@@ -0,0 +1,2364 @@
+/*
+ * Copyright (c) 2008-2015, NVIDIA CORPORATION. All rights reserved.
+ *
+ * NVIDIA CORPORATION and its licensors retain all intellectual property
+ * and proprietary rights in and to this software, related documentation
+ * and any modifications thereto. Any use, reproduction, disclosure or
+ * distribution of this software and related documentation without an express
+ * license agreement from NVIDIA CORPORATION is strictly prohibited.
+ */
+
+
+#ifndef APEXSHAREDUTILS_H
+#define APEXSHAREDUTILS_H
+
+#include "ApexUsingNamespace.h"
+#include "ApexDefs.h"
+#include "IProgressListener.h"
+#include "RenderMeshAsset.h"
+
+#include "PxStreamFromFileBuf.h"
+
+#include "ApexString.h"
+#include "PsArray.h"
+#include "ConvexHullMethod.h"
+#include "PxPlane.h"
+
+#include "ConvexHullParameters.h"
+
+#include "Cof44.h"
+
+#if PX_PHYSICS_VERSION_MAJOR == 3
+namespace physx
+{
+class PxConvexMesh;
+}
+typedef physx::PxConvexMesh ConvexMesh;
+#endif
+
+namespace nvidia
+{
+namespace apex
+{
+
+PX_INLINE PxPlane toPxPlane(const ConvexHullParametersNS::Plane_Type& plane)
+{
+ return PxPlane(plane.normal.x, plane.normal.y, plane.normal.z, plane.d);
+}
+
+/*
+File-local functions and definitions
+*/
+
+
+/*
+Global utilities
+*/
+
+// Diagonalize a symmetric 3x3 matrix. Returns the eigenvectors in the first parameter, eigenvalues as the return value.
+PxVec3 diagonalizeSymmetric(PxMat33& eigenvectors, const PxMat33& m);
+
+
+
+PX_INLINE bool worldToLocalRay(PxVec3& localorig, PxVec3& localdir,
+ const PxVec3& worldorig, const PxVec3& worlddir,
+ const physx::PxTransform& localToWorldRT, const PxVec3& scale)
+{
+ // Invert scales
+ const float detS = scale.x * scale.y * scale.z;
+ if (detS == 0.0f)
+ {
+ return false; // Not handling singular TMs
+ }
+ const float recipDetS = 1.0f / detS;
+
+ // Is it faster to do a bunch of multiplies, or a few divides?
+ const PxVec3 invS(scale.y * scale.z * recipDetS, scale.z * scale.x * recipDetS, scale.x * scale.y * recipDetS);
+
+ // Create hull-local ray
+ localorig = localToWorldRT.transformInv(worldorig);
+ localorig = invS.multiply(localorig);
+ localdir = localToWorldRT.rotateInv(worlddir);
+ localdir = invS.multiply(localdir);
+
+ return true;
+}
+
+// barycentric utilities
+/**
+This function starts recording the number of cycles elapsed.
+\param pa [in] first vertex of triangle
+\param pb [in] second vertex of triangle
+\param pc [in] third vertex of triangle
+\param p [in] vertex to generate barycentric coordinates for
+\param s [out] the first barycentric coordinate
+\param t [out] the second barycentric coordinate
+\note the third barycentric coordinate is defined as (1 - s - t)
+\see EndProfile
+*/
+void generateBarycentricCoordinatesTri(const PxVec3& pa, const PxVec3& pb, const PxVec3& pc, const PxVec3& p, float& s, float& t);
+void generateBarycentricCoordinatesTet(const PxVec3& pa, const PxVec3& pb, const PxVec3& pc, const PxVec3& pd, const PxVec3& p, PxVec3& bary);
+
+struct OverlapLineSegmentAABBCache
+{
+ PxVec3 sgnDir;
+ PxVec3 invDir;
+};
+
+PX_INLINE void computeOverlapLineSegmentAABBCache(OverlapLineSegmentAABBCache& cache, const PxVec3& segmentDisp)
+{
+ cache.sgnDir = PxVec3((float)(1 - (((int)(segmentDisp.x < 0.0f)) << 1)), (float)(1 - (((int)(segmentDisp.y < 0.0f)) << 1)), (float)(1 - (((int)(segmentDisp.z < 0.0f)) << 1)));
+ PxVec3 absDir = cache.sgnDir.multiply(segmentDisp);
+ absDir += PxVec3(PX_EPS_F32); // To avoid divide-by-zero
+ cache.invDir = PxVec3(absDir.y * absDir.z, absDir.z * absDir.x, absDir.x * absDir.y);
+ cache.invDir *= 1.0f / (absDir.x * cache.invDir.x);
+}
+
+PX_INLINE bool overlapLineSegmentAABBCached(const PxVec3& segmentOrig, const OverlapLineSegmentAABBCache& cache, const PxBounds3& aabb)
+{
+ const PxVec3 center = 0.5f * (aabb.maximum + aabb.minimum);
+ const PxVec3 radii = 0.5f * (aabb.maximum - aabb.minimum);
+ PxVec3 disp = (center - segmentOrig).multiply(cache.sgnDir);
+ PxVec3 tMin = (disp - radii).multiply(cache.invDir);
+ PxVec3 tMax = (disp + radii).multiply(cache.invDir);
+ int maxMinIndex = tMin.y > tMin.x;
+ const int maxMinIndexIs2 = tMin.z > tMin[(unsigned int)maxMinIndex];
+ maxMinIndex = (maxMinIndex | maxMinIndexIs2) << maxMinIndexIs2;
+ int minMaxIndex = tMax.y < tMax.x;
+ const int minMaxIndexIs2 = tMax.z > tMax[(unsigned int)minMaxIndex];
+ minMaxIndex = (minMaxIndex | minMaxIndexIs2) << minMaxIndexIs2;
+ const float tIn = tMin[(unsigned int)maxMinIndex];
+ const float tOut = tMax[(unsigned int)minMaxIndex];
+ return tIn < tOut && tOut > 0.0f && tIn < 1.0f;
+}
+
+PX_INLINE bool overlapLineSegmentAABB(const PxVec3& segmentOrig, const PxVec3& segmentDisp, const PxBounds3& aabb)
+{
+ OverlapLineSegmentAABBCache cache;
+ computeOverlapLineSegmentAABBCache(cache, segmentDisp);
+ return overlapLineSegmentAABBCached(segmentOrig, cache, aabb);
+}
+
+struct IntPair
+{
+ void set(int32_t _i0, int32_t _i1)
+ {
+ i0 = _i0;
+ i1 = _i1;
+ }
+
+ int32_t i0, i1;
+
+ static int compare(const void* a, const void* b)
+ {
+ const int32_t diff0 = ((IntPair*)a)->i0 - ((IntPair*)b)->i0;
+ return diff0 ? diff0 : (((IntPair*)a)->i1 - ((IntPair*)b)->i1);
+ }
+};
+
+PX_INLINE PxFileBuf& operator >> (PxFileBuf& stream, IntPair& p)
+{
+ p.i0 = (int32_t)stream.readDword();
+ p.i1 = (int32_t)stream.readDword();
+ return stream;
+}
+PX_INLINE PxFileBuf& operator << (PxFileBuf& stream, const IntPair& p)
+{
+ stream.storeDword((uint32_t)p.i0);
+ stream.storeDword((uint32_t)p.i1);
+ return stream;
+}
+
+struct BoundsRep
+{
+ BoundsRep() : type(0)
+ {
+ aabb.setEmpty();
+ }
+
+ PxBounds3 aabb;
+ uint32_t type; // By default only reports if subtypes are the same, configurable. Valid range {0...7}
+};
+
+struct BoundsInteractions
+{
+ BoundsInteractions() : bits(0x8040201008040201ULL) {}
+ BoundsInteractions(bool setAll) : bits(setAll ? 0xFFFFFFFFFFFFFFFFULL : 0x0000000000000000ULL) {}
+
+ bool set(unsigned group1, unsigned group2, bool interacts)
+ {
+ if (group1 >= 8 || group2 >= 8)
+ {
+ return false;
+ }
+ const uint64_t mask = (uint64_t)1 << ((group1 << 3) + group2) | (uint64_t)1 << ((group2 << 3) + group1);
+ if (interacts)
+ {
+ bits |= mask;
+ }
+ else
+ {
+ bits &= ~mask;
+ }
+ return true;
+ }
+
+ uint64_t bits;
+};
+
+enum Bounds3Axes
+{
+ Bounds3X = 1,
+ Bounds3Y = 2,
+ Bounds3Z = 4,
+
+ Bounds3XY = Bounds3X | Bounds3Y,
+ Bounds3YZ = Bounds3Y | Bounds3Z,
+ Bounds3ZX = Bounds3Z | Bounds3X,
+
+ Bounds3XYZ = Bounds3X | Bounds3Y | Bounds3Z
+};
+
+void boundsCalculateOverlaps(physx::Array<IntPair>& overlaps, Bounds3Axes axesToUse, const BoundsRep* bounds, uint32_t boundsCount, uint32_t boundsByteStride,
+ const BoundsInteractions& interactions = BoundsInteractions(), bool append = false);
+
+
+/*
+Descriptor for building a ConvexHullImpl, below
+*/
+class ConvexHullDesc
+{
+public:
+ const void* vertices;
+ uint32_t numVertices;
+ uint32_t vertexStrideBytes;
+ uint32_t* indices;
+ uint32_t numIndices;
+ uint8_t* faceIndexCounts;
+ uint32_t numFaces;
+
+ ConvexHullDesc() :
+ vertices(NULL),
+ numVertices(0),
+ vertexStrideBytes(0),
+ indices(NULL),
+ numIndices(0),
+ faceIndexCounts(NULL),
+ numFaces(0)
+ {
+ }
+
+ bool isValid() const
+ {
+ return
+ vertices != NULL &&
+ numVertices != 0 &&
+ vertexStrideBytes != 0 &&
+ indices != NULL &&
+ numIndices != 0 &&
+ faceIndexCounts != NULL &&
+ numFaces != 0;
+ }
+};
+
+/*
+ConvexHullImpl - precomputed (redundant) information about a convex hull: vertices, hull planes, etc.
+*/
+class ConvexHullImpl
+{
+public:
+ struct Separation
+ {
+ PxPlane plane;
+ float min0, max0, min1, max1;
+
+ float getDistance()
+ {
+ return PxMax(min0 - max1, min1 - max0);
+ }
+ };
+
+ ConvexHullImpl();
+ ConvexHullImpl(const ConvexHullImpl& hull) : mParams(NULL), mOwnsParams(false)
+ {
+ *this = hull;
+ }
+ virtual ~ConvexHullImpl();
+
+ // If params == NULL, this will build (and own) its own internal parameters
+ void init(NvParameterized::Interface* params = NULL);
+
+ ConvexHullImpl& operator = (const ConvexHullImpl& hull)
+ {
+ mParams = hull.mParams;
+ mOwnsParams = false;
+ return *this;
+ }
+
+ // Only returns non-NULL value if this object owns its parameters.
+ NvParameterized::Interface* giveOwnersipOfNvParameters();
+
+ // Releases parameters if it owns them
+ void term();
+
+ void buildFromDesc(const ConvexHullDesc& desc);
+ void buildFromPoints(const void* points, uint32_t numPoints, uint32_t pointStrideBytes);
+ void buildFromPlanes(const PxPlane* planes, uint32_t numPlanes, float eps);
+#if PX_PHYSICS_VERSION_MAJOR == 3
+ void buildFromConvexMesh(const ConvexMesh* mesh);
+#endif
+ void buildFromAABB(const PxBounds3& aabb);
+ void buildKDOP(const void* points, uint32_t numPoints, uint32_t pointStrideBytes, const PxVec3* directions, uint32_t numDirections);
+
+ void intersectPlaneSide(const PxPlane& plane);
+ void intersectHull(const ConvexHullImpl& hull);
+
+ // If the distance between the hulls exceeds maxDistance, false is returned.
+ // Otherwise, true is returned. In this case, if 'separation' is not NULL, then separation plane
+ // and projected extents are returned in *separation.
+ static bool hullsInProximity(const ConvexHullImpl& hull0, const physx::PxTransform& localToWorldRT0, const PxVec3& scale0,
+ const ConvexHullImpl& hull1, const physx::PxTransform& localToWorldRT1, const PxVec3& scale1,
+ float maxDistance, Separation* separation = NULL);
+
+ // If the distance between this hull and the given sphere exceeds maxDistance, false is returned.
+ // Otherwise, true is returned. In this case, if 'separation' is not NULL, then separation plane
+ // and projected extents are returned in *separation. The '0' values will correspond to the hull,
+ // and the '1' values to the sphere.
+ bool sphereInProximity(const physx::PxTransform& hullLocalToWorldRT, const PxVec3& hullScale,
+ const PxVec3& sphereWorldCenter, float sphereRadius,
+ float maxDistance, Separation* separation = NULL);
+
+#if PX_PHYSICS_VERSION_MAJOR == 3
+ bool intersects(const PxShape& shape, const physx::PxTransform& localToWorldRT, const PxVec3& scale, float padding) const;
+#endif
+
+ // worldRay.dir need not be normalized. in & out times are relative to worldRay.dir length
+ // N.B. in & out are both input and output variables:
+ // input: in = minimum possible ray intersect time
+ // out = maximum possible ray intersect time
+ // output: in = time ray enters hull
+ // out = time ray exits hull
+ bool rayCast(float& in, float& out, const PxVec3& orig, const PxVec3& dir,
+ const physx::PxTransform& localToWorldRT, const PxVec3& scale, PxVec3* normal = NULL) const;
+
+ // in & out times are relative to worldDisp length
+ // N.B. in & out are both input and output variables:
+ // input: in = minimum possible ray intersect time
+ // out = maximum possible ray intersect time
+ // output: in = time ray enters hull
+ // out = time ray exits hull
+ bool obbSweep(float& in, float& out, const PxVec3& worldBoxCenter, const PxVec3& worldBoxExtents, const PxVec3 worldBoxAxes[3],
+ const PxVec3& worldDisp, const physx::PxTransform& localToWorldRT, const PxVec3& scale, PxVec3* normal = NULL) const;
+
+ // Returns the min and max dot product of the vertices with the given normal
+ void extent(float& min, float& max, const PxVec3& normal) const;
+
+ void fill(physx::Array<PxVec3>& outPoints, const physx::PxTransform& localToWorldRT, const PxVec3& scale,
+ float spacing, float jitter, uint32_t maxPoints, bool adjustSpacing) const;
+
+ void setEmpty()
+ {
+ NvParameterized::Handle handle(*mParams);
+ mParams->getParameterHandle("vertices", handle);
+ mParams->resizeArray(handle, 0);
+ mParams->getParameterHandle("uniquePlanes", handle);
+ mParams->resizeArray(handle, 0);
+ mParams->getParameterHandle("widths", handle);
+ mParams->resizeArray(handle, 0);
+ mParams->getParameterHandle("edges", handle);
+ mParams->resizeArray(handle, 0);
+ mParams->bounds.setEmpty();
+ mParams->volume = 0.0f;
+ mParams->uniqueEdgeDirectionCount = 0;
+ mParams->planeCount = 0;
+ }
+
+ bool isEmpty() const
+ {
+ PX_ASSERT(mParams->bounds.isEmpty() == (mParams->vertices.arraySizes[0] == 0));
+ PX_ASSERT(mParams->bounds.isEmpty() == (mParams->uniquePlanes.arraySizes[0] == 0));
+ PX_ASSERT(mParams->bounds.isEmpty() == (mParams->widths.arraySizes[0] == 0));
+ PX_ASSERT(mParams->bounds.isEmpty() == (mParams->edges.arraySizes[0] == 0));
+ PX_ASSERT(mParams->bounds.isEmpty() == (mParams->volume == 0.0f));
+ return mParams->bounds.isEmpty();
+ }
+
+ uint32_t getVertexCount() const
+ {
+ return (uint32_t)mParams->vertices.arraySizes[0];
+ }
+ const PxVec3& getVertex(uint32_t index) const
+ {
+ return mParams->vertices.buf[index];
+ }
+
+ uint32_t getPlaneCount() const
+ {
+ return mParams->planeCount;
+ }
+ uint32_t getUniquePlaneNormalCount() const
+ {
+ return (uint32_t)mParams->uniquePlanes.arraySizes[0];
+ }
+ PxPlane getPlane(uint32_t index) const
+ {
+ PX_ASSERT(index < getPlaneCount());
+ if (index < (uint32_t)mParams->uniquePlanes.arraySizes[0])
+ {
+ return toPxPlane(mParams->uniquePlanes.buf[index]);
+ }
+ index -= mParams->uniquePlanes.arraySizes[0];
+ PxPlane plane = toPxPlane(mParams->uniquePlanes.buf[index]);
+ plane.n = -plane.n;
+ plane.d = -plane.d - mParams->widths.buf[index];
+ return plane;
+ }
+
+ uint32_t getWidthCount() const
+ {
+ return (uint32_t)mParams->widths.arraySizes[0];
+ }
+ float getWidth(uint32_t index) const
+ {
+ return mParams->widths.buf[index];
+ }
+
+ uint32_t getEdgeCount() const
+ {
+ return (uint32_t)mParams->edges.arraySizes[0];
+ }
+ uint32_t getEdgeEndpointIndex(uint32_t edgeIndex, uint32_t endpointIndex) const // endpointIndex = 0 or 1
+ {
+ PX_ASSERT(edgeIndex < getEdgeCount());
+ PX_ASSERT((endpointIndex & 1) == endpointIndex);
+ endpointIndex &= 1;
+ const uint32_t edge = mParams->edges.buf[edgeIndex];
+ return (endpointIndex & 1) ? (edge & 0x0000FFFF) : (edge >> 16);
+ }
+ uint32_t getEdgeAdjacentFaceIndex(uint32_t edgeIndex, uint32_t adjacencyIndex) const // adjacencyIndex = 0 or 1
+ {
+ PX_ASSERT(edgeIndex < getEdgeCount());
+ PX_ASSERT((adjacencyIndex & 1) == adjacencyIndex);
+ adjacencyIndex &= 1;
+ const uint32_t adj = mParams->adjacentFaces.buf[edgeIndex];
+ return (adjacencyIndex & 1) ? (adj & 0x0000FFFF) : (adj >> 16);
+ }
+ uint32_t getUniqueEdgeDirectionCount() const
+ {
+ return mParams->uniqueEdgeDirectionCount;
+ }
+ PxVec3 getEdgeDirection(uint32_t index) const
+ {
+ PX_ASSERT(index < getEdgeCount());
+ uint32_t edge = mParams->edges.buf[index];
+ return mParams->vertices.buf[edge & 0xFFFF] - mParams->vertices.buf[edge >> 16];
+ }
+
+ const PxBounds3& getBounds() const
+ {
+ return mParams->bounds;
+ }
+ float getVolume() const
+ {
+ return (float)mParams->volume;
+ }
+
+ // transform may include an arbitrary 3x3 block and a translation
+ void applyTransformation(const PxMat44& tm)
+ {
+ PX_ASSERT(mParams);
+
+ const float det3 = PxMat33(tm.getBasis(0), tm.getBasis(1), tm.getBasis(2)).getDeterminant();
+ PX_ASSERT(det3 > 0.0f); // mirroring or degeneracy won't work well here
+
+ // planes and slab widths
+ const Cof44 cof(tm);
+ const uint32_t numPlanes = (uint32_t)mParams->uniquePlanes.arraySizes[0];
+ ConvexHullParametersNS::Plane_Type* planes = mParams->uniquePlanes.buf;
+ PX_ASSERT(planes);
+ PX_ASSERT(numPlanes == (uint32_t)mParams->widths.arraySizes[0]);
+ float* widths = mParams->widths.buf;
+ PX_ASSERT(widths);
+ for (uint32_t i = 0; i < numPlanes; i++)
+ {
+ PxPlane src(planes[i].normal, planes[i].d);
+ PxPlane dst;
+ cof.transform(src, dst);
+ planes[i].normal = dst.n;
+ planes[i].d = dst.d;
+ const float n2 = dst.n.magnitudeSquared();
+ if (n2 > 0.0f)
+ {
+ const float recipN = PxRecipSqrt(n2);
+ planes[i].normal *= recipN;
+ planes[i].d *= recipN;
+ widths[i] *= det3*recipN;
+ }
+ }
+
+ // vertices
+ const uint32_t numVertices = (uint32_t)mParams->vertices.arraySizes[0];
+ PxVec3* vertices = mParams->vertices.buf;
+ PX_ASSERT(vertices);
+
+ mParams->bounds.setEmpty();
+ for (uint32_t i = 0; i < numVertices; i++)
+ {
+ vertices[i] = tm.transform(vertices[i]);
+ mParams->bounds.include(vertices[i]);
+ }
+
+ // volume
+ mParams->volume *= det3;
+ }
+
+ // Special case - transformation must be a pure rotation plus translation, and we only allow a positive, uniform scale
+ // Note, we could implement this with applyTransformation(const PxMat44& tm), above, but we will keep this
+ // old implementation to ensure that behavior doesn't change
+ void applyTransformation(const PxMat44& transformation, float scale)
+ {
+ PX_ASSERT(mParams);
+ PX_ASSERT(scale > 0.0f); // negative scale won't work well here
+
+ // planes
+ const uint32_t numPlanes = (uint32_t)mParams->uniquePlanes.arraySizes[0];
+ ConvexHullParametersNS::Plane_Type* planes = mParams->uniquePlanes.buf;
+ PX_ASSERT(planes);
+ for (uint32_t i = 0; i < numPlanes; i++)
+ {
+ planes[i].normal = transformation.rotate(planes[i].normal);
+ planes[i].d *= scale;
+ }
+
+ // slab widths
+ const uint32_t numWidths = (uint32_t)mParams->widths.arraySizes[0];
+ float* widths = mParams->widths.buf;
+ PX_ASSERT(widths);
+ for (uint32_t i = 0; i < numWidths; i++)
+ {
+ widths[i] *= scale;
+ }
+
+ // vertices
+ const uint32_t numVertices = (uint32_t)mParams->vertices.arraySizes[0];
+ PxVec3* vertices = mParams->vertices.buf;
+ PX_ASSERT(vertices);
+
+ mParams->bounds.setEmpty();
+ for (uint32_t i = 0; i < numVertices; i++)
+ {
+ vertices[i] = transformation.transform(vertices[i]) * scale; // Works since scale is uniform
+ mParams->bounds.include(vertices[i]);
+ }
+
+ // volume
+ mParams->volume *= scale*scale*scale;
+ }
+
+#if PX_PHYSICS_VERSION_MAJOR == 3
+ // Returns the number of vertices and faces of the cooked mesh. If inflated = false,
+ // these should be the same as the values returned by getVertexCount() and getPlaneCount().
+ // However, the numerical properties of the cooker could result in different values. If inflated = true,
+ // then sharp edges will be beveled by the cooker, resulting in more vertices and faces.
+ // Note: the number of edges E may be calculated from the number of vertices V and faces F using E = V + F - 2.
+ // Return value = size in bytes of the cooked convex mesh
+ uint32_t calculateCookedSizes(uint32_t& vertexCount, uint32_t& faceCount, bool inflated) const;
+
+ // Removes vertices from the hull until the bounds given in the function's parameters are met.
+ // If inflated = true, then the maximum counts given are compared with the cooked hull, which may have higher counts due to beveling.
+ // Note: a value of zero indicates no limit, effectively infinite.
+ // Return value: true if successful, i.e. the limits were met. False otherwise.
+ bool reduceHull(uint32_t maxVertexCount, uint32_t maxEdgeCount, uint32_t maxFaceCount, bool inflated);
+
+ // Replaces vertices with cooked, un-inflated vertices, if the latter set is smaller. Returns true if the number of vertices is reduced.
+ bool reduceByCooking();
+#endif
+
+ // Utility function
+ static bool createKDOPDirections(physx::Array<PxVec3>& directions, ConvexHullMethod::Enum method);
+
+// DeclareArray(PxVec3) vertices;
+// DeclareArray(PxPlane) uniquePlanes; // These are the unique face directions. If there is an opposite face, the corresponding widths[i] will give its distance
+// physx::Array<float> widths; // Same size as uniquePlanes. Gives width of hull in uniquePlane direction
+// physx::Array<uint32_t> edges; // Vertex indices stored in high/low words. The first uniqueEdgeDirectionCount elements give the unique directions.
+// PxBounds3 bounds;
+// float volume;
+// uint32_t uniqueEdgeDirectionCount;
+// uint32_t planeCount; // Total number of faces. Greater than or equal to size of uniquePlanes.
+
+ ConvexHullParameters* mParams;
+ bool mOwnsParams;
+};
+
+
+/*
+ConvexMeshBuilder - creates triangles for a convex hull defined by a set of planes. Copied from physx samples (RenderClothActor)
+*/
+struct ConvexMeshBuilder
+{
+ ConvexMeshBuilder(const PxVec4* planes)
+ : mPlanes(planes)
+ {}
+
+ void operator()(uint32_t mask, float scale=1.0f);
+
+ const PxVec4* mPlanes;
+ Array<PxVec3> mVertices;
+ Array<uint16_t> mIndices;
+};
+
+
+// Fast implementation for sse
+PX_INLINE float RecipSqrt(float x)
+{
+#if defined( APEX_SUPPORT_SSE )
+ const float three = 3.0f;
+ const float oneHalf = 0.5f;
+ float y;
+ _asm
+ {
+ movss xmm2, three;
+ rsqrtss xmm0, x
+ movss xmm1, xmm0
+ mulss xmm1, oneHalf
+ mulss xmm0, xmm0
+ mulss xmm0, x
+ subss xmm2, xmm0
+ mulss xmm1, xmm2
+ movss y, xmm1
+ }
+ return y;
+#else
+ return 1.0f / sqrtf(x);
+#endif
+}
+
+/*
+ Array find utility
+ */
+
+// If t is found in array, index is set to the array element and the function returns true
+// If t is not found in the array, index is not modified and the function returns false
+template<class T>
+bool arrayFind(uint32_t& index, const T& t, const physx::Array<T>& array)
+{
+ const uint32_t size = array.size();
+ for (uint32_t i = 0; i < size; ++i)
+ {
+ if (array[i] == t)
+ {
+ index = i;
+ return true;
+ }
+ }
+ return false;
+}
+
+#include "ApexUsingNamespace.h"
+#if PX_X64
+#pragma warning(push)
+#pragma warning(disable: 4324) // 'IndexBank<IndexType>' : structure was padded due to __declspec(align())
+#endif
+
+/*
+ Index bank - double-sided free list for O(1) borrow/return of unique IDs
+
+ Type IndexType should be an unsigned integer type or something that can be cast to and from
+ an integer
+ */
+template <class IndexType>
+class IndexBank
+{
+ public:
+ IndexBank<IndexType>(uint32_t capacity = 0) : indexCount(0), capacityLocked(false)
+ {
+ maxCapacity = calculateMaxCapacity();
+ reserve_internal(capacity);
+ }
+
+ // Copy constructor
+ IndexBank<IndexType>(const IndexBank<IndexType>& other)
+ {
+ *this = other;
+ }
+
+ virtual ~IndexBank<IndexType>() {}
+
+ // Assignment operator
+ IndexBank<IndexType>& operator = (const IndexBank<IndexType>& other)
+ {
+ indices = other.indices;
+ ranks = other.ranks;
+ maxCapacity = other.maxCapacity;
+ indexCount = other.indexCount;
+ capacityLocked = other.capacityLocked;
+ return *this;
+ }
+
+ void setIndicesAndRanks(uint16_t* indicesIn, uint16_t* ranksIn, uint32_t capacityIn, uint32_t usedCountIn)
+ {
+ indexCount = usedCountIn;
+ reserve_internal(capacityIn);
+ for (uint32_t i = 0; i < capacityIn; ++i)
+ {
+ indices[i] = indicesIn[i];
+ ranks[i] = ranksIn[i];
+ }
+ }
+
+ void clear(uint32_t capacity = 0, bool used = false)
+ {
+ capacityLocked = false;
+ indices.reset();
+ ranks.reset();
+ reserve_internal(capacity);
+ if (used)
+ {
+ indexCount = capacity;
+ indices.resize(capacity);
+ for (IndexType i = (IndexType)0; i < (IndexType)capacity; ++i)
+ {
+ indices[i] = i;
+ }
+ }
+ else
+ {
+ indexCount = 0;
+ }
+ }
+
+ // Equivalent to calling freeLastUsed() until the used list is empty.
+ void clearFast()
+ {
+ indexCount = 0;
+ }
+
+ // This is the reserve size. The bank can only grow, due to shuffling of indices
+ virtual void reserve(uint32_t capacity)
+ {
+ reserve_internal(capacity);
+ }
+
+ // If lock = true, keeps bank from automatically resizing
+ void lockCapacity(bool lock)
+ {
+ capacityLocked = lock;
+ }
+
+ bool isCapacityLocked() const
+ {
+ return capacityLocked;
+ }
+
+ void setMaxCapacity(uint32_t inMaxCapacity)
+ {
+ // Cannot drop below current capacity, nor above max set by data types
+ maxCapacity = PxClamp(inMaxCapacity, capacity(), calculateMaxCapacity());
+ }
+
+ uint32_t capacity() const
+ {
+ return indices.size();
+ }
+ uint32_t usedCount() const
+ {
+ return indexCount;
+ }
+ uint32_t freeCount() const
+ {
+ return capacity() - usedCount();
+ }
+
+ // valid from [0] to [size()-1]
+ const IndexType* usedIndices() const
+ {
+ return indices.begin();
+ }
+
+ // valid from [0] to [free()-1]
+ const IndexType* freeIndices() const
+ {
+ return indices.begin() + usedCount();
+ }
+
+ bool isValid(IndexType index) const
+ {
+ return index < (IndexType)capacity();
+ }
+ bool isUsed(IndexType index) const
+ {
+ return isValid(index) && (ranks[index] < (IndexType)usedCount());
+ }
+ bool isFree(IndexType index) const
+ {
+ return isValid(index) && !isUsed();
+ }
+
+ IndexType getRank(IndexType index) const
+ {
+ return ranks[index];
+ }
+
+ // Gets the next available index, if any
+ bool useNextFree(IndexType& index)
+ {
+ if (freeCount() == 0)
+ {
+ if (capacityLocked)
+ {
+ return false;
+ }
+ if (capacity() >= maxCapacity)
+ {
+ return false;
+ }
+ reserve(PxClamp(capacity() * 2, (uint32_t)1, maxCapacity));
+ PX_ASSERT(freeCount() > 0);
+ }
+ index = indices[indexCount++];
+ return true;
+ }
+
+ // Frees the last used index, if any
+ bool freeLastUsed(IndexType& index)
+ {
+ if (usedCount() == 0)
+ {
+ return false;
+ }
+ index = indices[--indexCount];
+ return true;
+ }
+
+ // Requests a particular index. If that index is available, it is borrowed and the function
+ // returns true. Otherwise nothing happens and the function returns false.
+ bool use(IndexType index)
+ {
+ if (!indexIsValidForUse(index))
+ {
+ return false;
+ }
+ IndexType oldRank;
+ placeIndexAtRank(index, (IndexType)indexCount++, oldRank);
+ return true;
+ }
+
+ bool free(IndexType index)
+ {
+ if (!indexIsValidForFreeing(index))
+ {
+ return false;
+ }
+ IndexType oldRank;
+ placeIndexAtRank(index, (IndexType)--indexCount, oldRank);
+ return true;
+ }
+
+ bool useAndReturnRanks(IndexType index, IndexType& newRank, IndexType& oldRank)
+ {
+ if (!indexIsValidForUse(index))
+ {
+ return false;
+ }
+ newRank = (IndexType)indexCount++;
+ placeIndexAtRank(index, newRank, oldRank);
+ return true;
+ }
+
+ bool freeAndReturnRanks(IndexType index, IndexType& newRank, IndexType& oldRank)
+ {
+ if (!indexIsValidForFreeing(index))
+ {
+ return false;
+ }
+ newRank = (IndexType)--indexCount;
+ placeIndexAtRank(index, newRank, oldRank);
+ return true;
+ }
+
+ protected:
+
+ bool indexIsValidForUse(IndexType index)
+ {
+ if (!isValid(index))
+ {
+ if (capacityLocked)
+ {
+ return false;
+ }
+ if (capacity() >= maxCapacity)
+ {
+ return false;
+ }
+ reserve(PxClamp(2*(uint32_t)index, (uint32_t)1, maxCapacity));
+ PX_ASSERT(isValid(index));
+ }
+ return !isUsed(index);
+ }
+
+ bool indexIsValidForFreeing(IndexType index)
+ {
+ if (!isValid(index))
+ {
+ // Invalid index
+ return false;
+ }
+ return isUsed(index);
+ }
+
+ // This is the reserve size. The bank can only grow, due to shuffling of indices
+ void reserve_internal(uint32_t capacity)
+ {
+ capacity = PxMin(capacity, maxCapacity);
+ const uint32_t oldCapacity = indices.size();
+ if (capacity > oldCapacity)
+ {
+ indices.resize(capacity);
+ ranks.resize(capacity);
+ for (IndexType i = (IndexType)oldCapacity; i < (IndexType)capacity; ++i)
+ {
+ indices[i] = i;
+ ranks[i] = i;
+ }
+ }
+ }
+
+ private:
+
+ void placeIndexAtRank(IndexType index, IndexType newRank, IndexType& oldRank) // returns old rank
+ {
+ const IndexType replacementIndex = indices[newRank];
+ oldRank = ranks[index];
+ indices[oldRank] = replacementIndex;
+ indices[newRank] = index;
+ ranks[replacementIndex] = oldRank;
+ ranks[index] = newRank;
+ }
+
+ uint32_t calculateMaxCapacity()
+ {
+#pragma warning(push)
+#pragma warning(disable: 4127) // conditional expression is constant
+ if (sizeof(IndexType) >= sizeof(uint32_t))
+ {
+ return 0xFFFFFFFF; // Limited by data type we use to report capacity
+ }
+ else
+ {
+ return (1u << (8 * PxMin((uint32_t)sizeof(IndexType), 3u))) - 1; // Limited by data type we use for indices
+ }
+#pragma warning(pop)
+ }
+
+ protected:
+
+ Array<IndexType> indices;
+ Array<IndexType> ranks;
+ uint32_t maxCapacity;
+ uint32_t indexCount;
+ bool capacityLocked;
+};
+
+#if PX_X64
+#pragma warning(pop)
+#endif
+
+/*
+ Bank - Index bank of type IndexType with an associated object array of type T
+ */
+template <class T, class IndexType>
+class Bank : public IndexBank<IndexType>
+{
+ public:
+ Bank<T, IndexType>(uint32_t capacity = 0) : IndexBank<IndexType>(capacity)
+ {
+ objects = (T*)PX_ALLOC(IndexBank<IndexType>::indices.size() * sizeof(T), PX_DEBUG_EXP("Bank"));
+ if (objects != NULL)
+ {
+ PX_ASSERT(memset(objects, 0, IndexBank<IndexType>::indices.size() * sizeof(T)));
+ }
+ }
+ Bank<T, IndexType>(const Bank<T, IndexType>& bank) : objects(NULL)
+ {
+ *this = bank;
+ }
+
+ ~Bank<T, IndexType>()
+ {
+ clear();
+ }
+
+ Bank<T, IndexType>& operator = (const Bank<T, IndexType>& bank)
+ {
+ if (&bank == this)
+ {
+ return *this;
+ }
+
+ this->clear();
+
+ this->indices = bank.indices;
+ this->ranks = bank.ranks;
+ this->maxCapacity = bank.maxCapacity;
+ this->indexCount = bank.indexCount;
+ this->capacityLocked = bank.capacityLocked;
+
+ if (this->indices.size())
+ {
+ objects = (T*)PX_ALLOC(IndexBank<IndexType>::indices.size() * sizeof(T), PX_DEBUG_EXP("Bank"));
+ PX_ASSERT(memset(objects, 0, IndexBank<IndexType>::indices.size() * sizeof(T)));
+ for (uint32_t i = 0; i < this->indexCount; ++i)
+ {
+ uint32_t index = this->indices[i];
+ new(objects + index) T();
+ objects[index] = bank.objects[index];
+ }
+ }
+ return *this;
+ }
+
+ // This is the reserve size. The bank can only grow, due to shuffling of indices
+ virtual void reserve(uint32_t capacity)
+ {
+ const uint32_t oldSize = IndexBank<IndexType>::indices.size();
+ IndexBank<IndexType>::reserve_internal(capacity);
+ if (IndexBank<IndexType>::indices.size() > oldSize)
+ {
+ T* nb = (T*)PX_ALLOC(IndexBank<IndexType>::indices.size() * sizeof(T), PX_DEBUG_EXP("Bank"));
+ if (nb)
+ {
+ PX_ASSERT(memset(nb, 0, IndexBank<IndexType>::indices.size() * sizeof(T)));
+
+ const IndexType* usedIndices = IndexBank<IndexType>::usedIndices();
+ uint32_t numIndices = IndexBank<IndexType>::usedCount();
+
+ // this copy needs to be correct for nonPOD type T's
+ for (int32_t i = (int32_t)numIndices - 1; i >= 0; i--)
+ {
+ IndexType index = usedIndices[i];
+ new(nb + index) T(objects[index]);
+ objects[index].~T();
+ }
+ //memcpy( nb, objects, IndexBank<IndexType>::indices.size()*sizeof(T) );
+ }
+ PX_FREE(objects);
+ objects = nb;
+ }
+ }
+
+ // Indirect array accessors: rank in [0,usedCount()-1] returns all "used" indexed objects
+ const T& getUsed(IndexType rank) const
+ {
+ return objects[ IndexBank<IndexType>::indices[rank] ];
+ }
+ T& getUsed(IndexType rank)
+ {
+ return objects[ IndexBank<IndexType>::indices[rank] ];
+ }
+
+ // Direct array accessors
+ const T& direct(IndexType index) const
+ {
+ return objects[index];
+ }
+ T& direct(IndexType index)
+ {
+ return objects[index];
+ }
+
+ // Wrappers for base class, which call appropriate constructors and destructors of objects
+ bool useNextFree(IndexType& index)
+ {
+ if (IndexBank<IndexType>::useNextFree(index))
+ {
+ new(objects + index) T();
+ return true;
+ }
+ return false;
+ }
+
+ bool freeLastUsed(IndexType& index)
+ {
+ if (IndexBank<IndexType>::freeLastUsed(index))
+ {
+ objects[index].~T();
+ return true;
+ }
+ return false;
+ }
+
+ bool use(IndexType index)
+ {
+ if (IndexBank<IndexType>::use(index))
+ {
+ new(objects + index) T();
+ return true;
+ }
+ return false;
+ }
+
+ bool free(IndexType index)
+ {
+ if (IndexBank<IndexType>::free(index))
+ {
+ objects[index].~T();
+ return true;
+ }
+ return false;
+ }
+
+ bool useAndReturnRanks(IndexType index, IndexType& newRank, IndexType& oldRank)
+ {
+ if (IndexBank<IndexType>::useAndReturnRanks(index, newRank, oldRank))
+ {
+ new(objects + index) T();
+ return true;
+ }
+ return false;
+ }
+
+ bool freeAndReturnRanks(IndexType index, IndexType& newRank, IndexType& oldRank)
+ {
+ if (IndexBank<IndexType>::freeAndReturnRanks(index, newRank, oldRank))
+ {
+ objects[index].~T();
+ return true;
+ }
+ return false;
+ }
+
+ // Erases all object, index, and rank arrays (complete deallocation)
+ void clear()
+ {
+ const IndexType* usedIndices = IndexBank<IndexType>::usedIndices();
+ uint32_t numIndices = IndexBank<IndexType>::usedCount();
+
+ for (int32_t i = (int32_t)numIndices - 1; i >= 0; i--)
+ {
+ bool test = free(usedIndices[i]);
+ PX_UNUSED(test);
+ PX_ASSERT(test);
+ }
+
+ IndexBank<IndexType>::clear();
+ PX_FREE(objects);
+ objects = NULL;
+ }
+
+ // Re-arranges objects internally into rank-order, afterwards rank = index
+ void clean()
+ {
+ for (IndexType i = 0; i < IndexBank<IndexType>::capacity(); ++i)
+ {
+ const IndexType index = IndexBank<IndexType>::indices[i];
+ if (index != i)
+ {
+ nvidia::swap(objects[i], objects[index]);
+ const IndexType displacedRank = IndexBank<IndexType>::ranks[i];
+ IndexBank<IndexType>::indices[i] = i;
+ IndexBank<IndexType>::ranks[i] = i;
+ IndexBank<IndexType>::indices[displacedRank] = index;
+ IndexBank<IndexType>::ranks[index] = displacedRank;
+ }
+ }
+ }
+
+ protected:
+ T* objects;
+};
+
+
+/*
+ Ring buffer
+*/
+template <class T>
+class RingBuffer
+{
+ public:
+ RingBuffer() : frontIndex(0), backIndex(0xFFFFFFFF), usedSize(0), bufferSize(0), buffer(NULL) {}
+ ~RingBuffer()
+ {
+ erase();
+ }
+
+ uint32_t size() const
+ {
+ return usedSize;
+ }
+
+ T& operator [](uint32_t i)
+ {
+ PX_ASSERT(i < usedSize);
+ i += frontIndex;
+ return buffer[ i < bufferSize ? i : i - bufferSize ];
+ }
+
+ const T& operator [](uint32_t i) const
+ {
+ return (const T&)(const_cast<RingBuffer<T>*>(this)->operator[](i));
+ }
+
+ T& back() const
+ {
+ return buffer[backIndex];
+ }
+ T& front() const
+ {
+ return buffer[frontIndex];
+ }
+
+ T& pushBack()
+ {
+ if (bufferSize == usedSize)
+ {
+ reserve(2 * (bufferSize + 1));
+ }
+ ++usedSize;
+ if (++backIndex == bufferSize)
+ {
+ backIndex = 0;
+ }
+ T& back = buffer[backIndex];
+ PX_PLACEMENT_NEW(&back, T)();
+ return back;
+ }
+
+ void popBack()
+ {
+ PX_ASSERT(size() != 0);
+ if (size() == 0)
+ {
+ return;
+ }
+ buffer[backIndex].~T();
+ --usedSize;
+ if (backIndex-- == 0)
+ {
+ backIndex += bufferSize;
+ }
+ }
+
+ T& pushFront()
+ {
+ if (bufferSize == usedSize)
+ {
+ reserve(2 * (bufferSize + 1));
+ }
+ ++usedSize;
+ if (frontIndex-- == 0)
+ {
+ frontIndex += bufferSize;
+ }
+ T& front = buffer[frontIndex];
+ PX_PLACEMENT_NEW(&front, T)();
+ return front;
+ }
+
+ void popFront()
+ {
+ PX_ASSERT(size() != 0);
+ if (size() == 0)
+ {
+ return;
+ }
+ buffer[frontIndex].~T();
+ --usedSize;
+ if (++frontIndex == bufferSize)
+ {
+ frontIndex = 0;
+ }
+ }
+
+ void clear()
+ {
+ while (size() != 0)
+ {
+ popBack();
+ }
+ frontIndex = 0;
+ backIndex = 0xFFFFFFFF;
+ }
+
+ void erase()
+ {
+ clear();
+ if (buffer != NULL)
+ {
+ PX_FREE(buffer);
+ buffer = NULL;
+ }
+ bufferSize = 0;
+ }
+
+ void reserve(uint32_t newBufferSize)
+ {
+ if (newBufferSize <= bufferSize)
+ {
+ return;
+ }
+ T* newBuffer = (T*)PX_ALLOC(newBufferSize * sizeof(T), PX_DEBUG_EXP("RingBuffer"));
+ const uint32_t lastIndex = frontIndex + usedSize;
+ if (lastIndex <= bufferSize)
+ {
+ for (uint32_t i = 0; i < usedSize; i++)
+ {
+ PX_PLACEMENT_NEW(newBuffer + i, T)(buffer[i]);
+ buffer[i].~T();
+ }
+ //memcpy( newBuffer, buffer+frontIndex, usedSize*sizeof( T ) );
+ }
+ else
+ {
+ for (uint32_t i = 0; i < (bufferSize - frontIndex); i++)
+ {
+ PX_PLACEMENT_NEW(newBuffer + i, T)(buffer[i + frontIndex]);
+ buffer[i + frontIndex].~T();
+ }
+ //memcpy( newBuffer, buffer+frontIndex, (bufferSize-frontIndex)*sizeof( T ) );
+
+ for (uint32_t i = 0; i < (lastIndex - bufferSize); i++)
+ {
+ PX_PLACEMENT_NEW(newBuffer + i + (bufferSize - frontIndex), T)(buffer[i]);
+ buffer[i].~T();
+ }
+ //memcpy( newBuffer + (bufferSize-frontIndex), buffer, (lastIndex-bufferSize)*sizeof( T ) );
+ }
+ bufferSize = newBufferSize;
+ frontIndex = 0;
+ backIndex = frontIndex + usedSize - 1;
+ if (buffer)
+ {
+ PX_FREE(buffer);
+ }
+ buffer = newBuffer;
+ }
+
+ class It
+ {
+ public:
+ It(const RingBuffer<T>& buffer) :
+ m_bufferStart(buffer.buffer), m_bufferStop(buffer.buffer + buffer.bufferSize),
+ m_current(buffer.usedSize > 0 ? buffer.buffer + buffer.frontIndex : NULL), m_remaining(buffer.usedSize) {}
+
+ operator T* () const
+ {
+ return m_current;
+ }
+ T* operator ++ ()
+ {
+ inc();
+ return m_current;
+ }
+ T* operator ++ (int)
+ {
+ T* prev = m_current;
+ inc();
+ return prev;
+ }
+
+ private:
+ void inc()
+ {
+ if (m_remaining > 1)
+ {
+ --m_remaining;
+ if (++m_current == m_bufferStop)
+ {
+ m_current = m_bufferStart;
+ }
+ }
+ else
+ {
+ m_remaining = 0;
+ m_current = NULL;
+ }
+ }
+
+ T* m_bufferStart;
+ T* m_bufferStop;
+ T* m_current;
+ uint32_t m_remaining;
+ };
+
+ friend class It;
+
+ protected:
+ uint32_t frontIndex;
+ uint32_t backIndex;
+ uint32_t usedSize;
+ uint32_t bufferSize;
+ T* buffer;
+};
+
+
+template<class T>
+class Pool
+{
+ enum { DefaultBlockSizeInBytes = 1024 }; // This must be positive
+
+ public:
+ Pool(uint32_t objectsPerBlock = 0) : m_head(NULL), m_inUse(0)
+ {
+ PX_ASSERT(sizeof(T) >= sizeof(void*));
+ setBlockSize(objectsPerBlock);
+ }
+
+ ~Pool()
+ {
+ empty();
+ }
+
+ void setBlockSize(uint32_t objectsPerBlock)
+ {
+ m_objectsPerBlock = objectsPerBlock > 0 ? objectsPerBlock : ((uint32_t)DefaultBlockSizeInBytes + sizeof(T) - 1) / sizeof(T);
+ }
+
+ /* Gives a single object, allocating if necessary */
+ T* borrow()
+ {
+ if (m_head == NULL)
+ {
+ allocateBlock();
+ }
+ T* ptr = (T*)m_head;
+ m_head = *(void**)m_head;
+ new(ptr) T();
+ ++m_inUse;
+ return ptr;
+ }
+
+ /* Return a single object */
+ void replace(T* ptr)
+ {
+ if (ptr != NULL)
+ {
+ ptr->~T();
+ *(void**)ptr = m_head;
+ m_head = (void*)ptr;
+ --m_inUse;
+ }
+ }
+
+ void allocateBlock()
+ {
+ T* block = (T*)PX_ALLOC(sizeof(T) * m_objectsPerBlock, PX_DEBUG_EXP("ApexSharedUtils::Pool"));
+ m_blocks.pushBack(block);
+ for (T* ptr = block + m_objectsPerBlock; ptr-- != block;)
+ {
+ *(void**)ptr = m_head;
+ m_head = (void*)ptr;
+ }
+ }
+
+ int32_t empty()
+ {
+ while (m_blocks.size())
+ {
+ PX_FREE(m_blocks.back());
+ m_blocks.popBack();
+ }
+ m_blocks.reset();
+ m_head = NULL;
+ const int32_t inUse = m_inUse;
+ m_inUse = 0;
+ return inUse;
+ }
+
+ protected:
+
+ void* m_head;
+ uint32_t m_objectsPerBlock;
+ physx::Array<T*>m_blocks;
+ int32_t m_inUse;
+};
+
+
+// Progress listener implementation for hierarchical progress reporting
+class HierarchicalProgressListener : public IProgressListener
+{
+ public:
+ HierarchicalProgressListener(int totalWork, IProgressListener* parent) :
+ m_work(0), m_subtaskWork(1), m_totalWork(PxMax(totalWork, 1)), m_taskName(NULL), m_parent(parent) {}
+
+ void setSubtaskWork(int subtaskWork, const char* taskName = NULL)
+ {
+ if (subtaskWork < 0)
+ {
+ subtaskWork = m_totalWork - m_work;
+ }
+
+ m_subtaskWork = subtaskWork;
+ PX_ASSERT(m_work + m_subtaskWork <= m_totalWork);
+ m_taskName = taskName;
+ setProgress(0, m_taskName);
+ }
+
+ void completeSubtask()
+ {
+ setProgress(100, m_taskName);
+ m_work += m_subtaskWork;
+ }
+
+ void setProgress(int progress, const char* taskName = NULL)
+ {
+ PX_ASSERT(progress >= 0);
+ PX_ASSERT(progress <= 100);
+
+ if (taskName == NULL)
+ {
+ taskName = m_taskName;
+ }
+
+ if (m_parent != NULL)
+ {
+ const int parentProgress = m_totalWork > 0 ? (m_work * 100 + m_subtaskWork * progress) / m_totalWork : 100;
+ m_parent->setProgress(PxClamp(parentProgress, 0, 100), taskName);
+ }
+ }
+
+ protected:
+ int m_work;
+ int m_subtaskWork;
+ int m_totalWork;
+ const char* m_taskName;
+ IProgressListener* m_parent;
+};
+
+void createIndexStartLookup(physx::Array<uint32_t>& lookup, int32_t indexBase, uint32_t indexRange, int32_t* indexSource, uint32_t indexCount, uint32_t indexByteStride);
+
+void findIslands(physx::Array< physx::Array<uint32_t> >& islands, const physx::Array<IntPair>& overlaps, uint32_t indexRange);
+
+// Neighbor-finding utility class
+class NeighborLookup
+{
+public:
+ void setBounds(const BoundsRep* bounds, uint32_t boundsCount, uint32_t boundsByteStride);
+
+ uint32_t getNeighborCount(const uint32_t index) const;
+ const uint32_t* getNeighbors(const uint32_t index) const;
+
+protected:
+ physx::Array<uint32_t> m_neighbors;
+ physx::Array<uint32_t> m_firstNeighbor;
+};
+
+
+// TriangleFrame - calculates interpolation data for triangle quantities
+class TriangleFrame
+{
+ public:
+
+ enum VertexField
+ {
+// Position_x, Position_y, Position_z, // Not interpolating positions
+ Normal_x, Normal_y, Normal_z,
+ Tangent_x, Tangent_y, Tangent_z,
+ Binormal_x, Binormal_y, Binormal_z,
+ UV0_u, UV0_v, UV1_u, UV1_v, UV2_u, UV2_v, UV3_u, UV3_v,
+ Color_r, Color_g, Color_b, Color_a,
+
+ VertexFieldCount
+ };
+
+ TriangleFrame() : m_fieldMask(0) {}
+ TriangleFrame(const ExplicitRenderTriangle& tri, uint64_t fieldMask = 0xFFFFFFFFFFFFFFFFULL)
+ {
+ setFromTriangle(tri, fieldMask);
+ }
+ TriangleFrame(const PxMat44& tm, const PxVec2& uvScale, const PxVec2& uvOffset, uint64_t fieldMask = 0xFFFFFFFFFFFFFFFFULL)
+ {
+ setFlat(tm, uvScale, uvOffset, fieldMask);
+ }
+
+ PX_INLINE void setFromTriangle(const ExplicitRenderTriangle& tri, uint64_t fieldMask = 0xFFFFFFFFFFFFFFFFULL);
+ PX_INLINE void setFlat(const PxMat44& tm, const PxVec2& uvScale, const PxVec2& uvOffset, uint64_t fieldMask = 0xFFFFFFFFFFFFFFFFULL);
+
+ PX_INLINE void interpolateVertexData(Vertex& vertex) const;
+
+ private:
+
+ static size_t s_offsets[VertexFieldCount];
+ PxPlane m_frames[VertexFieldCount];
+ uint64_t m_fieldMask;
+
+ friend class TriangleFrameBuilder;
+};
+
+PX_INLINE void
+TriangleFrame::setFromTriangle(const ExplicitRenderTriangle& tri, uint64_t fieldMask)
+{
+ m_fieldMask = fieldMask;
+
+ PxVec3 p0, p1, p2;
+ p0 = tri.vertices[0].position;
+ p1 = tri.vertices[1].position;
+ p2 = tri.vertices[2].position;
+ const PxVec3 p1xp2 = p1.cross(p2);
+ const PxVec3 p2xp0 = p2.cross(p0);
+ const PxVec3 p0xp1 = p0.cross(p1);
+ const PxVec3 n = p1xp2 + p2xp0 + p0xp1;
+ const float n2 = n.dot(n);
+ if (n2 < PX_EPS_F32 * PX_EPS_F32)
+ {
+ for (uint32_t i = 0; fieldMask != 0 && i < VertexFieldCount; ++i, (fieldMask >>= 1))
+ {
+ if (fieldMask & 1)
+ {
+ m_frames[i] = PxPlane(0, 0, 0, 0);
+ }
+ }
+ return;
+ }
+
+ // Calculate inverse 4x4 matrix (only need first three columns):
+ const PxVec3 nP = n / n2; // determinant is -n2
+ const PxVec3 Q0(nP.z * (p1.y - p2.y) - nP.y * (p1.z - p2.z), nP.z * (p2.y - p0.y) - nP.y * (p2.z - p0.z), nP.z * (p0.y - p1.y) - nP.y * (p0.z - p1.z));
+ const PxVec3 Q1(nP.x * (p1.z - p2.z) - nP.z * (p1.x - p2.x), nP.x * (p2.z - p0.z) - nP.z * (p2.x - p0.x), nP.x * (p0.z - p1.z) - nP.z * (p0.x - p1.x));
+ const PxVec3 Q2(nP.y * (p1.x - p2.x) - nP.x * (p1.y - p2.y), nP.y * (p2.x - p0.x) - nP.x * (p2.y - p0.y), nP.y * (p0.x - p1.x) - nP.x * (p0.y - p1.y));
+ const PxVec3 r(nP.dot(p1xp2), nP.dot(p2xp0), nP.dot(p0xp1));
+
+ for (uint32_t i = 0; fieldMask != 0 && i < VertexFieldCount; ++i, (fieldMask >>= 1))
+ {
+ if (fieldMask & 1)
+ {
+ const size_t offset = s_offsets[i];
+ const PxVec3 vi(*(float*)(((uint8_t*)&tri.vertices[0]) + offset), *(float*)(((uint8_t*)&tri.vertices[1]) + offset), *(float*)(((uint8_t*)&tri.vertices[2]) + offset));
+ m_frames[i] = PxPlane(Q0.dot(vi), Q1.dot(vi), Q2.dot(vi), r.dot(vi));
+ }
+ }
+}
+
+PX_INLINE void
+TriangleFrame::setFlat(const PxMat44& tm, const PxVec2& uvScale, const PxVec2& uvOffset, uint64_t fieldMask)
+{
+ m_fieldMask = fieldMask;
+
+ // Local z ~ normal = tangents[2], x ~ u and tangent = tangents[0], y ~ v and binormal = tangents[1]
+ if ((fieldMask >> Normal_x) & 1)
+ {
+ m_frames[Normal_x] = PxPlane(PxVec3((float)0), tm(0, 2));
+ }
+ if ((fieldMask >> Normal_y) & 1)
+ {
+ m_frames[Normal_y] = PxPlane(PxVec3((float)0), tm(1, 2));
+ }
+ if ((fieldMask >> Normal_z) & 1)
+ {
+ m_frames[Normal_z] = PxPlane(PxVec3((float)0), tm(2, 2));
+ }
+ if ((fieldMask >> Tangent_x) & 1)
+ {
+ m_frames[Tangent_x] = PxPlane(PxVec3((float)0), tm(0, 0));
+ }
+ if ((fieldMask >> Tangent_y) & 1)
+ {
+ m_frames[Tangent_y] = PxPlane(PxVec3((float)0), tm(1, 0));
+ }
+ if ((fieldMask >> Tangent_z) & 1)
+ {
+ m_frames[Tangent_z] = PxPlane(PxVec3((float)0), tm(2, 0));
+ }
+ if ((fieldMask >> Binormal_x) & 1)
+ {
+ m_frames[Binormal_x] = PxPlane(PxVec3((float)0), tm(0, 1));
+ }
+ if ((fieldMask >> Binormal_y) & 1)
+ {
+ m_frames[Binormal_y] = PxPlane(PxVec3((float)0), tm(1, 1));
+ }
+ if ((fieldMask >> Binormal_z) & 1)
+ {
+ m_frames[Binormal_z] = PxPlane(PxVec3((float)0), tm(2, 1));
+ }
+ const PxVec3 psu = (uvScale[0] ? 1 / uvScale[0] : (float)0) * tm.column0.getXYZ();
+ const PxVec3 psv = (uvScale[1] ? 1 / uvScale[1] : (float)0) * tm.column1.getXYZ();
+ if ((fieldMask >> UV0_u) & 1)
+ {
+ m_frames[UV0_u] = PxPlane(psu, uvOffset[0]);
+ }
+ if ((fieldMask >> UV0_v) & 1)
+ {
+ m_frames[UV0_v] = PxPlane(psv, uvOffset[1]);
+ }
+ if ((fieldMask >> UV1_u) & 1)
+ {
+ m_frames[UV1_u] = PxPlane(psu, uvOffset[0]);
+ }
+ if ((fieldMask >> UV1_v) & 1)
+ {
+ m_frames[UV1_v] = PxPlane(psv, uvOffset[1]);
+ }
+ if ((fieldMask >> UV2_u) & 1)
+ {
+ m_frames[UV2_u] = PxPlane(psu, uvOffset[0]);
+ }
+ if ((fieldMask >> UV2_v) & 1)
+ {
+ m_frames[UV2_v] = PxPlane(psv, uvOffset[1]);
+ }
+ if ((fieldMask >> UV3_u) & 1)
+ {
+ m_frames[UV3_u] = PxPlane(psu, uvOffset[0]);
+ }
+ if ((fieldMask >> UV3_v) & 1)
+ {
+ m_frames[UV3_v] = PxPlane(psv, uvOffset[1]);
+ }
+ if ((fieldMask >> Color_r) & 1)
+ {
+ m_frames[Color_r] = PxPlane(PxVec3((float)0), (float)1);
+ }
+ if ((fieldMask >> Color_g) & 1)
+ {
+ m_frames[Color_g] = PxPlane(PxVec3((float)0), (float)1);
+ }
+ if ((fieldMask >> Color_b) & 1)
+ {
+ m_frames[Color_b] = PxPlane(PxVec3((float)0), (float)1);
+ }
+ if ((fieldMask >> Color_a) & 1)
+ {
+ m_frames[Color_a] = PxPlane(PxVec3((float)0), (float)1);
+ }
+}
+
+PX_INLINE void
+TriangleFrame::interpolateVertexData(Vertex& vertex) const
+{
+ uint64_t fieldMask = m_fieldMask;
+ for (uint32_t i = 0; fieldMask != 0 && i < VertexFieldCount; ++i, (fieldMask >>= 1))
+ {
+ if (fieldMask & 1)
+ {
+ float& value = *(float*)(((uint8_t*)&vertex) + s_offsets[i]);
+ value = m_frames[i].distance(vertex.position);
+ }
+ }
+}
+
+class TriangleFrameBuilder
+{
+ public:
+ TriangleFrameBuilder()
+ {
+#define CREATE_TF_OFFSET( field, element ) (size_t)((uintptr_t)&vertex.field.element-(uintptr_t)&vertex)
+#define CREATE_TF_OFFSET_IDX( field, element, index ) (size_t)((uintptr_t)&vertex.field[index].element-(uintptr_t)&vertex)
+
+ Vertex vertex;
+// TriangleFrame::s_offsets[TriangleFrame::Position_x] = CREATE_TF_OFFSET( position, x );
+// TriangleFrame::s_offsets[TriangleFrame::Position_y] = CREATE_TF_OFFSET( position, y );
+// TriangleFrame::s_offsets[TriangleFrame::Position_z] = CREATE_TF_OFFSET( position, z );
+ TriangleFrame::s_offsets[TriangleFrame::Normal_x] = CREATE_TF_OFFSET(normal, x);
+ TriangleFrame::s_offsets[TriangleFrame::Normal_y] = CREATE_TF_OFFSET(normal, y);
+ TriangleFrame::s_offsets[TriangleFrame::Normal_z] = CREATE_TF_OFFSET(normal, z);
+ TriangleFrame::s_offsets[TriangleFrame::Tangent_x] = CREATE_TF_OFFSET(tangent, x);
+ TriangleFrame::s_offsets[TriangleFrame::Tangent_y] = CREATE_TF_OFFSET(tangent, y);
+ TriangleFrame::s_offsets[TriangleFrame::Tangent_z] = CREATE_TF_OFFSET(tangent, z);
+ TriangleFrame::s_offsets[TriangleFrame::Binormal_x] = CREATE_TF_OFFSET(binormal, x);
+ TriangleFrame::s_offsets[TriangleFrame::Binormal_y] = CREATE_TF_OFFSET(binormal, y);
+ TriangleFrame::s_offsets[TriangleFrame::Binormal_z] = CREATE_TF_OFFSET(binormal, z);
+ TriangleFrame::s_offsets[TriangleFrame::UV0_u] = CREATE_TF_OFFSET_IDX(uv, u, 0);
+ TriangleFrame::s_offsets[TriangleFrame::UV0_v] = CREATE_TF_OFFSET_IDX(uv, v, 0);
+ TriangleFrame::s_offsets[TriangleFrame::UV1_u] = CREATE_TF_OFFSET_IDX(uv, u, 1);
+ TriangleFrame::s_offsets[TriangleFrame::UV1_v] = CREATE_TF_OFFSET_IDX(uv, v, 1);
+ TriangleFrame::s_offsets[TriangleFrame::UV2_u] = CREATE_TF_OFFSET_IDX(uv, u, 2);
+ TriangleFrame::s_offsets[TriangleFrame::UV2_v] = CREATE_TF_OFFSET_IDX(uv, v, 2);
+ TriangleFrame::s_offsets[TriangleFrame::UV3_u] = CREATE_TF_OFFSET_IDX(uv, u, 3);
+ TriangleFrame::s_offsets[TriangleFrame::UV3_v] = CREATE_TF_OFFSET_IDX(uv, v, 3);
+ TriangleFrame::s_offsets[TriangleFrame::Color_r] = CREATE_TF_OFFSET(color, r);
+ TriangleFrame::s_offsets[TriangleFrame::Color_g] = CREATE_TF_OFFSET(color, g);
+ TriangleFrame::s_offsets[TriangleFrame::Color_b] = CREATE_TF_OFFSET(color, b);
+ TriangleFrame::s_offsets[TriangleFrame::Color_a] = CREATE_TF_OFFSET(color, a);
+ }
+};
+
+
+// Format conversion utilities
+
+// Explicit data layouts, used for data conversion
+
+typedef uint8_t UBYTE1_TYPE;
+typedef uint8_t UBYTE2_TYPE[2];
+typedef uint8_t UBYTE3_TYPE[3];
+typedef uint8_t UBYTE4_TYPE[4];
+
+typedef uint16_t USHORT1_TYPE;
+typedef uint16_t USHORT2_TYPE[2];
+typedef uint16_t USHORT3_TYPE[3];
+typedef uint16_t USHORT4_TYPE[4];
+
+typedef int16_t SHORT1_TYPE;
+typedef int16_t SHORT2_TYPE[2];
+typedef int16_t SHORT3_TYPE[3];
+typedef int16_t SHORT4_TYPE[4];
+
+typedef uint32_t UINT1_TYPE;
+typedef uint32_t UINT2_TYPE[2];
+typedef uint32_t UINT3_TYPE[3];
+typedef uint32_t UINT4_TYPE[4];
+
+struct R8G8B8A8_TYPE
+{
+ uint8_t r, g, b, a;
+};
+struct B8G8R8A8_TYPE
+{
+ uint8_t b, g, r, a;
+};
+struct R32G32B32A32_FLOAT_TYPE
+{
+ float r, g, b, a;
+};
+struct B32G32R32A32_FLOAT_TYPE
+{
+ float b, g, r, a;
+};
+
+typedef uint8_t BYTE_UNORM1_TYPE;
+typedef uint8_t BYTE_UNORM2_TYPE[2];
+typedef uint8_t BYTE_UNORM3_TYPE[3];
+typedef uint8_t BYTE_UNORM4_TYPE[4];
+
+typedef uint16_t SHORT_UNORM1_TYPE;
+typedef uint16_t SHORT_UNORM2_TYPE[2];
+typedef uint16_t SHORT_UNORM3_TYPE[3];
+typedef uint16_t SHORT_UNORM4_TYPE[4];
+
+typedef int8_t BYTE_SNORM1_TYPE;
+typedef int8_t BYTE_SNORM2_TYPE[2];
+typedef int8_t BYTE_SNORM3_TYPE[3];
+typedef int8_t BYTE_SNORM4_TYPE[4];
+
+typedef int16_t SHORT_SNORM1_TYPE;
+typedef int16_t SHORT_SNORM2_TYPE[2];
+typedef int16_t SHORT_SNORM3_TYPE[3];
+typedef int16_t SHORT_SNORM4_TYPE[4];
+
+typedef uint16_t HALF1_TYPE;
+typedef uint16_t HALF2_TYPE[2];
+typedef uint16_t HALF3_TYPE[3];
+typedef uint16_t HALF4_TYPE[4];
+
+typedef float FLOAT1_TYPE;
+typedef float FLOAT2_TYPE[2];
+typedef float FLOAT3_TYPE[3];
+typedef float FLOAT4_TYPE[4];
+
+typedef PxMat44 FLOAT4x4_TYPE;
+typedef PxMat33 FLOAT3x3_TYPE;
+
+typedef PxQuat FLOAT4_QUAT_TYPE;
+typedef int8_t BYTE_SNORM4_QUATXYZW_TYPE[4];
+typedef int16_t SHORT_SNORM4_QUATXYZW_TYPE[4];
+
+
+// Data converters
+
+// USHORT1_TYPE -> UINT1_TYPE
+PX_INLINE void convert_UINT1_from_USHORT1(UINT1_TYPE& dst, const USHORT1_TYPE& src)
+{
+ dst = (uint32_t)src;
+}
+
+// USHORT2_TYPE -> UINT2_TYPE
+PX_INLINE void convert_UINT2_from_USHORT2(UINT2_TYPE& dst, const USHORT2_TYPE& src)
+{
+ convert_UINT1_from_USHORT1(dst[0], src[0]);
+ convert_UINT1_from_USHORT1(dst[1], src[1]);
+}
+
+// USHORT3_TYPE -> UINT3_TYPE
+PX_INLINE void convert_UINT3_from_USHORT3(UINT3_TYPE& dst, const USHORT3_TYPE& src)
+{
+ convert_UINT1_from_USHORT1(dst[0], src[0]);
+ convert_UINT1_from_USHORT1(dst[1], src[1]);
+ convert_UINT1_from_USHORT1(dst[2], src[2]);
+}
+
+// USHORT4_TYPE -> UINT4_TYPE
+PX_INLINE void convert_UINT4_from_USHORT4(UINT4_TYPE& dst, const USHORT4_TYPE& src)
+{
+ convert_UINT1_from_USHORT1(dst[0], src[0]);
+ convert_UINT1_from_USHORT1(dst[1], src[1]);
+ convert_UINT1_from_USHORT1(dst[2], src[2]);
+ convert_UINT1_from_USHORT1(dst[3], src[3]);
+}
+
+// UINT1_TYPE -> USHORT1_TYPE
+PX_INLINE void convert_USHORT1_from_UINT1(USHORT1_TYPE& dst, const UINT1_TYPE& src)
+{
+ dst = (uint16_t)src;
+}
+
+// UINT2_TYPE -> USHORT2_TYPE
+PX_INLINE void convert_USHORT2_from_UINT2(USHORT2_TYPE& dst, const UINT2_TYPE& src)
+{
+ convert_USHORT1_from_UINT1(dst[0], src[0]);
+ convert_USHORT1_from_UINT1(dst[1], src[1]);
+}
+
+// UINT3_TYPE -> USHORT3_TYPE
+PX_INLINE void convert_USHORT3_from_UINT3(USHORT3_TYPE& dst, const UINT3_TYPE& src)
+{
+ convert_USHORT1_from_UINT1(dst[0], src[0]);
+ convert_USHORT1_from_UINT1(dst[1], src[1]);
+ convert_USHORT1_from_UINT1(dst[2], src[2]);
+}
+
+// UINT4_TYPE -> USHORT4_TYPE
+PX_INLINE void convert_USHORT4_from_UINT4(USHORT4_TYPE& dst, const UINT4_TYPE& src)
+{
+ convert_USHORT1_from_UINT1(dst[0], src[0]);
+ convert_USHORT1_from_UINT1(dst[1], src[1]);
+ convert_USHORT1_from_UINT1(dst[2], src[2]);
+ convert_USHORT1_from_UINT1(dst[3], src[3]);
+}
+
+// BYTE_SNORM1_TYPE -> FLOAT1_TYPE
+PX_INLINE void convert_FLOAT1_from_BYTE_SNORM1(FLOAT1_TYPE& dst, const BYTE_SNORM1_TYPE& src)
+{
+ dst = (float)src / 127.0f;
+}
+
+// BYTE_SNORM2_TYPE -> FLOAT2_TYPE
+PX_INLINE void convert_FLOAT2_from_BYTE_SNORM2(FLOAT2_TYPE& dst, const BYTE_SNORM2_TYPE& src)
+{
+ convert_FLOAT1_from_BYTE_SNORM1(dst[0], src[0]);
+ convert_FLOAT1_from_BYTE_SNORM1(dst[1], src[1]);
+}
+
+// BYTE_SNORM3_TYPE -> FLOAT3_TYPE
+PX_INLINE void convert_FLOAT3_from_BYTE_SNORM3(FLOAT3_TYPE& dst, const BYTE_SNORM3_TYPE& src)
+{
+ convert_FLOAT1_from_BYTE_SNORM1(dst[0], src[0]);
+ convert_FLOAT1_from_BYTE_SNORM1(dst[1], src[1]);
+ convert_FLOAT1_from_BYTE_SNORM1(dst[2], src[2]);
+}
+
+// BYTE_SNORM4_TYPE -> FLOAT4_TYPE
+PX_INLINE void convert_FLOAT4_from_BYTE_SNORM4(FLOAT4_TYPE& dst, const BYTE_SNORM4_TYPE& src)
+{
+ convert_FLOAT1_from_BYTE_SNORM1(dst[0], src[0]);
+ convert_FLOAT1_from_BYTE_SNORM1(dst[1], src[1]);
+ convert_FLOAT1_from_BYTE_SNORM1(dst[2], src[2]);
+ convert_FLOAT1_from_BYTE_SNORM1(dst[3], src[3]);
+}
+
+// BYTE_SNORM4_QUATXYZW_TYPE -> FLOAT4_QUAT_TYPE
+PX_INLINE void convert_FLOAT4_QUAT_from_BYTE_SNORM4_QUATXYZW(FLOAT4_QUAT_TYPE& dst, const BYTE_SNORM4_QUATXYZW_TYPE& src)
+{
+ convert_FLOAT1_from_BYTE_SNORM1(dst.x, src[0]);
+ convert_FLOAT1_from_BYTE_SNORM1(dst.y, src[1]);
+ convert_FLOAT1_from_BYTE_SNORM1(dst.z, src[2]);
+ convert_FLOAT1_from_BYTE_SNORM1(dst.w, src[3]);
+}
+
+// SHORT_SNORM1_TYPE -> FLOAT1_TYPE
+PX_INLINE void convert_FLOAT1_from_SHORT_SNORM1(FLOAT1_TYPE& dst, const SHORT_SNORM1_TYPE& src)
+{
+ dst = (float)src / 32767.0f;
+}
+
+// SHORT_SNORM2_TYPE -> FLOAT2_TYPE
+PX_INLINE void convert_FLOAT2_from_SHORT_SNORM2(FLOAT2_TYPE& dst, const SHORT_SNORM2_TYPE& src)
+{
+ convert_FLOAT1_from_SHORT_SNORM1(dst[0], src[0]);
+ convert_FLOAT1_from_SHORT_SNORM1(dst[1], src[1]);
+}
+
+// SHORT_SNORM3_TYPE -> FLOAT3_TYPE
+PX_INLINE void convert_FLOAT3_from_SHORT_SNORM3(FLOAT3_TYPE& dst, const SHORT_SNORM3_TYPE& src)
+{
+ convert_FLOAT1_from_SHORT_SNORM1(dst[0], src[0]);
+ convert_FLOAT1_from_SHORT_SNORM1(dst[1], src[1]);
+ convert_FLOAT1_from_SHORT_SNORM1(dst[2], src[2]);
+}
+
+// SHORT_SNORM4_TYPE -> FLOAT4_TYPE
+PX_INLINE void convert_FLOAT4_from_SHORT_SNORM4(FLOAT4_TYPE& dst, const SHORT_SNORM4_TYPE& src)
+{
+ convert_FLOAT1_from_SHORT_SNORM1(dst[0], src[0]);
+ convert_FLOAT1_from_SHORT_SNORM1(dst[1], src[1]);
+ convert_FLOAT1_from_SHORT_SNORM1(dst[2], src[2]);
+ convert_FLOAT1_from_SHORT_SNORM1(dst[3], src[3]);
+}
+
+// SHORT_SNORM4_QUATXYZW_TYPE -> FLOAT4_QUAT_TYPE
+PX_INLINE void convert_FLOAT4_QUAT_from_SHORT_SNORM4_QUATXYZW(FLOAT4_QUAT_TYPE& dst, const SHORT_SNORM4_QUATXYZW_TYPE& src)
+{
+ convert_FLOAT1_from_SHORT_SNORM1(dst.x, src[0]);
+ convert_FLOAT1_from_SHORT_SNORM1(dst.y, src[1]);
+ convert_FLOAT1_from_SHORT_SNORM1(dst.z, src[2]);
+ convert_FLOAT1_from_SHORT_SNORM1(dst.w, src[3]);
+}
+
+// FLOAT1_TYPE -> BYTE_SNORM1_TYPE
+PX_INLINE void convert_BYTE_SNORM1_from_FLOAT1(BYTE_SNORM1_TYPE& dst, const FLOAT1_TYPE& src)
+{
+ dst = (int8_t)((int16_t)(src * 127.0f + 127.5f) - 127); // Doing it this way to avoid nonuniform mapping near zero
+}
+
+// FLOAT2_TYPE -> BYTE_SNORM2_TYPE
+PX_INLINE void convert_BYTE_SNORM2_from_FLOAT2(BYTE_SNORM2_TYPE& dst, const FLOAT2_TYPE& src)
+{
+ convert_BYTE_SNORM1_from_FLOAT1(dst[0], src[0]);
+ convert_BYTE_SNORM1_from_FLOAT1(dst[1], src[1]);
+}
+
+// FLOAT3_TYPE -> BYTE_SNORM3_TYPE
+PX_INLINE void convert_BYTE_SNORM3_from_FLOAT3(BYTE_SNORM3_TYPE& dst, const FLOAT3_TYPE& src)
+{
+ convert_BYTE_SNORM1_from_FLOAT1(dst[0], src[0]);
+ convert_BYTE_SNORM1_from_FLOAT1(dst[1], src[1]);
+ convert_BYTE_SNORM1_from_FLOAT1(dst[2], src[2]);
+}
+
+// FLOAT4_TYPE -> BYTE_SNORM4_TYPE
+PX_INLINE void convert_BYTE_SNORM4_from_FLOAT4(BYTE_SNORM4_TYPE& dst, const FLOAT4_TYPE& src)
+{
+ convert_BYTE_SNORM1_from_FLOAT1(dst[0], src[0]);
+ convert_BYTE_SNORM1_from_FLOAT1(dst[1], src[1]);
+ convert_BYTE_SNORM1_from_FLOAT1(dst[2], src[2]);
+ convert_BYTE_SNORM1_from_FLOAT1(dst[3], src[3]);
+}
+
+// FLOAT4_QUAT_TYPE -> BYTE_SNORM4_QUATXYZW_TYPE
+PX_INLINE void convert_BYTE_SNORM4_QUATXYZW_from_FLOAT4_QUAT(BYTE_SNORM4_QUATXYZW_TYPE& dst, const FLOAT4_QUAT_TYPE& src)
+{
+ convert_BYTE_SNORM1_from_FLOAT1(dst[0], src.x);
+ convert_BYTE_SNORM1_from_FLOAT1(dst[1], src.y);
+ convert_BYTE_SNORM1_from_FLOAT1(dst[2], src.z);
+ convert_BYTE_SNORM1_from_FLOAT1(dst[3], src.w);
+}
+
+// FLOAT1_TYPE -> SHORT_SNORM1_TYPE
+PX_INLINE void convert_SHORT_SNORM1_from_FLOAT1(SHORT_SNORM1_TYPE& dst, const FLOAT1_TYPE& src)
+{
+ dst = (int16_t)((int32_t)(src * 32767.0f + 32767.5f) - 32767); // Doing it this way to avoid nonuniform mapping near zero
+}
+
+// FLOAT2_TYPE -> SHORT_SNORM2_TYPE
+PX_INLINE void convert_SHORT_SNORM2_from_FLOAT2(SHORT_SNORM2_TYPE& dst, const FLOAT2_TYPE& src)
+{
+ convert_SHORT_SNORM1_from_FLOAT1(dst[0], src[0]);
+ convert_SHORT_SNORM1_from_FLOAT1(dst[1], src[1]);
+}
+
+// FLOAT3_TYPE -> SHORT_SNORM3_TYPE
+PX_INLINE void convert_SHORT_SNORM3_from_FLOAT3(SHORT_SNORM3_TYPE& dst, const FLOAT3_TYPE& src)
+{
+ convert_SHORT_SNORM1_from_FLOAT1(dst[0], src[0]);
+ convert_SHORT_SNORM1_from_FLOAT1(dst[1], src[1]);
+ convert_SHORT_SNORM1_from_FLOAT1(dst[2], src[2]);
+}
+
+// FLOAT4_TYPE -> SHORT_SNORM4_TYPE
+PX_INLINE void convert_SHORT_SNORM4_from_FLOAT4(SHORT_SNORM4_TYPE& dst, const FLOAT4_TYPE& src)
+{
+ convert_SHORT_SNORM1_from_FLOAT1(dst[0], src[0]);
+ convert_SHORT_SNORM1_from_FLOAT1(dst[1], src[1]);
+ convert_SHORT_SNORM1_from_FLOAT1(dst[2], src[2]);
+ convert_SHORT_SNORM1_from_FLOAT1(dst[3], src[3]);
+}
+
+// FLOAT4_QUAT_TYPE -> SHORT_SNORM4_QUATXYZW_TYPE
+PX_INLINE void convert_SHORT_SNORM4_QUATXYZW_from_FLOAT4_QUAT(SHORT_SNORM4_QUATXYZW_TYPE& dst, const FLOAT4_QUAT_TYPE& src)
+{
+ convert_SHORT_SNORM1_from_FLOAT1(dst[0], src.x);
+ convert_SHORT_SNORM1_from_FLOAT1(dst[1], src.y);
+ convert_SHORT_SNORM1_from_FLOAT1(dst[2], src.z);
+ convert_SHORT_SNORM1_from_FLOAT1(dst[3], src.w);
+}
+
+// Color format conversions
+PX_INLINE void convert_B8G8R8A8_from_R8G8B8A8(B8G8R8A8_TYPE& dst, const R8G8B8A8_TYPE& src)
+{
+ dst.r = src.r;
+ dst.g = src.g;
+ dst.b = src.b;
+ dst.a = src.a;
+}
+
+PX_INLINE void convert_R8G8B8A8_from_B8G8R8A8(R8G8B8A8_TYPE& dst, const B8G8R8A8_TYPE& src)
+{
+ dst.r = src.r;
+ dst.g = src.g;
+ dst.b = src.b;
+ dst.a = src.a;
+}
+
+PX_INLINE void convert_R32G32B32A32_FLOAT_from_R8G8B8A8(R32G32B32A32_FLOAT_TYPE& dst, const R8G8B8A8_TYPE& src)
+{
+ (VertexColor&)dst = VertexColor((const ColorRGBA&)src);
+}
+
+PX_INLINE void convert_R8G8B8A8_from_R32G32B32A32_FLOAT(R8G8B8A8_TYPE& dst, const R32G32B32A32_FLOAT_TYPE& src)
+{
+ (ColorRGBA&)dst = ((const VertexColor&)src).toColorRGBA();
+}
+
+PX_INLINE void convert_B32G32R32A32_FLOAT_from_R8G8B8A8(B32G32R32A32_FLOAT_TYPE& dst, const R8G8B8A8_TYPE& src)
+{
+ (VertexColor&)dst = VertexColor((const ColorRGBA&)src);
+ float t = dst.r;
+ dst.r = dst.b;
+ dst.b = t;
+}
+
+PX_INLINE void convert_R8G8B8A8_from_B32G32R32A32_FLOAT(R8G8B8A8_TYPE& dst, const B32G32R32A32_FLOAT_TYPE& src)
+{
+ (ColorRGBA&)dst = ((const VertexColor&)src).toColorRGBA();
+ uint8_t t = dst.r;
+ dst.r = dst.b;
+ dst.b = t;
+}
+
+PX_INLINE void convert_R32G32B32A32_FLOAT_from_B8G8R8A8(R32G32B32A32_FLOAT_TYPE& dst, const B8G8R8A8_TYPE& src)
+{
+ (VertexColor&)dst = VertexColor((const ColorRGBA&)src);
+ float t = dst.r;
+ dst.r = dst.b;
+ dst.b = t;
+}
+
+PX_INLINE void convert_B8G8R8A8_from_R32G32B32A32_FLOAT(B8G8R8A8_TYPE& dst, const R32G32B32A32_FLOAT_TYPE& src)
+{
+ (ColorRGBA&)dst = ((const VertexColor&)src).toColorRGBA();
+ uint8_t t = dst.r;
+ dst.r = dst.b;
+ dst.b = t;
+}
+
+PX_INLINE void convert_B32G32R32A32_FLOAT_from_B8G8R8A8(B32G32R32A32_FLOAT_TYPE& dst, const B8G8R8A8_TYPE& src)
+{
+ (VertexColor&)dst = VertexColor((const ColorRGBA&)src);
+}
+
+PX_INLINE void convert_B8G8R8A8_from_B32G32R32A32_FLOAT(B8G8R8A8_TYPE& dst, const B32G32R32A32_FLOAT_TYPE& src)
+{
+ (ColorRGBA&)dst = ((const VertexColor&)src).toColorRGBA();
+}
+
+PX_INLINE void convert_B32G32R32A32_FLOAT_from_R32G32B32A32_FLOAT(B32G32R32A32_FLOAT_TYPE& dst, const R32G32B32A32_FLOAT_TYPE& src)
+{
+ dst.r = src.r;
+ dst.g = src.g;
+ dst.b = src.b;
+ dst.a = src.a;
+}
+
+PX_INLINE void convert_R32G32B32A32_FLOAT_from_B32G32R32A32_FLOAT(R32G32B32A32_FLOAT_TYPE& dst, const B32G32R32A32_FLOAT_TYPE& src)
+{
+ dst.r = src.r;
+ dst.g = src.g;
+ dst.b = src.b;
+ dst.a = src.a;
+}
+
+// Data conversion macros
+#define HANDLE_CONVERT1( _DstFormat, _SrcFormat ) \
+ case RenderDataFormat::_DstFormat : \
+ if( srcFormat == RenderDataFormat::_SrcFormat ) \
+ { \
+ convert_##_DstFormat##_from_##_SrcFormat( ((_DstFormat##_TYPE*)dst)[dstIndex], ((const _SrcFormat##_TYPE*)src)[srcIndex] ); \
+ } \
+ break;
+
+#define HANDLE_CONVERT2( _DstFormat, _SrcFormat1, _SrcFormat2 ) \
+ case RenderDataFormat::_DstFormat : \
+ if( srcFormat == RenderDataFormat::_SrcFormat1 ) \
+ { \
+ convert_##_DstFormat##_from_##_SrcFormat1( ((_DstFormat##_TYPE*)dst)[dstIndex], ((const _SrcFormat1##_TYPE*)src)[srcIndex] ); \
+ } \
+ else if( srcFormat == RenderDataFormat::_SrcFormat2 ) \
+ { \
+ convert_##_DstFormat##_from_##_SrcFormat2( ((_DstFormat##_TYPE*)dst)[dstIndex], ((const _SrcFormat2##_TYPE*)src)[srcIndex] ); \
+ } \
+ break;
+
+#define HANDLE_CONVERT3( _DstFormat, _SrcFormat1, _SrcFormat2, _SrcFormat3 ) \
+ case RenderDataFormat::_DstFormat : \
+ if( srcFormat == RenderDataFormat::_SrcFormat1 ) \
+ { \
+ convert_##_DstFormat##_from_##_SrcFormat1( ((_DstFormat##_TYPE*)dst)[dstIndex], ((const _SrcFormat1##_TYPE*)src)[srcIndex] ); \
+ } \
+ else if( srcFormat == RenderDataFormat::_SrcFormat2 ) \
+ { \
+ convert_##_DstFormat##_from_##_SrcFormat2( ((_DstFormat##_TYPE*)dst)[dstIndex], ((const _SrcFormat2##_TYPE*)src)[srcIndex] ); \
+ } \
+ else if( srcFormat == RenderDataFormat::_SrcFormat3 ) \
+ { \
+ convert_##_DstFormat##_from_##_SrcFormat3( ((_DstFormat##_TYPE*)dst)[dstIndex], ((const _SrcFormat3##_TYPE*)src)[srcIndex] ); \
+ } \
+ break;
+
+// ... etc.
+
+PX_INLINE bool copyRenderVertexData(void* dst, RenderDataFormat::Enum dstFormat, uint32_t dstIndex, const void* src, RenderDataFormat::Enum srcFormat, uint32_t srcIndex)
+{
+ if (dstFormat == srcFormat)
+ {
+ // Direct data copy
+ if (dstFormat != RenderDataFormat::UNSPECIFIED)
+ {
+ uint8_t* srcPtr = (uint8_t*)src;
+ uint8_t* dstPtr = (uint8_t*)dst;
+
+ const uint32_t size = RenderDataFormat::getFormatDataSize(dstFormat);
+ memcpy(dstPtr + (dstIndex * size), srcPtr + (srcIndex * size), size);
+ }
+ return true;
+ }
+
+ switch (dstFormat)
+ {
+ case RenderDataFormat::UNSPECIFIED:
+ break; // The simplest case, do nothing
+
+ // Put format converters here
+
+ HANDLE_CONVERT1(USHORT1, UINT1)
+ HANDLE_CONVERT1(USHORT2, UINT2)
+ HANDLE_CONVERT1(USHORT3, UINT3)
+ HANDLE_CONVERT1(USHORT4, UINT4)
+
+ HANDLE_CONVERT1(UINT1, USHORT1)
+ HANDLE_CONVERT1(UINT2, USHORT2)
+ HANDLE_CONVERT1(UINT3, USHORT3)
+ HANDLE_CONVERT1(UINT4, USHORT4)
+
+ HANDLE_CONVERT1(BYTE_SNORM1, FLOAT1)
+ HANDLE_CONVERT1(BYTE_SNORM2, FLOAT2)
+ HANDLE_CONVERT1(BYTE_SNORM3, FLOAT3)
+ HANDLE_CONVERT1(BYTE_SNORM4, FLOAT4)
+ HANDLE_CONVERT1(BYTE_SNORM4_QUATXYZW, FLOAT4_QUAT)
+ HANDLE_CONVERT1(SHORT_SNORM1, FLOAT1)
+ HANDLE_CONVERT1(SHORT_SNORM2, FLOAT2)
+ HANDLE_CONVERT1(SHORT_SNORM3, FLOAT3)
+ HANDLE_CONVERT1(SHORT_SNORM4, FLOAT4)
+ HANDLE_CONVERT1(SHORT_SNORM4_QUATXYZW, FLOAT4_QUAT)
+
+ HANDLE_CONVERT2(FLOAT1, BYTE_SNORM1, SHORT_SNORM1)
+ HANDLE_CONVERT2(FLOAT2, BYTE_SNORM2, SHORT_SNORM2)
+ HANDLE_CONVERT2(FLOAT3, BYTE_SNORM3, SHORT_SNORM3)
+ HANDLE_CONVERT2(FLOAT4, BYTE_SNORM4, SHORT_SNORM4)
+ HANDLE_CONVERT2(FLOAT4_QUAT, BYTE_SNORM4_QUATXYZW, SHORT_SNORM4_QUATXYZW)
+
+ HANDLE_CONVERT3(R8G8B8A8, B8G8R8A8, R32G32B32A32_FLOAT, B32G32R32A32_FLOAT)
+ HANDLE_CONVERT3(B8G8R8A8, R8G8B8A8, R32G32B32A32_FLOAT, B32G32R32A32_FLOAT)
+ HANDLE_CONVERT3(R32G32B32A32_FLOAT, R8G8B8A8, B8G8R8A8, B32G32R32A32_FLOAT)
+ HANDLE_CONVERT3(B32G32R32A32_FLOAT, R8G8B8A8, B8G8R8A8, R32G32B32A32_FLOAT)
+
+ default:
+ {
+ PX_ALWAYS_ASSERT(); // Format conversion not handled
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool copyRenderVertexBuffer(void* dst, RenderDataFormat::Enum dstFormat, uint32_t dstStride, uint32_t dstStart,
+ const void* src, RenderDataFormat::Enum srcFormat, uint32_t srcStride, uint32_t srcStart,
+ uint32_t numVertices, int32_t* invMap = NULL);
+
+/*
+ Local utilities
+ */
+PX_INLINE bool vertexSemanticFormatValid(RenderVertexSemantic::Enum semantic, RenderDataFormat::Enum format)
+{
+ switch (semantic)
+ {
+ case RenderVertexSemantic::POSITION:
+ return format == RenderDataFormat::FLOAT3;
+ case RenderVertexSemantic::NORMAL:
+ case RenderVertexSemantic::BINORMAL:
+ return format == RenderDataFormat::FLOAT3 || format == RenderDataFormat::BYTE_SNORM3;
+ case RenderVertexSemantic::TANGENT:
+ return format == RenderDataFormat::FLOAT3 || format == RenderDataFormat::BYTE_SNORM3 ||
+ format == RenderDataFormat::FLOAT4 || format == RenderDataFormat::BYTE_SNORM4;
+ case RenderVertexSemantic::COLOR:
+ return format == RenderDataFormat::R8G8B8A8 || format == RenderDataFormat::B8G8R8A8;
+ case RenderVertexSemantic::TEXCOORD0:
+ case RenderVertexSemantic::TEXCOORD1:
+ case RenderVertexSemantic::TEXCOORD2:
+ case RenderVertexSemantic::TEXCOORD3:
+ return format == RenderDataFormat::FLOAT2; // Not supporting other formats yet
+ case RenderVertexSemantic::DISPLACEMENT_TEXCOORD:
+ return format == RenderDataFormat::FLOAT2 || format == RenderDataFormat::FLOAT3;
+ case RenderVertexSemantic::DISPLACEMENT_FLAGS:
+ return format == RenderDataFormat::UINT1 || format == RenderDataFormat::USHORT1;
+ case RenderVertexSemantic::BONE_INDEX:
+ return format == RenderDataFormat::USHORT1 ||
+ format == RenderDataFormat::USHORT2 ||
+ format == RenderDataFormat::USHORT3 ||
+ format == RenderDataFormat::USHORT4; // Not supporting other formats yet
+ case RenderVertexSemantic::BONE_WEIGHT:
+ return format == RenderDataFormat::FLOAT1 ||
+ format == RenderDataFormat::FLOAT2 ||
+ format == RenderDataFormat::FLOAT3 ||
+ format == RenderDataFormat::FLOAT4; // Not supporting other formats yet
+ default:
+ return false;
+ }
+}
+
+PX_INLINE uint32_t vertexSemanticFormatElementCount(RenderVertexSemantic::Enum semantic, RenderDataFormat::Enum format)
+{
+ switch (semantic)
+ {
+ case RenderVertexSemantic::CUSTOM:
+ case RenderVertexSemantic::POSITION:
+ case RenderVertexSemantic::NORMAL:
+ case RenderVertexSemantic::TANGENT:
+ case RenderVertexSemantic::BINORMAL:
+ case RenderVertexSemantic::COLOR:
+ case RenderVertexSemantic::TEXCOORD0:
+ case RenderVertexSemantic::TEXCOORD1:
+ case RenderVertexSemantic::TEXCOORD2:
+ case RenderVertexSemantic::TEXCOORD3:
+ case RenderVertexSemantic::DISPLACEMENT_TEXCOORD:
+ case RenderVertexSemantic::DISPLACEMENT_FLAGS:
+ return 1;
+ case RenderVertexSemantic::BONE_INDEX:
+ switch (format)
+ {
+ case RenderDataFormat::USHORT1:
+ return 1;
+ case RenderDataFormat::USHORT2:
+ return 2;
+ case RenderDataFormat::USHORT3:
+ return 3;
+ case RenderDataFormat::USHORT4:
+ return 4;
+ default:
+ break;
+ }
+ return 0;
+ case RenderVertexSemantic::BONE_WEIGHT:
+ switch (format)
+ {
+ case RenderDataFormat::FLOAT1:
+ return 1;
+ case RenderDataFormat::FLOAT2:
+ return 2;
+ case RenderDataFormat::FLOAT3:
+ return 3;
+ case RenderDataFormat::FLOAT4:
+ return 4;
+ default:
+ break;
+ }
+ return 0;
+ default:
+ return 0;
+ }
+}
+
+
+}
+} // end namespace apex
+
+
+#endif // __APEXSHAREDUTILS_H__