diff options
| author | git perforce import user <a@b> | 2016-10-25 12:29:14 -0600 |
|---|---|---|
| committer | Sheikh Dawood Abdul Ajees <Sheikh Dawood Abdul Ajees> | 2016-10-25 18:56:37 -0500 |
| commit | 3dfe2108cfab31ba3ee5527e217d0d8e99a51162 (patch) | |
| tree | fa6485c169e50d7415a651bf838f5bcd0fd3bfbd /APEX_1.4/common/src | |
| download | physx-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/src')
35 files changed, 20428 insertions, 0 deletions
diff --git a/APEX_1.4/common/src/ApexActor.cpp b/APEX_1.4/common/src/ApexActor.cpp new file mode 100644 index 00000000..55a5c0eb --- /dev/null +++ b/APEX_1.4/common/src/ApexActor.cpp @@ -0,0 +1,94 @@ +/* + * 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. + */ + + +#include "Apex.h" +#include "ApexActor.h" +#include "ApexContext.h" + +#include "RenderDebugInterface.h" +#include "SceneIntl.h" + +#include "PsAtomic.h" + +namespace nvidia +{ +namespace apex +{ + +#if UNIQUE_ACTOR_ID +int32_t ApexActor::mUniqueActorIdCounter = 0; +#endif + +ApexActor::ApexActor() : mInRelease(false), mEnableDebugVisualization(true) +{ +#if UNIQUE_ACTOR_ID + mUniqueActorId = shdfnd::atomicIncrement(&mUniqueActorIdCounter); +#endif +} + + +ApexActor::~ApexActor() +{ + destroy(); +} + +void ApexActor::addSelfToContext(ApexContext& ctx, ApexActor* actorPtr) +{ + ContextTrack t; + + t.ctx = &ctx; + t.index = ctx.addActor(*this, actorPtr); + mContexts.pushBack(t); +} + +void ApexActor::updateIndex(ApexContext& ctx, uint32_t index) +{ + for (uint32_t i = 0 ; i < mContexts.size() ; i++) + { + ContextTrack& t = mContexts[i]; + if (t.ctx == &ctx) + { + t.index = index; + break; + } + } +} + +bool ApexActor::findSelfInContext(ApexContext& ctx) +{ + for (uint32_t i = 0 ; i < mContexts.size() ; i++) + { + ContextTrack& t = mContexts[i]; + if (t.ctx == &ctx) + { + return true; + } + } + + return false; +} + +void ApexActor::destroy() +{ + mInRelease = true; + + renderDataLock(); + + for (uint32_t i = 0 ; i < mContexts.size() ; i++) + { + ContextTrack& t = mContexts[i]; + t.ctx->removeActorAtIndex(t.index); + } + mContexts.clear(); +} + +} +} // end namespace nvidia::apex diff --git a/APEX_1.4/common/src/ApexAssetAuthoring.cpp b/APEX_1.4/common/src/ApexAssetAuthoring.cpp new file mode 100644 index 00000000..14845ff1 --- /dev/null +++ b/APEX_1.4/common/src/ApexAssetAuthoring.cpp @@ -0,0 +1,121 @@ +/* + * 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. + */ + + +#include "ApexAssetAuthoring.h" + +#include "P4Info.h" +#include "PsString.h" + +#include "PhysXSDKVersion.h" +#include "ApexSDKIntl.h" + + + +namespace nvidia +{ +namespace apex +{ + + +void ApexAssetAuthoring::setToolString(const char* toolName, const char* toolVersion, uint32_t toolChangelist) +{ +#ifdef WITHOUT_APEX_AUTHORING + PX_UNUSED(toolName); + PX_UNUSED(toolVersion); + PX_UNUSED(toolChangelist); +#else + const uint32_t buflen = 256; + char buf[buflen]; + nvidia::strlcpy(buf, buflen, toolName); + nvidia::strlcat(buf, buflen, " "); + + if (toolVersion != NULL) + { + nvidia::strlcat(buf, buflen, toolVersion); + nvidia::strlcat(buf, buflen, ":"); + } + + if (toolChangelist == 0) + { + toolChangelist = P4_TOOLS_CHANGELIST; + } + + { + char buf2[14]; + shdfnd::snprintf(buf2, 14, "CL %d", toolChangelist); + nvidia::strlcat(buf, buflen, buf2); + nvidia::strlcat(buf, buflen, " "); + } + + { +#ifdef WIN64 + nvidia::strlcat(buf, buflen, "Win64 "); +#elif defined(WIN32) + nvidia::strlcat(buf, buflen, "Win32 "); +#endif + } + + { + nvidia::strlcat(buf, buflen, "(Apex "); + nvidia::strlcat(buf, buflen, P4_APEX_VERSION_STRING); + char buf2[20]; + shdfnd::snprintf(buf2, 20, ", CL %d, ", P4_CHANGELIST); + nvidia::strlcat(buf, buflen, buf2); +#ifdef _DEBUG + nvidia::strlcat(buf, buflen, "DEBUG "); +#elif defined(PHYSX_PROFILE_SDK) + nvidia::strlcat(buf, buflen, "PROFILE "); +#endif + nvidia::strlcat(buf, buflen, P4_APEX_BRANCH); + nvidia::strlcat(buf, buflen, ") "); + } + + { + nvidia::strlcat(buf, buflen, "(PhysX "); + + char buf2[10] = { 0 }; +#if PX_PHYSICS_VERSION_MAJOR == 0 + shdfnd::snprintf(buf2, 10, "No) "); +#elif PX_PHYSICS_VERSION_MAJOR == 3 + shdfnd::snprintf(buf2, 10, "%d.%d) ", PX_PHYSICS_VERSION_MAJOR, PX_PHYSICS_VERSION_MINOR); +#endif + nvidia::strlcat(buf, buflen, buf2); + } + + + nvidia::strlcat(buf, buflen, "Apex Build Time: "); + nvidia::strlcat(buf, buflen, P4_BUILD_TIME); + + nvidia::strlcat(buf, buflen, "Distribution author: "); + nvidia::strlcat(buf, buflen, AUTHOR_DISTRO); + + nvidia::strlcat(buf, buflen, "The reason for the creation of the distribution: "); + nvidia::strlcat(buf, buflen, REASON_DISTRO); + + //uint32_t len = strlen(buf); + //len = len; + + //"<toolName> <toolVersion>:<toolCL> <platform> (Apex <apexVersion>, CL <apexCL> <apexConfiguration> <apexBranch>) (PhysX <physxVersion>) <toolBuildDate>" + + setToolString(buf); +#endif +} + + + +void ApexAssetAuthoring::setToolString(const char* /*toolString*/) +{ + PX_ALWAYS_ASSERT(); + APEX_INVALID_OPERATION("Not Implemented."); +} + +} // namespace apex +} // namespace nvidia
\ No newline at end of file diff --git a/APEX_1.4/common/src/ApexAssetTracker.cpp b/APEX_1.4/common/src/ApexAssetTracker.cpp new file mode 100644 index 00000000..2e442bbc --- /dev/null +++ b/APEX_1.4/common/src/ApexAssetTracker.cpp @@ -0,0 +1,303 @@ +/* + * 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. + */ + + +#include "ApexAssetTracker.h" +#include "Apex.h" +#include "ApexSDKIntl.h" +#include "ApexResource.h" +#include "ApexSDKHelpers.h" +#include "AuthorableObjectIntl.h" + +namespace nvidia +{ +namespace apex +{ + +/****************************************************************************** + * ApexAssetTracker class + * Intended to be a base class for asset classes that have named sub-assets. + * - Allows their actors to easily get asset pointers + * - Uses the NRP in an appropriate fashion + * - calls to checkResource(), createResource(), and getResource() + * - handles that tricky IOS asset double call mechanism + * - Sub class must implement initializeAssetNameTable() + * + */ +ApexAssetTracker::~ApexAssetTracker() +{ + /* Get the NRP */ + if (mSdk) + { + ResourceProviderIntl* nrp = mSdk->getInternalResourceProvider(); + + /* release references to rendermesh assets */ + for (uint32_t j = 0 ; j < mNameIdList.size() ; j++) + { + AssetNameIDMapping *nameId = mNameIdList[j]; + if (nameId->resID != INVALID_RESOURCE_ID) + { + if ( nameId->isOpaqueMesh ) + { + ResID opaqueMeshId = nrp->createResource(mSdk->getOpaqueMeshNameSpace(),nameId->assetName.c_str(),false); + PX_ASSERT( opaqueMeshId != INVALID_RESOURCE_ID ); + if (nrp->checkResource(opaqueMeshId)) + { + nrp->releaseResource(opaqueMeshId); + } + uint32_t refCount; + bool found =nrp->findRefCount(RENDER_MESH_AUTHORING_TYPE_NAME,nameId->assetName.c_str(),refCount); + PX_ASSERT(found); + PX_UNUSED(found); + if (nrp->checkResource(nameId->resID)) + { + if ( refCount == 1 ) + { + void *asset = nrp->getResource(nameId->resID); + PX_ASSERT(asset); + if ( asset ) + { + Asset *apexAsset = (Asset *)asset; + apexAsset->release(); + } + } + nrp->releaseResource(nameId->resID); + } + } + else + { + if (nrp->checkResource(nameId->resID)) + { + nrp->releaseResource(nameId->resID); + } + } + } + delete nameId; + } + } +} + +bool ApexAssetTracker::addAssetName(const char* assetName, bool isOpaqueMesh) +{ + /* first see if the name is already here */ + for (uint32_t i = 0; i < mNameIdList.size(); i++) + { + if (mNameIdList[i]->assetName == assetName && mNameIdList[i]->isOpaqueMesh == isOpaqueMesh) + { + return false; + } + } + + /* now add it to the list */ + mNameIdList.pushBack(PX_NEW(AssetNameIDMapping)(assetName, isOpaqueMesh)); + + return true; +} + +bool ApexAssetTracker::addAssetName(const char* iosTypeName, const char* assetName) +{ + /* first see if the name is already here */ + for (uint32_t i = 0; i < mNameIdList.size(); i++) + { + if (mNameIdList[i]->assetName == assetName && + mNameIdList[i]->iosAssetTypeName == iosTypeName) + { + return false; + } + } + + /* now add it to the list */ + mNameIdList.pushBack(PX_NEW(AssetNameIDMapping)(assetName, iosTypeName)); + + return true; +} + +void ApexAssetTracker::removeAllAssetNames() +{ + /* Get the NRP */ + ResourceProviderIntl* nrp = mSdk->getInternalResourceProvider(); + + /* release references to rendermesh assets */ + for (uint32_t j = 0 ; j < mNameIdList.size() ; j++) + { + nrp->releaseResource(mNameIdList[j]->resID); + delete mNameIdList[j]; + } + mNameIdList.reset(); +} + +IosAsset* ApexAssetTracker::getIosAssetFromName(const char* iosTypeName, const char* assetName) +{ + /* This will actually call the NRP to force the asset to be loaded (if it isn't already loaded) + * loading the APS will cause the particle module to call setResource on the iosans + */ + void* asset = ApexAssetHelper::getAssetFromNameList(mSdk, + iosTypeName, + mNameIdList, + assetName); + + Asset* aa = static_cast<Asset*>(asset); + return DYNAMIC_CAST(IosAsset*)(aa); +} + +Asset* ApexAssetTracker::getAssetFromName(const char* assetName) +{ + /* handle the material namespace, which is different (not authorable) */ + ResID resID = INVALID_RESOURCE_ID; + if (mAuthoringTypeName == "") + { + resID = mSdk->getMaterialNameSpace(); + } + + void* tmp = ApexAssetHelper::getAssetFromNameList(mSdk, + mAuthoringTypeName.c_str(), + mNameIdList, + assetName, + resID); + + return static_cast<Asset*>(tmp); +} + +Asset* ApexAssetTracker::getMeshAssetFromName(const char* assetName, bool isOpaqueMesh) +{ + PX_UNUSED(isOpaqueMesh); + /* handle the material namespace, which is different (not authorable) */ + ResID resID = INVALID_RESOURCE_ID; + if (isOpaqueMesh) + { + resID = mSdk->getOpaqueMeshNameSpace(); + } + else if (mAuthoringTypeName == "") + { + resID = mSdk->getMaterialNameSpace(); + } + + void* tmp = ApexAssetHelper::getAssetFromNameList(mSdk, + mAuthoringTypeName.c_str(), + mNameIdList, + assetName, + resID); + + return static_cast<Asset*>(tmp); +} + +ResID ApexAssetTracker::getResourceIdFromName(const char* assetName, bool isOpaqueMesh) +{ + /* handle the material namespace, which is different (not authorable) */ + ResID assetPsId = INVALID_RESOURCE_ID; + if (isOpaqueMesh) + { + assetPsId = mSdk->getOpaqueMeshNameSpace(); + } + else if (mAuthoringTypeName == "") + { + assetPsId = mSdk->getMaterialNameSpace(); + } + + // find the index of the asset name in our list of name->resID maps + uint32_t assetIdx = 0; + for (assetIdx = 0; assetIdx < mNameIdList.size(); assetIdx++) + { + if (mNameIdList[assetIdx]->assetName == assetName) + { + break; + } + } + // This can't ever happen + PX_ASSERT(assetIdx < mNameIdList.size()); + if (assetIdx < mNameIdList.size()) + { + ApexAssetHelper::getAssetFromName(mSdk, + mAuthoringTypeName.c_str(), + assetName, + mNameIdList[assetIdx]->resID, + assetPsId); + + return mNameIdList[assetIdx]->resID; + } + else + { + APEX_DEBUG_WARNING("Request for asset %s of type %s not registered in asset tracker's list", assetName, mAuthoringTypeName.c_str()); + return INVALID_RESOURCE_ID; + } +} + +uint32_t ApexAssetTracker::forceLoadAssets() +{ + if (mNameIdList.size() == 0) + { + return 0; + } + + uint32_t assetLoadedCount = 0; + + /* handle the material namespace, which is different (not authorable) */ + ResID assetPsID = INVALID_RESOURCE_ID; + if (mAuthoringTypeName == "") + { + assetPsID = mSdk->getMaterialNameSpace(); + } + else + { + AuthorableObjectIntl* ao = mSdk->getAuthorableObject(mAuthoringTypeName.c_str()); + if (ao) + { + assetPsID = ao->getResID(); + } + else + { + APEX_INTERNAL_ERROR("Unknown authorable type: %s, please load all required modules.", mAuthoringTypeName.c_str()); + } + } + + for (uint32_t i = 0 ; i < mNameIdList.size() ; i++) + { + bool useIosNamespace = false; + /* Check if we are using the special IOS namespace ID */ + if (!(mNameIdList[i]->iosAssetTypeName == "")) + { + AuthorableObjectIntl* ao = mSdk->getAuthorableObject(mNameIdList[i]->iosAssetTypeName.c_str()); + if (!ao) + { + APEX_INTERNAL_ERROR("Particles Module is not loaded, cannot create particle system asset."); + return 0; + } + + assetPsID = ao->getResID(); + + useIosNamespace = true; + } + + // check if the asset has loaded yet + if (mNameIdList[i]->resID == INVALID_RESOURCE_ID) + { + // if not, go ahead and ask the NRP for them + if (useIosNamespace) + { + getIosAssetFromName(mNameIdList[i]->iosAssetTypeName.c_str(), mNameIdList[i]->assetName.c_str()); + } + else if (mNameIdList[i]->isOpaqueMesh) + { + getMeshAssetFromName(mNameIdList[i]->assetName.c_str(), true); + } + else + { + getAssetFromName(mNameIdList[i]->assetName.c_str()); + } + assetLoadedCount++; + } + } + + return assetLoadedCount; +} + +} +} // end namespace nvidia::apex + diff --git a/APEX_1.4/common/src/ApexCollision.cpp b/APEX_1.4/common/src/ApexCollision.cpp new file mode 100644 index 00000000..2e16a714 --- /dev/null +++ b/APEX_1.4/common/src/ApexCollision.cpp @@ -0,0 +1,750 @@ +/* + * 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. + */ + + +#include "ApexDefs.h" +#include "ApexCollision.h" + +namespace nvidia +{ +namespace apex +{ + +bool capsuleCapsuleIntersection(const Capsule& worldCaps0, const Capsule& worldCaps1, float tolerance) +{ + float s, t; + float squareDist = APEX_segmentSegmentSqrDist(worldCaps0, worldCaps1, &s, &t); + + float totRad = (worldCaps0.radius * tolerance) + (worldCaps1.radius * tolerance); //incl a bit of tolerance. + return squareDist < totRad * totRad; +} + +//----------------------------------------------------------------------------// + +/// \todo replace this hack +bool boxBoxIntersection(const Box& worldBox0, const Box& worldBox1) +{ + Capsule worldCaps0, worldCaps1; + worldCaps0.p0 = worldBox0.center - worldBox0.rot * PxVec3(0, worldBox0.extents.y, 0); + worldCaps0.p1 = worldBox0.center + worldBox0.rot * PxVec3(0, worldBox0.extents.y, 0); + worldCaps0.radius = worldBox0.extents.x / 0.7f; + worldCaps1.p0 = worldBox1.center - worldBox1.rot * PxVec3(0, worldBox1.extents.y, 0); + worldCaps1.p1 = worldBox1.center + worldBox1.rot * PxVec3(0, worldBox1.extents.y, 0); + worldCaps1.radius = worldBox1.extents.x / 0.7f; + + float s, t; + float squareDist = APEX_segmentSegmentSqrDist(worldCaps0, worldCaps1, &s, &t); + + float totRad = (worldCaps0.radius * 1.2f) + (worldCaps1.radius * 1.2f); //incl a bit of tolerance. + return squareDist < totRad * totRad; +} + +//----------------------------------------------------------------------------// + +float APEX_pointTriangleSqrDst(const Triangle& triangle, const PxVec3& position) +{ + PxVec3 d1 = triangle.v1 - triangle.v0; + PxVec3 d2 = triangle.v2 - triangle.v0; + PxVec3 pp1 = position - triangle.v0; + float a = d1.dot(d1); + float b = d2.dot(d1); + float c = pp1.dot(d1); + float d = b; + float e = d2.dot(d2); + float f = pp1.dot(d2); + float det = a * e - b * d; + if (det != 0.0f) + { + float s = (c * e - b * f) / det; + float t = (a * f - c * d) / det; + if (s > 0.0f && t > 0.0f && (s + t) < 1.0f) + { + PxVec3 q = triangle.v0 + d1 * s + d2 * t; + return (q - position).magnitudeSquared(); + } + } + Segment segment; + segment.p0 = triangle.v0; + segment.p1 = triangle.v1; + float dist = APEX_pointSegmentSqrDist(segment , position, NULL); + segment.p0 = triangle.v1; + segment.p1 = triangle.v2; + dist = PxMin(dist, APEX_pointSegmentSqrDist(segment, position, NULL)); + segment.p0 = triangle.v2; + segment.p1 = triangle.v0; + dist = PxMin(dist, APEX_pointSegmentSqrDist(segment, position, NULL)); + return dist; + +} + +//----------------------------------------------------------------------------// + +#define PARALLEL_TOLERANCE 1e-02f + +float APEX_segmentSegmentSqrDist(const Segment& seg0, const Segment& seg1, float* s, float* t) +{ + PxVec3 rkSeg0Direction = seg0.p1 - seg0.p0; + PxVec3 rkSeg1Direction = seg1.p1 - seg1.p0; + + PxVec3 kDiff = seg0.p0 - seg1.p0; + float fA00 = rkSeg0Direction.magnitudeSquared(); + float fA01 = -rkSeg0Direction.dot(rkSeg1Direction); + float fA11 = rkSeg1Direction.magnitudeSquared(); + float fB0 = kDiff.dot(rkSeg0Direction); + float fC = kDiff.magnitudeSquared(); + float fDet = PxAbs(fA00 * fA11 - fA01 * fA01); + + float fB1, fS, fT, fSqrDist, fTmp; + + if (fDet >= PARALLEL_TOLERANCE) + { + // line segments are not parallel + fB1 = -kDiff.dot(rkSeg1Direction); + fS = fA01 * fB1 - fA11 * fB0; + fT = fA01 * fB0 - fA00 * fB1; + + if (fS >= 0.0f) + { + if (fS <= fDet) + { + if (fT >= 0.0f) + { + if (fT <= fDet) // region 0 (interior) + { + // minimum at two interior points of 3D lines + float fInvDet = 1.0f / fDet; + fS *= fInvDet; + fT *= fInvDet; + fSqrDist = fS * (fA00 * fS + fA01 * fT + 2.0f * fB0) + + fT * (fA01 * fS + fA11 * fT + 2.0f * fB1) + fC; + } + else // region 3 (side) + { + fT = 1.0f; + fTmp = fA01 + fB0; + if (fTmp >= 0.0f) + { + fS = 0.0f; + fSqrDist = fA11 + 2.0f * fB1 + fC; + } + else if (-fTmp >= fA00) + { + fS = 1.0f; + fSqrDist = fA00 + fA11 + fC + 2.0f * (fB1 + fTmp); + } + else + { + fS = -fTmp / fA00; + fSqrDist = fTmp * fS + fA11 + 2.0f * fB1 + fC; + } + } + } + else // region 7 (side) + { + fT = 0.0f; + if (fB0 >= 0.0f) + { + fS = 0.0f; + fSqrDist = fC; + } + else if (-fB0 >= fA00) + { + fS = 1.0f; + fSqrDist = fA00 + 2.0f * fB0 + fC; + } + else + { + fS = -fB0 / fA00; + fSqrDist = fB0 * fS + fC; + } + } + } + else + { + if (fT >= 0.0) + { + if (fT <= fDet) // region 1 (side) + { + fS = 1.0f; + fTmp = fA01 + fB1; + if (fTmp >= 0.0f) + { + fT = 0.0f; + fSqrDist = fA00 + 2.0f * fB0 + fC; + } + else if (-fTmp >= fA11) + { + fT = 1.0f; + fSqrDist = fA00 + fA11 + fC + 2.0f * (fB0 + fTmp); + } + else + { + fT = -fTmp / fA11; + fSqrDist = fTmp * fT + fA00 + 2.0f * fB0 + fC; + } + } + else // region 2 (corner) + { + fTmp = fA01 + fB0; + if (-fTmp <= fA00) + { + fT = 1.0f; + if (fTmp >= 0.0f) + { + fS = 0.0f; + fSqrDist = fA11 + 2.0f * fB1 + fC; + } + else + { + fS = -fTmp / fA00; + fSqrDist = fTmp * fS + fA11 + 2.0f * fB1 + fC; + } + } + else + { + fS = 1.0f; + fTmp = fA01 + fB1; + if (fTmp >= 0.0f) + { + fT = 0.0f; + fSqrDist = fA00 + 2.0f * fB0 + fC; + } + else if (-fTmp >= fA11) + { + fT = 1.0f; + fSqrDist = fA00 + fA11 + fC + 2.0f * (fB0 + fTmp); + } + else + { + fT = -fTmp / fA11; + fSqrDist = fTmp * fT + fA00 + 2.0f * fB0 + fC; + } + } + } + } + else // region 8 (corner) + { + if (-fB0 < fA00) + { + fT = 0.0f; + if (fB0 >= 0.0f) + { + fS = 0.0f; + fSqrDist = fC; + } + else + { + fS = -fB0 / fA00; + fSqrDist = fB0 * fS + fC; + } + } + else + { + fS = 1.0f; + fTmp = fA01 + fB1; + if (fTmp >= 0.0f) + { + fT = 0.0f; + fSqrDist = fA00 + 2.0f * fB0 + fC; + } + else if (-fTmp >= fA11) + { + fT = 1.0f; + fSqrDist = fA00 + fA11 + fC + 2.0f * (fB0 + fTmp); + } + else + { + fT = -fTmp / fA11; + fSqrDist = fTmp * fT + fA00 + 2.0f * fB0 + fC; + } + } + } + } + } + else + { + if (fT >= 0.0f) + { + if (fT <= fDet) // region 5 (side) + { + fS = 0.0f; + if (fB1 >= 0.0f) + { + fT = 0.0f; + fSqrDist = fC; + } + else if (-fB1 >= fA11) + { + fT = 1.0f; + fSqrDist = fA11 + 2.0f * fB1 + fC; + } + else + { + fT = -fB1 / fA11; + fSqrDist = fB1 * fT + fC; + } + } + else // region 4 (corner) + { + fTmp = fA01 + fB0; + if (fTmp < 0.0f) + { + fT = 1.0f; + if (-fTmp >= fA00) + { + fS = 1.0f; + fSqrDist = fA00 + fA11 + fC + 2.0f * (fB1 + fTmp); + } + else + { + fS = -fTmp / fA00; + fSqrDist = fTmp * fS + fA11 + 2.0f * fB1 + fC; + } + } + else + { + fS = 0.0f; + if (fB1 >= 0.0f) + { + fT = 0.0f; + fSqrDist = fC; + } + else if (-fB1 >= fA11) + { + fT = 1.0f; + fSqrDist = fA11 + 2.0f * fB1 + fC; + } + else + { + fT = -fB1 / fA11; + fSqrDist = fB1 * fT + fC; + } + } + } + } + else // region 6 (corner) + { + if (fB0 < 0.0f) + { + fT = 0.0f; + if (-fB0 >= fA00) + { + fS = 1.0f; + fSqrDist = fA00 + 2.0f * fB0 + fC; + } + else + { + fS = -fB0 / fA00; + fSqrDist = fB0 * fS + fC; + } + } + else + { + fS = 0.0f; + if (fB1 >= 0.0f) + { + fT = 0.0f; + fSqrDist = fC; + } + else if (-fB1 >= fA11) + { + fT = 1.0f; + fSqrDist = fA11 + 2.0f * fB1 + fC; + } + else + { + fT = -fB1 / fA11; + fSqrDist = fB1 * fT + fC; + } + } + } + } + } + else + { + // line segments are parallel + if (fA01 > 0.0f) + { + // direction vectors form an obtuse angle + if (fB0 >= 0.0f) + { + fS = 0.0f; + fT = 0.0f; + fSqrDist = fC; + } + else if (-fB0 <= fA00) + { + fS = -fB0 / fA00; + fT = 0.0f; + fSqrDist = fB0 * fS + fC; + } + else + { + fB1 = -kDiff.dot(rkSeg1Direction); + fS = 1.0f; + fTmp = fA00 + fB0; + if (-fTmp >= fA01) + { + fT = 1.0f; + fSqrDist = fA00 + fA11 + fC + 2.0f * (fA01 + fB0 + fB1); + } + else + { + fT = -fTmp / fA01; + fSqrDist = fA00 + 2.0f * fB0 + fC + fT * (fA11 * fT + 2.0f * (fA01 + fB1)); + } + } + } + else + { + // direction vectors form an acute angle + if (-fB0 >= fA00) + { + fS = 1.0f; + fT = 0.0f; + fSqrDist = fA00 + 2.0f * fB0 + fC; + } + else if (fB0 <= 0.0f) + { + fS = -fB0 / fA00; + fT = 0.0f; + fSqrDist = fB0 * fS + fC; + } + else + { + fB1 = -kDiff.dot(rkSeg1Direction); + fS = 0.0f; + if (fB0 >= -fA01) + { + fT = 1.0f; + fSqrDist = fA11 + 2.0f * fB1 + fC; + } + else + { + fT = -fB0 / fA01; + fSqrDist = fC + fT * (2.0f * fB1 + fA11 * fT); + } + } + } + } + + if (s) + { + *s = fS; + } + if (t) + { + *t = fT; + } + + return PxAbs(fSqrDist); + +} + +//----------------------------------------------------------------------------// + +float APEX_pointSegmentSqrDist(const Segment& seg, const PxVec3& point, float* param) +{ + PxVec3 Diff = point - seg.p0; + PxVec3 segExtent = seg.p1 - seg.p0; + float fT = Diff.dot(segExtent); + + if (fT <= 0.0f) + { + fT = 0.0f; + } + else + { + float SqrLen = (seg.p1 - seg.p0).magnitudeSquared(); + if (fT >= SqrLen) + { + fT = 1.0f; + Diff -= segExtent; + } + else + { + fT /= SqrLen; + Diff -= fT * segExtent; + } + } + + if (param) + { + *param = fT; + } + + return Diff.magnitudeSquared(); +} + +//----------------------------------------------------------------------------// + +uint32_t APEX_RayCapsuleIntersect(const PxVec3& origin, const PxVec3& dir, const Capsule& capsule, float s[2]) +{ + // set up quadratic Q(t) = a*t^2 + 2*b*t + c + + PxVec3 kU, kV, kW; + const PxVec3 capsDir = capsule.p1 - capsule.p0; + kW = capsDir; + + float fWLength = kW.normalize(); + + // generate orthonormal basis + + float fInvLength; + if (PxAbs(kW.x) >= PxAbs(kW.y)) + { + // W.x or W.z is the largest magnitude component, swap them + fInvLength = 1.0f / PxSqrt(kW.x * kW.x + kW.z * kW.z); + kU.x = -kW.z * fInvLength; + kU.y = 0.0f; + kU.z = +kW.x * fInvLength; + } + else + { + // W.y or W.z is the largest magnitude component, swap them + fInvLength = 1.0f / PxSqrt(kW.y * kW.y + kW.z * kW.z); + kU.x = 0.0f; + kU.y = +kW.z * fInvLength; + kU.z = -kW.y * fInvLength; + } + kV = kW.cross(kU); + kV.normalize(); // PT: fixed november, 24, 2004. This is a bug in Magic. + + // compute intersection + + PxVec3 kD(kU.dot(dir), kV.dot(dir), kW.dot(dir)); + float fDLength = kD.normalize(); + + float fInvDLength = 1.0f / fDLength; + PxVec3 kDiff = origin - capsule.p0; + PxVec3 kP(kU.dot(kDiff), kV.dot(kDiff), kW.dot(kDiff)); + float fRadiusSqr = capsule.radius * capsule.radius; + + float fInv, fA, fB, fC, fDiscr, fRoot, fT, fTmp; + + // Is the velocity parallel to the capsule direction? (or zero) + if (PxAbs(kD.z) >= 1.0f - PX_EPS_F32 || fDLength < PX_EPS_F32) + { + + float fAxisDir = dir.dot(capsDir); + + fDiscr = fRadiusSqr - kP.x * kP.x - kP.y * kP.y; + if (fAxisDir < 0 && fDiscr >= 0.0f) + { + // Velocity anti-parallel to the capsule direction + fRoot = PxSqrt(fDiscr); + s[0] = (kP.z + fRoot) * fInvDLength; + s[1] = -(fWLength - kP.z + fRoot) * fInvDLength; + return 2; + } + else if (fAxisDir > 0 && fDiscr >= 0.0f) + { + // Velocity parallel to the capsule direction + fRoot = PxSqrt(fDiscr); + s[0] = -(kP.z + fRoot) * fInvDLength; + s[1] = (fWLength - kP.z + fRoot) * fInvDLength; + return 2; + } + else + { + // sphere heading wrong direction, or no velocity at all + return 0; + } + } + + // test intersection with infinite cylinder + fA = kD.x * kD.x + kD.y * kD.y; + fB = kP.x * kD.x + kP.y * kD.y; + fC = kP.x * kP.x + kP.y * kP.y - fRadiusSqr; + fDiscr = fB * fB - fA * fC; + if (fDiscr < 0.0f) + { + // line does not intersect infinite cylinder + return 0; + } + + int iQuantity = 0; + + if (fDiscr > 0.0f) + { + // line intersects infinite cylinder in two places + fRoot = PxSqrt(fDiscr); + fInv = 1.0f / fA; + fT = (-fB - fRoot) * fInv; + fTmp = kP.z + fT * kD.z; + if (0.0f <= fTmp && fTmp <= fWLength) + { + s[iQuantity++] = fT * fInvDLength; + } + + fT = (-fB + fRoot) * fInv; + fTmp = kP.z + fT * kD.z; + if (0.0f <= fTmp && fTmp <= fWLength) + { + s[iQuantity++] = fT * fInvDLength; + } + + if (iQuantity == 2) + { + // line intersects capsule wall in two places + return 2; + } + } + else + { + // line is tangent to infinite cylinder + fT = -fB / fA; + fTmp = kP.z + fT * kD.z; + if (0.0f <= fTmp && fTmp <= fWLength) + { + s[0] = fT * fInvDLength; + return 1; + } + } + + // test intersection with bottom hemisphere + // fA = 1 + fB += kP.z * kD.z; + fC += kP.z * kP.z; + fDiscr = fB * fB - fC; + if (fDiscr > 0.0f) + { + fRoot = PxSqrt(fDiscr); + fT = -fB - fRoot; + fTmp = kP.z + fT * kD.z; + if (fTmp <= 0.0f) + { + s[iQuantity++] = fT * fInvDLength; + if (iQuantity == 2) + { + return 2; + } + } + + fT = -fB + fRoot; + fTmp = kP.z + fT * kD.z; + if (fTmp <= 0.0f) + { + s[iQuantity++] = fT * fInvDLength; + if (iQuantity == 2) + { + return 2; + } + } + } + else if (fDiscr == 0.0f) + { + fT = -fB; + fTmp = kP.z + fT * kD.z; + if (fTmp <= 0.0f) + { + s[iQuantity++] = fT * fInvDLength; + if (iQuantity == 2) + { + return 2; + } + } + } + + // test intersection with top hemisphere + // fA = 1 + fB -= kD.z * fWLength; + fC += fWLength * (fWLength - 2.0f * kP.z); + + fDiscr = fB * fB - fC; + if (fDiscr > 0.0f) + { + fRoot = PxSqrt(fDiscr); + fT = -fB - fRoot; + fTmp = kP.z + fT * kD.z; + if (fTmp >= fWLength) + { + s[iQuantity++] = fT * fInvDLength; + if (iQuantity == 2) + { + return 2; + } + } + + fT = -fB + fRoot; + fTmp = kP.z + fT * kD.z; + if (fTmp >= fWLength) + { + s[iQuantity++] = fT * fInvDLength; + if (iQuantity == 2) + { + return 2; + } + } + } + else if (fDiscr == 0.0f) + { + fT = -fB; + fTmp = kP.z + fT * kD.z; + if (fTmp >= fWLength) + { + s[iQuantity++] = fT * fInvDLength; + if (iQuantity == 2) + { + return 2; + } + } + } + + return (uint32_t)iQuantity; +} + + +//----------------------------------------------------------------------------// + +bool APEX_RayTriangleIntersect(const PxVec3& orig, const PxVec3& dir, const PxVec3& a, const PxVec3& b, const PxVec3& c, float& t, float& u, float& v) +{ + PxVec3 edge1 = b - a; + PxVec3 edge2 = c - a; + PxVec3 pvec = dir.cross(edge2); + + // if determinant is near zero, ray lies in plane of triangle + float det = edge1.dot(pvec); + + if (det == 0.0f) + { + return false; + } + + float iPX_det = 1.0f / det; + + // calculate distance from vert0 to ray origin + PxVec3 tvec = orig - a; + + // calculate U parameter and test bounds + u = tvec.dot(pvec) * iPX_det; + if (u < 0.0f || u > 1.0f) + { + return false; + } + + // prepare to test V parameter + PxVec3 qvec = tvec.cross(edge1); + + // calculate V parameter and test bounds + v = dir.dot(qvec) * iPX_det; + if (v < 0.0f || u + v > 1.0f) + { + return false; + } + + // calculate t, ray intersects triangle + t = edge2.dot(qvec) * iPX_det; + + return true; +} + +} // namespace apex +} // namespace nvidia diff --git a/APEX_1.4/common/src/ApexContext.cpp b/APEX_1.4/common/src/ApexContext.cpp new file mode 100644 index 00000000..0b6cf03c --- /dev/null +++ b/APEX_1.4/common/src/ApexContext.cpp @@ -0,0 +1,330 @@ +/* + * 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. + */ + + +#include "Apex.h" +#include "ApexContext.h" +#include "ApexActor.h" + +namespace nvidia +{ +namespace apex +{ + +//*** Apex Context *** +ApexContext::~ApexContext() +{ + if (mIterator) + { + mIterator->release(); + } + removeAllActors(); + mActorArray.clear(); + mActorArrayCallBacks.clear(); +} + +uint32_t ApexContext::addActor(ApexActor& actor, ApexActor* actorPtr) +{ + mActorListLock.lockWriter(); + uint32_t index = mActorArray.size(); + mActorArray.pushBack(&actor); + if (actorPtr != NULL) + { + mActorArrayCallBacks.pushBack(actorPtr); + } + callContextCreationCallbacks(&actor); + mActorListLock.unlockWriter(); + return index; +} + +void ApexContext::callContextCreationCallbacks(ApexActor* actorPtr) +{ + Asset* assetPtr; + AuthObjTypeID ObjectTypeID; + uint32_t numCallBackActors; + + numCallBackActors = mActorArrayCallBacks.size(); + for (uint32_t i = 0; i < numCallBackActors; i++) + { + assetPtr = actorPtr->getAsset(); + if (assetPtr != NULL) + { + // get the resIds + ObjectTypeID = assetPtr->getObjTypeID(); + // call the call back function + mActorArrayCallBacks[i]->ContextActorCreationNotification(ObjectTypeID, + actorPtr); + } + } +} + +void ApexContext::callContextDeletionCallbacks(ApexActor* actorPtr) +{ + Asset* assetPtr; + AuthObjTypeID ObjectTypeID; + uint32_t numCallBackActors; + + numCallBackActors = mActorArrayCallBacks.size(); + for (uint32_t i = 0; i < numCallBackActors; i++) + { + assetPtr = actorPtr->getAsset(); + if (assetPtr != NULL) + { + // get the resIds + ObjectTypeID = assetPtr->getObjTypeID(); + // call the call back function + mActorArrayCallBacks[i]->ContextActorDeletionNotification(ObjectTypeID, + actorPtr); + } + } + +} + +void ApexContext::removeActorAtIndex(uint32_t index) +{ + ApexActor* actorPtr; + + // call the callbacks so they know this actor is going to be deleted! + callContextDeletionCallbacks(mActorArray[index]); + + mActorListLock.lockWriter(); + + // remove the actor from the call back array if it is in it + actorPtr = mActorArray[index]; + + for (uint32_t i = 0; i < mActorArrayCallBacks.size(); i++) + { + if (actorPtr == mActorArrayCallBacks[i]) // is this the actor to be removed? + { + mActorArrayCallBacks.replaceWithLast(i); // yes, remove it + } + } + + if (mIterator) + { + Renderable* renderable = mActorArray[ index ]->getRenderable(); + if (renderable) + { + mIterator->removeCachedActor(*(mActorArray[ index ])); + } + } + mActorArray.replaceWithLast(index); + if (index < mActorArray.size()) + { + mActorArray[index]->updateIndex(*this, index); + } + mActorListLock.unlockWriter(); +} + +void ApexContext::renderLockAllActors() +{ + // Hold the render lock of all actors in the scene. Used to protect PxScene::fetchResults() + mActorListLock.lockReader(); + for (uint32_t i = 0 ; i < mActorArray.size() ; i++) + { + mActorArray[i]->renderDataLock(); + } + + // NOTE: We are unlocking here now, and locking at the beginning of renderUnLockAllActors, below. + // This is under the assumption that this lock is ONLY to protect a loop over mActorArray, not + // anything between renderLockAllActors() and renderUnLockAllActors() in fetchResults(). + mActorListLock.unlockReader(); +} + +void ApexContext::renderUnLockAllActors() +{ + // NOTE: We are unlocking at the end of renderLockAllActors(), above, and unlocking here now. + // This is under the assumption that this lock is ONLY to protect a loop over mActorArray, not + // anything between renderLockAllActors() and renderUnLockAllActors() in fetchResults(). + mActorListLock.lockReader(); + + for (uint32_t i = 0 ; i < mActorArray.size() ; i++) + { + mActorArray[i]->renderDataUnLock(); + } + mActorListLock.unlockReader(); +} + +void ApexContext::removeAllActors() +{ + while (mActorArray.size()) + { + mActorArray.back()->release(); + } + mActorArrayCallBacks.clear(); +} + +RenderableIterator* ApexContext::createRenderableIterator() +{ + if (mIterator) + { + PX_ALWAYS_ASSERT(); // Only one per context at a time, please + return NULL; + } + else + { + mIterator = PX_NEW(ApexRenderableIterator)(*this); + return mIterator; + } +} +void ApexContext::releaseRenderableIterator(RenderableIterator& iter) +{ + if (mIterator == DYNAMIC_CAST(ApexRenderableIterator*)(&iter)) + { + mIterator->destroy(); + mIterator = NULL; + } + else + { + PX_ASSERT(mIterator == DYNAMIC_CAST(ApexRenderableIterator*)(&iter)); + } +} + +ApexRenderableIterator::ApexRenderableIterator(ApexContext& _ctx) : + ctx(&_ctx), + curActor(0), + mLockedActor(NULL) +{ + // Make copy of list of renderable actors currently in the context. + // If an actor is later removed, we mark it as NULL in our cached + // array. If an actor is added, we do _NOT_ add it to our list since + // it would be quite dangerous to call dispatchRenderResources() on an + // actor that has never had updateRenderResources() called to it. + + mCachedActors.reserve(ctx->mActorArray.size()); + ctx->mActorListLock.lockWriter(); + for (uint32_t i = 0 ; i < ctx->mActorArray.size() ; i++) + { + Renderable* renderable = ctx->mActorArray[ i ]->getRenderable(); + if (renderable) + { + mCachedActors.pushBack(ctx->mActorArray[ i ]); + } + } + ctx->mActorListLock.unlockWriter(); +} + +void ApexRenderableIterator::removeCachedActor(ApexActor& actor) +{ + // This function is called with a locked context, so we can modify our + // internal lists at will. + + for (uint32_t i = 0 ; i < mCachedActors.size() ; i++) + { + if (&actor == mCachedActors[ i ]) + { + mCachedActors[ i ] = NULL; + break; + } + } + for (uint32_t i = 0 ; i < mSkippedActors.size() ; i++) + { + if (&actor == mSkippedActors[ i ]) + { + mSkippedActors.replaceWithLast(i); + break; + } + } + if (&actor == mLockedActor) + { + mLockedActor->renderDataUnLock(); + mLockedActor = NULL; + } +} + +Renderable* ApexRenderableIterator::getFirst() +{ + curActor = 0; + mSkippedActors.reserve(mCachedActors.size()); + return getNext(); +} + +Renderable* ApexRenderableIterator::getNext() +{ + if (mLockedActor) + { + mLockedActor->renderDataUnLock(); + mLockedActor = NULL; + } + + //physx::ScopedReadLock scopedContextLock( ctx->mActorListLock ); + ctx->mActorListLock.lockReader(); + while (curActor < mCachedActors.size()) + { + ApexActor* actor = mCachedActors[ curActor++ ]; + if (actor) + { + if (actor->renderDataTryLock()) + { + mLockedActor = actor; + ctx->mActorListLock.unlockReader(); + return actor->getRenderable(); + } + else + { + mSkippedActors.pushBack(actor); + } + } + } + if (mSkippedActors.size()) + { + ApexActor* actor = mSkippedActors.back(); + mSkippedActors.popBack(); + ctx->mActorListLock.unlockReader(); + actor->renderDataLock(); + mLockedActor = actor; + return actor->getRenderable(); + } + ctx->mActorListLock.unlockReader(); + return NULL; +} + +void ApexRenderableIterator::release() +{ + ctx->releaseRenderableIterator(*this); +} + +void ApexRenderableIterator::reset() +{ + if (mLockedActor) + { + mLockedActor->renderDataUnLock(); + mLockedActor = NULL; + } + + curActor = 0; + + mCachedActors.reserve(ctx->mActorArray.size()); + mCachedActors.reset(); + ctx->mActorListLock.lockWriter(); + for (uint32_t i = 0 ; i < ctx->mActorArray.size() ; i++) + { + Renderable* renderable = ctx->mActorArray[ i ]->getRenderable(); + if (renderable) + { + mCachedActors.pushBack(ctx->mActorArray[ i ]); + } + } + ctx->mActorListLock.unlockWriter(); +} + +void ApexRenderableIterator::destroy() +{ + if (mLockedActor) + { + mLockedActor->renderDataUnLock(); + mLockedActor = NULL; + } + delete this; +} + +} +} // end namespace nvidia::apex + diff --git a/APEX_1.4/common/src/ApexCudaProfile.cpp b/APEX_1.4/common/src/ApexCudaProfile.cpp new file mode 100644 index 00000000..cee4cfc2 --- /dev/null +++ b/APEX_1.4/common/src/ApexCudaProfile.cpp @@ -0,0 +1,332 @@ +/* + * 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. + */ + + +#include "ApexDefs.h" +#if APEX_CUDA_SUPPORT && !defined(INSTALLER) + +#include "ApexCudaProfile.h" +#include "ApexCudaWrapper.h" +#include <cuda.h> +#include "ModuleIntl.h" +#include "ApexSDKHelpers.h" + +namespace nvidia +{ +namespace apex +{ + + ApexCudaProfileSession::ApexCudaProfileSession() + : mTimer(NULL) + , mFrameStart(PX_MAX_F32) + , mFrameFinish(0.f) + { + mMemBuf.setEndianMode(nvidia::PsMemoryBuffer::ENDIAN_LITTLE); + } + ApexCudaProfileSession::~ApexCudaProfileSession() + { + if (mTimer) + { + CUT_SAFE_CALL(cuEventDestroy((CUevent)mTimer)); + } + } + + void ApexCudaProfileSession::nextFrame() + { + mFrameStart = PX_MAX_F32; + mFrameFinish = 0.f; + float sumElapsed = 0.f; + for (uint32_t i = 0; i < mProfileDataList.size(); i++) + { + sumElapsed += flushProfileInfo(mProfileDataList[i]); + } + + // Write frame as fictive event + uint32_t op = 1, id = 0; + uint64_t start = static_cast<uint64_t>(mFrameStart * mManager->mTimeFormat); + mMemBuf.write(&op, sizeof(op)); + mMemBuf.write(&start, sizeof(start)); + mMemBuf.write(&id, sizeof(id)); + + op = 2; + uint64_t stop = static_cast<uint64_t>(mFrameFinish * mManager->mTimeFormat); + mMemBuf.write(&op, sizeof(op)); + mMemBuf.write(&stop, sizeof(stop)); + mMemBuf.write(&id, sizeof(id)); + + // Write summary of elapsed gpu kernel time as event + op = 1, id = 1; + start = static_cast<uint64_t>(mFrameStart * mManager->mTimeFormat); + mMemBuf.write(&op, sizeof(op)); + mMemBuf.write(&start, sizeof(start)); + mMemBuf.write(&id, sizeof(id)); + + op = 2; + stop = static_cast<uint64_t>((mFrameStart + sumElapsed) * mManager->mTimeFormat); + mMemBuf.write(&op, sizeof(op)); + mMemBuf.write(&stop, sizeof(stop)); + mMemBuf.write(&id, sizeof(id)); + + mProfileDataList.clear(); + } + + void ApexCudaProfileSession::start() + { + if (!mManager || !mManager->mApexScene) return; + + mLock.lock(); + + mMemBuf.seekWrite(0); + uint32_t op = 0, sz, id = 0; + const char* frameEvent = "Frame"; sz = sizeof(frameEvent); + mMemBuf.write(&op, sizeof(op)); + mMemBuf.write(&sz, sizeof(sz)); + mMemBuf.write(frameEvent, sz); + mMemBuf.write(&id, sizeof(id)); + + const char* summaryElapsed = "Summary of elapsed time"; sz = sizeof(summaryElapsed); + id = 1; + mMemBuf.write(&op, sizeof(op)); + mMemBuf.write(&sz, sizeof(sz)); + mMemBuf.write(summaryElapsed, sz); + mMemBuf.write(&id, sizeof(id)); + + //Register kernels + for (uint32_t i = 0; i < mManager->mKernels.size(); i++) + { + ApexCudaProfileManager::KernelInfo& ki = mManager->mKernels[i]; + sz = ki.functionName.size(); + mMemBuf.write(&op, sizeof(op)); + mMemBuf.write(&sz, sizeof(sz)); + mMemBuf.write(ki.functionName.c_str(), sz); + mMemBuf.write(&ki.id, sizeof(ki.id)); + + ModuleSceneIntl* moduleScene = mManager->mApexScene->getInternalModuleScene(ki.moduleName.c_str()); + ApexCudaObj* obj = NULL; + if (moduleScene) + { + obj = static_cast<ApexCudaObj*>(moduleScene->getHeadCudaObj()); + } + while(obj) + { + if (obj->getType() == ApexCudaObj::FUNCTION) + { + if (ApexSimpleString(DYNAMIC_CAST(ApexCudaFunc*)(obj)->getName()) == ki.functionName) + { + DYNAMIC_CAST(ApexCudaFunc*)(obj)->setProfileSession(this); + break; + } + } + obj = obj->next(); + } + } + + { + PxCudaContextManager* ctx = mManager->mApexScene->getTaskManager()->getGpuDispatcher()->getCudaContextManager(); + PxScopedCudaLock s(*ctx); + + //Run timer + if (mTimer == NULL) + { + CUT_SAFE_CALL(cuEventCreate((CUevent*)&mTimer, CU_EVENT_DEFAULT)); + } + CUT_SAFE_CALL(cuEventRecord((CUevent)mTimer, 0)); + } + mLock.unlock(); + } + + uint32_t ApexCudaProfileSession::getProfileId(const char* name, const char* moduleName) + { + Array <ApexCudaProfileManager::KernelInfo>::Iterator it + = mManager->mKernels.find(ApexCudaProfileManager::KernelInfo(name, moduleName)); + if (it != mManager->mKernels.end()) + { + return it->id; + } + return 0; + } + + void ApexCudaProfileSession::onFuncStart(uint32_t id, void* stream) + { + mLock.lock(); + CUevent start; + CUevent stop; + + CUT_SAFE_CALL(cuEventCreate(&start, CU_EVENT_DEFAULT)); + CUT_SAFE_CALL(cuEventCreate(&stop, CU_EVENT_DEFAULT)); + + CUT_SAFE_CALL(cuEventRecord(start, (CUstream)stream)); + + ProfileData data; + data.id = id; + data.start = start; + data.stop = stop; + mProfileDataList.pushBack(data); + + } + void ApexCudaProfileSession::onFuncFinish(uint32_t id, void* stream) + { + PX_UNUSED(id); + ProfileData& data = mProfileDataList.back(); + PX_ASSERT(data.id == id); + + CUT_SAFE_CALL(cuEventRecord((CUevent)data.stop, (CUstream)stream)); + + mLock.unlock(); + } + + float ApexCudaProfileSession::flushProfileInfo(ProfileData& pd) + { + CUevent start = (CUevent)pd.start; + CUevent stop = (CUevent)pd.stop; + + uint32_t op = 1; + float startTf = 0.f, stopTf = 0.f; + uint64_t startT = 0, stopT = 0; + CUT_SAFE_CALL(cuEventSynchronize(start)); + CUT_SAFE_CALL(cuEventElapsedTime(&startTf, (CUevent)mTimer, start)); + startT = static_cast<uint64_t>(startTf * mManager->mTimeFormat) ; + mMemBuf.write(&op, sizeof(op)); + mMemBuf.write(&startT, sizeof(startT)); + mMemBuf.write(&pd.id, sizeof(pd.id)); + + op = 2; + CUT_SAFE_CALL(cuEventSynchronize((CUevent)stop)); + CUT_SAFE_CALL(cuEventElapsedTime(&stopTf, (CUevent)mTimer, (CUevent)stop)); + stopT = static_cast<uint64_t>(stopTf * mManager->mTimeFormat); + mMemBuf.write(&op, sizeof(op)); + mMemBuf.write(&stopT, sizeof(stopT)); + mMemBuf.write(&pd.id, sizeof(pd.id)); + + CUT_SAFE_CALL(cuEventDestroy((CUevent)start)); + CUT_SAFE_CALL(cuEventDestroy((CUevent)stop)); + + mFrameStart = PxMin(mFrameStart, startTf); + mFrameFinish = PxMax(mFrameFinish, stopTf); + return stopTf - startTf; + } + + bool ApexCudaProfileSession::stopAndSave() + { + if (!mManager || !mManager->mApexScene) return false; + + //unregister functions + for (uint32_t i = 0; i < mManager->mKernels.size(); i++) + { + ApexCudaProfileManager::KernelInfo& ki = mManager->mKernels[i]; + + ModuleSceneIntl* moduleScene = mManager->mApexScene->getInternalModuleScene(ki.moduleName.c_str()); + ApexCudaObj* obj = NULL; + if (moduleScene) + { + obj = static_cast<ApexCudaObj*>(moduleScene->getHeadCudaObj()); + } + while(obj) + { + if (obj->getType() == ApexCudaObj::FUNCTION) + { + if (ApexSimpleString(DYNAMIC_CAST(ApexCudaFunc*)(obj)->getName()) == ki.functionName) + { + DYNAMIC_CAST(ApexCudaFunc*)(obj)->setProfileSession(NULL); + break; + } + } + obj = obj->next(); + } + } + + //save to file + ApexSimpleString path(mManager->mPath); + path += ApexSimpleString("profileSesion_"); + path += ApexSimpleString(mManager->mSessionCount, 3); + FILE* saveFile = fopen(path.c_str(), "wb"); + if (saveFile) + { + fwrite(mMemBuf.getWriteBuffer(), mMemBuf.getWriteBufferSize(), 1, saveFile); + return !fclose(saveFile); + } + return false; + } + + ApexCudaProfileManager::ApexCudaProfileManager() + : mState(false) + , mTimeFormat(NANOSECOND) + , mSessionCount(0) + , mReservedId(2) + { + mSession.init(this); + } + + ApexCudaProfileManager::~ApexCudaProfileManager() + { + } + + void ApexCudaProfileManager::setKernel(const char* functionName, const char* moduleName) + { + if (mKernels.find(KernelInfo(functionName, moduleName)) == mKernels.end()) + { + if (ApexSimpleString(functionName) == "*") + { + //Add all function registered in module + ModuleSceneIntl* moduleScene = mApexScene->getInternalModuleScene(moduleName); + ApexCudaObj* obj = NULL; + if (moduleScene) + { + obj = static_cast<ApexCudaObj*>(moduleScene->getHeadCudaObj()); + } + while(obj) + { + if (obj->getType() == ApexCudaObj::FUNCTION) + { + const char* name = DYNAMIC_CAST(ApexCudaFunc*)(obj)->getName(); + if (mKernels.find(KernelInfo(name, moduleName)) == mKernels.end()) + { + mKernels.pushBack(KernelInfo(name, moduleName, mKernels.size() + mReservedId)); + } + } + obj = obj->next(); + } + } + else + { + mKernels.pushBack(KernelInfo(functionName, moduleName, mKernels.size() + mReservedId)); + } + enable(false); + } + } + + void ApexCudaProfileManager::enable(bool state) + { + if (state != mState) + { + if (state) + { + mSession.start(); + mSessionCount++; + } + else + { + mSession.stopAndSave(); + } + } + mState = state; + } + + void ApexCudaProfileManager::nextFrame() + { + if (mApexScene && mState) + { + mSession.nextFrame(); + } + } +} +} // namespace nvidia::apex + +#endif diff --git a/APEX_1.4/common/src/ApexCudaTest.cpp b/APEX_1.4/common/src/ApexCudaTest.cpp new file mode 100644 index 00000000..35ad85e3 --- /dev/null +++ b/APEX_1.4/common/src/ApexCudaTest.cpp @@ -0,0 +1,1209 @@ +/* + * 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. + */ + + +#include "ApexDefs.h" +#if APEX_CUDA_SUPPORT && !defined(INSTALLER) + +#include "ApexCudaTest.h" +#include "ApexCudaWrapper.h" +#include <cuda.h> +#include "ModuleIntl.h" +#include "ApexSDKHelpers.h" + +# define CUT_SAFE_CALL(call) { CUresult ret = call; \ + if( CUDA_SUCCESS != ret ) { \ + APEX_INTERNAL_ERROR("Cuda Error %d", ret); \ + PX_ASSERT(!ret); } } + +#define ALIGN_OFFSET(offset, alignment) (offset) = ((offset) + (alignment) - 1) & ~((alignment) - 1) + +#define WRITE_SCALAR(val) mMemBuf.alignWrite(4); mMemBuf.write(&val, sizeof(val)); + +#define WRITE_ALIGN_ARRAY(ptr, size, align) { uint32_t nsz = size; \ + mMemBuf.alignWrite(4); mMemBuf.write(&nsz, sizeof(nsz)); mMemBuf.alignWrite(align); mMemBuf.write(ptr, nsz); } + +#define WRITE_ARRAY(ptr, size) WRITE_ALIGN_ARRAY(ptr, size, 4) + +#define WRITE_STRING(str) { mMemBuf.alignWrite(4); str.serialize(mMemBuf); } + +#define READ_SCALAR(val) mMemBuf->alignRead(4); mMemBuf->read(&val, sizeof(val)); + +#define READ_STRING(str) { mMemBuf->alignRead(4); str.deserialize(*mMemBuf); } + +namespace nvidia +{ +namespace apex +{ + + ApexCudaTestKernelContextReader::ApexCudaTestKernelContextReader(const char* path, SceneIntl* scene) + : mMemBuf(NULL) + , mHeadCudaObj(NULL) + , mFunc(NULL) + , mApexScene(scene) + , mCuStream(NULL) + , mTmpArray(*scene, __FILE__, __LINE__) + , mCopyQueue(*(scene->getTaskManager()->getGpuDispatcher())) + , mCudaArrayCount(0) + , mCudaArrayList(NULL) + { + FILE* loadFile; + loadFile = fopen(path, "rb"); + if (loadFile) + { + uint32_t serviceInfo[5]; + + fread(serviceInfo, sizeof(uint32_t), 5, loadFile); + if (serviceInfo[0] != ApexCudaTestFileVersion) + { + PX_ASSERT(!"Unknown version of cuda context file"); + } + fseek(loadFile, 0, 0); + + mMemBuf = PX_NEW(nvidia::PsMemoryBuffer)(serviceInfo[1]); + mMemBuf->initWriteBuffer(serviceInfo[1]); + mCudaObjOffset = serviceInfo[3]; + mParamOffset = serviceInfo[4]; + fread((void*)mMemBuf->getWriteBuffer(), 1, serviceInfo[1], loadFile); + + // Header + mMemBuf->seekRead(serviceInfo[2]); + READ_STRING(mName); + READ_STRING(mModuleName); + READ_SCALAR(mFrame); + READ_SCALAR(mCallPerFrame); + + READ_SCALAR(mFuncInstId); + READ_SCALAR(mSharedSize); + READ_SCALAR(mBlockDim.x); + READ_SCALAR(mBlockDim.y); + READ_SCALAR(mBlockDim.z); + READ_SCALAR(mGridDim.x); + READ_SCALAR(mGridDim.y); + mGridDim.z = 0; + READ_SCALAR(mKernelType); + READ_SCALAR(mThreadCount[0]); + READ_SCALAR(mThreadCount[1]); + READ_SCALAR(mThreadCount[2]); + READ_SCALAR(mBlockCountY); + + ModuleSceneIntl* moduleScene = scene->getInternalModuleScene(mModuleName.c_str()); + if (moduleScene) + { + mHeadCudaObj = static_cast<ApexCudaObj*>(moduleScene->getHeadCudaObj()); + } + + ApexCudaObj* obj = mHeadCudaObj; + while(obj) + { + if (obj->getType() == ApexCudaObj::FUNCTION) + { + if (ApexSimpleString(DYNAMIC_CAST(ApexCudaFunc*)(obj)->getName()) == mName) + { + mFunc = DYNAMIC_CAST(ApexCudaFunc*)(obj); + break; + } + } + obj = obj->next(); + } + } + } + + ApexCudaTestKernelContextReader::~ApexCudaTestKernelContextReader() + { + if (mMemBuf) + { + PX_DELETE(mMemBuf); + } + if (mCudaArrayList) + { + PX_DELETE_ARRAY(mCudaArrayList); + } + } + + bool ApexCudaTestKernelContextReader::runKernel() + { + if (mFunc) + { + //launch1 + ApexCudaFuncParams params; + int* tmp = NULL; + int itmp = 0; + + PxScopedCudaLock _lock_(*mApexScene->getTaskManager()->getGpuDispatcher()->getCudaContextManager()); + + mFunc->setParam(params, tmp); // profile buffer (NULL) + mFunc->setParam(params, itmp); // kernelID (0) + + switch(mKernelType) + { + case apexCudaTest::KT_SYNC : + PX_ASSERT(!"Not implemented!"); + break; + case apexCudaTest::KT_FREE2D : + mFunc->setParam(params, mThreadCount[0]); + mFunc->setParam(params, mThreadCount[1]); + break; + case apexCudaTest::KT_FREE3D : + mFunc->setParam(params, mThreadCount[0]); + mFunc->setParam(params, mThreadCount[1]); + mFunc->setParam(params, mThreadCount[2]); + mFunc->setParam(params, mBlockCountY); + break; + case apexCudaTest::KT_BOUND : + case apexCudaTest::KT_FREE : + mFunc->setParam(params, mThreadCount[0]); + break; + default : + PX_ASSERT(!"Wrong kernel type"); + } + + loadContext(params); + + void *config[5] = { + CU_LAUNCH_PARAM_BUFFER_POINTER, params.mParams, + CU_LAUNCH_PARAM_BUFFER_SIZE, ¶ms.mOffset, + CU_LAUNCH_PARAM_END + }; + PX_ASSERT(mFuncInstId < mFunc->mFuncInstCount); + CUT_SAFE_CALL(cuLaunchKernel(mFunc->mFuncInstData[mFuncInstId].mCuFunc, (uint32_t)mGridDim.x, (uint32_t)mGridDim.y, 1, (uint32_t)mBlockDim.x, (uint32_t)mBlockDim.y, (uint32_t)mBlockDim.z, mSharedSize, (CUstream)mCuStream, 0, (void **)config)); + + mTmpArray.copyDeviceToHostQ(mCopyQueue); + mCopyQueue.flushEnqueued(); + + //copy mOutArrayRefs to host + uint32_t outArrayRefsOffset = 0; + for (uint32_t i = 0; i < mOutArrayRefs.size(); i++) + { + if (mOutArrayRefs[i].cudaArray != NULL) + { + outArrayRefsOffset += mOutArrayRefs[i].size; + } + } + Array <uint8_t> outArrayRefsBuffer(outArrayRefsOffset); + outArrayRefsOffset = 0; + for (uint32_t i = 0; i < mOutArrayRefs.size(); i++) + { + if (mOutArrayRefs[i].cudaArray != NULL) + { + mOutArrayRefs[i].cudaArray->copyToHost((CUstream)mCuStream, outArrayRefsBuffer.begin() + outArrayRefsOffset); + outArrayRefsOffset += mOutArrayRefs[i].size; + } + } + + CUT_SAFE_CALL(cuStreamSynchronize((CUstream)mCuStream)); + + for (uint32_t i = 0; i < mTexRefs.size(); i++) + { + if (mTexRefs[i].cudaTexRef) + { + mTexRefs[i].cudaTexRef->unbind(); + } + } + for (uint32_t i = 0; i < mSurfRefs.size(); i++) + { + if (mSurfRefs[i].cudaSurfRef) + { + mSurfRefs[i].cudaSurfRef->unbind(); + } + } + + bool isOk = true; + for (uint32_t i = 0; i < mOutMemRefs.size() && isOk; i++) + { + isOk = compare( + (const uint8_t*)mTmpArray.getPtr() + mOutMemRefs[i].bufferOffset, + (const uint8_t*)mOutMemRefs[i].gpuPtr, + mOutMemRefs[i].size, + mOutMemRefs[i].fpType, + mOutMemRefs[i].name.c_str()); + } + outArrayRefsOffset = 0; + for (uint32_t i = 0; i < mOutArrayRefs.size() && isOk; i++) + { + if (mOutArrayRefs[i].cudaArray != NULL) + { + uint32_t fpType; + switch (mOutArrayRefs[i].cudaArray->getFormat()) + { + case CU_AD_FORMAT_HALF: + fpType = 2; + break; + case CU_AD_FORMAT_FLOAT: + fpType = 4; + break; + default: + fpType = 0; + break; + }; + isOk = compare( + outArrayRefsBuffer.begin() + outArrayRefsOffset, + mOutArrayRefs[i].bufferPtr, + mOutArrayRefs[i].size, + fpType, + mOutArrayRefs[i].name.c_str()); + outArrayRefsOffset += mOutArrayRefs[i].size; + } + } + return isOk; + } + + APEX_DEBUG_WARNING("can't find kernel '%s'", mName.c_str()); + return false; + } + + class Float16Compressor + { + union Bits + { + float f; + int32_t si; + uint32_t ui; + }; + + static int32_t const shift = 13; + static int32_t const shiftSign = 16; + + static int32_t const infN = 0x7F800000; // flt32 infinity + static int32_t const maxN = 0x477FE000; // max flt16 normal as a flt32 + static int32_t const minN = 0x38800000; // min flt16 normal as a flt32 + static int32_t const signN = 0x80000000; // flt32 sign bit + + static int32_t const infC = infN >> shift; + static int32_t const nanN = (infC + 1) << shift; // minimum flt16 nan as a flt32 + static int32_t const maxC = maxN >> shift; + static int32_t const minC = minN >> shift; + static int32_t const signC = signN >> shiftSign; // flt16 sign bit + + static int32_t const mulN = 0x52000000; // (1 << 23) / minN + static int32_t const mulC = 0x33800000; // minN / (1 << (23 - shift)) + + static int32_t const subC = 0x003FF; // max flt32 subnormal down shifted + static int32_t const norC = 0x00400; // min flt32 normal down shifted + + static int32_t const maxD = infC - maxC - 1; + static int32_t const minD = minC - subC - 1; + + public: + static float decompress(uint16_t value) + { + Bits v; + v.ui = value; + int32_t sign = v.si & signC; + v.si ^= sign; + sign <<= shiftSign; + v.si ^= ((v.si + minD) ^ v.si) & -(v.si > subC); + v.si ^= ((v.si + maxD) ^ v.si) & -(v.si > maxC); + Bits s; + s.si = mulC; + s.f *= v.si; + int32_t mask = -(norC > v.si); + v.si <<= shift; + v.si ^= (s.si ^ v.si) & mask; + v.si |= sign; + return v.f; + } + }; + + bool ApexCudaTestKernelContextReader::compare(const uint8_t* resData, const uint8_t* refData, size_t size, uint32_t fpType, const char* name) + { + char str[4096]; + bool isOk = true; + switch (fpType) + { + case 2: + for (uint32_t j = 0; j < size && isOk; j += 2) + { + float ref = Float16Compressor::decompress(*reinterpret_cast<const uint16_t*>(refData + j)); + float res = Float16Compressor::decompress(*reinterpret_cast<const uint16_t*>(resData + j)); + isOk = PxAbs(res - ref) <= 2.5e-3 * PxMax(2.f, PxAbs(res + ref)); + if (!isOk) + { + sprintf(str, "data mismatch at %d (%f != %f) in kernel '%s' param '%s'", (j / 2), res, ref, mName.c_str(), name); + dumpParams(str); + APEX_DEBUG_WARNING(str); + } + } + break; + case 4: + for (uint32_t j = 0; j < size && isOk; j += 4) + { + float ref = *reinterpret_cast<const float*>(refData + j); + float res = *reinterpret_cast<const float*>(resData + j); + isOk = PxAbs(res - ref) <= 2.5e-7 * PxMax(2.f, PxAbs(res + ref)); + if (!isOk) + { + sprintf(str, "data mismatch at %d (%f != %f) in kernel '%s' param '%s'", (j / 4), res, ref, mName.c_str(), name); + dumpParams(str); + APEX_DEBUG_WARNING(str); + } + } + break; + case 8: + for (uint32_t j = 0; j < size && isOk; j += 8) + { + double ref = *reinterpret_cast<const double*>(refData + j); + double res = *reinterpret_cast<const double*>(resData + j); + isOk = PxAbs(res - ref) <= 2.5e-14 * PxMax(2., PxAbs(res + ref)); + if (!isOk) + { + sprintf(str, "data mismatch at %d (%lf != %lf) in kernel '%s' param '%s'", (j / 8), res, ref, mName.c_str(), name); + dumpParams(str); + APEX_DEBUG_WARNING(str); + } + } + break; + default: + for (uint32_t j = 0; j < size && isOk; j += 4) + { + int ref = *reinterpret_cast<const int*>(refData + j); + int res = *reinterpret_cast<const int*>(resData + j); + isOk = (res == ref); + if (!isOk) + { + sprintf(str, "data mismatch at %d (%d != %d) in kernel '%s' param '%s'", (j / 4), res, ref, mName.c_str(), name); + dumpParams(str); + APEX_DEBUG_WARNING(str); + } + } + break; + }; + return isOk; + } + + void ApexCudaTestKernelContextReader::dumpParams(char* str) + { + size_t len = strlen(str); + str += len; + *str++ = '\n'; + sprintf(str, "blockDim = (%d, %d, %d) GridDim = (%d, %d, %d) threadCount = (%d, %d, %d)", mBlockDim.x, mBlockDim.y, mBlockDim.z, mGridDim.x, mGridDim.y, mGridDim.z, mThreadCount[0], mThreadCount[1], mThreadCount[2]); + for (uint32_t i = 0; i < mParamRefs.size(); ++i) + { + size_t len = strlen(str); + str += len; + *str++ = '\n'; + sprintf(str, "arg '%s' = 0x%x", mParamRefs[i].name.c_str(), mParamRefs[i].value); + } + } + + void ApexCudaTestKernelContextReader::loadContext(ApexCudaFuncParams& params) + { + uint32_t n; + uint32_t cudaMemOffset = 0; + + //Read cuda objs + mMemBuf->seekRead(mCudaObjOffset); + READ_SCALAR(n) + mCudaArrayList = PX_NEW(ApexCudaArray)[n]; + mCudaArrayCount = 0; + for (uint32_t i = 0; i < n; i++) + { + uint32_t t; + READ_SCALAR(t); + switch(t) + { + case apexCudaTest::OBJ_TYPE_TEX_REF_MEM: + loadTexRef(cudaMemOffset, false); + break; + case apexCudaTest::OBJ_TYPE_CONST_MEM: + loadConstMem(); + break; + case apexCudaTest::OBJ_TYPE_SURF_REF: + loadSurfRef(); + break; + case apexCudaTest::OBJ_TYPE_TEX_REF_ARR: + loadTexRef(cudaMemOffset, true); + break; + default: + PX_ASSERT(!"Wrong type"); + return; + } + } + + + //Read call params + mMemBuf->seekRead(mParamOffset); + READ_SCALAR(n); + uint32_t cudaMemOffsetPS = 0; + for (uint32_t i = 0; i < n; i++) + { + cudaMemOffsetPS += getParamSize(); + ALIGN_OFFSET(cudaMemOffsetPS, APEX_CUDA_TEX_MEM_ALIGNMENT); + } + + uint32_t arrSz = PxMax(cudaMemOffset + cudaMemOffsetPS, 4U); + mTmpArray.reserve(arrSz, ApexMirroredPlace::CPU_GPU); + mTmpArray.setSize(arrSz); + + mMemBuf->seekRead(this->mParamOffset + sizeof(n)); + for (uint32_t i = 0; i < n; i++) + { + loadParam(cudaMemOffset, params); + } + + for (uint32_t i = 0; i < mInMemRefs.size(); i++) + { + memcpy(mTmpArray.getPtr() + mInMemRefs[i].bufferOffset, mInMemRefs[i].gpuPtr, mInMemRefs[i].size); + } + + if (cudaMemOffset > 0) + { + mCopyQueue.reset((CUstream)mCuStream, 1); + mTmpArray.copyHostToDeviceQ(mCopyQueue, cudaMemOffset); + mCopyQueue.flushEnqueued(); + } + for (uint32_t i = 0; i < mInArrayRefs.size(); i++) + { + if (mInArrayRefs[i].cudaArray != NULL) + { + mInArrayRefs[i].cudaArray->copyFromHost((CUstream)mCuStream, mInArrayRefs[i].bufferPtr); + } + } + + for (uint32_t i = 0; i < mTexRefs.size(); i++) + { + if (mTexRefs[i].cudaTexRef) + { + if (mTexRefs[i].memRefIdx != uint32_t(-1)) + { + const apexCudaTest::MemRef& memRef = mInMemRefs[ mTexRefs[i].memRefIdx ]; + mTexRefs[i].cudaTexRef->bindTo(mTmpArray.getGpuPtr() + memRef.bufferOffset, memRef.size); + } + else if (mTexRefs[i].cudaArray != NULL) + { + mTexRefs[i].cudaTexRef->bindTo(*mTexRefs[i].cudaArray); + } + } + } + for (uint32_t i = 0; i < mSurfRefs.size(); i++) + { + if (mSurfRefs[i].cudaArray != NULL) + { + mSurfRefs[i].cudaSurfRef->bindTo(*mSurfRefs[i].cudaArray, mSurfRefs[i].flags); + } + } + } + + ApexCudaArray* ApexCudaTestKernelContextReader::loadCudaArray() + { + uint32_t format, numChannels, width, height, depth, flags; + READ_SCALAR(format); + READ_SCALAR(numChannels); + READ_SCALAR(width); + READ_SCALAR(height); + READ_SCALAR(depth); + READ_SCALAR(flags); + + CUDA_ARRAY3D_DESCRIPTOR desc; + desc.Format = CUarray_format(format); + desc.NumChannels = numChannels; + desc.Width = width; + desc.Height = height; + desc.Depth = depth; + desc.Flags = flags; + + ApexCudaArray* cudaArray = &mCudaArrayList[mCudaArrayCount++]; + cudaArray->create(desc); + + return cudaArray; + } + + void ApexCudaTestKernelContextReader::loadTexRef(uint32_t& memOffset, bool bBindToArray) + { + ApexSimpleString name; + READ_STRING(name); + + TexRef texRef; + texRef.memRefIdx = uint32_t(-1); + texRef.cudaArray = NULL; + if (bBindToArray) + { + texRef.cudaArray = loadCudaArray(); + const uint32_t size = uint32_t(texRef.cudaArray->getByteSize()); + + mMemBuf->alignRead(4); + mInArrayRefs.pushBack( ArrayRef(name.c_str(), texRef.cudaArray, mMemBuf->getReadLoc(), size) ); + mMemBuf->advanceReadLoc(size); + } + else + { + uint32_t size; + READ_SCALAR(size); + if (size > 0) + { + texRef.memRefIdx = mInMemRefs.size(); + + mMemBuf->alignRead(4); + mInMemRefs.pushBack( apexCudaTest::MemRef(mMemBuf->getReadLoc(), size, 0, memOffset) ); + mMemBuf->advanceReadLoc(size); + + memOffset += size; ALIGN_OFFSET(memOffset, APEX_CUDA_TEX_MEM_ALIGNMENT); + } + } + + //Find texture + for (ApexCudaObj* obj = mHeadCudaObj; obj; obj = obj->next()) + { + if (obj->getType() == ApexCudaObj::TEXTURE && ::strcmp(obj->getName(), name.c_str()) == 0) + { + texRef.cudaTexRef = DYNAMIC_CAST(ApexCudaTexRef*)(obj); + mTexRefs.pushBack(texRef); + break; + } + } + } + + void ApexCudaTestKernelContextReader::loadSurfRef() + { + ApexSimpleString name; + uint32_t flags; + READ_STRING(name); + READ_SCALAR(flags); + + SurfRef surfRef; + surfRef.flags = ApexCudaMemFlags::Enum(flags); + surfRef.cudaArray = loadCudaArray(); + const uint32_t size = uint32_t(surfRef.cudaArray->getByteSize()); + + if (surfRef.flags & ApexCudaMemFlags::IN) + { + mMemBuf->alignRead(4); + mInArrayRefs.pushBack( ArrayRef(name.c_str(), surfRef.cudaArray, mMemBuf->getReadLoc(), size) ); + mMemBuf->advanceReadLoc(size); + } + if (surfRef.flags & ApexCudaMemFlags::OUT) + { + mMemBuf->alignRead(4); + mOutArrayRefs.pushBack( ArrayRef(name.c_str(), surfRef.cudaArray, mMemBuf->getReadLoc(), size) ); + mMemBuf->advanceReadLoc(size); + } + + //Find surface + for (ApexCudaObj* obj = mHeadCudaObj; obj; obj = obj->next()) + { + if (obj->getType() == ApexCudaObj::SURFACE && ::strcmp(obj->getName(), name.c_str()) == 0) + { + surfRef.cudaSurfRef = DYNAMIC_CAST(ApexCudaSurfRef*)(obj); + mSurfRefs.pushBack(surfRef); + break; + } + } + } + + void ApexCudaTestKernelContextReader::loadConstMem() + { + uint32_t size; + ApexSimpleString name; + READ_STRING(name); + READ_SCALAR(size); + + //Load const mem + ApexCudaObj* obj = mHeadCudaObj; + while(obj) + { + if (obj->getType() == ApexCudaObj::CONST_STORAGE) + { + ApexCudaConstStorage* constMem = DYNAMIC_CAST(ApexCudaConstStorage*)(obj); + if (ApexSimpleString(constMem->getName()) == name) + { + PX_ASSERT(constMem->mHostBuffer != 0); + PX_ASSERT(constMem->mHostBuffer->getSize() >= size); + void* hostPtr = reinterpret_cast<void*>(constMem->mHostBuffer->getPtr()); + + mMemBuf->read(hostPtr, size); + CUT_SAFE_CALL(cuMemcpyHtoDAsync(constMem->mDevPtr, hostPtr, size, NULL)); + break; + } + } + obj = obj->next(); + } + } + + uint32_t ApexCudaTestKernelContextReader::getParamSize() + { + ApexSimpleString name; + uint32_t size, align, intent; + int32_t dataOffset; + READ_STRING(name); + READ_SCALAR(align); + READ_SCALAR(intent); + READ_SCALAR(dataOffset); + READ_SCALAR(size); + if (size > 0) + { + mMemBuf->alignRead(align); + mMemBuf->advanceReadLoc(size); + + if ((intent & 3) == 3) + { + mMemBuf->alignRead(align); + mMemBuf->advanceReadLoc(size); + } + if (intent & 3) + { + return size; + } + } + return 0; + } + + void ApexCudaTestKernelContextReader::loadParam(uint32_t& memOffset, ApexCudaFuncParams& params) + { + ParamRef paramRef; + uint32_t size, align, intent; + int32_t dataOffset; + READ_STRING(paramRef.name); + READ_SCALAR(align); + READ_SCALAR(intent); + READ_SCALAR(dataOffset); + READ_SCALAR(size); + if (size > 0) + { + if (!intent) // scalar param + { + paramRef.value = *(uint32_t*)(mMemBuf->getReadLoc()); + mParamRefs.pushBack(paramRef); + + mFunc->setParam(params, align, size, (void*)(mMemBuf->getReadLoc())); + mMemBuf->advanceReadLoc(size); + } + else + { + mMemBuf->alignRead(align); + mInMemRefs.pushBack(apexCudaTest::MemRef(mMemBuf->getReadLoc(), size, dataOffset, memOffset)); + if (intent & 0x01) // input intent + { + mMemBuf->advanceReadLoc(size); + } + if (intent & 0x02) // output intent + { + mMemBuf->alignRead(align); + mOutMemRefs.pushBack(apexCudaTest::MemRef(mMemBuf->getReadLoc(), size, dataOffset, memOffset, intent >> 2)); + mOutMemRefs.back().name = paramRef.name; + mMemBuf->advanceReadLoc(size); + } + void* ptr = mTmpArray.getGpuPtr() + memOffset - dataOffset; + mFunc->setParam(params, align, sizeof(void*), &ptr); + memOffset += size; ALIGN_OFFSET(memOffset, APEX_CUDA_TEX_MEM_ALIGNMENT); + } + } + else + { + void* ptr = NULL;//mTmpArray.getGpuPtr() + memOffset - dataOffset; + mFunc->setParam(params, align, sizeof(void*), &ptr); + } + } + + ApexCudaTestKernelContext::ApexCudaTestKernelContext(const char* path, const char* functionName, const char* moduleName, uint32_t frame, uint32_t callPerFrame, + bool isWriteForNonSuccessfulKernel, bool isContextForSave) + : mVersion(ApexCudaTestFileVersion) + , mFrame(frame) + , mCallPerFrame(callPerFrame) + , mPath(path) + , mName(functionName) + , mModuleName(moduleName) + , mCudaObjsCounter(0) + , mCallParamsCounter(0) + , mIsCompleteContext(false) + , mIsWriteForNonSuccessfulKernel(isWriteForNonSuccessfulKernel) + , mIsContextForSave(isContextForSave) + { + uint32_t writeLoc; + // service info + mMemBuf.setEndianMode(nvidia::PsMemoryBuffer::ENDIAN_LITTLE); + mMemBuf.write(&mVersion, sizeof(uint32_t)); // Version of format + mMemBuf.seekWrite(2 * sizeof(uint32_t)); // Space for file size + writeLoc = 32; // Offset for header block + mMemBuf.write(&writeLoc, sizeof(uint32_t)); + + // header info + mMemBuf.seekWrite(writeLoc); + WRITE_STRING(mName) // Name of function + WRITE_STRING(mModuleName) // Name of module + WRITE_SCALAR(frame) // Current frame + WRITE_SCALAR(callPerFrame) // Call of kernel per current frame + + writeLoc = mMemBuf.tellWrite(); + writeLoc += 12 * sizeof(uint32_t); // Space for cuda kernel parameters + + mCudaObjsOffset = writeLoc; // Offset for cuda objects block + mMemBuf.seekWrite(3 * sizeof(uint32_t)); + mMemBuf.write(&mCudaObjsOffset, sizeof(uint32_t)); + + writeLoc = mCudaObjsOffset + sizeof(uint32_t); // Space for N of cuda objs + mMemBuf.seekWrite(writeLoc); + } + + ApexCudaTestKernelContext::~ApexCudaTestKernelContext() + { + } + + PX_INLINE uint32_t ApexCudaTestKernelContext::advanceMemBuf(uint32_t size, uint32_t align) + { + uint32_t writeLoc = mMemBuf.tellWrite(); + ALIGN_OFFSET(writeLoc, align); + const uint32_t ret = writeLoc; + writeLoc += size; + mMemBuf.seekWrite(writeLoc); + return ret; + } + PX_INLINE void ApexCudaTestKernelContext::copyToMemBuf(const apexCudaTest::MemRef& memRef) + { + CUT_SAFE_CALL(cuMemcpyDtoHAsync( + (void*)(mMemBuf.getWriteBuffer() + memRef.bufferOffset), CUdeviceptr((const uint8_t*)memRef.gpuPtr + memRef.dataOffset), memRef.size, CUstream(mCuStream)) + ); + } + PX_INLINE void ApexCudaTestKernelContext::copyToMemBuf(const ArrayRef& arrayRef) + { + ApexCudaArray cudaArray; + cudaArray.assign(arrayRef.cuArray, false); + cudaArray.copyToHost((CUstream)mCuStream, (void*)(mMemBuf.getWriteBuffer() + arrayRef.bufferOffset)); + } + + void ApexCudaTestKernelContext::completeCudaObjsBlock() + { + uint32_t writeLoc = mMemBuf.tellWrite(); + mMemBuf.seekWrite(4 * sizeof(uint32_t)); // Offset for call param block + mMemBuf.write(&writeLoc, sizeof(uint32_t)); + mCallParamsOffset = writeLoc; + + mMemBuf.seekWrite(mCudaObjsOffset); // Write N of cuda objs + mMemBuf.write(&mCudaObjsCounter, sizeof(uint32_t)); + + writeLoc += sizeof(uint32_t); // Space for N of call params + mMemBuf.seekWrite(writeLoc); + } + + void ApexCudaTestKernelContext::completeCallParamsBlock() + { + uint32_t writeLoc = mMemBuf.tellWrite(); + mMemBuf.seekWrite(mCallParamsOffset); // Write N of call params + mMemBuf.write(&mCallParamsCounter, sizeof(uint32_t)); + mMemBuf.seekWrite(writeLoc); + } + + void ApexCudaTestKernelContext::setFreeKernel(uint32_t threadCount) + { + uint32_t writeLoc = mMemBuf.tellWrite(); + mMemBuf.seekWrite(mCudaObjsOffset - 5 * sizeof(uint32_t)); + uint32_t tmp = apexCudaTest::KT_FREE; + mMemBuf.write(&tmp, sizeof(tmp)); + mMemBuf.write(&threadCount, sizeof(threadCount)); + tmp = 0; + mMemBuf.write(&tmp, sizeof(tmp)); + mMemBuf.write(&tmp, sizeof(tmp)); + mMemBuf.write(&tmp, sizeof(tmp)); + mMemBuf.seekWrite(writeLoc); + } + void ApexCudaTestKernelContext::setFreeKernel(uint32_t threadCountX, uint32_t threadCountY) + { + uint32_t writeLoc = mMemBuf.tellWrite(); + mMemBuf.seekWrite(mCudaObjsOffset - 5 * sizeof(uint32_t)); + uint32_t tmp = apexCudaTest::KT_FREE2D; + mMemBuf.write(&tmp, sizeof(tmp)); + mMemBuf.write(&threadCountX, sizeof(threadCountX)); + mMemBuf.write(&threadCountY, sizeof(threadCountY)); + tmp = 0; + mMemBuf.write(&tmp, sizeof(tmp)); + mMemBuf.write(&tmp, sizeof(tmp)); + mMemBuf.seekWrite(writeLoc); + } + void ApexCudaTestKernelContext::setFreeKernel(uint32_t threadCountX, uint32_t threadCountY, uint32_t threadCountZ, uint32_t blockCountY) + { + uint32_t writeLoc = mMemBuf.tellWrite(); + mMemBuf.seekWrite(mCudaObjsOffset - 5 * sizeof(uint32_t)); + uint32_t tmp = apexCudaTest::KT_FREE3D; + mMemBuf.write(&tmp, sizeof(tmp)); + mMemBuf.write(&threadCountX, sizeof(threadCountX)); + mMemBuf.write(&threadCountY, sizeof(threadCountY)); + mMemBuf.write(&threadCountZ, sizeof(threadCountZ)); + mMemBuf.write(&blockCountY, sizeof(blockCountY)); + mMemBuf.seekWrite(writeLoc); + } + void ApexCudaTestKernelContext::setBoundKernel(uint32_t threadCount) + { + uint32_t writeLoc = mMemBuf.tellWrite(); + mMemBuf.seekWrite(mCudaObjsOffset - 5 * sizeof(uint32_t)); + uint32_t tmp = apexCudaTest::KT_BOUND; + mMemBuf.write(&tmp, sizeof(tmp)); + mMemBuf.write(&threadCount, sizeof(threadCount)); + tmp = 0; + mMemBuf.write(&tmp, sizeof(tmp)); + mMemBuf.write(&tmp, sizeof(tmp)); + mMemBuf.write(&tmp, sizeof(tmp)); + mMemBuf.seekWrite(writeLoc); + } + void ApexCudaTestKernelContext::setSyncKernel() + { + uint32_t writeLoc = mMemBuf.tellWrite(); + mMemBuf.seekWrite(mCudaObjsOffset - 5 * sizeof(uint32_t)); + uint32_t tmp = apexCudaTest::KT_SYNC; + mMemBuf.write(&tmp, sizeof(tmp)); + mMemBuf.seekWrite(writeLoc); + } + + void ApexCudaTestKernelContext::setSharedSize(uint32_t size) + { + uint32_t writeLoc = mMemBuf.tellWrite(); + mMemBuf.seekWrite(mCudaObjsOffset - 11 * sizeof(uint32_t)); + mMemBuf.write(&size, sizeof(int)); + mMemBuf.seekWrite(writeLoc); + } + void ApexCudaTestKernelContext::setFuncInstId(int id) + { + uint32_t writeLoc = mMemBuf.tellWrite(); + mMemBuf.seekWrite(mCudaObjsOffset - 12 * sizeof(uint32_t)); + mMemBuf.write(&id, sizeof(int)); + mMemBuf.seekWrite(writeLoc); + } + + void ApexCudaTestKernelContext::setBlockDim(uint32_t x, uint32_t y, uint32_t z) + { + uint32_t writeLoc = mMemBuf.tellWrite(); + mMemBuf.seekWrite(mCudaObjsOffset - 10 * sizeof(uint32_t)); + mMemBuf.write(&x, sizeof(int)); + mMemBuf.write(&y, sizeof(int)); + mMemBuf.write(&z, sizeof(int)); + mMemBuf.seekWrite(writeLoc); + } + + void ApexCudaTestKernelContext::setGridDim(uint32_t x, uint32_t y) + { + uint32_t writeLoc = mMemBuf.tellWrite(); + mMemBuf.seekWrite(mCudaObjsOffset - 7 * sizeof(uint32_t)); + mMemBuf.write(&x, sizeof(int)); + mMemBuf.write(&y, sizeof(int)); + mMemBuf.seekWrite(writeLoc); + } + + void ApexCudaTestKernelContext::addParam(const char* name, uint32_t align, const void *val, size_t size, int memRefIntent, int dataOffset, uint32_t fpType) + { + if (val == 0) + { + //handle NULL-ptr case + size = 0; + dataOffset = 0; + } + uint32_t sz = (uint32_t)size; + mCallParamsCounter++; + ApexSimpleString tName(name); + WRITE_STRING(tName); + WRITE_SCALAR(align); + uint32_t intent = (uint32_t)memRefIntent; + intent += fpType << 2; + WRITE_SCALAR(intent); + WRITE_SCALAR(dataOffset); + if (memRefIntent == 0) + { + WRITE_ALIGN_ARRAY(val, sz, align); + } + else + { + WRITE_SCALAR(sz); + if (sz > 0) + { + if (memRefIntent & ApexCudaMemFlags::IN) + { + const uint32_t offset = advanceMemBuf(sz, align); + apexCudaTest::MemRef memRef(val, size, dataOffset, offset); + copyToMemBuf(memRef); + } + if (memRefIntent & ApexCudaMemFlags::OUT) + { + const uint32_t offset = advanceMemBuf(sz, align); + apexCudaTest::MemRef memRef(val, size, dataOffset, offset); + mMemRefs.pushBack(memRef); + } + } + } + } + + void ApexCudaTestKernelContext::startObjList() + { + } + void ApexCudaTestKernelContext::finishObjList() + { + completeCudaObjsBlock(); + } + + uint32_t ApexCudaTestKernelContext::addCuArray(CUarray cuArray) + { + ApexCudaArray cudaArray; + cudaArray.assign(cuArray, false); + + const CUDA_ARRAY3D_DESCRIPTOR& desc = cudaArray.getDesc(); + uint32_t format = uint32_t(desc.Format); + uint32_t numChannels = uint32_t(desc.NumChannels); + uint32_t width = uint32_t(desc.Width); + uint32_t height = uint32_t(desc.Height); + uint32_t depth = uint32_t(desc.Depth); + uint32_t flags = uint32_t(desc.Flags); + + WRITE_SCALAR(format); + WRITE_SCALAR(numChannels); + WRITE_SCALAR(width); + WRITE_SCALAR(height); + WRITE_SCALAR(depth); + WRITE_SCALAR(flags); + + return uint32_t(cudaArray.getByteSize()); + } + + void ApexCudaTestKernelContext::addTexRef(const char* name, const void* mem, size_t size, CUarray arr) + { + PX_ASSERT(!mIsCompleteContext); + const uint32_t objType = (arr != NULL) ? apexCudaTest::OBJ_TYPE_TEX_REF_ARR : apexCudaTest::OBJ_TYPE_TEX_REF_MEM; + mCudaObjsCounter++; + WRITE_SCALAR(objType); + ApexSimpleString tName(name); + WRITE_STRING(tName); + if (arr != NULL) + { + const uint32_t sz = addCuArray(arr); + const uint32_t offset = advanceMemBuf(sz); + ArrayRef arrayRef(arr, offset); + copyToMemBuf(arrayRef); + } + else + { + const uint32_t sz = uint32_t(size); + WRITE_SCALAR(sz); + if (sz > 0) + { + const uint32_t offset = advanceMemBuf(sz); + apexCudaTest::MemRef memRef(mem, size, 0, offset); + copyToMemBuf(memRef); + } + } + } + + void ApexCudaTestKernelContext::addSurfRef(const char* name, CUarray arr, ApexCudaMemFlags::Enum flags) + { + PX_ASSERT(!mIsCompleteContext); + const uint32_t objType = apexCudaTest::OBJ_TYPE_SURF_REF; + mCudaObjsCounter++; + WRITE_SCALAR(objType); + ApexSimpleString tName(name); + WRITE_STRING(tName); + const uint32_t intent = flags; + WRITE_SCALAR(intent); + + const uint32_t sz = addCuArray(arr); + if (intent & ApexCudaMemFlags::IN) + { + const uint32_t offset = advanceMemBuf(sz); + ArrayRef arrayRef(arr, offset); + copyToMemBuf(arrayRef); + } + if (intent & ApexCudaMemFlags::OUT) + { + const uint32_t offset = advanceMemBuf(sz); + ArrayRef arrayRef(arr, offset); + mArrayRefs.pushBack(arrayRef); + } + } + + void ApexCudaTestKernelContext::addConstMem(const char* name, const void* mem, size_t size) + { + PX_ASSERT(!mIsCompleteContext); + const uint32_t objType = apexCudaTest::OBJ_TYPE_CONST_MEM; + mCudaObjsCounter++; + WRITE_SCALAR(objType); + ApexSimpleString cmName(name); + WRITE_STRING(cmName); + WRITE_ARRAY(mem, (uint32_t)size); + } + + void ApexCudaTestKernelContext::copyMemRefs() + { + for (uint32_t i = 0; i < mMemRefs.size(); i++) + { + copyToMemBuf(mMemRefs[i]); + } + mMemRefs.clear(); + } + + void ApexCudaTestKernelContext::copyArrayRefs() + { + for (uint32_t i = 0; i < mArrayRefs.size(); i++) + { + copyToMemBuf(mArrayRefs[i]); + } + mArrayRefs.clear(); + } + + void ApexCudaTestKernelContext::setKernelStatus() + { + if (mIsWriteForNonSuccessfulKernel) + { + int cuResult = cuCtxSynchronize();//= cudaPeekAtLastError(); + //cudaDeviceSynchronize(); + + if (cuResult) + { + mErrorCode += 'E'; + mErrorCode += ApexSimpleString((uint32_t)cuResult, 3); + saveToFile(); + APEX_INTERNAL_ERROR("Cuda Error %d", cuResult); + } + else if (mIsContextForSave) + { + copyMemRefs(); + copyArrayRefs(); + } + } + else + { + copyMemRefs(); + copyArrayRefs(); + } + } + + bool ApexCudaTestKernelContext::saveToFile() + { + if (!mIsContextForSave && mErrorCode.size() == 0) + { + return false; + } + if (!mIsCompleteContext) + { + completeCallParamsBlock(); + + uint32_t writeLoc = mMemBuf.tellWrite(); + mMemBuf.seekWrite(sizeof(uint32_t)); // Write size of file + mMemBuf.write(&writeLoc, sizeof(uint32_t)); + mIsCompleteContext = true; + + mMemBuf.seekWrite(writeLoc); + } + + ApexSimpleString path(mPath); + path += mName; + path += '_'; + path += ApexSimpleString(mCallPerFrame, 3); + path += ApexSimpleString(mFrame, 5); + path += mErrorCode; + FILE* saveFile = fopen(path.c_str(), "wb"); + + if (saveFile) + { + fwrite(mMemBuf.getWriteBuffer(), mMemBuf.getWriteBufferSize(), 1, saveFile); + return !fclose(saveFile); + } + + return false; + } + + + ApexCudaTestManager::ApexCudaTestManager() + : mCurrentFrame(0) + , mMaxSamples(0) + , mFramePeriod(0) + , mCallPerFrameMaxCount(1) + , mIsWriteForNonSuccessfulKernel(false) + { + } + + ApexCudaTestManager::~ApexCudaTestManager() + { + for (uint32_t i = 0; i < mContexts.size(); i++) + { + PX_DELETE(mContexts[i]); + } + } + + void ApexCudaTestManager::setWriteForFunction(const char* functionName, const char* moduleName) + { + if (::strcmp(functionName, "*") == 0) + { + //Add all function registered in module + ModuleSceneIntl* moduleScene = mApexScene->getInternalModuleScene(moduleName); + ApexCudaObj* obj = NULL; + if (moduleScene) + { + obj = static_cast<ApexCudaObj*>(moduleScene->getHeadCudaObj()); + } + while(obj) + { + if (obj->getType() == ApexCudaObj::FUNCTION) + { + const char* name = DYNAMIC_CAST(ApexCudaFunc*)(obj)->getName(); + if (mKernels.find(KernelInfo(name, moduleName)) == mKernels.end()) + { + mKernels.pushBack(KernelInfo(name, moduleName)); + } + } + obj = obj->next(); + } + } + else + { + ApexSimpleString fName(moduleName); + fName += '_'; + fName += ApexSimpleString(functionName); + mKernels.pushBack(KernelInfo(fName.c_str(), moduleName)); + } + } + + bool ApexCudaTestManager::runKernel(const char* path) + { + if (mApexScene) + { + ApexCudaTestKernelContextReader contextReader(path, mApexScene); + return contextReader.runKernel(); + } + return false; + } + + void ApexCudaTestManager::nextFrame() + { + mCurrentFrame++; + + if (mContexts.size() > 0) + { + for (uint32_t i = 0; i < mContexts.size(); i++) + { + mContexts[i]->saveToFile(); + PX_DELETE(mContexts[i]); + } + mContexts.clear(); + + for (uint32_t i = 0; i < mKernels.size(); i++) + { + mKernels[i].callCount = 0; + } + } + } + + ApexCudaTestKernelContext* ApexCudaTestManager::isTestKernel(const char* functionName, const char* moduleName) + { + KernelInfo* kernel = NULL; + if ( mContexts.size() < mMaxSamples + && ( mSampledFrames.find(mCurrentFrame) != mSampledFrames.end() + || mFramePeriod && (mCurrentFrame % mFramePeriod) == 0 + ) + && (kernel = mKernels.find(KernelInfo(functionName, moduleName))) != mKernels.end() + && (kernel->callCount < mCallPerFrameMaxCount) + ) + { + mContexts.pushBack(PX_NEW(ApexCudaTestKernelContext)(mPath.c_str(), functionName, moduleName, mCurrentFrame, ++(kernel->callCount), mIsWriteForNonSuccessfulKernel, true)); + return mContexts.back(); + } + else if (mIsWriteForNonSuccessfulKernel && (mKernels.size() == 0 || (mKernels.find(KernelInfo(functionName, moduleName)) != mKernels.end()))) + { + mContexts.pushBack(PX_NEW(ApexCudaTestKernelContext)(mPath.c_str(), functionName, moduleName, mCurrentFrame, 0, true, false)); + return mContexts.back(); + } + return NULL; + } +} +} // namespace nvidia::apex + +#endif diff --git a/APEX_1.4/common/src/ApexGeneralizedCubeTemplates.cpp b/APEX_1.4/common/src/ApexGeneralizedCubeTemplates.cpp new file mode 100644 index 00000000..87a9d6ea --- /dev/null +++ b/APEX_1.4/common/src/ApexGeneralizedCubeTemplates.cpp @@ -0,0 +1,677 @@ +/* + * 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. + */ + + +#include "ApexGeneralizedCubeTemplates.h" + +namespace nvidia +{ +namespace apex +{ + +ApexGeneralizedCubeTemplates::ApexGeneralizedCubeTemplates() +{ + // init basis + uint32_t p = 0; + const float d = 1.0f / (SUB_GRID_LEN - 1); + for (uint32_t xi = 0; xi < SUB_GRID_LEN; xi++) + { + const float wx = d * xi; + const float mx = 1.0f - wx; + for (uint32_t yi = 0; yi < SUB_GRID_LEN; yi++) + { + const float wy = d * yi; + const float my = 1.0f - wy; + for (uint32_t zi = 0; zi < SUB_GRID_LEN; zi++) + { + const float wz = d * zi; + const float mz = 1.0f - wz; + mBasis[p][0] = mx * my * mz; + mBasis[p][1] = wx * my * mz; + mBasis[p][2] = wx * wy * mz; + mBasis[p][3] = mx * wy * mz; + mBasis[p][4] = mx * my * wz; + mBasis[p][5] = wx * my * wz; + mBasis[p][6] = wx * wy * wz; + mBasis[p][7] = mx * wy * wz; + p++; + } + } + } + + // default vertex positions + mVertPos[ 0] = PxVec3(0.5f, 0.0f, 0.0f); // on edges + mVertPos[ 1] = PxVec3(1.0f, 0.5f, 0.0f); + mVertPos[ 2] = PxVec3(0.5f, 1.0f, 0.0f); + mVertPos[ 3] = PxVec3(0.0f, 0.5f, 0.0f); + mVertPos[ 4] = PxVec3(0.5f, 0.0f, 1.0f); + mVertPos[ 5] = PxVec3(1.0f, 0.5f, 1.0f); + mVertPos[ 6] = PxVec3(0.5f, 1.0f, 1.0f); + mVertPos[ 7] = PxVec3(0.0f, 0.5f, 1.0f); + mVertPos[ 8] = PxVec3(0.0f, 0.0f, 0.5f); + mVertPos[ 9] = PxVec3(1.0f, 0.0f, 0.5f); + mVertPos[10] = PxVec3(1.0f, 1.0f, 0.5f); + mVertPos[11] = PxVec3(0.0f, 1.0f, 0.5f); + mVertPos[12] = PxVec3(0.0f, 0.5f, 0.5f); // on faces + mVertPos[13] = PxVec3(1.0f, 0.5f, 0.5f); + mVertPos[14] = PxVec3(0.5f, 0.0f, 0.5f); + mVertPos[15] = PxVec3(0.5f, 1.0f, 0.5f); + mVertPos[16] = PxVec3(0.5f, 0.5f, 0.0f); + mVertPos[17] = PxVec3(0.5f, 0.5f, 1.0f); + mVertPos[18] = PxVec3(0.5f, 0.5f, 0.5f); // in center + + // init other data structures + for (int i = 0; i < NUM_SUB_CELLS; i++) + { + mSubGrid[i].init(); + } + + for (int i = 0; i < NUM_CASES_3; i++) + { + mFirst3[i] = -1; + } + + mLookupIndices3.clear(); + + createLookupTable3(); +} + + + + +void ApexGeneralizedCubeTemplates::getTriangles(const int groups[8], physx::Array<int32_t> &indices) +{ + int32_t maxGroup = 0; + for (uint32_t i = 0; i < 8; i++) + { + if (groups[i] > maxGroup) + { + maxGroup = groups[i]; + } + } + + //physx::Array<int32_t> currentIndices; + + indices.clear(); + PX_ASSERT(maxGroup < 8); + if (maxGroup <= 2 && !mLookupIndices3.empty()) + { + uint32_t code = 0; + for (int32_t i = 7; i >= 0; i--) + { + code = code * 3 + groups[i]; + } + + int32_t first = mFirst3[code]; + if (first >= 0) + { + for (uint32_t i = (uint32_t)first; mLookupIndices3[i] >= 0; i++) + { + indices.pushBack(mLookupIndices3[i]); + //currentIndices.pushBack(mLookupIndices3[i]); + } + return; + } + } + + setCellGroups(groups); + splitDisconnectedGroups(); + findVertices(); + createTriangles(indices); +} + + + + +void ApexGeneralizedCubeTemplates::createLookupTable3() +{ + if (!mLookupIndices3.empty()) + { + return; + } + + int32_t groups[8]; + physx::Array<int32_t> indices; + indices.reserve(32); + + mLookupIndices3.clear(); + mLookupIndices3.reserve(169200); // otherwise it crashes in release mode! + + for (uint32_t i = 0; i < NUM_CASES_3; i++) + { + int32_t c = (int32_t)i; + for (uint32_t j = 0; j < 8; j++) + { + groups[j] = c % 3; + c /= 3; + } + + getTriangles(groups, indices); + + mFirst3[i] = (int32_t)mLookupIndices3.size(); + for (uint32_t j = 0; j < indices.size(); j++) + { + mLookupIndices3.pushBack(indices[j]); + } + + mLookupIndices3.pushBack(-1); // marks end + } +} + + + +void ApexGeneralizedCubeTemplates::setCellGroups(const int32_t groups[8]) +{ + float groupWeights[8]; + uint32_t p = 0; + + for (uint32_t xi = 0; xi < SUB_GRID_LEN; xi++) + { + for (uint32_t yi = 0; yi < SUB_GRID_LEN; yi++) + { + for (uint32_t zi = 0; zi < SUB_GRID_LEN; zi++) + { + GenSubCell& c = mSubGrid[p]; + + float* basis = mBasis[p]; + p++; + c.init(); + + memset(groupWeights, 0, sizeof(float) * 8); + + for (uint32_t corner = 0; corner < 8; corner++) + { + groupWeights[groups[corner]] += basis[corner]; + } + + c.group = 0; + float maxWeight = groupWeights[0]; + for (int32_t group = 1; group < 8; group++) + { + if (groupWeights[group] > maxWeight) + { + maxWeight = groupWeights[group]; + c.group = group; + } + } + } + } + } +} + + + +void ApexGeneralizedCubeTemplates::splitDisconnectedGroups() +{ + // in certain cases groups are not connected sub spaces + // in that case we want to create (more) new connected groups + + physx::Array<GenCoord> queue; + //GenCoord c; + + const int n[6][3] = + { + { -1, 0, 0}, + { 1, 0, 0}, + { 0, -1, 0}, + { 0, 1, 0}, + { 0, 0, -1}, + { 0, 0, 1} + }; + + int newGroup = -1; + for (int32_t xi = 0; xi < SUB_GRID_LEN; xi++) + { + for (int32_t yi = 0; yi < SUB_GRID_LEN; yi++) + { + for (int32_t zi = 0; zi < SUB_GRID_LEN; zi++) + { + GenSubCell& start = mSubGrid[cellNr((uint32_t)xi, (uint32_t)yi, (uint32_t)zi)]; + + if (start.group < 0 || start.marked) + { + continue; + } + + newGroup++; + int32_t oldGroup = start.group; + + GenCoord c; + c.xi = xi; + c.yi = yi; + c.zi = zi; + + queue.pushBack(c); + while (!queue.empty()) + { + c = queue[queue.size() - 1]; + queue.popBack(); + + GenSubCell& cell = mSubGrid[cellNr((uint32_t)c.xi, (uint32_t)c.yi, (uint32_t)c.zi)]; + + if (cell.marked) + { + continue; + } + + cell.marked = true; + cell.group = newGroup; + + for (uint32_t i = 0; i < 6; i++) + { + GenCoord ci = c; + ci.xi = c.xi + n[i][0]; + ci.yi = c.yi + n[i][1]; + ci.zi = c.zi + n[i][2]; + if (ci.xi < 0 || ci.xi >= SUB_GRID_LEN || + ci.yi < 0 || ci.yi >= SUB_GRID_LEN || + ci.zi < 0 || ci.zi >= SUB_GRID_LEN) + { + continue; + } + + GenSubCell& celli = mSubGrid[cellNr((uint32_t)ci.xi, (uint32_t)ci.yi, (uint32_t)ci.zi)]; + + if (celli.marked || celli.group != oldGroup) + { + continue; + } + + queue.pushBack(ci); + } + } + } + } + } +} + + + +void ApexGeneralizedCubeTemplates::findVertices() +{ + //int i,j,xi,yi,zi; + + for (uint32_t i = 0; i < 8; i++) + for (uint32_t j = 0; j < 8; j++) + { + mFirstPairVertex[i][j] = -1; + } + + const int faces[6][4] = + { + {0, 4, 7, 3}, + {1, 2, 6, 5}, + {0, 1, 5, 4}, + {2, 3, 7, 6}, + {0, 3, 2, 1}, + {4, 5, 6, 7}, + }; + const int edges[12][6] = + { + {0, 1, 2, 3, 4, 5}, + {0, 1, 2, 3, 5, 6}, + {0, 1, 2, 3, 6, 7}, + {0, 1, 2, 3, 7, 4}, + {4, 5, 6, 7, 0, 1}, + {4, 5, 6, 7, 1, 2}, + {4, 5, 6, 7, 2, 3}, + {4, 5, 6, 7, 3, 0}, + {0, 1, 4, 5, 3, 7}, + {0, 1, 4, 5, 2, 6}, + {3, 2, 7, 6, 1, 5}, + {3, 2, 7, 6, 0, 4}, + }; + + for (int32_t xi = 0; xi <= SUB_GRID_LEN; xi++) + { + for (int32_t yi = 0; yi <= SUB_GRID_LEN; yi++) + { + for (int32_t zi = 0; zi <= SUB_GRID_LEN; zi++) + { + int32_t groups[8]; + groups[0] = groupAt(xi - 1, yi - 1, zi - 1); + groups[1] = groupAt(xi , yi - 1, zi - 1); + groups[2] = groupAt(xi , yi , zi - 1); + groups[3] = groupAt(xi - 1, yi, zi - 1); + groups[4] = groupAt(xi - 1, yi - 1, zi); + groups[5] = groupAt(xi , yi - 1, zi); + groups[6] = groupAt(xi , yi, zi); + groups[7] = groupAt(xi - 1, yi, zi); + + // edges test + uint32_t numEdges = 0; + for (uint32_t i = 0; i < 6; i++) + { + int32_t g0 = groups[faces[i][0]]; + int32_t g1 = groups[faces[i][1]]; + int32_t g2 = groups[faces[i][2]]; + int32_t g3 = groups[faces[i][3]]; + uint32_t flips = 0; + if (g0 != g1) + { + flips++; + } + if (g1 != g2) + { + flips++; + } + if (g2 != g3) + { + flips++; + } + if (g3 != g0) + { + flips++; + } + if (flips > 2) + { + numEdges++; + } + } + + int32_t vertexNr = -1; + + // edge vertex? + if (numEdges > 1) + { + for (uint32_t i = 0; i < 12; i++) + { + if (groups[edges[i][0]] < 0 && + groups[edges[i][1]] < 0 && + groups[edges[i][2]] < 0 && + groups[edges[i][3]] < 0 && + groups[edges[i][4]] < 0 && + groups[edges[i][5]] < 0) + { + vertexNr = (int32_t)i; + } + } + } + + // face vertex? + if (vertexNr < 0 && numEdges > 2) + { + for (uint32_t i = 0; i < 6; i++) + { + if (groups[faces[i][0]] < 0 && + groups[faces[i][1]] < 0 && + groups[faces[i][2]] < 0 && + groups[faces[i][3]] < 0) + { + vertexNr = (int32_t)i + 12; + } + } + } + + // inner vertex + if (vertexNr < 0 && numEdges > 2) + { + vertexNr = 18; + } + + mVertexAt[xi][yi][zi] = vertexNr; + + if (vertexNr < 0) + { + continue; + } + + for (uint32_t i = 0; i < 8; i++) + { + if (groups[i] < 0) + { + continue; + } + + for (uint32_t j = 0; j < 8; j++) + { + if (groups[j] < 0) + { + continue; + } + + if (vertexNr > mFirstPairVertex[groups[i]][groups[j]]) + { + mFirstPairVertex[groups[i]][groups[j]] = vertexNr; + mFirstPairCoord[groups[i]][groups[j]].init(xi, yi, zi); + } + } + } + } + } + } +} + + +void ApexGeneralizedCubeTemplates::createTriangles(physx::Array<int32_t>& currentIndices) +{ + physx::Array<int32_t> verts; + physx::Array<GenCoord> queue; + + currentIndices.clear(); + + for (int32_t group0 = 0; group0 < 8; group0++) + { + for (int32_t group1 = group0 + 1; group1 < 8; group1++) + { + int32_t first = mFirstPairVertex[group0][group1]; + + if (first < 0) + { + continue; + } + + // walk around the edge of the surface between group 0 and group 1 + verts.clear(); + + GenCoord c; + for (c.xi = 0; c.xi <= SUB_GRID_LEN; c.xi++) + for (c.yi = 0; c.yi <= SUB_GRID_LEN; c.yi++) + for (c.zi = 0; c.zi <= SUB_GRID_LEN; c.zi++) + { + mVertexMarked[c.xi][c.yi][c.zi] = false; + } + + c = mFirstPairCoord[group0][group1]; + queue.pushBack(c); + while (!queue.empty()) + { + c = queue[queue.size() - 1]; + queue.popBack(); + + if (vertexMarked(c)) + { + continue; + } + + markVertex(c); + + int vert = mVertexAt[c.xi][c.yi][c.zi]; + if (vert >= 0) + { + if (verts.size() == 1) + { + queue.clear(); // otherwise the search goes both ways around the border + } + if (verts.empty() || verts[verts.size() - 1] != vert) + { + verts.pushBack(vert); + } + } + + GenCoord next; + next = c; + next.xi--; + if (!vertexMarked(next) && isEdge(next, 0, group0, group1)) + { + queue.pushBack(next); + } + next = c; + next.yi--; + if (!vertexMarked(next) && isEdge(next, 1, group0, group1)) + { + queue.pushBack(next); + } + next = c; + next.zi--; + if (!vertexMarked(next) && isEdge(next, 2, group0, group1)) + { + queue.pushBack(next); + } + next = c; + next.xi++; + if (!vertexMarked(next) && isEdge(c, 0, group0, group1)) + { + queue.pushBack(next); + } + next = c; + next.yi++; + if (!vertexMarked(next) && isEdge(c, 1, group0, group1)) + { + queue.pushBack(next); + } + next = c; + next.zi++; + if (!vertexMarked(next) && isEdge(c, 2, group0, group1)) + { + queue.pushBack(next); + } + } + + if (verts.size() < 3) + { + continue; + } + + // create triangle fan + if (verts[0] == verts[verts.size() - 1]) + { + verts.popBack(); + } + + for (uint32_t i = 1; i + 1 < verts.size(); i++) + { + // PH: Attemt to fix: This is a double fan where the initial value also appears somewhere in the middle + if (verts[0] == verts[i] || verts[0] == verts[i + 1]) + { + continue; + } + + PX_ASSERT(verts[0] != verts[i]); + PX_ASSERT(verts[0] != verts[i + 1]); + PX_ASSERT(verts[i] != verts[i + 1]); + currentIndices.pushBack(verts[0]); + currentIndices.pushBack(verts[i]); + currentIndices.pushBack(verts[i + 1]); + } + } + } +} + + + +bool ApexGeneralizedCubeTemplates::isEdge(const GenCoord& c, int32_t dim, int32_t group0, int32_t group1) +{ + if (dim < 0 || dim >= 3) + { + return false; + } + + int g0, g1, g2, g3; + if (dim == 0) + { + g0 = groupAt(c.xi, c.yi, c.zi); + g1 = groupAt(c.xi, c.yi - 1, c.zi); + g2 = groupAt(c.xi, c.yi - 1, c.zi - 1); + g3 = groupAt(c.xi, c.yi, c.zi - 1); + } + else if (dim == 1) + { + g0 = groupAt(c.xi, c.yi, c.zi); + g1 = groupAt(c.xi, c.yi, c.zi - 1); + g2 = groupAt(c.xi - 1, c.yi, c.zi - 1); + g3 = groupAt(c.xi - 1, c.yi, c.zi); + } + else + { + g0 = groupAt(c.xi, c.yi, c.zi); + g1 = groupAt(c.xi - 1, c.yi, c.zi); + g2 = groupAt(c.xi - 1, c.yi - 1, c.zi); + g3 = groupAt(c.xi, c.yi - 1, c.zi); + } + + uint32_t flips = 0; + if (g0 != g1) + { + flips++; + } + if (g1 != g2) + { + flips++; + } + if (g2 != g3) + { + flips++; + } + if (g3 != g0) + { + flips++; + } + + if (flips <= 2) + { + return false; + } + + if (group0 < 0 || group1 < 0) + { + return true; + } + + if (g0 == group0 && g1 == group1) + { + return true; + } + if (g1 == group0 && g2 == group1) + { + return true; + } + if (g2 == group0 && g3 == group1) + { + return true; + } + if (g3 == group0 && g0 == group1) + { + return true; + } + + if (g0 == group1 && g1 == group0) + { + return true; + } + if (g1 == group1 && g2 == group0) + { + return true; + } + if (g2 == group1 && g3 == group0) + { + return true; + } + if (g3 == group1 && g0 == group0) + { + return true; + } + + return false; +} + +} +} // end namespace nvidia::apex + diff --git a/APEX_1.4/common/src/ApexGeneralizedMarchingCubes.cpp b/APEX_1.4/common/src/ApexGeneralizedMarchingCubes.cpp new file mode 100644 index 00000000..b2f8b1e3 --- /dev/null +++ b/APEX_1.4/common/src/ApexGeneralizedMarchingCubes.cpp @@ -0,0 +1,1222 @@ +/* + * 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. + */ + + +#include "ApexGeneralizedMarchingCubes.h" + +#include "ApexGeneralizedCubeTemplates.h" + +#include "PxMath.h" + +#include "ApexSharedUtils.h" + +#include "PsSort.h" + +namespace nvidia +{ +namespace apex +{ + +struct TriangleEdge +{ + void init(int32_t v0, int32_t v1, int32_t edgeNr, int32_t triNr) + { + this->v0 = PxMin(v0, v1); + this->v1 = PxMax(v0, v1); + this->edgeNr = edgeNr; + this->triNr = triNr; + } + bool operator == (const TriangleEdge& e) const + { + if (v0 == e.v0 && v1 == e.v1) + { + return true; + } + if (v0 == e.v1 && v1 == e.v0) + { + return true; + } + return false; + } + bool operator()(const TriangleEdge& a, const TriangleEdge& b) const + { + if (a.v0 < b.v0) + { + return true; + } + if (a.v0 > b.v0) + { + return false; + } + if (a.v1 < b.v1) + { + return true; + } + if (a.v1 > b.v1) + { + return false; + } + return a.triNr < b.triNr; + } + int32_t v0, v1; + int32_t edgeNr; + int32_t triNr; +}; + + + +struct BorderEdge +{ + void set(int vertNr, int prev = -1, int depth = 0) + { + this->vertNr = vertNr; + this->prev = prev; + this->depth = depth; + } + int32_t vertNr; + int32_t prev; + int32_t depth; +}; + + + + +ApexGeneralizedMarchingCubes::ApexGeneralizedMarchingCubes(const PxBounds3& bound, uint32_t subdivision) : + mTemplates(NULL) +{ + mCubes.clear(); + mBound = bound; + PX_ASSERT(subdivision > 0); + mSpacing = (bound.maximum - bound.minimum).magnitude() / (float)subdivision; + mInvSpacing = 1.0f / mSpacing; + + memset(mFirstCube, -1, sizeof(int32_t) * HASH_INDEX_SIZE); + + mVertices.clear(); + mIndices.clear(); +} + + +ApexGeneralizedMarchingCubes::~ApexGeneralizedMarchingCubes() +{ + if (mTemplates != NULL) + { + delete mTemplates; + mTemplates = NULL; + } +} + + + + + +void ApexGeneralizedMarchingCubes::registerTriangle(const PxVec3& p0, const PxVec3& p1, const PxVec3& p2) +{ + PxBounds3 bounds; + bounds.setEmpty(); + bounds.include(p0); + bounds.include(p1); + bounds.include(p2); + PX_ASSERT(!bounds.isEmpty()); + bounds.fattenFast(0.001f * mSpacing); + PxVec3 n, vertPos, q0, q1, qt; + n = (p1 - p0).cross(p2 - p0); + float d = n.dot(p0); + + int32_t min[3] = { (int32_t)PxFloor(bounds.minimum.x* mInvSpacing), (int32_t)PxFloor(bounds.minimum.y* mInvSpacing), (int32_t)PxFloor(bounds.minimum.z* mInvSpacing) }; + int32_t max[3] = { (int32_t)PxFloor(bounds.maximum.x* mInvSpacing) + 1, (int32_t)PxFloor(bounds.maximum.y* mInvSpacing) + 1, (int32_t)PxFloor(bounds.maximum.z* mInvSpacing) + 1 }; + + int32_t coord[3]; + + for (uint32_t dim0 = 0; dim0 < 3; dim0++) + { + if (n[dim0] == 0.0f) + { + continue; // singular case + } + + const uint32_t dim1 = (dim0 + 1) % 3; + const uint32_t dim2 = (dim1 + 1) % 3; + + for (coord[dim1] = min[dim1]; coord[dim1] <= max[dim1]; coord[dim1]++) + { + for (coord[dim2] = min[dim2]; coord[dim2] <= max[dim2]; coord[dim2]++) + { + //const float axis0 = coord[dim0] * mSpacing; + const float axis1 = coord[dim1] * mSpacing; + const float axis2 = coord[dim2] * mSpacing; + + // does the ray go through the triangle? + bool intersection = true; + if (n[dim0] > 0.0f) + { + if ((p1[dim1] - p0[dim1]) * (axis2 - p0[dim2]) - (axis1 - p0[dim1]) * (p1[dim2] - p0[dim2]) < 0.0f) + { + intersection = false; + } + if ((p2[dim1] - p1[dim1]) * (axis2 - p1[dim2]) - (axis1 - p1[dim1]) * (p2[dim2] - p1[dim2]) < 0.0f) + { + intersection = false; + } + if ((p0[dim1] - p2[dim1]) * (axis2 - p2[dim2]) - (axis1 - p2[dim1]) * (p0[dim2] - p2[dim2]) < 0.0f) + { + intersection = false; + } + } + else + { + if ((p1[dim1] - p0[dim1]) * (axis2 - p0[dim2]) - (axis1 - p0[dim1]) * (p1[dim2] - p0[dim2]) > 0.0f) + { + intersection = false; + } + if ((p2[dim1] - p1[dim1]) * (axis2 - p1[dim2]) - (axis1 - p1[dim1]) * (p2[dim2] - p1[dim2]) > 0.0f) + { + intersection = false; + } + if ((p0[dim1] - p2[dim1]) * (axis2 - p2[dim2]) - (axis1 - p2[dim1]) * (p0[dim2] - p2[dim2]) > 0.0f) + { + intersection = false; + } + } + + if (intersection) + { + const float pos = (d - axis1 * n[dim1] - axis2 * n[dim2]) / n[dim0]; + coord[dim0] = (int32_t)PxFloor(pos * mInvSpacing); + + uint32_t nr = (uint32_t)createCube(coord[0], coord[1], coord[2]); + GeneralizedCube& cube = mCubes[nr]; + + if (cube.vertRefs[dim0].vertNr < 0) + { + vertPos = PxVec3(coord[0] * mSpacing, coord[1] * mSpacing, coord[2] * mSpacing); + vertPos[dim0] = pos; + cube.vertRefs[dim0].vertNr = (int32_t)mVertices.size(); + mVertices.pushBack(vertPos); + } + } + } + } + } + + // does a triangle edge cut a cube face? + PxBounds3 cellBounds; + for (int32_t xi = min[0]; xi <= max[0]; xi++) + { + for (int32_t yi = min[1]; yi <= max[1]; yi++) + { + for (int32_t zi = min[2]; zi <= max[2]; zi++) + { + cellBounds.minimum = PxVec3(xi * mSpacing, yi * mSpacing, zi * mSpacing); + cellBounds.maximum = PxVec3((xi + 1) * mSpacing, (yi + 1) * mSpacing, (zi + 1) * mSpacing); + + for (uint32_t i = 0; i < 3; i++) + { + switch (i) + { + case 0 : + q0 = p0; + q1 = p1; + break; + case 1 : + q0 = p1; + q1 = p2; + break; + case 2 : + q0 = p2; + q1 = p0; + break; + default: // Make compiler happy + q0 = q1 = PxVec3(0.0f); + break; + } + + if (q0.x != q1.x) + { + const float t = (cellBounds.minimum.x - q0.x) / (q1.x - q0.x); + if (0.0f <= t && t <= 1.0f) + { + qt = q0 + (q1 - q0) * t; + if (cellBounds.minimum.y <= qt.y && qt.y <= cellBounds.maximum.y && cellBounds.minimum.z <= qt.z && qt.z <= cellBounds.maximum.z) + { + GeneralizedCube& cube = mCubes[(uint32_t)createCube(xi, yi, zi)]; + cube.sideBounds[0].include(qt); + } + } + } + if (q0.y != q1.y) + { + const float t = (cellBounds.minimum.y - q0.y) / (q1.y - q0.y); + if (0.0f <= t && t <= 1.0f) + { + qt = q0 + (q1 - q0) * t; + if (cellBounds.minimum.z <= qt.z && qt.z <= cellBounds.maximum.z && cellBounds.minimum.x <= qt.x && qt.x <= cellBounds.maximum.x) + { + GeneralizedCube& cube = mCubes[(uint32_t)createCube(xi, yi, zi)]; + cube.sideBounds[1].include(qt); + } + } + } + if (q0.z != q1.z) + { + const float t = (cellBounds.minimum.z - q0.z) / (q1.z - q0.z); + if (0.0f <= t && t <= 1.0f) + { + qt = q0 + (q1 - q0) * t; + if (cellBounds.minimum.x <= qt.x && qt.x <= cellBounds.maximum.x && cellBounds.minimum.y <= qt.y && qt.y <= cellBounds.maximum.y) + { + GeneralizedCube& cube = mCubes[(uint32_t)createCube(xi, yi, zi)]; + cube.sideBounds[2].include(qt); + } + } + } + } + } + } + } +} + + + +bool ApexGeneralizedMarchingCubes::endRegistration(uint32_t bubleSizeToRemove, IProgressListener* progressListener) +{ + HierarchicalProgressListener progress(100, progressListener); + + progress.setSubtaskWork(20, "Complete Cells"); + completeCells(); + progress.completeSubtask(); + + progress.setSubtaskWork(20, "Create Triangles"); + for (int i = 0; i < (int)mCubes.size(); i++) + { + createTrianglesForCube(i); + } + progress.completeSubtask(); + + progress.setSubtaskWork(20, "Create Neighbor Info"); + createNeighbourInfo(); + progress.completeSubtask(); + + uint32_t numTris = mIndices.size() / 3; + + mTriangleDeleted.resize(numTris); + for (uint32_t i = 0; i < numTris; i++) + { + mTriangleDeleted[i] = 0; + } + + if (bubleSizeToRemove > 0) + { + progress.setSubtaskWork(10, "Remove Bubbles"); + determineGroups(); + removeBubbles((int32_t)bubleSizeToRemove); + progress.completeSubtask(); + } + progress.setSubtaskWork(20, "Fix Orientation"); + determineGroups(); + fixOrientations(); + progress.completeSubtask(); + + progress.setSubtaskWork(-1, "Compress"); + compress(); + progress.completeSubtask(); + + return true; +} + + +int32_t ApexGeneralizedMarchingCubes::createCube(int32_t xi, int32_t yi, int32_t zi) +{ + int32_t nr = findCube(xi, yi, zi); + if (nr >= 0) + { + return nr; + } + + GeneralizedCube newCube; + newCube.init(xi, yi, zi); + int32_t h = hashFunction(xi, yi, zi); + newCube.next = mFirstCube[h]; + mFirstCube[h] = (int32_t)mCubes.size(); + nr = (int32_t)mCubes.size(); + mCubes.pushBack(newCube); + return nr; +} + + + +int32_t ApexGeneralizedMarchingCubes::findCube(int32_t xi, int32_t yi, int32_t zi) +{ + int32_t h = hashFunction(xi, yi, zi); + int32_t i = mFirstCube[h]; + + while (i >= 0) + { + GeneralizedCube& c = mCubes[(uint32_t)i]; + if (!c.deleted && c.xi == xi && c.yi == yi && c.zi == zi) + { + return i; + } + i = mCubes[(uint32_t)i].next; + } + return -1; +} + + + +void ApexGeneralizedMarchingCubes::completeCells() +{ + // make sure we have the boarder cells as well + uint32_t numCubes = mCubes.size(); + for (uint32_t i = 0; i < numCubes; i++) + { + int32_t xi = mCubes[i].xi; + int32_t yi = mCubes[i].yi; + int32_t zi = mCubes[i].zi; + //createCube(xi-1,yi, zi); + //createCube(xi, yi-1,zi); + //createCube(xi, yi, zi-1); + + createCube(xi - 1, yi, zi); + createCube(xi - 1, yi - 1, zi); + createCube(xi, yi - 1, zi); + createCube(xi, yi, zi - 1); + createCube(xi - 1, yi, zi - 1); + createCube(xi - 1, yi - 1, zi - 1); + createCube(xi, yi - 1, zi - 1); + } +} + + + +void ApexGeneralizedMarchingCubes::createTrianglesForCube(int32_t cellNr) +{ + const int sideEdges[6][4] = + { + {3, 7, 8, 11}, {1, 5, 9, 10}, {0, 4, 8, 9}, {2, 6, 10, 11}, {0, 1, 2, 3}, {4, 5, 6, 7} + }; + const int adjVerts[12][8] = + { + {16, 1, 2, 3, 14, 4, 8, 9}, {16, 0, 2, 3, 13, 5, 9, 10}, {16, 0, 1, 3, 15, 6, 10, 11}, {16, 0, 1, 2, 12, 7, 8, 11}, + {17, 5, 6, 7, 14, 0, 8, 9}, {17, 4, 6, 7, 13, 1, 9, 10}, {17, 4, 5, 7, 15, 2, 10, 11}, {17, 4, 5, 6, 12, 3, 8, 11}, + {12, 3, 7, 11, 14, 0, 4, 9}, {14, 0, 4, 8, 13, 1, 5, 10}, {13, 1, 5, 9, 15, 2, 6, 11}, {15, 2, 6, 10, 12, 3, 7, 8} + }; + + int32_t groups[8]; + int32_t vertNrs[19]; + + memset(vertNrs, -1, sizeof(int32_t) * 19); + + // get edge vertices + GeneralizedVertRef* vertRefs[12]; + getCubeEdgesAndGroups(cellNr, vertRefs, groups); + + uint32_t numCuts = 0; + for (uint32_t i = 0; i < 12; i++) + { + if (vertRefs[i] != NULL) + { + vertNrs[i] = vertRefs[i]->vertNr; + if (vertNrs[i] >= 0) + { + numCuts++; + } + } + } + + if (numCuts == 0) + { + return; + } + + GeneralizedCube& c = mCubes[(uint32_t)cellNr]; + + int startFace = -1, endFace = -1; + + PX_UNUSED(endFace); // Make compiler happy + + // create side vertices if necessary + for (uint32_t i = 0; i < 6; i++) + { + uint32_t faceNr = 12 + i; + int32_t* faceVertNr = NULL; + PxBounds3* b = NULL; + switch (faceNr) + { + case 12: + faceVertNr = &c.sideVertexNr[0]; + b = &c.sideBounds[0]; + break; + case 13: + { + int32_t nr = findCube(c.xi + 1, c.yi, c.zi); + if (nr >= 0) + { + faceVertNr = &mCubes[(uint32_t)nr].sideVertexNr[0]; + b = &mCubes[(uint32_t)nr].sideBounds[0]; + } + } + break; + case 14: + faceVertNr = &c.sideVertexNr[1]; + b = &c.sideBounds[1]; + break; + case 15: + { + int32_t nr = findCube(c.xi, c.yi + 1, c.zi); + if (nr >= 0) + { + faceVertNr = &mCubes[(uint32_t)nr].sideVertexNr[1]; + b = &mCubes[(uint32_t)nr].sideBounds[1]; + } + } + break; + case 16: + faceVertNr = &c.sideVertexNr[2]; + b = &c.sideBounds[2]; + break; + case 17: + { + int32_t nr = findCube(c.xi, c.yi, c.zi + 1); + if (nr >= 0) + { + faceVertNr = &mCubes[(uint32_t)nr].sideVertexNr[2]; + b = &mCubes[(uint32_t)nr].sideBounds[2]; + } + } + break; + } + + PxVec3 pos(0.0f, 0.0f, 0.0f); + uint32_t num = 0; + + for (uint32_t j = 0; j < 4; j++) + { + int32_t edgeVertNr = vertNrs[sideEdges[i][j]]; + if (edgeVertNr < 0) + { + continue; + } + + pos += mVertices[(uint32_t)edgeVertNr]; + num++; + } + + if (num == 0 || num == 2) + { + continue; + } + + pos = pos / (float)num; + + if (num == 1) + { + if (startFace < 0) + { + startFace = (int32_t)faceNr; + } + else + { + endFace = (int32_t)faceNr; + } + + if (!(b->isEmpty())) + { + if (PxAbs(pos.x - b->minimum.x) > PxAbs(pos.x - b->maximum.x)) + { + pos.x = b->minimum.x; + } + else + { + pos.x = b->maximum.x; + } + if (PxAbs(pos.y - b->minimum.y) > PxAbs(pos.y - b->maximum.y)) + { + pos.y = b->minimum.y; + } + else + { + pos.y = b->maximum.y; + } + if (PxAbs(pos.z - b->minimum.z) > PxAbs(pos.z - b->maximum.z)) + { + pos.z = b->minimum.z; + } + else + { + pos.z = b->maximum.z; + } + } + else + { + continue; + } + } + + if (*faceVertNr < 0) + { + *faceVertNr = (int32_t)mVertices.size(); + mVertices.pushBack(pos); + } + vertNrs[faceNr] = *faceVertNr; + } + + int32_t maxGroup = groups[0]; + for (uint32_t i = 1; i < 8; i++) + { + if (groups[i] > maxGroup) + { + maxGroup = groups[i]; + } + } + + // boundary cell + physx::Array<BorderEdge> queue; + if (startFace >= 0) + { + BorderEdge edge; + queue.clear(); + if (queue.capacity() < 20) + { + queue.reserve(20); + } + + int32_t prev[19]; + bool visited[19]; + for (uint32_t i = 0; i < 19; i++) + { + prev[i] = -1; + visited[i] = false; + } + + edge.set(startFace); + queue.pushBack(edge); + + int32_t maxVert = -1; + int32_t maxDepth = -1; + + while (!queue.empty()) + { + edge = queue[queue.size() - 1]; + queue.popBack(); + + int32_t v = edge.vertNr; + if (visited[v]) + { + continue; + } + + visited[v] = true; + prev[v] = edge.prev; + edge.prev = v; + edge.depth++; + + if (edge.depth > maxDepth) + { + maxDepth = edge.depth; + maxVert = v; + } + // if (v == endFace) break; + + if (v < 12) + { + for (uint32_t i = 0; i < 8; i += 4) + { + edge.vertNr = adjVerts[v][i]; + if (vertNrs[edge.vertNr] >= 0) + { + if (!visited[edge.vertNr]) + { + queue.pushBack(edge); + } + } + else + { + for (uint32_t j = i + 1; j < i + 4; j++) + { + edge.vertNr = adjVerts[v][j]; + if (vertNrs[edge.vertNr] >= 0 && !visited[edge.vertNr]) + { + queue.pushBack(edge); + } + } + } + } + } + else + { + for (uint32_t i = 0; i < 4; i++) + { + edge.vertNr = sideEdges[v - 12][i]; + if (!visited[edge.vertNr] && vertNrs[edge.vertNr] >= 0) + { + queue.pushBack(edge); + } + } + } + } + + int32_t chain[14]; + uint32_t chainLen = 0; + int32_t v = maxVert; + + if (vertNrs[v] >= 0) + { + chain[chainLen++] = v; + } + + v = prev[v]; + while (v >= 0) + { + if (vertNrs[v] >= 0) + { + chain[chainLen++] = v; + } + v = prev[v]; + } + + int32_t numTris = (int32_t)chainLen - 2; + + c.firstTriangle = (int32_t)mIndices.size() / 3; + for (int i = 0; i < numTris; i++) + { + mIndices.pushBack((uint32_t)vertNrs[(uint32_t)chain[0]]); + mIndices.pushBack((uint32_t)vertNrs[(uint32_t)chain[i + 1]]); + mIndices.pushBack((uint32_t)vertNrs[(uint32_t)chain[i + 2]]); + c.numTriangles++; + } + return; + } + + // inner cell + if (mTemplates == NULL) + { + mTemplates = PX_NEW(ApexGeneralizedCubeTemplates)(); + } + mTemplates->getTriangles(groups, mGeneralizedTriangles); + + // create face and inner vertices if necessary + for (uint32_t i = 0; i < mGeneralizedTriangles.size(); i++) + { + int32_t localNr = mGeneralizedTriangles[i]; + + if (vertNrs[localNr] < 0) + { + if (localNr < 12) + { + return; // impossible to create triangles, border cube + } + + if (localNr == 18 && vertNrs[18] < 0) + { + // center vertex, not shared with other cells + vertNrs[18] = (int32_t)mVertices.size(); + PxVec3 pos(0.0f); + float num = 0.0f; + for (int j = 0; j < 12; j++) + { + if (vertNrs[j] >= 0) + { + pos += mVertices[(uint32_t)vertNrs[j]]; + num = num + 1.0f; + } + } + mVertices.pushBack(pos / num); + } + } + } + + c.firstTriangle = (int32_t)mIndices.size() / 3; + const uint32_t numTris = mGeneralizedTriangles.size() / 3; + for (uint32_t i = 0; i < numTris; i++) + { + PX_ASSERT(vertNrs[mGeneralizedTriangles[3 * i + 0]] != vertNrs[mGeneralizedTriangles[3 * i + 1]]); + PX_ASSERT(vertNrs[mGeneralizedTriangles[3 * i + 0]] != vertNrs[mGeneralizedTriangles[3 * i + 2]]); + PX_ASSERT(vertNrs[mGeneralizedTriangles[3 * i + 1]] != vertNrs[mGeneralizedTriangles[3 * i + 2]]); + mIndices.pushBack((uint32_t)vertNrs[mGeneralizedTriangles[3 * i + 0]]); + mIndices.pushBack((uint32_t)vertNrs[mGeneralizedTriangles[3 * i + 1]]); + mIndices.pushBack((uint32_t)vertNrs[mGeneralizedTriangles[3 * i + 2]]); + c.numTriangles++; + } +} + + + +void ApexGeneralizedMarchingCubes::createNeighbourInfo() +{ + physx::Array<TriangleEdge> edges; + edges.reserve(mIndices.size()); + + mFirstNeighbour.resize(mIndices.size()); + for (uint32_t i = 0; i < mFirstNeighbour.size(); i++) + { + mFirstNeighbour[i] = -1; + } + + mNeighbours.clear(); + + const uint32_t numTriangles = mIndices.size() / 3; + for (uint32_t i = 0; i < numTriangles; i++) + { + TriangleEdge edge; + int32_t i0 = (int32_t)mIndices[3 * i]; + int32_t i1 = (int32_t)mIndices[3 * i + 1]; + int32_t i2 = (int32_t)mIndices[3 * i + 2]; + PX_ASSERT(i0 != i1); + PX_ASSERT(i0 != i2); + PX_ASSERT(i1 != i2); + edge.init(i0, i1, 0, (int32_t)i); + edges.pushBack(edge); + edge.init(i1, i2, 1, (int32_t)i); + edges.pushBack(edge); + edge.init(i2, i0, 2, (int32_t)i); + edges.pushBack(edge); + } + shdfnd::sort(edges.begin(), edges.size(), TriangleEdge()); + + uint32_t i = 0; + const uint32_t numEdges = edges.size(); + while (i < numEdges) + { + const TriangleEdge& e0 = edges[i]; + const int32_t first = (int32_t)mNeighbours.size(); + PX_ASSERT(mFirstNeighbour[(uint32_t)(e0.triNr * 3 + e0.edgeNr)] == -1); + mFirstNeighbour[(uint32_t)(e0.triNr * 3 + e0.edgeNr)] = first; + mNeighbours.pushBack(e0.triNr); + i++; + + while (i < numEdges && edges[i] == e0) + { + const TriangleEdge& e1 = edges[i]; + PX_ASSERT(mFirstNeighbour[(uint32_t)(e1.triNr * 3 + e1.edgeNr)] == -1); + mFirstNeighbour[(uint32_t)(e1.triNr * 3 + e1.edgeNr)] = first; + mNeighbours.pushBack(e1.triNr); + i++; + } + mNeighbours.pushBack(-1); // end marker + } +} + + + +void ApexGeneralizedMarchingCubes::getCubeEdgesAndGroups(int32_t cellNr, GeneralizedVertRef* vertRefs[12], int32_t groups[8]) +{ + const int32_t adjCorners[8][3] = + { + {1, 3, 4}, {0, 2, 5}, {1, 3, 6}, {0, 2, 7}, {0, 5, 7}, {1, 4, 6}, {2, 5, 7}, {3, 4, 6} + }; + + const int32_t adjEdges[8][3] = + { + {0, 3, 8}, {0, 1, 9}, {1, 2, 10}, {3, 2, 11}, {8, 4, 7}, {9, 4, 5}, {10, 5, 6}, {11, 7, 6} + }; + + // collect edge vertices + GeneralizedCube& c = mCubes[(uint32_t)cellNr]; + for (uint32_t i = 0; i < 12; i++) + { + vertRefs[i] = NULL; + } + + vertRefs[0] = &c.vertRefs[0]; + vertRefs[3] = &c.vertRefs[1]; + vertRefs[8] = &c.vertRefs[2]; + + int32_t nr = findCube(c.xi + 1, c.yi, c.zi); + if (nr >= 0) + { + vertRefs[1] = &mCubes[(uint32_t)nr].vertRefs[1]; + vertRefs[9] = &mCubes[(uint32_t)nr].vertRefs[2]; + } + nr = findCube(c.xi, c.yi + 1, c.zi); + if (nr >= 0) + { + vertRefs[2] = &mCubes[(uint32_t)nr].vertRefs[0]; + vertRefs[11] = &mCubes[(uint32_t)nr].vertRefs[2]; + } + nr = findCube(c.xi, c.yi, c.zi + 1); + if (nr >= 0) + { + vertRefs[4] = &mCubes[(uint32_t)nr].vertRefs[0]; + vertRefs[7] = &mCubes[(uint32_t)nr].vertRefs[1]; + } + nr = findCube(c.xi + 1, c.yi + 1, c.zi); + if (nr >= 0) + { + vertRefs[10] = &mCubes[(uint32_t)nr].vertRefs[2]; + } + nr = findCube(c.xi, c.yi + 1, c.zi + 1); + if (nr >= 0) + { + vertRefs[6] = &mCubes[(uint32_t)nr].vertRefs[0]; + } + nr = findCube(c.xi + 1, c.yi, c.zi + 1); + if (nr >= 0) + { + vertRefs[5] = &mCubes[(uint32_t)nr].vertRefs[1]; + } + + // assign groups using flood fill on the cube edges + for (uint32_t i = 0; i < 8; i++) + { + groups[i] = -1; + } + + int groupNr = -1; + for (uint32_t i = 0; i < 8; i++) + { + if (groups[i] >= 0) + { + continue; + } + + groupNr++; + mCubeQueue.clear(); + mCubeQueue.pushBack((int32_t)i); + while (!mCubeQueue.empty()) + { + int32_t cornerNr = mCubeQueue[mCubeQueue.size() - 1]; + mCubeQueue.popBack(); + + if (groups[cornerNr] >= 0) + { + continue; + } + + groups[cornerNr] = groupNr; + + for (uint32_t j = 0; j < 3; j++) + { + int32_t adjCorner = adjCorners[cornerNr][j]; + int32_t adjEdge = adjEdges[cornerNr][j]; + if (vertRefs[adjEdge] != NULL && vertRefs[adjEdge]->vertNr >= 0) // edge blocked by vertex + { + continue; + } + + if (groups[adjCorner] < 0) + { + mCubeQueue.pushBack(adjCorner); + } + } + } + } +} + + + +void ApexGeneralizedMarchingCubes::determineGroups() +{ + const uint32_t numTris = mIndices.size() / 3; + + mTriangleGroup.resize(numTris); + for (uint32_t i = 0; i < numTris; i++) + { + mTriangleGroup[i] = -1; + } + + mGroupFirstTriangle.clear(); + mGroupTriangles.clear(); + + for (uint32_t i = 0; i < numTris; i++) + { + if (mTriangleDeleted[i]) + { + continue; + } + if (mTriangleGroup[i] >= 0) + { + continue; + } + + int32_t group = (int32_t)mGroupFirstTriangle.size(); + mGroupFirstTriangle.pushBack((int32_t)mGroupTriangles.size()); + + mCubeQueue.clear(); + mCubeQueue.pushBack((int32_t)i); + while (!mCubeQueue.empty()) + { + const uint32_t t = (uint32_t)mCubeQueue[mCubeQueue.size() - 1]; + mCubeQueue.popBack(); + + if (mTriangleGroup[t] >= 0) + { + continue; + } + + mTriangleGroup[t] = group; + mGroupTriangles.pushBack((int32_t)t); + + for (uint32_t j = 0; j < 3; j++) + { + uint32_t first = (uint32_t)mFirstNeighbour[3 * t + j]; + uint32_t num = 0; + int32_t n = -1; + while (mNeighbours[first] >= 0) + { + if (mNeighbours[first] != (int32_t)t) + { + n = mNeighbours[first]; + } + + first++; + num++; + } + if (num == 2 && n >= 0 && mTriangleGroup[(uint32_t)n] < 0) + { + if (!mTriangleDeleted[(uint32_t)n]) + { + mCubeQueue.pushBack(n); + } + } + } + } + } +} + + + +void ApexGeneralizedMarchingCubes::removeBubbles(int32_t minGroupSize) +{ + const uint32_t numGroups = mGroupFirstTriangle.size(); + for (uint32_t i = 0; i < numGroups; i++) + { + int32_t firstTri = mGroupFirstTriangle[i]; + int32_t lastTri = i < numGroups - 1 ? mGroupFirstTriangle[i + 1] : (int32_t)mGroupTriangles.size(); + + if (lastTri - firstTri > minGroupSize) + { + continue; + } + + bool OK = true; + + for (int32_t j = firstTri; j < lastTri; j++) + { + int32_t t = mGroupTriangles[(uint32_t)j]; + for (uint32_t k = 0; k < 3; k++) + { + uint32_t first = (uint32_t)mFirstNeighbour[3 * t + k]; + uint32_t num = 0; + int32_t n = -1; + while (mNeighbours[first] >= 0) + { + if (mNeighbours[first] != t) + { + n = mNeighbours[first]; + } + + num++; + first++; + } + if (num == 2 && mTriangleGroup[(uint32_t)n] != (int32_t)i) + { + OK = false; + break; + } + } + if (!OK) + { + break; + } + } + + + if (!OK) + { + continue; + } + + for (int32_t j = firstTri; j < lastTri; j++) + { + uint32_t t = (uint32_t)mGroupTriangles[(uint32_t)j]; + // remove neighbor info + for (int k = 0; k < 3; k++) + { + uint32_t first = (uint32_t)mFirstNeighbour[3 * t + k]; + int32_t pos = -1; + while (mNeighbours[first] >= 0) + { + if (mNeighbours[first] == (int32_t)t) + { + PX_ASSERT(pos == -1); + pos = (int32_t)first; + } + + first++; + } + if (pos >= 0) + { + mNeighbours[(uint32_t)pos] = mNeighbours[first - 1]; + mNeighbours[first - 1] = -1; + } + } + // remove triangle + mTriangleDeleted[t] = true; + + /* + // debugging only + mDebugLines.pushBack(mVertices[mIndices[t * 3 + 0]]); + mDebugLines.pushBack(mVertices[mIndices[t * 3 + 1]]); + mDebugLines.pushBack(mVertices[mIndices[t * 3 + 1]]); + mDebugLines.pushBack(mVertices[mIndices[t * 3 + 2]]); + mDebugLines.pushBack(mVertices[mIndices[t * 3 + 2]]); + mDebugLines.pushBack(mVertices[mIndices[t * 3 + 0]]); + */ + } + } +} + + + +void ApexGeneralizedMarchingCubes::fixOrientations() +{ + if (mIndices.size() == 0) + { + return; + } + + const uint32_t numTris = mIndices.size() / 3; + + physx::Array<uint8_t> marks; + marks.resize(numTris); + + // 0 = non visited, 1 = visited, 2 = visited, to be flipped + for (uint32_t i = 0; i < numTris; i++) + { + marks[i] = 0; + } + + physx::Array<int32_t> queue; + for (uint32_t i = 0; i < numTris; i++) + { + if (mTriangleDeleted[i]) + { + continue; + } + + if (marks[i] != 0) + { + continue; + } + + queue.clear(); + + marks[i] = 1; + queue.pushBack((int32_t)i); + while (!queue.empty()) + { + uint32_t triNr = (uint32_t)queue[queue.size() - 1]; + queue.popBack(); + + for (uint32_t j = 0; j < 3; j++) + { + uint32_t first = (uint32_t)mFirstNeighbour[3 * triNr + j]; + uint32_t num = 0; + int32_t adjNr = -1; + + while (mNeighbours[first] >= 0) + { + if (mNeighbours[first] != (int32_t)triNr) + { + adjNr = mNeighbours[first]; + } + + first++; + num++; + } + + if (num != 2) + { + continue; + } + if (marks[(uint32_t)adjNr] != 0) + { + continue; + } + + queue.pushBack(adjNr); + + uint32_t i0, i1; + if (marks[(uint32_t)triNr] == 1) + { + i0 = mIndices[3 * triNr + j]; + i1 = mIndices[3 * triNr + ((j + 1) % 3)]; + } + else + { + i1 = mIndices[3 * triNr + j]; + i0 = mIndices[3 * triNr + ((j + 1) % 3)]; + } + + // don't swap here because this would corrupt the neighbor information + marks[(uint32_t)adjNr] = 1; + if (mIndices[3 * (uint32_t)adjNr + 0] == i0 && mIndices[3 * (uint32_t)adjNr + 1] == i1) + { + marks[(uint32_t)adjNr] = 2; + } + if (mIndices[3 * (uint32_t)adjNr + 1] == i0 && mIndices[3 * (uint32_t)adjNr + 2] == i1) + { + marks[(uint32_t)adjNr] = 2; + } + if (mIndices[3 * (uint32_t)adjNr + 2] == i0 && mIndices[3 * (uint32_t)adjNr + 0] == i1) + { + marks[(uint32_t)adjNr] = 2; + } + } + } + } + + for (uint32_t i = 0; i < numTris; i++) + { + if (marks[i] == 2) + { + uint32_t i0 = mIndices[3 * i]; + mIndices[3 * i] = mIndices[3 * i + 1]; + mIndices[3 * i + 1] = i0; + } + } +} + + + +void ApexGeneralizedMarchingCubes::compress() +{ + PX_ASSERT(mTriangleDeleted.size() * 3 == mIndices.size()); + + uint32_t writePos = 0; + + for (uint32_t i = 0; i < mTriangleDeleted.size(); i++) + { + if (mTriangleDeleted[i] == 1) + { + continue; + } + for (uint32_t j = 0; j < 3; j++) + { + mIndices[3 * writePos + j] = mIndices[3 * i + j]; + } + writePos++; + } + + mIndices.resize(3 * writePos); + + mTriangleDeleted.clear(); + mTriangleDeleted.reset(); + + mNeighbours.clear(); + mNeighbours.reset(); + + mFirstNeighbour.clear(); + mFirstNeighbour.reset(); +} + +} +} // end namespace nvidia::apex diff --git a/APEX_1.4/common/src/ApexIsoMesh.cpp b/APEX_1.4/common/src/ApexIsoMesh.cpp new file mode 100644 index 00000000..7700c9e2 --- /dev/null +++ b/APEX_1.4/common/src/ApexIsoMesh.cpp @@ -0,0 +1,658 @@ +/* + * 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. + */ + + +#include "ApexIsoMesh.h" +#include "ApexCollision.h" +#include "ApexMarchingCubes.h" +#include "ApexSharedUtils.h" + +#include "ApexSDKIntl.h" + +#include "PsSort.h" + +namespace nvidia +{ +namespace apex +{ + +ApexIsoMesh::ApexIsoMesh(uint32_t isoGridSubdivision, uint32_t keepNBiggestMeshes, bool discardInnerMeshes) : + mIsoGridSubdivision(isoGridSubdivision), + mKeepNBiggestMeshes(keepNBiggestMeshes), + mDiscardInnerMeshes(discardInnerMeshes), + mCellSize(0.0f), + mThickness(0.0f), + mOrigin(0.0f, 0.0f, 0.0f), + mNumX(0), mNumY(0), mNumZ(0), + mIsoValue(0.5f) +{ + if (mIsoGridSubdivision == 0) + { + APEX_INVALID_PARAMETER("isoGridSubdivision must be bigger than 0, setting it to 10"); + mIsoGridSubdivision = 10; + } + mBound.setEmpty(); +} + + + +ApexIsoMesh::~ApexIsoMesh() +{ +} + + + +void ApexIsoMesh::setBound(const PxBounds3& bound) +{ + mBound = bound; + mCellSize = (mBound.maximum - mBound.minimum).magnitude() / mIsoGridSubdivision; + if (mCellSize == 0.0f) + { + mCellSize = 1.0f; + } + mThickness = mCellSize * 1.5f; + PX_ASSERT(!mBound.isEmpty()); + mBound.fattenFast(2.0f * mThickness); + + mOrigin = mBound.minimum; + float invH = 1.0f / mCellSize; + mNumX = (int32_t)((mBound.maximum.x - mBound.minimum.x) * invH) + 1; + mNumY = (int32_t)((mBound.maximum.y - mBound.minimum.y) * invH) + 1; + mNumZ = (int32_t)((mBound.maximum.z - mBound.minimum.z) * invH) + 1; +} + + + +void ApexIsoMesh::clear() +{ + mCellSize = 0.0f; + mThickness = 0.0f; + mOrigin = PxVec3(0.0f); + mNumX = mNumY = mNumZ = 0; + mBound.setEmpty(); + + clearTemp(); + + mIsoVertices.clear(); + mIsoTriangles.clear(); + mIsoEdges.clear(); +} + + + +void ApexIsoMesh::clearTemp() +{ + mGrid.clear(); + mGrid.reset(); +} + + + +void ApexIsoMesh::addTriangle(const PxVec3& v0, const PxVec3& v1, const PxVec3& v2) +{ + int32_t num = mNumX * mNumY * mNumZ; + if (mGrid.size() != (uint32_t) num) + { + IsoCell cell; + cell.init(); + mGrid.resize((uint32_t)num, cell); + } + + PX_ASSERT(mThickness != 0.0f); + + PxBounds3 bounds; + bounds.minimum = bounds.maximum = v0; + bounds.include(v1); + bounds.include(v2); + PX_ASSERT(!bounds.isEmpty()); + bounds.fattenFast(mThickness); + + float h = mCellSize; + float invH = 1.0f / h; + float invT = 1.0f / mThickness; + + int32_t x0 = PxMax(0, (int32_t)((bounds.minimum.x - mOrigin.x) * invH)); + int32_t x1 = PxMin(mNumX - 1, (int32_t)((bounds.maximum.x - mOrigin.x) * invH)); + int32_t y0 = PxMax(0, (int32_t)((bounds.minimum.y - mOrigin.y) * invH)); + int32_t y1 = PxMin(mNumY - 1, (int32_t)((bounds.maximum.y - mOrigin.y) * invH)); + int32_t z0 = PxMax(0, (int32_t)((bounds.minimum.z - mOrigin.z) * invH)); + int32_t z1 = PxMin(mNumZ - 1, (int32_t)((bounds.maximum.z - mOrigin.z) * invH)); + + for (int32_t xi = x0; xi <= x1; xi++) + { + PxVec3 pos; + pos.x = mOrigin.x + h * xi; + for (int32_t yi = y0; yi <= y1; yi++) + { + pos.y = mOrigin.y + h * yi; + for (int32_t zi = z0; zi <= z1; zi++) + { + pos.z = mOrigin.z + h * zi; + Triangle triangle(v0, v1, v2); + float dist = PxSqrt(APEX_pointTriangleSqrDst(triangle, pos)); + if (dist > mThickness) + { + continue; + } + + IsoCell& cell = cellAt(xi, yi, zi); + float density = 1.0f - dist * invT; + PX_ASSERT(density >= 0); + cell.density = PxMax(cell.density, density); ///< \todo, is this right? + } + } + } +} + + + +bool ApexIsoMesh::update(IProgressListener* progressListener) +{ + HierarchicalProgressListener progress(100, progressListener); + progress.setSubtaskWork(90, "Generating first mesh"); + + if (generateMesh(&progress)) + { + + progress.completeSubtask(); + progress.setSubtaskWork(5, "Finding Neighbors"); + + if (findNeighbors(&progress)) + { + + if (mKeepNBiggestMeshes > 0 || mDiscardInnerMeshes) + { + progress.completeSubtask(); + progress.setSubtaskWork(3, "Removing layers"); + removeLayers(); + // removeSide(2); + } + progress.completeSubtask(); + progress.setSubtaskWork(2, "Removing Triangles and Vertices"); + + removeTrisAndVerts(); + } + } + progress.completeSubtask(); + + return true; +} + + + +void ApexIsoMesh::getTriangle(uint32_t index, uint32_t& v0, uint32_t& v1, uint32_t& v2) const +{ + PX_ASSERT(index < mIsoTriangles.size()); + const IsoTriangle& t = mIsoTriangles[index]; + v0 = (uint32_t)t.vertexNr[0]; + v1 = (uint32_t)t.vertexNr[1]; + v2 = (uint32_t)t.vertexNr[2]; +} + + + +bool ApexIsoMesh::generateMesh(IProgressListener* progressListener) +{ + mIsoVertices.clear(); + + int xi, yi, zi; + float h = mCellSize; + PxVec3 pos, vPos; + + uint32_t maximum = (uint32_t)(mNumX * mNumY * mNumZ); + uint32_t progressCounter = 0; + + HierarchicalProgressListener progress(100, progressListener); + progress.setSubtaskWork(80, "Generating Vertices"); + + // generate vertices + for (xi = 0; xi < mNumX; xi++) + { + pos.x = mOrigin.x + h * xi; + for (yi = 0; yi < mNumY; yi++) + { + pos.y = mOrigin.y + h * yi; + for (zi = 0; zi < mNumZ; zi++) + { + + if ((progressCounter++ & 0xff) == 0) + { + const int32_t curr = ((xi * mNumY) + yi) * mNumZ + zi; + const int32_t percent = 100 * curr / (int32_t)maximum; + progress.setProgress(percent); + } + + pos.z = mOrigin.z + h * zi; + IsoCell& cell = cellAt(xi, yi, zi); + float d = cell.density; + if (xi < mNumX - 1) + { + float dx = cellAt(xi + 1, yi, zi).density; + if (interpolate(d, dx, pos, pos + PxVec3(h, 0.0f, 0.0f), vPos)) + { + cell.vertNrX = (int32_t)mIsoVertices.size(); + mIsoVertices.pushBack(vPos); + } + } + if (yi < mNumY - 1) + { + float dy = cellAt(xi, yi + 1, zi).density; + if (interpolate(d, dy, pos, pos + PxVec3(0.0f, h, 0.0f), vPos)) + { + cell.vertNrY = (int32_t)mIsoVertices.size(); + mIsoVertices.pushBack(vPos); + } + } + if (zi < mNumZ - 1) + { + float dz = cellAt(xi, yi, zi + 1).density; + if (interpolate(d, dz, pos, pos + PxVec3(0.0f, 0.0f, h), vPos)) + { + cell.vertNrZ = (int32_t)mIsoVertices.size(); + mIsoVertices.pushBack(vPos); + } + } + } + } + } + + progress.completeSubtask(); + progress.setSubtaskWork(20, "Generating Faces"); + progressCounter = 0; + + mIsoTriangles.clear(); + // generate triangles + int edges[12]; + IsoTriangle triangle; + + for (xi = 0; xi < mNumX - 1; xi++) + { + pos.x = mOrigin.x + h * xi; + for (yi = 0; yi < mNumY - 1; yi++) + { + pos.y = mOrigin.y + h * yi; + for (zi = 0; zi < mNumZ - 1; zi++) + { + + if ((progressCounter++ & 0xff) == 0) + { + const int32_t curr = ((xi * mNumY) + yi) * mNumZ + zi; + const int32_t percent = 100 * curr / (int32_t)maximum; + progress.setProgress(percent); + } + pos.z = mOrigin.z + h * zi; + int code = 0; + if (cellAt(xi, yi, zi).density > mIsoValue) + { + code |= 1; + } + if (cellAt(xi + 1, yi, zi).density > mIsoValue) + { + code |= 2; + } + if (cellAt(xi + 1, yi + 1, zi).density > mIsoValue) + { + code |= 4; + } + if (cellAt(xi, yi + 1, zi).density > mIsoValue) + { + code |= 8; + } + if (cellAt(xi, yi, zi + 1).density > mIsoValue) + { + code |= 16; + } + if (cellAt(xi + 1, yi, zi + 1).density > mIsoValue) + { + code |= 32; + } + if (cellAt(xi + 1, yi + 1, zi + 1).density > mIsoValue) + { + code |= 64; + } + if (cellAt(xi, yi + 1, zi + 1).density > mIsoValue) + { + code |= 128; + } + if (code == 0 || code == 255) + { + continue; + } + + edges[ 0] = cellAt(xi, yi, zi).vertNrX; + edges[ 1] = cellAt(xi + 1, yi, zi).vertNrY; + edges[ 2] = cellAt(xi, yi + 1, zi).vertNrX; + edges[ 3] = cellAt(xi, yi, zi).vertNrY; + + edges[ 4] = cellAt(xi, yi, zi + 1).vertNrX; + edges[ 5] = cellAt(xi + 1, yi, zi + 1).vertNrY; + edges[ 6] = cellAt(xi, yi + 1, zi + 1).vertNrX; + edges[ 7] = cellAt(xi, yi, zi + 1).vertNrY; + + edges[ 8] = cellAt(xi, yi, zi).vertNrZ; + edges[ 9] = cellAt(xi + 1, yi, zi).vertNrZ; + edges[10] = cellAt(xi + 1, yi + 1, zi).vertNrZ; + edges[11] = cellAt(xi, yi + 1, zi).vertNrZ; + + IsoCell& c = cellAt(xi, yi, zi); + c.firstTriangle = (int32_t)mIsoTriangles.size(); + + const int* v = MarchingCubes::triTable[code]; + while (*v >= 0) + { + int v0 = edges[*v++]; + PX_ASSERT(v0 >= 0); + int v1 = edges[*v++]; + PX_ASSERT(v1 >= 0); + int v2 = edges[*v++]; + PX_ASSERT(v2 >= 0); + triangle.set(v0, v1, v2, xi, yi, zi); + mIsoTriangles.pushBack(triangle); + c.numTriangles++; + } + } + } + } + progress.completeSubtask(); + + return true; +} + + + +bool ApexIsoMesh::interpolate(float d0, float d1, const PxVec3& pos0, const PxVec3& pos1, PxVec3& pos) +{ + if ((d0 < mIsoValue && d1 < mIsoValue) || (d0 > mIsoValue && d1 > mIsoValue)) + { + return false; + } + + float s = (mIsoValue - d0) / (d1 - d0); + s = PxClamp(s, 0.01f, 0.99f); // safety not to produce vertices at the same location + pos = pos0 * (1.0f - s) + pos1 * s; + return true; +} + + + +bool ApexIsoMesh::findNeighbors(IProgressListener* progress) +{ + mIsoEdges.clear(); + IsoEdge edge; + + for (int32_t i = 0; i < (int32_t)mIsoTriangles.size(); i++) + { + IsoTriangle& t = mIsoTriangles[(uint32_t)i]; + edge.set(t.vertexNr[0], t.vertexNr[1], i); + mIsoEdges.pushBack(edge); + edge.set(t.vertexNr[1], t.vertexNr[2], i); + mIsoEdges.pushBack(edge); + edge.set(t.vertexNr[2], t.vertexNr[0], i); + mIsoEdges.pushBack(edge); + } + + progress->setProgress(30); + + shdfnd::sort(mIsoEdges.begin(), mIsoEdges.size(), IsoEdge()); + + progress->setProgress(60); + + uint32_t i = 0; + while (i < mIsoEdges.size()) + { + uint32_t j = i + 1; + while (j < mIsoEdges.size() && mIsoEdges[j] == mIsoEdges[i]) + { + j++; + } + if (j > i + 1) + { + mIsoTriangles[(uint32_t)mIsoEdges[i ].triangleNr].addNeighbor(mIsoEdges[j - 1].triangleNr); + mIsoTriangles[(uint32_t)mIsoEdges[j - 1].triangleNr].addNeighbor(mIsoEdges[i ].triangleNr); + } + i = j; + } + + progress->setProgress(100); + return true; +} + +struct GroupAndSize +{ + int32_t group; + uint32_t size; + bool deleteMesh; + + bool operator()(const GroupAndSize& a, const GroupAndSize& b) const + { + return a.size > b.size; + } +}; + +struct TimeAndDot +{ + float time; + float dot; + + bool operator()(const TimeAndDot& a, const TimeAndDot& b) const + { + return a.time > b.time; + } +}; + +void ApexIsoMesh::removeLayers() +{ + // mark regions + nvidia::Array<GroupAndSize> triangleGroups; + + for (uint32_t i = 0; i < mIsoTriangles.size(); i++) + { + if (mIsoTriangles[i].groupNr >= 0) + { + continue; + } + + GroupAndSize gas; + gas.size = 0; + gas.group = (int32_t)triangleGroups.size(); + gas.deleteMesh = false; + + gas.size = floodFill(i, (uint32_t)gas.group); + + triangleGroups.pushBack(gas); + } + + if (triangleGroups.size() == 0) + { + return; + } + + // clear regions + if (mDiscardInnerMeshes) + { + nvidia::Array<TimeAndDot> raycastHits; + for (uint32_t group = 0; group < triangleGroups.size(); group++) + { + // see if this group is an inner mesh + raycastHits.clear(); + + const int32_t groupNumber = triangleGroups[group].group; + + uint32_t start = 0; + const uint32_t numTriangles = mIsoTriangles.size(); + for (uint32_t i = 0; i < numTriangles; i++) + { + if (mIsoTriangles[i].groupNr == groupNumber) + { + start = i; + break; + } + } + + // raycast from the start triangle + const PxVec3 rayOrig = (mIsoVertices[(uint32_t)mIsoTriangles[start].vertexNr[0]] + + mIsoVertices[(uint32_t)mIsoTriangles[start].vertexNr[1]] + + mIsoVertices[(uint32_t)mIsoTriangles[start].vertexNr[2]]) / 3.0f; + PxVec3 rayDir(1.0f, 1.0f, 1.0f); + rayDir.normalize(); // can really be anything. + + // Find all ray hits + for (uint32_t i = start; i < numTriangles; i++) + { + if (mIsoTriangles[i].groupNr != groupNumber) + { + continue; + } + + float t, u, v; + PxVec3 verts[3] = + { + mIsoVertices[(uint32_t)mIsoTriangles[i].vertexNr[0]], + mIsoVertices[(uint32_t)mIsoTriangles[i].vertexNr[1]], + mIsoVertices[(uint32_t)mIsoTriangles[i].vertexNr[2]], + }; + + if (APEX_RayTriangleIntersect(rayOrig, rayDir, verts[0], verts[1], verts[2], t, u, v)) + { + TimeAndDot hit; + hit.time = t; + + PxVec3 faceNormal = (verts[1] - verts[0]).cross(verts[2] - verts[0]); + faceNormal.normalize(); + + hit.dot = faceNormal.dot(rayDir); + + raycastHits.pushBack(hit); + } + } + + if (raycastHits.size() > 0) + { + shdfnd::sort(raycastHits.begin(), raycastHits.size(), TimeAndDot()); + + triangleGroups[group].deleteMesh = raycastHits[0].dot > 0.0f; + } + } + } + + if (mKeepNBiggestMeshes > 0) + { + shdfnd::sort(triangleGroups.begin(), triangleGroups.size(), GroupAndSize()); + + for (uint32_t i = mKeepNBiggestMeshes; i < triangleGroups.size(); i++) + { + triangleGroups[i].deleteMesh = true; + } + } + + + for (uint32_t i = 0; i < triangleGroups.size(); i++) + { + if (!triangleGroups[i].deleteMesh) + { + continue; + } + + const int32_t group = triangleGroups[i].group; + + const uint32_t size = mIsoTriangles.size(); + for (uint32_t j = 0; j < size; j++) + { + if (mIsoTriangles[j].groupNr == group) + { + mIsoTriangles[j].deleted = true; + } + } + } +} + + + +uint32_t ApexIsoMesh::floodFill(uint32_t triangleNr, uint32_t groupNr) +{ + uint32_t numTriangles = 0; + nvidia::Array<uint32_t> queue; + queue.pushBack(triangleNr); + + while (!queue.empty()) + { + IsoTriangle& t = mIsoTriangles[queue.back()]; + queue.popBack(); + if (t.groupNr == int32_t(groupNr)) + { + continue; + } + + t.groupNr = (int32_t)groupNr; + numTriangles++; + + int32_t adj = t.adjTriangles[0]; + if (adj >= 0 && mIsoTriangles[(uint32_t)adj].groupNr < 0) + { + queue.pushBack((uint32_t)adj); + } + adj = t.adjTriangles[1]; + if (adj >= 0 && mIsoTriangles[(uint32_t)adj].groupNr < 0) + { + queue.pushBack((uint32_t)adj); + } + adj = t.adjTriangles[2]; + if (adj >= 0 && mIsoTriangles[(uint32_t)adj].groupNr < 0) + { + queue.pushBack((uint32_t)adj); + } + } + + return numTriangles; +} + + + +void ApexIsoMesh::removeTrisAndVerts() +{ + for (int32_t i = (int32_t)mIsoTriangles.size() - 1; i >= 0; i--) + { + if (mIsoTriangles[(uint32_t)i].deleted) + { + mIsoTriangles.replaceWithLast((uint32_t)i); + } + } + + Array<int32_t> oldToNew; + Array<PxVec3> newVertices; + + oldToNew.resize(mIsoVertices.size(), -1); + + for (uint32_t i = 0; i < mIsoTriangles.size(); i++) + { + IsoTriangle& t = mIsoTriangles[i]; + for (uint32_t j = 0; j < 3; j++) + { + uint32_t vNr = (uint32_t)t.vertexNr[j]; + if (oldToNew[vNr] < 0) + { + oldToNew[vNr] = (int32_t)newVertices.size(); + newVertices.pushBack(mIsoVertices[vNr]); + } + t.vertexNr[j] = oldToNew[vNr]; + } + } + + mIsoVertices.clear(); + mIsoVertices.reserve(newVertices.size()); + + for (uint32_t i = 0; i < newVertices.size(); i++) + { + mIsoVertices.pushBack(newVertices[i]); + } +} + +} // end namespace apex +} // end namespace nvidia diff --git a/APEX_1.4/common/src/ApexMath.cpp b/APEX_1.4/common/src/ApexMath.cpp new file mode 100644 index 00000000..0aaf9e9d --- /dev/null +++ b/APEX_1.4/common/src/ApexMath.cpp @@ -0,0 +1,49 @@ +#include "NvSimd4f.h" + +#include "PxMat44.h" +#include "PxMat33.h" +#include "PxVec3.h" +#include "PxVec4.h" +#include "PxQuat.h" + +using physx::PxMat44; +using physx::PxMat33; +using physx::PxVec3; +using physx::PxVec4; +using physx::PxQuat; + +#include "PxPreprocessor.h" +#include "PxAssert.h" +#include "ApexMath.h" + +namespace nvidia +{ + + bool operator != (const PxMat44& a, const PxMat44& b) + { + PX_ASSERT((((size_t)&a) & 0xf) == 0); // verify 16 byte alignment + PX_ASSERT((((size_t)&b) & 0xf) == 0); // verify 16 byte alignment + + int allEq = true; + + typedef Simd4fLoadFactory loadFactory; + + const Simd4f ca1 = loadFactory(&a.column0.x); + const Simd4f cb1 = loadFactory(&b.column0.x); + allEq &= allEqual(ca1, cb1); + + const Simd4f ca2 = loadFactory(&a.column1.x); + const Simd4f cb2 = loadFactory(&b.column1.x); + allEq &= allEqual(ca2, cb2); + + const Simd4f ca3 = loadFactory(&a.column2.x); + const Simd4f cb3 = loadFactory(&b.column2.x); + allEq &= allEqual(ca3, cb3); + + const Simd4f ca4 = loadFactory(&a.column3.x); + const Simd4f cb4 = loadFactory(&b.column3.x); + allEq &= allEqual(ca4, cb4); + + return allEq == 0; + } +} diff --git a/APEX_1.4/common/src/ApexMeshContractor.cpp b/APEX_1.4/common/src/ApexMeshContractor.cpp new file mode 100644 index 00000000..a9e4b1fe --- /dev/null +++ b/APEX_1.4/common/src/ApexMeshContractor.cpp @@ -0,0 +1,1708 @@ +/* + * 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. + */ + + +#include "ApexMeshContractor.h" +#include "ApexSDKIntl.h" +#include "PxBounds3.h" +#include "ApexBinaryHeap.h" +#include "ApexSharedUtils.h" + +#include "PsSort.h" + +namespace nvidia +{ +namespace apex +{ + +#define EDGE_PRESERVATION 1 + +struct TriangleEdge +{ + void init(uint32_t v0, uint32_t v1, uint32_t edgeNr, uint32_t triNr) + { + PX_ASSERT(v0 != v1); + this->v0 = PxMin(v0, v1); + this->v1 = PxMax(v0, v1); + this->edgeNr = edgeNr; + this->triNr = triNr; + } + bool operator < (const TriangleEdge& e) const + { + if (v0 < e.v0) + { + return true; + } + if (v0 > e.v0) + { + return false; + } + return v1 < e.v1; + } + bool operator()(const TriangleEdge& a, const TriangleEdge& b) const + { + if (a.v0 < b.v0) + { + return true; + } + if (a.v0 > b.v0) + { + return false; + } + return a.v1 < b.v1; + } + bool operator == (const TriangleEdge& e) const + { + if (v0 == e.v0 && v1 == e.v1) + { + return true; + } + PX_ASSERT(!(v0 == e.v1 && v1 == e.v0)); + //if (v0 == e.v1 && v1 == e.v0) return true; + return false; + } + uint32_t v0, v1; + uint32_t edgeNr; + uint32_t triNr; +}; + + + +struct CellCoord +{ + uint32_t xi, yi, zi; + float value; + bool operator < (CellCoord& c) const + { + return value < c.value; + } +}; + + + +ApexMeshContractor::ApexMeshContractor() : + mCellSize(1.0f), + mOrigin(0.0f, 0.0f, 0.0f), + mNumX(0), mNumY(0), mNumZ(0), + mInitialVolume(0.0f), + mCurrentVolume(0.0f) +{ + PX_COMPILE_TIME_ASSERT(sizeof(ContractorCell) == 12); +} + + + +void ApexMeshContractor::registerVertex(const PxVec3& pos) +{ + mVertices.pushBack(pos); +} + + + +void ApexMeshContractor::registerTriangle(uint32_t v0, uint32_t v1, uint32_t v2) +{ + mIndices.pushBack(v0); + mIndices.pushBack(v1); + mIndices.pushBack(v2); +} + + + +bool ApexMeshContractor::endRegistration(uint32_t subdivision, IProgressListener* progressListener) +{ + HierarchicalProgressListener progress(100, progressListener); + progress.setSubtaskWork(30, "Compute Neighbors"); + computeNeighbours(); + + PxBounds3 bounds; + bounds.setEmpty(); + for (unsigned i = 0; i < mVertices.size(); i++) + { + bounds.include(mVertices[i]); + } + + mCellSize = (bounds.maximum - bounds.minimum).magnitude() / subdivision; + if (mCellSize == 0.0f) + { + mCellSize = 1.0f; + } + PX_ASSERT(!bounds.isEmpty()); + bounds.fattenFast(2.0f * mCellSize); + + mOrigin = bounds.minimum; + float invH = 1.0f / mCellSize; + mNumX = (uint32_t)((bounds.maximum.x - bounds.minimum.x) * invH) + 1; + mNumY = (uint32_t)((bounds.maximum.y - bounds.minimum.y) * invH) + 1; + mNumZ = (uint32_t)((bounds.maximum.z - bounds.minimum.z) * invH) + 1; + unsigned num = mNumX * mNumY * mNumZ; + mGrid.resize(num, ContractorCell()); + + progress.completeSubtask(); + + progress.setSubtaskWork(60, "Compute Signed Distance Field"); + computeSignedDistanceField(); + progress.completeSubtask(); + + progress.setSubtaskWork(10, "Computing Volume"); + float area = 0; + computeAreaAndVolume(area, mInitialVolume); + progress.completeSubtask(); + + return true; +} + + + +uint32_t ApexMeshContractor::contract(int32_t steps, float abortionRatio, float& volumeRatio, IProgressListener* progressListener) +{ + if (steps == -1 && (abortionRatio <= 0 || abortionRatio > 1)) + { + APEX_INTERNAL_ERROR("Invalid abortionRatio when doing infinite steps (%f)", abortionRatio); + return 0; + } + + HierarchicalProgressListener progress(100, progressListener); + progress.setSubtaskWork(100, "Contract"); + + bool abort = false; + int32_t currStep = 0; + for (; (steps < 0 || currStep < steps) && !abort; currStep++) + { + { + int32_t percent = 100 * currStep / steps; + progress.setProgress(percent); + } + if (!abort) + { + contractionStep(); + subdivide(1.5f * mCellSize); + collapse(0.3f * mCellSize); + + float area; + computeAreaAndVolume(area, mCurrentVolume); + + volumeRatio = mCurrentVolume / mInitialVolume; + abort = volumeRatio < abortionRatio; + } + } + + progress.completeSubtask(); + return (uint32_t)currStep; +} + + + +void ApexMeshContractor::expandBorder() +{ + const float localRadius = 2.0f * mCellSize; + //const float minDist = 1.0f * mCellSize; + float maxDot = 0.0f; + + if (mNeighbours.size() != mIndices.size()) + { + return; + } + + const uint32_t numTris = mIndices.size() / 3; + const uint32_t numVerts = mVertices.size(); + + void* memory = PX_ALLOC(sizeof(uint32_t) * numTris + sizeof(PxVec3) * numTris, PX_DEBUG_EXP("ApexMeshContractor")); + uint32_t* triMarks = (uint32_t*)memory; + PxVec3* triComs = (PxVec3*)(triMarks + numTris); + memset(triMarks, -1, sizeof(uint32_t) * numTris); + + physx::Array<PxVec3> dispField; + dispField.resize(numVerts); + + physx::Array<float> dispWeights; + dispWeights.resize(numVerts); + + for (uint32_t i = 0; i < numVerts; i++) + { + dispField[i] = PxVec3(0.0f); + dispWeights[i] = 0.0f; + } + + physx::Array<int32_t> localTris; + physx::Array<float> localDists; + + for (uint32_t i = 0; i < numTris; i++) + { + triComs[i] = PxVec3(0.0f); + + // is there a border edge? + uint32_t i0 = mIndices[3 * i]; + uint32_t i1 = mIndices[3 * i + 1]; + uint32_t i2 = mIndices[3 * i + 2]; + + const PxVec3& p0 = mVertices[i0]; + const PxVec3& p1 = mVertices[i1]; + const PxVec3& p2 = mVertices[i2]; + + const PxVec3 ci = (p0 + p1 + p2) / 3.0f; + + PxVec3 ni = (p1 - p0).cross(p2 - p0); + ni.normalize(); + + bool edgeFound = false; + for (uint32_t j = 0; j < 3; j++) + { + int32_t adj = mNeighbours[3 * i + j]; + if (adj < 0) + { + continue; + } + + PxVec3& q0 = mVertices[mIndices[3 * adj + j]]; + PxVec3& q1 = mVertices[mIndices[3 * adj + (j + 1) % 3]]; + PxVec3& q2 = mVertices[mIndices[3 * adj + (j + 2) % 3]]; + + PxVec3 nadj = (q1 - q0).cross(q2 - q0); + nadj.normalize(); + + if (ni.dot(nadj) < maxDot) + { + edgeFound = true; + break; + } + } + + if (!edgeFound) + { + continue; + } + + collectNeighborhood((int32_t)i, localRadius, i, localTris, localDists, triMarks); + uint32_t numLocals = localTris.size(); + + // is the neighborhood double sided? + float doubleArea = 0.0f; + PxVec3 com(0.0f, 0.0f, 0.0f); + float totalW = 0.0f; + + for (uint32_t j = 0; j < numLocals; j++) + { + uint32_t triJ = (uint32_t)localTris[j]; + const PxVec3& p0 = mVertices[mIndices[3 * triJ + 0]]; + const PxVec3& p1 = mVertices[mIndices[3 * triJ + 1]]; + const PxVec3& p2 = mVertices[mIndices[3 * triJ + 2]]; + + PxVec3 nj = (p1 - p0).cross(p2 - p0); + const float area = nj.normalize(); + const float w = area; + totalW += w; + com += (p0 + p1 + p2) / 3.0f * w; + + for (uint32_t k = 0; k < numLocals; k++) + { + uint32_t triK = (uint32_t)localTris[k]; + + const PxVec3& q0 = mVertices[mIndices[3 * triK + 0]]; + const PxVec3& q1 = mVertices[mIndices[3 * triK + 1]]; + const PxVec3& q2 = mVertices[mIndices[3 * triK + 2]]; + + PxVec3 nk = (q1 - p0).cross(q2 - q0); + nk.normalize(); + + if (nj.dot(nk) < -0.999f) + { + doubleArea += area; + break; + } + } + } + + float totalArea = PxPi * localRadius * localRadius; + if (doubleArea < 0.5f * totalArea) + { + continue; + } + + if (totalW > 0.0f) + { + com /= totalW; + } + + triComs[i] = com; + + // update displacement field + PxVec3 disp = ci - com; + disp.normalize(); + disp *= 2.0f * mCellSize; + + float minT = findMin(ci, disp); + disp *= minT; + + float maxDist = 0.0f; + for (uint32_t j = 0; j < numLocals; j++) + { + maxDist = PxMax(maxDist, localDists[j]); + } + + for (uint32_t j = 0; j < numLocals; j++) + { + int32_t triJ = localTris[j]; + + float w = 1.0f; + if (maxDist != 0.0f) + { + w = 1.0f - localDists[j] / maxDist; + } + + for (uint32_t k = 0; k < 3; k++) + { + uint32_t v = mIndices[3 * triJ + k]; + dispField[v] += disp * w * w; + dispWeights[v] += w; + } + } + } + + for (uint32_t i = 0; i < mVertices.size(); i++) + { + const float w = dispWeights[i]; + if (w == 0.0f) + { + continue; + } + + mVertices[i] += dispField[i] / w; + } + + PX_FREE(memory); +} + + + + +void ApexMeshContractor::computeNeighbours() +{ + physx::Array<TriangleEdge> edges; + edges.reserve(mIndices.size()); + + mNeighbours.resize(mIndices.size()); + for (uint32_t i = 0; i < mNeighbours.size(); i++) + { + mNeighbours[i] = -1; + } + + PX_ASSERT(mIndices.size() % 3 == 0); + const uint32_t numTriangles = mIndices.size() / 3; + for (uint32_t i = 0; i < numTriangles; i++) + { + uint32_t i0 = mIndices[3 * i ]; + uint32_t i1 = mIndices[3 * i + 1]; + uint32_t i2 = mIndices[3 * i + 2]; + TriangleEdge edge; + edge.init(i0, i1, 0, i); + edges.pushBack(edge); + edge.init(i1, i2, 1, i); + edges.pushBack(edge); + edge.init(i2, i0, 2, i); + edges.pushBack(edge); + } + + shdfnd::sort(edges.begin(), edges.size(), TriangleEdge()); + + uint32_t i = 0; + const uint32_t numEdges = edges.size(); + while (i < numEdges) + { + const TriangleEdge& e0 = edges[i]; + i++; + while (i < numEdges && edges[i] == e0) + { + const TriangleEdge& e1 = edges[i]; + PX_ASSERT(mNeighbours[e0.triNr * 3 + e0.edgeNr] == -1);///? + mNeighbours[e0.triNr * 3 + e0.edgeNr] = (int32_t)e1.triNr; + PX_ASSERT(mNeighbours[e1.triNr * 3 + e1.edgeNr] == -1);///? + mNeighbours[e1.triNr * 3 + e1.edgeNr] = (int32_t)e0.triNr; + i++; + } + } +} + + + +void ApexMeshContractor::computeSignedDistanceField() +{ + // init + for (uint32_t i = 0; i < mGrid.size(); i++) + { + PX_ASSERT(mGrid[i].distance == PX_MAX_F32); + PX_ASSERT(mGrid[i].inside == 0); + } + + PX_ASSERT(mIndices.size() % 3 == 0); + uint32_t numTris = mIndices.size() / 3; + + for (uint32_t i = 0; i < numTris; i++) + { + PxVec3 p0 = mVertices[mIndices[3 * i ]] - mOrigin; + PxVec3 p1 = mVertices[mIndices[3 * i + 1]] - mOrigin; + PxVec3 p2 = mVertices[mIndices[3 * i + 2]] - mOrigin; + addTriangle(p0, p1, p2); + } + + + // fast marching, see J.A. Sethian "Level Set Methods and Fast Marching Methods" + // create front + ApexBinaryHeap<CellCoord> heap; + //CellCoord cc; + + //int xi,yi,zi; + + for (uint32_t xi = 0; xi < mNumX - 1; xi++) + { + CellCoord cc; + cc.xi = xi; + for (uint32_t yi = 0; yi < mNumY - 1; yi++) + { + cc.yi = yi; + for (uint32_t zi = 0; zi < mNumZ - 1; zi++) + { + cc.zi = zi; + const ContractorCell& c = cellAt((int32_t)xi, (int32_t)yi, (int32_t)zi); + if (!c.marked) + { + continue; + } + cc.value = c.distance; + heap.push(cc); + } + } + } + + while (!heap.isEmpty()) + { + CellCoord cc; + + // Make compiler happy + cc.xi = cc.yi = cc.zi = 0; + cc.value = 0.0f; + + heap.pop(cc); + + ContractorCell& c = cellAt((int32_t)cc.xi, (int32_t)cc.yi, (int32_t)cc.zi); + c.marked = true; + for (int i = 0; i < 6; i++) + { + CellCoord ncc = cc; + switch (i) + { + case 0: + if (ncc.xi < 1) + { + continue; + } + else + { + ncc.xi--; + } + break; + case 1: + if (ncc.xi >= mNumX - 1) + { + continue; + } + else + { + ncc.xi++; + } + break; + case 2: + if (ncc.yi < 1) + { + continue; + } + else + { + ncc.yi--; + } + break; + case 3: + if (ncc.yi >= mNumY - 1) + { + continue; + } + else + { + ncc.yi++; + } + break; + case 4: + if (ncc.zi < 1) + { + continue; + } + else + { + ncc.zi--; + } + break; + case 5: + if (ncc.zi >= mNumZ - 1) + { + continue; + } + else + { + ncc.zi++; + } + break; + } + + ContractorCell& cn = cellAt((int32_t)ncc.xi, (int32_t)ncc.yi, (int32_t)ncc.zi); + if (cn.marked) + { + continue; + } + if (!updateDistance(ncc.xi, ncc.yi, ncc.zi)) + { + continue; + } + ncc.value = cn.distance; + heap.push(ncc); + } + } + + setInsideOutside(); + for (unsigned i = 0; i < mGrid.size(); i++) + { + if (mGrid[i].inside < 3) // majority vote + { + mGrid[i].distance = -mGrid[i].distance; + } + } +} + + + +void ApexMeshContractor::contractionStep() +{ +#if EDGE_PRESERVATION + unsigned numTris = mIndices.size() / 3; + mVertexCurvatures.resize(mVertices.size()); + for (unsigned i = 0; i < mVertexCurvatures.size(); i++) + { + mVertexCurvatures[i] = 0.0f; + } + + for (unsigned i = 0; i < numTris; i++) + { + for (unsigned j = 0; j < 3; j++) + { + unsigned vertNr = mIndices[3 * i + j]; + if (mVertexCurvatures[vertNr] == 0.0f) + { + float curv = curvatureAt((int32_t)i, (int32_t)vertNr); + mVertexCurvatures[vertNr] = curv; + } + } + } +#endif + + float s = 0.1f * mCellSize; + for (unsigned i = 0; i < mVertices.size(); i++) + { + PxVec3& p = mVertices[i]; + PxVec3 grad; + interpolateGradientAt(p, grad); + +#if EDGE_PRESERVATION + p += grad * s * (1.0f - mVertexCurvatures[i]); +#else + p += grad * s; +#endif + } +} + + + +void ApexMeshContractor::computeAreaAndVolume(float& area, float& volume) +{ + area = volume = 0.0f; + + for (uint32_t i = 0; i < mIndices.size(); i += 3) + { + PxVec3& d0 = mVertices[mIndices[i]]; + PxVec3 d1 = mVertices[mIndices[i + 1]] - mVertices[mIndices[i]]; + PxVec3 d2 = mVertices[mIndices[i + 2]] - mVertices[mIndices[i]]; + PxVec3 n = d1.cross(d2); + area += n.magnitude(); + volume += n.dot(d0); + } + + area /= 2.0f; + volume /= 6.0f; +} + + + +void ApexMeshContractor::addTriangle(const PxVec3& p0, const PxVec3& p1, const PxVec3& p2) +{ + PxBounds3 bounds; + bounds.setEmpty(); + bounds.include(p0); + bounds.include(p1); + bounds.include(p2); + PX_ASSERT(!bounds.isEmpty()); + bounds.fattenFast(0.01f * mCellSize); + + PxVec3 n = (p1 - p0).cross(p2 - p0); + + float d_big = n.dot(p0); + float invH = 1.0f / mCellSize; + + int32_t min[3]; + min[0] = (int32_t)PxFloor(bounds.minimum.x * invH) + 1; + min[1] = (int32_t)PxFloor(bounds.minimum.y * invH) + 1; + min[2] = (int32_t)PxFloor(bounds.minimum.z * invH) + 1; + int32_t max[3]; + max[0] = (int32_t)PxFloor(bounds.maximum.x * invH); + max[1] = (int32_t)PxFloor(bounds.maximum.y * invH); + max[2] = (int32_t)PxFloor(bounds.maximum.z * invH); + + if (min[0] < 0) + { + min[0] = 0; + } + if (min[1] < 0) + { + min[1] = 0; + } + if (min[2] < 0) + { + min[2] = 0; + } + if (max[0] >= (int32_t)mNumX) + { + max[0] = (int32_t)mNumX - 1; + } + if (max[1] >= (int32_t)mNumY) + { + max[1] = (int32_t)mNumY - 1; + } + if (max[2] >= (int32_t)mNumZ) + { + max[2] = (int32_t)mNumZ - 1; + } + + int32_t num[3] = { (int32_t)mNumX, (int32_t)mNumY, (int32_t)mNumZ }; + + int32_t coord[3]; + + for (uint32_t dim0 = 0; dim0 < 3; dim0++) + { + if (n[dim0] == 0.0f) + { + continue; // singular case + } + + const uint32_t dim1 = (dim0 + 1) % 3; + const uint32_t dim2 = (dim1 + 1) % 3; + for (coord[dim1] = min[dim1]; coord[dim1] <= max[dim1]; coord[dim1]++) + { + for (coord[dim2] = min[dim2]; coord[dim2] <= max[dim2]; coord[dim2]++) + { + float axis1 = coord[dim1] * mCellSize; + float axis2 = coord[dim2] * mCellSize; + /// \todo prevent singular cases when the same spacing was used in previous steps ???? + axis1 += 0.00017f * mCellSize; + axis2 += 0.00017f * mCellSize; + + // does the ray go through the triangle? + if (n[dim0] > 0.0f) + { + if ((p1[dim1] - p0[dim1]) * (axis2 - p0[dim2]) - (axis1 - p0[dim1]) * (p1[dim2] - p0[dim2]) < 0.0f) + { + continue; + } + if ((p2[dim1] - p1[dim1]) * (axis2 - p1[dim2]) - (axis1 - p1[dim1]) * (p2[dim2] - p1[dim2]) < 0.0f) + { + continue; + } + if ((p0[dim1] - p2[dim1]) * (axis2 - p2[dim2]) - (axis1 - p2[dim1]) * (p0[dim2] - p2[dim2]) < 0.0f) + { + continue; + } + } + else + { + if ((p1[dim1] - p0[dim1]) * (axis2 - p0[dim2]) - (axis1 - p0[dim1]) * (p1[dim2] - p0[dim2]) > 0.0f) + { + continue; + } + if ((p2[dim1] - p1[dim1]) * (axis2 - p1[dim2]) - (axis1 - p1[dim1]) * (p2[dim2] - p1[dim2]) > 0.0f) + { + continue; + } + if ((p0[dim1] - p2[dim1]) * (axis2 - p2[dim2]) - (axis1 - p2[dim1]) * (p0[dim2] - p2[dim2]) > 0.0f) + { + continue; + } + } + + float pos = (d_big - axis1 * n[dim1] - axis2 * n[dim2]) / n[dim0]; + coord[dim0] = (int)PxFloor(pos * invH); + if (coord[dim0] < 0 || coord[dim0] >= num[dim0]) + { + continue; + } + + ContractorCell& cell = cellAt(coord[0], coord[1], coord[2]); + cell.numCuts[dim0]++; + + float d = PxAbs(pos - coord[dim0] * mCellSize) * invH; + if (d < cell.distance) + { + cell.distance = d; + cell.marked = true; + } + + if (coord[dim0] < num[dim0] - 1) + { + int adj[3] = { coord[0], coord[1], coord[2] }; + adj[dim0]++; + ContractorCell& ca = cellAt(adj[0], adj[1], adj[2]); + d = 1.0f - d; + if (d < ca.distance) + { + ca.distance = d; + ca.marked = true; + } + } + } + } + } +} + + + +bool ApexMeshContractor::updateDistance(uint32_t xi, uint32_t yi, uint32_t zi) +{ + ContractorCell& ci = cellAt((int32_t)xi, (int32_t)yi, (int32_t)zi); + if (ci.marked) + { + return false; + } + + // create quadratic equation from the stencil + float a = 0.0f; + float b = 0.0f; + float c = -1.0f; + for (uint32_t i = 0; i < 3; i++) + { + float min = PX_MAX_F32; + int32_t x = (int32_t)xi, y = (int32_t)yi, z = (int32_t)zi; + switch (i) + { + case 0: + x--; + break; + case 1: + y--; + break; + case 2: + z--; + break; + } + + if (x >= 0 && y >= 0 && z >= 0) + { + ContractorCell& c = cellAt(x, y, z); + if (c.marked) + { + min = c.distance; + } + } + + switch (i) + { + case 0: + x += 2; + break; + case 1: + y += 2; + break; + case 2: + z += 2; + break; + } + + if (x < (int32_t)mNumX && y < (int32_t)mNumY && z < (int32_t)mNumZ) + { + ContractorCell& c = cellAt(x, y, z); + if (c.marked && c.distance < min) + { + min = c.distance; + } + } + + if (min != PX_MAX_F32) + { + a += 1.0f; + b -= 2.0f * min; + c += min * min; + } + } + + // solve the equation, the larger solution is the right one (distances grow as we proceed) + if (a == 0.0f) + { + return false; + } + + float d = b * b - 4.0f * a * c; + + if (d < 0.0f) + { + d = 0.0f; // debug + } + + float distance = (-b + PxSqrt(d)) / (2.0f * a); + + if (distance < ci.distance) + { + ci.distance = distance; + return true; + } + + return false; +} +void ApexMeshContractor::setInsideOutside() +{ + int32_t num[3] = { (int32_t)mNumX, (int32_t)mNumY, (int32_t)mNumZ }; + + int32_t coord[3]; + for (uint32_t dim0 = 0; dim0 < 3; dim0++) + { + const uint32_t dim1 = (dim0 + 1) % 3; + const uint32_t dim2 = (dim0 + 2) % 3; + for (coord[dim1] = 0; coord[dim1] < num[dim1]; coord[dim1]++) + { + for (coord[dim2] = 0; coord[dim2] < num[dim2]; coord[dim2]++) + { + bool inside = false; + for (coord[dim0] = 0; coord[dim0] < num[dim0]; coord[dim0]++) + { + ContractorCell& cell = cellAt(coord[0], coord[1], coord[2]); + if (inside) + { + cell.inside++; + } + if ((cell.numCuts[dim0] % 2) == 1) + { + inside = !inside; + } + } + + inside = false; + for (coord[dim0] = num[dim0] - 1; coord[dim0] >= 0; coord[dim0]--) + { + ContractorCell& cell = cellAt(coord[0], coord[1], coord[2]); + if ((cell.numCuts[dim0] % 2) == 1) + { + inside = !inside; + } + if (inside) + { + cell.inside++; + } + } + } + } + } +} + + + +void ApexMeshContractor::interpolateGradientAt(const PxVec3& pos, PxVec3& grad) +{ + // the derivatives of the distances are defined on the center of the edges + // from there they are interpolated trilinearly + const PxVec3 p = pos - mOrigin; + const float h = mCellSize; + const float h1 = 1.0f / h; + const float h2 = 0.5f * h; + int32_t x0 = (int32_t)PxFloor(p.x * h1); + const float dx0 = (p.x - h * x0) * h1; + const float ex0 = 1.0f - dx0; + int32_t y0 = (int32_t)PxFloor(p.y * h1); + const float dy0 = (p.y - h * y0) * h1; + const float ey0 = 1.0f - dy0; + int32_t z0 = (int32_t)PxFloor(p.z * h1); + const float dz0 = (p.z - h * z0) * h1; + const float ez0 = 1.0f - dz0; + + if (x0 < 0) + { + x0 = 0; + } + if (x0 + 1 >= (int32_t)mNumX) + { + x0 = (int32_t)mNumX - 2; // todo: handle boundary correctly + } + if (y0 < 0) + { + y0 = 0; + } + if (y0 + 1 >= (int32_t)mNumY) + { + y0 = (int32_t)mNumY - 2; + } + if (z0 < 0) + { + z0 = 0; + } + if (z0 + 1 >= (int32_t)mNumZ) + { + z0 = (int32_t)mNumZ - 2; + } + + // the following coefficients are used on the axis of the component that is computed + // everything is shifted there because the derivatives are defined at the center of edges + //float dx2,dy2,dz2,ex2,ey2,ez2; + int32_t x2 = (int32_t)PxFloor((p.x - h2) * h1); + const float dx2 = (p.x - h2 - h * x2) * h1; + const float ex2 = 1.0f - dx2; + int32_t y2 = (int32_t)PxFloor((p.y - h2) * h1); + const float dy2 = (p.y - h2 - h * y2) * h1; + const float ey2 = 1.0f - dy2; + int32_t z2 = (int32_t)PxFloor((p.z - h2) * h1); + const float dz2 = (p.z - h2 - h * z2) * h1; + const float ez2 = 1.0f - dz2; + + if (x2 < 0) + { + x2 = 0; + } + if (x2 + 2 >= (int32_t)mNumX) + { + x2 = (int32_t)mNumX - 3; // todo: handle boundary correctly + } + if (y2 < 0) + { + y2 = 0; + } + if (y2 + 2 >= (int32_t)mNumY) + { + y2 = (int32_t)mNumY - 3; + } + if (z2 < 0) + { + z2 = 0; + } + if (z2 + 2 >= (int32_t)mNumZ) + { + z2 = (int32_t)mNumZ - 3; + } + + float d, d0, d1, d2, d3, d4, d5, d6, d7; + d = cellAt(x2 + 1, y0 , z0).distance; + d0 = d - cellAt(x2, y0, z0).distance; + d4 = cellAt(x2 + 2, y0, z0).distance - d; + d = cellAt(x2 + 1, y0 + 1, z0).distance; + d1 = d - cellAt(x2, y0 + 1, z0).distance; + d5 = cellAt(x2 + 2, y0 + 1, z0).distance - d; + d = cellAt(x2 + 1, y0 + 1, z0 + 1).distance; + d2 = d - cellAt(x2, y0 + 1, z0 + 1).distance; + d6 = cellAt(x2 + 2, y0 + 1, z0 + 1).distance - d; + d = cellAt(x2 + 1, y0, z0 + 1).distance; + d3 = d - cellAt(x2, y0, z0 + 1).distance; + d7 = cellAt(x2 + 2, y0, z0 + 1).distance - d; + + grad.x = ex2 * (d0 * ey0 * ez0 + d1 * dy0 * ez0 + d2 * dy0 * dz0 + d3 * ey0 * dz0) + + dx2 * (d4 * ey0 * ez0 + d5 * dy0 * ez0 + d6 * dy0 * dz0 + d7 * ey0 * dz0); + + d = cellAt(x0, y2 + 1, z0).distance; + d0 = d - cellAt(x0, y2, z0).distance; + d4 = cellAt(x0, y2 + 2, z0).distance - d; + d = cellAt(x0, y2 + 1, z0 + 1).distance; + d1 = d - cellAt(x0, y2, z0 + 1).distance; + d5 = cellAt(x0, y2 + 2, z0 + 1).distance - d; + d = cellAt(x0 + 1, y2 + 1, z0 + 1).distance; + d2 = d - cellAt(x0 + 1, y2, z0 + 1).distance; + d6 = cellAt(x0 + 1, y2 + 2, z0 + 1).distance - d; + d = cellAt(x0 + 1, y2 + 1, z0).distance; + d3 = d - cellAt(x0 + 1, y2, z0).distance; + d7 = cellAt(x0 + 1, y2 + 2, z0).distance - d; + + grad.y = ey2 * (d0 * ez0 * ex0 + d1 * dz0 * ex0 + d2 * dz0 * dx0 + d3 * ez0 * dx0) + + dy2 * (d4 * ez0 * ex0 + d5 * dz0 * ex0 + d6 * dz0 * dx0 + d7 * ez0 * dx0); + + d = cellAt(x0, y0 , z2 + 1).distance; + d0 = d - cellAt(x0, y0 , z2).distance; + d4 = cellAt(x0, y0 , z2 + 2).distance - d; + d = cellAt(x0 + 1, y0, z2 + 1).distance; + d1 = d - cellAt(x0 + 1, y0, z2).distance; + d5 = cellAt(x0 + 1, y0, z2 + 2).distance - d; + d = cellAt(x0 + 1, y0 + 1, z2 + 1).distance; + d2 = d - cellAt(x0 + 1, y0 + 1, z2).distance; + d6 = cellAt(x0 + 1, y0 + 1, z2 + 2).distance - d; + d = cellAt(x0, y0 + 1, z2 + 1).distance; + d3 = d - cellAt(x0, y0 + 1, z2).distance; + d7 = cellAt(x0, y0 + 1, z2 + 2).distance - d; + + grad.z = ez2 * (d0 * ex0 * ey0 + d1 * dx0 * ey0 + d2 * dx0 * dy0 + d3 * ex0 * dy0) + + dz2 * (d4 * ex0 * ey0 + d5 * dx0 * ey0 + d6 * dx0 * dy0 + d7 * ex0 * dy0); +} + + + +void ApexMeshContractor::subdivide(float spacing) +{ + const float min2 = spacing * spacing; + const uint32_t numTris = mIndices.size() / 3; + //const uint32_t numVerts = mVertices.size(); + + for (uint32_t i = 0; i < numTris; i++) + { + const uint32_t triNr = i; + for (uint32_t j = 0; j < 3; j++) + { + uint32_t k = (j + 1) % 3; + uint32_t v0 = mIndices[3 * triNr + j]; + uint32_t v1 = mIndices[3 * triNr + k]; + + PxVec3& p0 = mVertices[v0]; + PxVec3& p1 = mVertices[v1]; + if ((p1 - p0).magnitudeSquared() < min2) + { + continue; + } + + uint32_t newVert = mVertices.size(); + mVertices.pushBack((p1 + p0) * 0.5f); + + int32_t adj; + int32_t t0, t1, t2, t3; + getButterfly(triNr, v0, v1, adj, t0, t1, t2, t3); + + uint32_t newTri = mIndices.size() / 3; + int newAdj = -1; + + int v = getOppositeVertex((int32_t)triNr, v0, v1); + PX_ASSERT(v >= 0); + mIndices.pushBack(newVert); + mIndices.pushBack(v1); + mIndices.pushBack((uint32_t)v); + if (adj >= 0) + { + v = getOppositeVertex(adj, v0, v1); + PX_ASSERT(v >= 0); + newAdj = (int32_t)mIndices.size() / 3; + mIndices.pushBack(newVert); + mIndices.pushBack((uint32_t)v); + mIndices.pushBack(v1); + } + + replaceVertex((int32_t)triNr, v1, newVert); + replaceVertex(adj, v1, newVert); + + replaceNeighbor((int32_t)triNr, t1, newTri); + replaceNeighbor(adj, t3, (uint32_t)newAdj); + replaceNeighbor(t1, (int32_t)triNr, newTri); + replaceNeighbor(t3, adj, (uint32_t)newAdj); + + mNeighbours.pushBack(newAdj); + mNeighbours.pushBack(t1); + mNeighbours.pushBack((int32_t)triNr); + if (adj >= 0) + { + mNeighbours.pushBack(adj); + mNeighbours.pushBack(t3); + mNeighbours.pushBack((int32_t)newTri); + } + } + } +} + + + +void ApexMeshContractor::collapse(float spacing) +{ + const float min2 = spacing * spacing; + const uint32_t numTris = mIndices.size() / 3; + const uint32_t numVerts = mVertices.size(); + + const uint32_t size = sizeof(int32_t) * numVerts + sizeof(int32_t) * numTris; + void* memory = PX_ALLOC(size, PX_DEBUG_EXP("ApexMeshContractor")); + memset(memory, 0, size); + + int32_t* old2newVerts = (int32_t*)memory; + int32_t* old2newTris = old2newVerts + numVerts; + + // collapse edges + for (uint32_t i = 0; i < numTris; i++) + { + const uint32_t triNr = i; + + if (old2newTris[triNr] < 0) + { + continue; + } + + for (uint32_t j = 0; j < 3; j++) + { + const uint32_t k = (j + 1) % 3; + const uint32_t v0 = mIndices[3 * triNr + j]; + const uint32_t v1 = mIndices[3 * triNr + k]; + + PxVec3& p0 = mVertices[v0]; + PxVec3& p1 = mVertices[v1]; + if ((p1 - p0).magnitudeSquared() > min2) + { + continue; + } + + //if (iterNr == 14 && triNr == 388 && j == 0) { + // int foo = 0; + // checkConsistency(); + //} + + if (!legalCollapse((int32_t)triNr, v0, v1)) + { + continue; + } + + int32_t adj, t0, t1, t2, t3; + getButterfly(triNr, v0, v1, adj, t0, t1, t2, t3); + + mVertices[v0] = (p1 + p0) * 0.5f; + + int32_t prev = (int32_t)triNr; + int32_t t = t1; + while (t >= 0) + { + replaceVertex(t, v1, v0); + advanceAdjTriangle(v1, t, prev); + if (t == (int32_t)triNr) + { + break; + } + } + + for (uint32_t k = 0; k < 3; k++) + { + if (t0 >= 0 && mNeighbours[3 * (uint32_t)t0 + k] == (int32_t)triNr) + { + mNeighbours[3 * (uint32_t)t0 + k] = t1; + } + if (t1 >= 0 && mNeighbours[3 * (uint32_t)t1 + k] == (int32_t)triNr) + { + mNeighbours[3 * (uint32_t)t1 + k] = t0; + } + if (t2 >= 0 && mNeighbours[3 * (uint32_t)t2 + k] == adj) + { + mNeighbours[3 * t2 + k] = t3; + } + if (t3 >= 0 && mNeighbours[3 * (uint32_t)t3 + k] == adj) + { + mNeighbours[3 * (uint32_t)t3 + k] = t2; + } + } + + old2newVerts[v1] = -1; + old2newTris[triNr] = -1; + if (adj >= 0) + { + old2newTris[adj] = -1; + } + + //checkConsistency(); + + break; + } + } + + // compress mesh + uint32_t vertNr = 0; + for (uint32_t i = 0; i < numVerts; i++) + { + if (old2newVerts[i] != -1) + { + old2newVerts[i] = (int32_t)vertNr; + mVertices[vertNr] = mVertices[i]; + vertNr++; + } + } + mVertices.resize(vertNr); + + uint32_t triNr = 0; + for (uint32_t i = 0; i < numTris; i++) + { + if (old2newTris[i] != -1) + { + old2newTris[i] = (int32_t)triNr; + for (uint32_t j = 0; j < 3; j++) + { + mIndices[3 * triNr + j] = (uint32_t)old2newVerts[mIndices[3 * i + j]]; + mNeighbours[3 * triNr + j] = mNeighbours[3 * i + j]; + } + triNr++; + } + } + mIndices.resize(triNr * 3); + mNeighbours.resize(triNr * 3); + for (uint32_t i = 0; i < mNeighbours.size(); i++) + { + PX_ASSERT(mNeighbours[i] != -1); + PX_ASSERT(old2newTris[mNeighbours[i]] != -1); + mNeighbours[i] = old2newTris[mNeighbours[i]]; + } + + PX_FREE(memory); +} + + + +void ApexMeshContractor::getButterfly(uint32_t triNr, uint32_t v0, uint32_t v1, int32_t& adj, int32_t& t0, int32_t& t1, int32_t& t2, int32_t& t3) const +{ + t0 = -1; + t1 = -1; + t2 = -1; + t3 = -1; + adj = -1; + for (uint32_t i = 0; i < 3; i++) + { + int32_t n = mNeighbours[triNr * 3 + i]; + + if (n < 0) + { + continue; + } + + if (triangleContains(n, v0) && triangleContains(n, v1)) + { + adj = n; + } + else if (triangleContains(n, v0)) + { + t0 = n; + } + else if (triangleContains(n, v1)) + { + t1 = n; + } + } + if (adj >= 0) + { + for (uint32_t i = 0; i < 3; i++) + { + int32_t n = mNeighbours[adj * 3 + i]; + + if (n < 0 || (uint32_t)n == triNr) + { + continue; + } + + if (triangleContains(n, v0)) + { + t2 = n; + } + else if (triangleContains(n, v1)) + { + t3 = n; + } + } + } +} + + + +int32_t ApexMeshContractor::getOppositeVertex(int32_t t, uint32_t v0, uint32_t v1) const +{ + if (t < 0 || 3 * t + 2 >= (int32_t)mIndices.size()) + { + return -1; + } + const uint32_t i = 3 * (uint32_t)t; + if (mIndices[i + 0] != v0 && mIndices[i + 0] != v1) + { + return (int32_t)mIndices[i + 0]; + } + if (mIndices[i + 1] != v0 && mIndices[i + 1] != v1) + { + return (int32_t)mIndices[i + 1]; + } + if (mIndices[i + 2] != v0 && mIndices[i + 2] != v1) + { + return (int32_t)mIndices[i + 2]; + } + return -1; +} + + + +void ApexMeshContractor::replaceVertex(int32_t t, uint32_t vOld, uint32_t vNew) +{ + if (t < 0 || 3 * t + 2 >= (int32_t)mIndices.size()) + { + return; + } + const uint32_t i = 3 * (uint32_t)t; + if (mIndices[i + 0] == vOld) + { + mIndices[i + 0] = vNew; + } + if (mIndices[i + 1] == vOld) + { + mIndices[i + 1] = vNew; + } + if (mIndices[i + 2] == vOld) + { + mIndices[i + 2] = vNew; + } +} + + + +void ApexMeshContractor::replaceNeighbor(int32_t t, int32_t nOld, uint32_t nNew) +{ + if (t < 0 || 3 * t + 2 >= (int32_t)mNeighbours.size()) + { + return; + } + const uint32_t i = 3 * (uint32_t)t; + if (mNeighbours[i + 0] == nOld) + { + mNeighbours[i + 0] = (int32_t)nNew; + } + if (mNeighbours[i + 1] == nOld) + { + mNeighbours[i + 1] = (int32_t)nNew; + } + if (mNeighbours[i + 2] == nOld) + { + mNeighbours[i + 2] = (int32_t)nNew; + } +} + + +bool ApexMeshContractor::triangleContains(int32_t t, uint32_t v) const +{ + if (t < 0 || 3 * t + 2 >= (int32_t)mIndices.size()) + { + return false; + } + const uint32_t i = 3 * (uint32_t)t; + return mIndices[i] == v || mIndices[i + 1] == v || mIndices[i + 2] == v; +} + + + +bool ApexMeshContractor::legalCollapse(int32_t triNr, uint32_t v0, uint32_t v1) const +{ + int32_t adj, t0, t1, t2, t3; + getButterfly((uint32_t)triNr, v0, v1, adj, t0, t1, t2, t3); + + // both of the end vertices must be completely surrounded by triangles + int32_t prev = triNr; + int32_t t = t0; + while (t >= 0) + { + advanceAdjTriangle(v0, t, prev); + if (t == triNr) + { + break; + } + } + + if (t != triNr) + { + return false; + } + + prev = triNr; + t = t1; + while (t >= 0) + { + advanceAdjTriangle(v1, t, prev); + if (t == triNr) + { + break; + } + } + if (t != triNr) + { + return false; + } + + // not legal if there exists v != v0,v1 with (v0,v) and (v1,v) edges + // but (v,v0,v1) is not a triangle + + prev = triNr; + t = t1; + int adjV = getOppositeVertex(triNr, v0, v1); + while (t >= 0) + { + if (t != t1) + { + int prev2 = prev; + int t2 = t; + while (t2 >= 0) + { + if (triangleContains(t2, v0)) + { + return false; + } + advanceAdjTriangle((uint32_t)adjV, t2, prev2); + if (t2 == t) + { + break; + } + } + } + adjV = getOppositeVertex(t, v1, (uint32_t)adjV); + advanceAdjTriangle(v1, t, prev); + if (t == triNr || t == adj) + { + break; + } + } + + if (areNeighbors(t0, t1)) + { + return false; + } + if (areNeighbors(t2, t3)) + { + return false; + } + return true; +} + + + +void ApexMeshContractor::advanceAdjTriangle(uint32_t v, int32_t& t, int32_t& prev) const +{ + int32_t next = -1; + for (uint32_t i = 0; i < 3; i++) + { + int32_t n = mNeighbours[3 * t + i]; + if (n >= 0 && n != prev && triangleContains(n, v)) + { + next = n; + } + } + prev = t; + t = next; +} + + + +bool ApexMeshContractor::areNeighbors(int32_t t0, int32_t t1) const +{ + if (t0 < 0 || 3 * t0 + 2 >= (int32_t)mNeighbours.size()) + { + return false; + } + const uint32_t i = 3 * (uint32_t)t0; + return mNeighbours[i] == t1 || mNeighbours[i + 1] == t1 || mNeighbours[i + 2] == t1; +} + + + +float ApexMeshContractor::findMin(const PxVec3& p, const PxVec3& maxDisp) const +{ + const uint32_t numSteps = 20; + const float dt = 1.0f / numSteps; + + float minDist = PxAbs(interpolateDistanceAt(p)); + float minT = 0.0f; + + for (uint32_t i = 1; i < numSteps; i++) + { + const float t = i * dt; + float dist = PxAbs(interpolateDistanceAt(p + maxDisp * t)); + if (dist < minDist) + { + minT = t; + // PH: isn't this missing? + //minDist = dist; + } + } + return minT; +} + + + +float ApexMeshContractor::interpolateDistanceAt(const PxVec3& pos) const +{ + const PxVec3 p = pos - mOrigin; + const float h = mCellSize; + const float h1 = 1.0f / h; + + int32_t x = (int32_t)PxFloor(p.x * h1); + const float dx = (p.x - h * x) * h1; + const float ex = 1.0f - dx; + int32_t y = (int32_t)PxFloor(p.y * h1); + const float dy = (p.y - h * y) * h1; + const float ey = 1.0f - dy; + int32_t z = (int32_t)PxFloor(p.z * h1); + const float dz = (p.z - h * z) * h1; + const float ez = 1.0f - dz; + + if (x < 0) + { + x = 0; + } + if (x + 1 >= (int32_t)mNumX) + { + x = (int32_t)mNumX - 2; // todo: handle boundary correctly + } + if (y < 0) + { + y = 0; + } + if (y + 1 >= (int32_t)mNumY) + { + y = (int32_t)mNumY - 2; + } + if (z < 0) + { + z = 0; + } + if (z + 1 >= (int32_t)mNumZ) + { + z = (int32_t)mNumZ - 2; + } + + float d0, d1, d2, d3, d4, d5, d6, d7; + d0 = constCellAt(x, y, z).distance; + d4 = constCellAt(x + 1, y, z).distance; + d1 = constCellAt(x, y + 1, z).distance; + d5 = constCellAt(x + 1, y + 1, z).distance; + d2 = constCellAt(x, y + 1, z + 1).distance; + d6 = constCellAt(x + 1, y + 1, z + 1).distance; + d3 = constCellAt(x, y, z + 1).distance; + d7 = constCellAt(x + 1, y, z + 1).distance; + + float dist = ex * (d0 * ey * ez + d1 * dy * ez + d2 * dy * dz + d3 * ey * dz) + + dx * (d4 * ey * ez + d5 * dy * ez + d6 * dy * dz + d7 * ey * dz); + + return dist; +} + + + + +struct DistTriangle +{ + int32_t triNr; + float dist; + bool operator < (const DistTriangle& t) const + { + return dist > t.dist; + } +}; + +void ApexMeshContractor::collectNeighborhood(int32_t triNr, float radius, uint32_t newMark, physx::Array<int32_t> &tris, physx::Array<float> &dists, uint32_t* triMarks) const +{ + tris.clear(); + dists.clear(); + ApexBinaryHeap<DistTriangle> heap; + + DistTriangle dt; + dt.triNr = triNr; + dt.dist = 0.0f; + heap.push(dt); + + while (!heap.isEmpty()) + { + heap.pop(dt); + + if (triMarks[dt.triNr] == newMark) + { + continue; + } + triMarks[dt.triNr] = newMark; + + tris.pushBack(dt.triNr); + dists.pushBack(-dt.dist); + PxVec3 center; + getTriangleCenter(dt.triNr, center); + + for (uint32_t i = 0; i < 3; i++) + { + int32_t adj = mNeighbours[3 * dt.triNr + i]; + if (adj < 0) + { + continue; + } + if (triMarks[adj] == newMark) + { + continue; + } + + PxVec3 adjCenter; + getTriangleCenter(adj, adjCenter); + DistTriangle adjDt; + adjDt.triNr = adj; + adjDt.dist = dt.dist - (adjCenter - center).magnitude(); // it is a max heap, we need the smallest dist first + //adjDt.dist = adjCenter.distance(center) - dt.dist; // PH: inverting heap distance, now it's a min heap + if (-adjDt.dist > radius) + { + continue; + } + + heap.push(adjDt); + } + } +} + + + +void ApexMeshContractor::getTriangleCenter(int32_t triNr, PxVec3& center) const +{ + const PxVec3& p0 = mVertices[mIndices[3 * (uint32_t)triNr + 0]]; + const PxVec3& p1 = mVertices[mIndices[3 * (uint32_t)triNr + 1]]; + const PxVec3& p2 = mVertices[mIndices[3 * (uint32_t)triNr + 2]]; + center = (p0 + p1 + p2) / 3.0f; +} + +//------------------------------------------------------------------------------------ +float ApexMeshContractor::curvatureAt(int triNr, int v) +{ + int prev = -1; + int t = triNr; + PxVec3 n0, n1; + float minDot = 1.0f; + while (t >= 0) + { + advanceAdjTriangle((uint32_t)v, t, prev); + if (t < 0 || t == triNr) + { + break; + } + + PxVec3& p0 = mVertices[mIndices[3 * (uint32_t)prev]]; + PxVec3& p1 = mVertices[mIndices[3 * (uint32_t)prev + 1]]; + PxVec3& p2 = mVertices[mIndices[3 * (uint32_t)prev + 2]]; + n0 = (p1 - p0).cross(p2 - p0); + n0.normalize(); + + PxVec3& q0 = mVertices[mIndices[3 * (uint32_t)t]]; + PxVec3& q1 = mVertices[mIndices[3 * (uint32_t)t + 1]]; + PxVec3& q2 = mVertices[mIndices[3 * (uint32_t)t + 2]]; + n1 = (q1 - q0).cross(q2 - q0); + n1.normalize(); + float dot = n0.dot(n1); + if (dot < minDot) + { + minDot = dot; + } + } + return (1.0f - minDot) * 0.5f; +} + +} +} // end namespace nvidia::apex diff --git a/APEX_1.4/common/src/ApexMeshHash.cpp b/APEX_1.4/common/src/ApexMeshHash.cpp new file mode 100644 index 00000000..36f40791 --- /dev/null +++ b/APEX_1.4/common/src/ApexMeshHash.cpp @@ -0,0 +1,297 @@ +/* + * 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. + */ + + +#include "ApexMeshHash.h" + +#include "PxBounds3.h" + +#include "PsSort.h" + +namespace nvidia +{ +namespace apex +{ + +ApexMeshHash::ApexMeshHash() : + mHashIndex(NULL) +{ + mHashIndex = (MeshHashRoot*)PX_ALLOC(sizeof(MeshHashRoot) * HashIndexSize, PX_DEBUG_EXP("ApexMeshHash")); + mTime = 1; + for (uint32_t i = 0; i < HashIndexSize; i++) + { + mHashIndex[i].first = -1; + mHashIndex[i].timeStamp = 0; + } + mSpacing = 0.25f; + mInvSpacing = 1.0f / mSpacing; +} + + + +ApexMeshHash::~ApexMeshHash() +{ + if (mHashIndex != NULL) + { + PX_FREE(mHashIndex); + mHashIndex = NULL; + } +} + + + +void ApexMeshHash::setGridSpacing(float spacing) +{ + mSpacing = spacing; + mInvSpacing = 1.0f / spacing; + reset(); +} + + + +void ApexMeshHash::reset() +{ + mTime++; + mEntries.clear(); +} + + + +void ApexMeshHash::add(const PxBounds3& bounds, uint32_t itemIndex) +{ + int32_t x1, y1, z1; + int32_t x2, y2, z2; + cellCoordOf(bounds.minimum, x1, y1, z1); + cellCoordOf(bounds.maximum, x2, y2, z2); + MeshHashEntry entry; + entry.itemIndex = itemIndex; + + for (int32_t x = x1; x <= x2; x++) + { + for (int32_t y = y1; y <= y2; y++) + { + for (int32_t z = z1; z <= z2; z++) + { + uint32_t h = hashFunction(x, y, z); + MeshHashRoot& r = mHashIndex[h]; + uint32_t n = mEntries.size(); + if (r.timeStamp != mTime || r.first < 0) + { + entry.next = -1; + } + else + { + entry.next = r.first; + } + r.first = (int32_t)n; + r.timeStamp = mTime; + mEntries.pushBack(entry); + } + } + } +} + + + +void ApexMeshHash::add(const PxVec3& pos, uint32_t itemIndex) +{ + int x, y, z; + cellCoordOf(pos, x, y, z); + MeshHashEntry entry; + entry.itemIndex = itemIndex; + + uint32_t h = hashFunction(x, y, z); + MeshHashRoot& r = mHashIndex[h]; + uint32_t n = mEntries.size(); + if (r.timeStamp != mTime || r.first < 0) + { + entry.next = -1; + } + else + { + entry.next = r.first; + } + r.first = (int32_t)n; + r.timeStamp = mTime; + mEntries.pushBack(entry); +} + + + +void ApexMeshHash::query(const PxBounds3& bounds, physx::Array<uint32_t>& itemIndices, int32_t maxIndices) +{ + int32_t x1, y1, z1; + int32_t x2, y2, z2; + cellCoordOf(bounds.minimum, x1, y1, z1); + cellCoordOf(bounds.maximum, x2, y2, z2); + itemIndices.clear(); + + for (int32_t x = x1; x <= x2; x++) + { + for (int32_t y = y1; y <= y2; y++) + { + for (int32_t z = z1; z <= z2; z++) + { + uint32_t h = hashFunction(x, y, z); + MeshHashRoot& r = mHashIndex[h]; + if (r.timeStamp != mTime) + { + continue; + } + int32_t i = r.first; + while (i >= 0) + { + MeshHashEntry& entry = mEntries[(uint32_t)i]; + itemIndices.pushBack(entry.itemIndex); + if (maxIndices >= 0 && itemIndices.size() >= (uint32_t)maxIndices) + { + return; + } + i = entry.next; + } + } + } + } +} + + + +void ApexMeshHash::queryUnique(const PxBounds3& bounds, physx::Array<uint32_t>& itemIndices, int32_t maxIndices) +{ + query(bounds, itemIndices, maxIndices); + compressIndices(itemIndices); +} + + + +void ApexMeshHash::query(const PxVec3& pos, physx::Array<uint32_t>& itemIndices, int32_t maxIndices) +{ + int32_t x, y, z; + cellCoordOf(pos, x, y, z); + itemIndices.clear(); + + uint32_t h = hashFunction(x, y, z); + MeshHashRoot& r = mHashIndex[h]; + if (r.timeStamp != mTime) + { + return; + } + int32_t i = r.first; + while (i >= 0) + { + MeshHashEntry& entry = mEntries[(uint32_t)i]; + itemIndices.pushBack(entry.itemIndex); + if (maxIndices >= 0 && itemIndices.size() >= (uint32_t)maxIndices) + { + return; + } + i = entry.next; + } +} + + + +void ApexMeshHash::queryUnique(const PxVec3& pos, physx::Array<uint32_t>& itemIndices, int32_t maxIndices) +{ + query(pos, itemIndices, maxIndices); + compressIndices(itemIndices); +} + + +class U32Less +{ +public: + bool operator()(uint32_t u1, uint32_t u2) const + { + return u1 < u2; + } +}; + + +void ApexMeshHash::compressIndices(physx::Array<uint32_t>& itemIndices) +{ + if (itemIndices.empty()) + { + return; + } + + shdfnd::sort(itemIndices.begin(), itemIndices.size(), U32Less()); + + // mark duplicates + uint32_t i = 0; + while (i < itemIndices.size()) + { + uint32_t j = i + 1; + while (j < itemIndices.size() && itemIndices[i] == itemIndices[j]) + { + itemIndices[j] = (uint32_t) - 1; + j++; + } + i = j; + } + + // remove duplicates + i = 0; + while (i < itemIndices.size()) + { + if (itemIndices[i] == (uint32_t)-1) + { + itemIndices.replaceWithLast(i); + } + else + { + i++; + } + } +} + +int32_t ApexMeshHash::getClosestPointNr(const PxVec3* points, uint32_t numPoints, uint32_t pointStride, const PxVec3& pos) +{ + PX_ASSERT(numPoints > 0); + PxBounds3 queryBounds; + queryBounds.minimum = pos; + queryBounds.maximum = pos; + PX_ASSERT(!queryBounds.isEmpty()); + queryBounds.fattenFast(mSpacing); + query(queryBounds, mTempIndices); + + // remove false positives due to hash collisions + uint32_t next = 0; + for (uint32_t i = 0; i < mTempIndices.size(); i++) + { + uint32_t pointNr = mTempIndices[i]; + const PxVec3* p = (PxVec3*)((uint8_t*)points + (pointNr * pointStride)); + if (pointNr < numPoints && queryBounds.contains(*p)) + { + mTempIndices[next++] = pointNr; + } + } + mTempIndices.resize(next); + bool fallBack = mTempIndices.size() == 0; + uint32_t numRes = fallBack ? numPoints : mTempIndices.size(); + + float min2 = 0.0f; + int minNr = -1; + for (uint32_t j = 0; j < numRes; j++) + { + uint32_t k = fallBack ? j : mTempIndices[j]; + const PxVec3* p = (PxVec3*)((uint8_t*)points + (k * pointStride)); + float d2 = (pos - *p).magnitudeSquared(); + if (minNr < 0 || d2 < min2) + { + min2 = d2; + minNr = (int32_t)k; + } + } + return minNr; +} + +} +} // end namespace nvidia::apex diff --git a/APEX_1.4/common/src/ApexPreview.cpp b/APEX_1.4/common/src/ApexPreview.cpp new file mode 100644 index 00000000..f2dc53c6 --- /dev/null +++ b/APEX_1.4/common/src/ApexPreview.cpp @@ -0,0 +1,49 @@ +/* + * 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. + */ + + +#include "Apex.h" +#include "ApexPreview.h" + +namespace nvidia +{ +namespace apex +{ + +ApexPreview::ApexPreview() : + mInRelease(false), + mPose(physx::PxIdentity) +{ +} + +ApexPreview::~ApexPreview() +{ + destroy(); +} + +void ApexPreview::setPose(const PxMat44& pose) +{ + mPose = pose; +} + +const PxMat44 ApexPreview::getPose() const +{ + return mPose; +} + +void ApexPreview::destroy() +{ + mInRelease = true; + + renderDataLock(); +} + +} +} // end namespace nvidia::apex diff --git a/APEX_1.4/common/src/ApexPvdClient.cpp b/APEX_1.4/common/src/ApexPvdClient.cpp new file mode 100644 index 00000000..95d24512 --- /dev/null +++ b/APEX_1.4/common/src/ApexPvdClient.cpp @@ -0,0 +1,378 @@ +/* + * 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. + */ + + +#include "ApexPvdClient.h" + +#ifndef WITHOUT_PVD + +#include "Module.h" +#include "ApexSDKIntl.h" +#include "ModuleIntl.h" + +#include "PxPvd.h" +#include "PxPvdUserRenderer.h" +#include "PxPvdDataStream.h" +#include "PxPvdTransport.h" +#include "PxPvdObjectModelBaseTypes.h" + +#include "PxAllocatorCallback.h" +#include "PsUserAllocated.h" + +#include "ModuleFrameworkRegistration.h" + +#include "PVDParameterizedHandler.h" + +#define INIT_PVD_CLASSES_PARAMETERIZED( parameterizedClassName ) { \ + pvdStream.createClass(NamespacedName(APEX_PVD_NAMESPACE, #parameterizedClassName)); \ + parameterizedClassName* params = DYNAMIC_CAST(parameterizedClassName*)(GetInternalApexSDK()->getParameterizedTraits()->createNvParameterized(#parameterizedClassName)); \ + mParameterizedHandler->initPvdClasses(*params->rootParameterDefinition(), #parameterizedClassName); \ + params->destroy(); } + +// NOTE: the PvdDataStream is not threadsafe. + +using namespace nvidia; +using namespace nvidia::shdfnd; +using namespace physx::pvdsdk; + +struct SimpleAllocator : public PxAllocatorCallback +{ + virtual void* allocate(size_t size, const char* typeName, const char* filename, int line) + { + PX_UNUSED(filename); + PX_UNUSED(line); + PX_UNUSED(typeName); + + // Ensure that we don't use a tracking allocation so that we don't get infinite recursion + return physx::shdfnd::getAllocator().allocate(size, typeName, filename, line); + } + + virtual void deallocate(void* ptr) + { + physx::shdfnd::getAllocator().deallocate(ptr); + } +}; +static SimpleAllocator sAllocator; + +class ApexPvdClientImpl : public UserAllocated, public ApexPvdClient, public PxAllocatorCallback +{ + PX_NOCOPY(ApexPvdClientImpl) + + PsPvd* mPvd; + bool mIsConnected; + Array<void*> mInstances; + PvdDataStream* mDataStream; + PvdUserRenderer* mRenderer; + PvdParameterizedHandler* mParameterizedHandler; + +public: + static const char* getApexPvdClientNamespace() { return "PVD.ApexPvdClient"; } + static const NamespacedName getApexPvdClientNamespacedName() + { + static NamespacedName instanceNamespace(APEX_PVD_NAMESPACE, "scene"); + return instanceNamespace; + } + + ApexPvdClientImpl( PxPvd* inPvd ) + : mDataStream( NULL ) + , mRenderer( NULL ) + , mParameterizedHandler( NULL ) + , mIsConnected(false) + { + mPvd = static_cast<PsPvd*>(inPvd); + if (mPvd) + { + mPvd->addClient(this); + } + } + + virtual ~ApexPvdClientImpl() + { + if (mPvd) + { + mPvd->removeClient(this); + } + } + + virtual PxPvd& getPxPvd() + { + return *mPvd; + } + + virtual bool isConnected() const + { + return mIsConnected; + } + + virtual void onPvdConnected() + { + if(mIsConnected || !mPvd) + return; + + mIsConnected = true; + mDataStream = PvdDataStream::create(mPvd); + mRenderer = PvdUserRenderer::create(); + + mParameterizedHandler = PX_NEW(PvdParameterizedHandler)(*mDataStream); + + if (mPvd->getInstrumentationFlags() & PxPvdInstrumentationFlag::eDEBUG) + { + initPvdClasses(); + initPvdInstances(); + } + + //Setting the namespace ensure that our class definition *can't* collide with + //someone else's. It doesn't protect against instance ids colliding which is why + //people normally use memory addresses casted to unsigned 64 bit numbers for those. + + //mDataStream->setNamespace( getApexPvdClientNamespace() ); + //mDataStream->createClass( "ApexPvdClient", 1 ); + //mDataStream->defineProperty( 1, "Frame", "", PvdCommLayerDatatype::Section, 1 ); + mDataStream->createClass( getApexPvdClientNamespacedName() ); + } + + virtual void onPvdDisconnected() + { + if(!mIsConnected) + return; + mIsConnected = false; + + if ( mParameterizedHandler != NULL ) + { + PX_DELETE(mParameterizedHandler); + mParameterizedHandler = NULL; + } + + if ( mDataStream != NULL ) + { + mDataStream->release(); + mDataStream = NULL; + } + + if ( mRenderer != NULL ) + { + mRenderer->release(); + mRenderer = NULL; + } + + mInstances.clear(); + } + + virtual void flush() + { + } + + void* ensureInstance( void* inInstance ) + { + uint32_t instCount = mInstances.size(); + for ( uint32_t idx = 0; idx < instCount; ++idx ) + { + if ( mInstances[idx] == inInstance ) + return inInstance; + } + if ( mDataStream ) + { + mDataStream->createInstance(getApexPvdClientNamespacedName(), inInstance); + } + mInstances.pushBack( inInstance ); + return inInstance; + } + + virtual void beginFrame( void* inInstance ) + { + if ( mDataStream == NULL ) return; + mDataStream->beginSection( ensureInstance( inInstance ), "ApexFrame"); + } + + virtual void endFrame( void* inInstance ) + { + if ( mDataStream == NULL ) return; + //Flush the outstanding memory events. PVD in some situations tracks memory events + //and can display graphs of memory usage at certain points. They have to get flushed + //at some point... + + //getConnectionManager().flushMemoryEvents(); + + //Also note that PVD is a consumer of the profiling system events. This ensures + //that PVD gets a view of the profiling events that pertained to the last frame. + + + //mDataStream->setPropertyValue( ensureInstance( inInstance ), 1, createSection( SectionType::End ) ); + PxPvdTransport* transport = mPvd->getTransport(); + if ( transport ) + { + //Flushes memory and profiling events out of any buffered areas. + transport->flush(); + } + + mDataStream->endSection( ensureInstance( inInstance ), "ApexFrame"); + + //flush our data to the main connection + //and flush the main connection. + //This could be an expensive call. + //mDataStream->flush(); + mRenderer->flushRenderEvents(); + } + + + //destroy this instance; + virtual void release() + { + PX_DELETE( this ); + } + + virtual void* allocate(size_t size, const char* typeName, const char* filename, int line) + { + PX_UNUSED(filename); + PX_UNUSED(line); + PX_UNUSED(typeName); + return PX_ALLOC(size, typeName); + } + + virtual void deallocate(void* ptr) + { + PX_FREE(ptr); + } + + virtual PvdDataStream* getDataStream() + { + //PX_ASSERT(mIsLocked); + return mDataStream; + } + + virtual PvdUserRenderer* getUserRender() + { + //PX_ASSERT(mIsLocked); + return mRenderer; + } + + virtual void initPvdClasses() + { + //PX_ASSERT(mIsLocked); + + // ApexSDK + NamespacedName apexSdkName = NamespacedName(APEX_PVD_NAMESPACE, "ApexSDK"); + mDataStream->createClass(apexSdkName); + mDataStream->createProperty(apexSdkName, "Platform", "", getPvdNamespacedNameForType<const char*>(), PropertyType::Scalar); + mDataStream->createProperty(apexSdkName, "Modules", "", getPvdNamespacedNameForType<ObjectRef>(), PropertyType::Array); + + // init framework parameterized classes + PvdDataStream& pvdStream = *mDataStream; + INIT_PVD_CLASSES_PARAMETERIZED(VertexFormatParameters); + INIT_PVD_CLASSES_PARAMETERIZED(VertexBufferParameters); + INIT_PVD_CLASSES_PARAMETERIZED(SurfaceBufferParameters); + INIT_PVD_CLASSES_PARAMETERIZED(SubmeshParameters); + INIT_PVD_CLASSES_PARAMETERIZED(RenderMeshAssetParameters); + INIT_PVD_CLASSES_PARAMETERIZED(BufferU8x1); + INIT_PVD_CLASSES_PARAMETERIZED(BufferU8x2); + INIT_PVD_CLASSES_PARAMETERIZED(BufferU8x3); + INIT_PVD_CLASSES_PARAMETERIZED(BufferU8x4); + INIT_PVD_CLASSES_PARAMETERIZED(BufferU16x1); + INIT_PVD_CLASSES_PARAMETERIZED(BufferU16x2); + INIT_PVD_CLASSES_PARAMETERIZED(BufferU16x3); + INIT_PVD_CLASSES_PARAMETERIZED(BufferU16x4); + INIT_PVD_CLASSES_PARAMETERIZED(BufferU32x1); + INIT_PVD_CLASSES_PARAMETERIZED(BufferU32x2); + INIT_PVD_CLASSES_PARAMETERIZED(BufferU32x3); + INIT_PVD_CLASSES_PARAMETERIZED(BufferU32x4); + INIT_PVD_CLASSES_PARAMETERIZED(BufferF32x1); + INIT_PVD_CLASSES_PARAMETERIZED(BufferF32x2); + INIT_PVD_CLASSES_PARAMETERIZED(BufferF32x3); + INIT_PVD_CLASSES_PARAMETERIZED(BufferF32x4); + + // module classes + ApexSDKIntl* niApexSDK = GetInternalApexSDK(); + uint32_t numModules = niApexSDK->getNbModules(); + for (uint32_t i = 0; i < numModules; ++i) + { + ModuleIntl* niModule = niApexSDK->getInternalModules()[i]; + Module* nxModule = niApexSDK->getModules()[i]; + + NamespacedName moduleName; + moduleName.mNamespace = APEX_PVD_NAMESPACE; + moduleName.mName = nxModule->getName(); + mDataStream->createClass(moduleName); + + niModule->initPvdClasses(*mDataStream); + } + } + + virtual void initPvdInstances() + { + //PX_ASSERT(mIsLocked); + + ApexSDK* apexSdk = GetApexSDK(); + mDataStream->createInstance( NamespacedName(APEX_PVD_NAMESPACE, "ApexSDK"), apexSdk); + + // set platform name + NvParameterized::SerializePlatform platform; + apexSdk->getCurrentPlatform(platform); + const char* platformName = apexSdk->getPlatformName(platform); + mDataStream->setPropertyValue(apexSdk, "Platform", platformName); + + mDataStream->setIsTopLevelUIElement(apexSdk, true); + + // add module instances + uint32_t numModules = apexSdk->getNbModules(); + ApexSDKIntl* niApexSDK = GetInternalApexSDK(); + for (uint32_t i = 0; i < numModules; ++i) + { + // init pvd instances + Module* nxModule = apexSdk->getModules()[i]; + ModuleIntl* niModule = niApexSDK->getInternalModules()[i]; + + NamespacedName moduleName; + moduleName.mNamespace = APEX_PVD_NAMESPACE; + moduleName.mName = nxModule->getName(); + mDataStream->createInstance(moduleName, nxModule); + mDataStream->pushBackObjectRef(apexSdk, "Modules", nxModule); + + niModule->initPvdInstances(*mDataStream); + } + } + + + virtual void initPvdClasses(const NvParameterized::Definition& paramsHandle, const char* pvdClassName) + { + //PX_ASSERT(mIsLocked); + + if (mParameterizedHandler != NULL) + { + mParameterizedHandler->initPvdClasses(paramsHandle, pvdClassName); + } + } + + + virtual void updatePvd(const void* pvdInstance, NvParameterized::Interface& params, PvdAction::Enum pvdAction) + { + //PX_ASSERT(mIsLocked); + + if (mParameterizedHandler != NULL) + { + NvParameterized::Handle paramsHandle(params); + mParameterizedHandler->updatePvd(pvdInstance, paramsHandle, pvdAction); + } + } +}; + + +ApexPvdClient* ApexPvdClient::create( PxPvd* inPvd ) +{ + return PX_NEW( ApexPvdClientImpl) ( inPvd ); +} + +#else + +physx::pvdsdk::ApexPvdClient* physx::pvdsdk::ApexPvdClient::create( physx::PxPvd& ) +{ + return NULL; +} + +#endif // WITHOUT_PVD
\ No newline at end of file diff --git a/APEX_1.4/common/src/ApexQuadricSimplifier.cpp b/APEX_1.4/common/src/ApexQuadricSimplifier.cpp new file mode 100644 index 00000000..56b5ea27 --- /dev/null +++ b/APEX_1.4/common/src/ApexQuadricSimplifier.cpp @@ -0,0 +1,1073 @@ +/* + * 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. + */ + + +#include "Apex.h" +#include "ApexQuadricSimplifier.h" + +#include "ApexSharedUtils.h" + +#define MERGE_THRESHOLD 1.0e-6f + +// PH: Verification code, must be set to 0 unless you're debugging this code +#define TESTING 0 + +namespace nvidia +{ +namespace apex +{ + +ApexQuadricSimplifier::ApexQuadricSimplifier() : mNumDeletedTriangles(0), mNumDeletedVertices(0), mNumDeletedHeapElements(0) +{ + mBounds.setEmpty(); +} + + + +ApexQuadricSimplifier::~ApexQuadricSimplifier() +{ + clear(); +} + + + +void ApexQuadricSimplifier::clear() +{ + for (uint32_t i = 0; i < mVertices.size(); i++) + { + delete mVertices[i]; + } + + mVertices.clear(); + mEdges.clear(); + mTriangles.clear(); + mHeap.clear(); + mVertexRefs.clear(); + mBounds.setEmpty(); + + mNumDeletedTriangles = 0; + mNumDeletedVertices = 0; + mNumDeletedHeapElements = 0; +} + + + +void ApexQuadricSimplifier::registerVertex(const PxVec3& pos) +{ + mVertices.pushBack(PX_NEW(QuadricVertex)(pos)); + mBounds.include(pos); +} + + + +void ApexQuadricSimplifier::registerTriangle(uint32_t v0, uint32_t v1, uint32_t v2) +{ + const uint32_t numVertices = mVertices.size(); + PX_ASSERT(v0 < numVertices); + PX_ASSERT(v1 < numVertices); + PX_ASSERT(v2 < numVertices); + PX_UNUSED(numVertices); + + QuadricTriangle t; + t.init(v0, v1, v2); + uint32_t tNr = mTriangles.size(); + mVertices[t.vertexNr[0]]->mTriangles.pushBack(tNr); + mVertices[t.vertexNr[0]]->bReferenced = 1; + mVertices[t.vertexNr[1]]->mTriangles.pushBack(tNr); + mVertices[t.vertexNr[1]]->bReferenced = 1; + mVertices[t.vertexNr[2]]->mTriangles.pushBack(tNr); + mVertices[t.vertexNr[2]]->bReferenced = 1; + mTriangles.pushBack(t); +} + + + +bool ApexQuadricSimplifier::endRegistration(bool mergeCloseVertices, IProgressListener* progressListener) +{ + HierarchicalProgressListener progress(100, progressListener); + + if (mergeCloseVertices) + { + progress.setSubtaskWork(20, "Merge Vertices"); + mergeVertices(); + progress.completeSubtask(); + } + + // remove unreferenced vertices + for (uint32_t i = 0; i < mVertices.size(); i++) + { + if (mVertices[i]->bReferenced == 0) + { + mVertices[i]->bDeleted = 1; + mNumDeletedVertices++; + } + } + progress.setSubtaskWork(20, "Init Edge List"); + + mEdges.clear(); + for (uint32_t i = 0; i < mTriangles.size(); i++) + { + QuadricTriangle& t = mTriangles[i]; + QuadricVertex* qv0 = mVertices[t.vertexNr[0]]; + QuadricVertex* qv1 = mVertices[t.vertexNr[1]]; + QuadricVertex* qv2 = mVertices[t.vertexNr[2]]; + Quadric q; + q.setFromPlane(qv0->pos, qv1->pos, qv2->pos); + qv0->q += q; + qv1->q += q; + qv2->q += q; + QuadricEdge e; + e.init((int32_t)t.vertexNr[0], (int32_t)t.vertexNr[1]); + mEdges.pushBack(e); + e.init((int32_t)t.vertexNr[1], (int32_t)t.vertexNr[2]); + mEdges.pushBack(e); + e.init((int32_t)t.vertexNr[2], (int32_t)t.vertexNr[0]); + mEdges.pushBack(e); + } + + progress.completeSubtask(); + + // remove duplicated edges + progress.setSubtaskWork(10, "Sort Edges"); + quickSortEdges(0, (int32_t)mEdges.size() - 1); + progress.completeSubtask(); + + progress.setSubtaskWork(10, "Process Edges"); + + physx::Array<QuadricEdge> edges2; + uint32_t i = 0; + while (i < mEdges.size()) + { + QuadricEdge& e = mEdges[i]; + edges2.pushBack(e); + i++; + + bool border = true; + while (i < mEdges.size() && mEdges[i] == e) + { + i++; + border = false; + } + if (border) + { + edges2.back().border = true; + mVertices[e.vertexNr[0]]->bBorder = 1; + mVertices[e.vertexNr[1]]->bBorder = 1; + } + } + progress.completeSubtask(); + + + + + progress.setSubtaskWork(10, "Init Edges"); + + mEdges.clear(); + mEdges.resize(edges2.size()); + + for (i = 0; i < edges2.size(); i++) + { + mEdges[i] = edges2[i]; + QuadricEdge& e = mEdges[i]; + computeCost(e); + mVertices[e.vertexNr[0]]->mEdges.pushBack(i); + mVertices[e.vertexNr[1]]->mEdges.pushBack(i); + } + + progress.completeSubtask(); + + progress.setSubtaskWork(10, "Insert Heap"); + + // make heap + uint32_t num = mEdges.size(); + mHeap.clear(); + mHeap.pushBack(NULL); // dummy, root must be at position 1! + for (i = 0; i < num; i++) + { + mHeap.pushBack(&mEdges[i]); + mEdges[i].heapPos = (int32_t)i + 1; + } + + progress.completeSubtask(); + + progress.setSubtaskWork(mergeCloseVertices ? 20 : 40, "Create Heap"); + + for (i = mHeap.size() >> 1; i >= 1; i--) + { + heapSift(i); + } + + progress.completeSubtask(); + + mNumDeletedTriangles = 0; + mNumDeletedHeapElements = 0; + return true; +} + + + +uint32_t ApexQuadricSimplifier::simplify(uint32_t subdivision, int32_t maxSteps, float maxError, IProgressListener* progressListener) +{ + float maxLength = 0.0f; + + uint32_t nbCollapsed = 0; + + if (subdivision > 0) + { + maxLength = (mBounds.minimum - mBounds.maximum).magnitude() / subdivision; + } + + uint32_t progressCounter = 0; + uint32_t maximum = maxSteps >= 0 ? maxSteps : mHeap.size(); + + HierarchicalProgressListener progress(100, progressListener); + progress.setSubtaskWork(90, "Isomesh simplicifaction"); +#if TESTING + testHeap(); +#endif + + while (maxSteps == -1 || (maxSteps-- > 0)) + { + + if ((++progressCounter & 0xff) == 0) + { + const int32_t percent = (int32_t)(100 * progressCounter / maximum); + progress.setProgress(percent); + } + + bool edgeFound = false; + QuadricEdge* e = NULL; + while (mHeap.size() - mNumDeletedHeapElements > 1) + { + e = mHeap[1]; + + if (maxError >= 0 && e->cost > maxError) + { + // get me out of here + edgeFound = false; + break; + } + + if (legalCollapse(*e, maxLength)) + { + heapRemove(1, false); +#if TESTING + testHeap(); +#endif + edgeFound = true; + break; + } + uint32_t vNr0 = e->vertexNr[0]; + uint32_t vNr1 = e->vertexNr[1]; + QuadricVertex* qv0 = mVertices[vNr0]; + QuadricVertex* qv1 = mVertices[vNr1]; + heapRemove(1, qv0->bDeleted == 0 && qv1->bDeleted == 0); +#if TESTING + testHeap(); +#endif + } + + if (!edgeFound) + { + break; + } + + collapseEdge(*e); + nbCollapsed++; + } + + progress.completeSubtask(); + progress.setSubtaskWork(10, "Heap rebuilding"); + + progressCounter = mNumDeletedHeapElements; + while (mNumDeletedHeapElements > 0) + { + if ((mNumDeletedHeapElements & 0x7f) == 0) + { + const int32_t percent = (int32_t)(100 * (progressCounter - mNumDeletedHeapElements) / progressCounter); + progress.setProgress(percent); + } +#if TESTING + testHeap(); +#endif + mNumDeletedHeapElements--; + heapUpdate(mHeap.size() - 1 - mNumDeletedHeapElements); + } + + progress.completeSubtask(); +#if TESTING + testHeap(); +#endif + return nbCollapsed; +} + + + +int32_t ApexQuadricSimplifier::getTriangleNr(uint32_t v0, uint32_t v1, uint32_t v2) const +{ + uint32_t num = mVertices.size(); + + if (v0 >= num || v1 >= num || v2 >= num) + { + return -1; + } + + QuadricVertex* qv0 = mVertices[v0]; + + if (qv0->bDeleted == 1) + { + return -1; + } + + for (unsigned i = 0; i < qv0->mTriangles.size(); i++) + { + const QuadricTriangle& t = mTriangles[qv0->mTriangles[i]]; + if (!t.containsVertex(v1) || !t.containsVertex(v2)) + { + continue; + } + + return (int32_t)qv0->mTriangles[i]; + } + + return -1; +} + + + +bool ApexQuadricSimplifier::getTriangle(uint32_t i, uint32_t& v0, uint32_t& v1, uint32_t& v2) const +{ + if (i >= mTriangles.size()) + { + return false; + } + + const QuadricTriangle& t = mTriangles[i]; + if (t.deleted) + { + return false; + } + + v0 = t.vertexNr[0]; + v1 = t.vertexNr[1]; + v2 = t.vertexNr[2]; + return true; +} + + + + + + + + +void ApexQuadricSimplifier::computeCost(QuadricEdge& edge) +{ + const uint32_t numSteps = 10; + + QuadricVertex* qv0 = mVertices[edge.vertexNr[0]]; + QuadricVertex* qv1 = mVertices[edge.vertexNr[1]]; + + edge.cost = FLT_MAX; + edge.lengthSquared = (qv0->pos - qv1->pos).magnitudeSquared(); + edge.ratio = -1.0f; + + Quadric q; + q = qv0->q + qv1->q; + + float sumCost = 0; + for (uint32_t i = 0; i <= numSteps; i++) + { + const float ratio = 1.0f / numSteps * i; + const PxVec3 pos = qv0->pos * (1.0f - ratio) + qv1->pos * ratio; + + const float cost = PxAbs(q.outerProduct(pos)); + sumCost += cost; + if (cost < edge.cost) + { + edge.cost = cost; + edge.ratio = ratio; + } + } + + if (sumCost < 0.0001f) + { + edge.cost = 0; + edge.ratio = 0.5f; + } +} + + + +bool ApexQuadricSimplifier::legalCollapse(QuadricEdge& edge, float maxLength) +{ + uint32_t vNr0 = edge.vertexNr[0]; + uint32_t vNr1 = edge.vertexNr[1]; + QuadricVertex* qv0 = mVertices[vNr0]; + QuadricVertex* qv1 = mVertices[vNr1]; + + // here we make sure that the border does not shrink + uint32_t numBorder = 0; + if (qv0->bBorder == 1) + { + numBorder++; + } + if (qv1->bBorder == 1) + { + numBorder++; + } + if (numBorder == 1) + { + return false; + } + + if (qv0->bDeleted == 1 || qv1->bDeleted == 1) + { + return false; + } + + // this is a test to make sure edges don't get too long + if (maxLength != 0.0f) + { + if ((qv0->pos - qv1->pos).magnitudeSquared() > maxLength * maxLength) + { + return false; + } + } + + // not legal if there exists v != v0,v1 with (v0,v) and (v1,v) edges + // but (v,v0,v1) is not a triangle + + for (uint32_t i = 0; i < qv0->mEdges.size(); i++) + { + uint32_t v = mEdges[qv0->mEdges[i]].otherVertex(vNr0); + bool edgeFound = false; + for (uint32_t j = 0; j < qv1->mEdges.size(); j++) + { + if (mEdges[qv1->mEdges[j]].otherVertex(vNr1) == v) + { + edgeFound = true; + break; + } + } + if (!edgeFound) + { + continue; + } + + bool triFound = false; + for (uint32_t j = 0; j < qv0->mTriangles.size(); j++) + { + QuadricTriangle& t = mTriangles[qv0->mTriangles[j]]; + if (t.containsVertex(vNr0) && t.containsVertex(vNr1) && t.containsVertex(v)) + { + triFound = true; + break; + } + } + if (!triFound) + { + return false; + } + + } + + // any triangle flips? + PxVec3 newPos = qv0->pos * (1.0f - edge.ratio) + qv1->pos * edge.ratio; + for (uint32_t i = 0; i < 2; i++) + { + QuadricVertex* v = (i == 0 ? qv0 : qv1); + for (uint32_t j = 0; j < v->mTriangles.size(); j++) + { + QuadricTriangle& t = mTriangles[v->mTriangles[j]]; + if (t.deleted) + { + continue; + } + if (t.containsVertex(vNr0) && t.containsVertex(vNr1)) // this one will be deleted + { + continue; + } + PxVec3 p[3], q[3]; + for (uint32_t k = 0; k < 3; k++) + { + p[k] = mVertices[t.vertexNr[k]]->pos; + if (t.vertexNr[k] == vNr0 || t.vertexNr[k] == vNr1) + { + q[k] = newPos; + } + else + { + q[k] = p[k]; + } + } + PxVec3 n0 = (p[1] - p[0]).cross(p[2] - p[0]); + // n0.normalize(); // not needed for 90 degree checks + PxVec3 n1 = (q[1] - q[0]).cross(q[2] - q[0]); + //n1.normalize(); // not needed for 90 degree checks + if (n0.dot(n1) < 0.0f) + { + return false; + } + } + } + + return true; +} + + + +void ApexQuadricSimplifier::collapseEdge(QuadricEdge& edge) +{ + uint32_t vNr0 = edge.vertexNr[0]; + uint32_t vNr1 = edge.vertexNr[1]; + QuadricVertex* qv0 = mVertices[vNr0]; + QuadricVertex* qv1 = mVertices[vNr1]; + + PX_ASSERT(qv0->bDeleted == 0); + PX_ASSERT(qv1->bDeleted == 0); + + //FILE* f = NULL; + //fopen_s(&f, "c:\\collapse.txt", "a"); + //fprintf_s(f, "Collapse Vertex %d -> %d\n", vNr1, vNr0); + + // contract edge to the vertex0 + qv0->pos = qv0->pos * (1.0f - edge.ratio) + qv1->pos * edge.ratio; + qv0->q += qv1->q; + + // merge the edges + for (uint32_t i = 0; i < qv1->mEdges.size(); i++) + { + QuadricEdge& ei = mEdges[qv1->mEdges[i]]; + uint32_t vi = ei.otherVertex(vNr1); + if (vi == vNr0) + { + continue; + } + + // test whether we already have this neighbor + bool found = false; + for (uint32_t j = 0; j < qv0->mEdges.size(); j++) + { + QuadricEdge& ej = mEdges[qv0->mEdges[j]]; + if (ej.otherVertex(vNr0) == vi) + { + found = true; + break; + } + } + if (found) + { + mVertices[vi]->removeEdge((int32_t)qv1->mEdges[i]); + ei.deleted = true; + if (ei.heapPos >= 0) + { + heapRemove((uint32_t)ei.heapPos, false); + } +#if TESTING + testHeap(); +#endif + } + else + { + ei.replaceVertex(vNr1, vNr0); + qv0->mEdges.pushBack(qv1->mEdges[i]); + } + } + // remove common edge and update adjacent edges + for (int32_t i = (int32_t)qv0->mEdges.size() - 1; i >= 0; i--) + { + QuadricEdge& ei = mEdges[qv0->mEdges[(uint32_t)i]]; + if (ei.otherVertex(vNr0) == vNr1) + { + qv0->mEdges.replaceWithLast((uint32_t)i); + } + else + { + computeCost(ei); + if (ei.heapPos >= 0) + { + heapUpdate((uint32_t)ei.heapPos); + } +#if TESTING + testHeap(); +#endif + } + } + + // delete collapsed triangles + for (int32_t i = (int32_t)qv0->mTriangles.size() - 1; i >= 0; i--) + { + uint32_t triangleIndex = qv0->mTriangles[(uint32_t)i]; + QuadricTriangle& t = mTriangles[triangleIndex]; + if (!t.deleted && t.containsVertex(vNr1)) + { + mNumDeletedTriangles++; + t.deleted = true; + //fprintf_s(f, "Delete Triangle %d\n", triangleIndex); + + PX_ASSERT(t.containsVertex(vNr0)); + + for (uint32_t j = 0; j < 3; j++) + { + mVertices[t.vertexNr[j]]->removeTriangle((int32_t)triangleIndex); + //fprintf_s(f, " v %d\n", t.vertexNr[j]); + } + } + } + // update triangles + for (uint32_t i = 0; i < qv1->mTriangles.size(); i++) + { + QuadricTriangle& t = mTriangles[qv1->mTriangles[i]]; + if (t.deleted) + { + continue; + } + if (t.containsVertex(vNr1)) + { + qv0->mTriangles.pushBack(qv1->mTriangles[i]); + } + t.replaceVertex(vNr1, vNr0); + } + + mNumDeletedVertices += qv1->bDeleted == 1 ? 0 : 1; + qv1->bDeleted = 1; + edge.deleted = true; + //fclose(f); + +#if TESTING + testMesh(); + testHeap(); +#endif +} + + + +void ApexQuadricSimplifier::quickSortEdges(int32_t l, int32_t r) +{ + uint32_t i, j, mi; + QuadricEdge k, m; + + i = (uint32_t)l; + j = (uint32_t)r; + mi = (uint32_t)(l + r) / 2; + m = mEdges[mi]; + while (i <= j) + { + while (mEdges[i] < m) + { + i++; + } + + while (m < mEdges[j]) + { + j--; + } + + if (i <= j) + { + k = mEdges[i]; + mEdges[i] = mEdges[j]; + mEdges[j] = k; + i++; + j--; + } + } + + if (l < (int32_t)j) + { + quickSortEdges(l, (int32_t)j); + } + + if ((int32_t)i < r) + { + quickSortEdges((int32_t)i, r); + } +} + + + +void ApexQuadricSimplifier::quickSortVertexRefs(int32_t l, int32_t r) +{ + uint32_t i, j, mi; + QuadricVertexRef k, m; + + i = (uint32_t)l; + j = (uint32_t)r; + mi = (uint32_t)(l + r) / 2; + m = mVertexRefs[mi]; + while (i <= j) + { + while (mVertexRefs[i] < m) + { + i++; + } + + while (m < mVertexRefs[j]) + { + j--; + } + + if (i <= j) + { + k = mVertexRefs[i]; + mVertexRefs[i] = mVertexRefs[j]; + mVertexRefs[j] = k; + i++; + j--; + } + } + + if (l < (int32_t)j) + { + quickSortVertexRefs(l, (int32_t)j); + } + + if ((int32_t)i < r) + { + quickSortVertexRefs((int32_t)i, r); + } +} + + + +void ApexQuadricSimplifier::mergeVertices() +{ + float d = (mBounds.minimum - mBounds.maximum).magnitude() * MERGE_THRESHOLD; + float d2 = d * d; + mVertexRefs.clear(); + QuadricVertexRef vr; + + for (uint32_t i = 0; i < mVertices.size(); i++) + { + QuadricVertex* v = mVertices[i]; + vr.init(v->pos, (int32_t)i); + mVertexRefs.pushBack(vr); + } + + quickSortVertexRefs(0, (int32_t)mVertexRefs.size() - 1); + + for (uint32_t i = 0; i < mVertexRefs.size() - 1; i++) + { + uint32_t iNr = mVertexRefs[i].vertexNr; + QuadricVertex* vi = mVertices[iNr]; + + if (vi->bDeleted == 1) + { + continue; + } + + PxVec3 pos = mVertexRefs[i].pos; + uint32_t j = i + 1; + while (j < mVertexRefs.size() && fabs(mVertexRefs[j].pos.x - pos.x) < MERGE_THRESHOLD) + { + if ((mVertexRefs[j].pos - pos).magnitudeSquared() < d2) + { + uint32_t jNr = mVertexRefs[j].vertexNr; + QuadricVertex* vj = mVertices[jNr]; + for (uint32_t k = 0; k < vj->mTriangles.size(); k++) + { + mTriangles[vj->mTriangles[k]].replaceVertex(jNr, iNr); + vi->addTriangle((int32_t)vj->mTriangles[k]); + } + mNumDeletedVertices += vj->bDeleted == 1 ? 0 : 1; + vj->bDeleted = 1; + } + j++; + } + } +} + + + +bool ApexQuadricSimplifier::heapElementSmaller(QuadricEdge* e0, QuadricEdge* e1) +{ + // if both costs are 0 use edge length + if (e0->cost == 0 && e1->cost == 0) + { + return e0->lengthSquared < e1->lengthSquared; + } + + const float costDiff = e0->cost - e1->cost; + + if (costDiff != 0.0f) + { + return costDiff < 0; + } + + // else use edge length + return e0->lengthSquared < e1->lengthSquared; +} + + + +void ApexQuadricSimplifier::heapUpdate(uint32_t i) +{ + const uint32_t num = mHeap.size() - mNumDeletedHeapElements; + PX_ASSERT(1 <= i && i < num); + QuadricEdge* e = mHeap[i]; + while (i > 1) + { + uint32_t j = i >> 1; + if (heapElementSmaller(e, mHeap[j])) + { + mHeap[i] = mHeap[j]; + mHeap[i]->heapPos = (int32_t)i; + i = j; + } + else + { + break; + } + } + + while ((i << 1) < num) + { + uint32_t j = i << 1; + if (j + 1 < num && heapElementSmaller(mHeap[j + 1], mHeap[j])) + { + j++; + } + + if (heapElementSmaller(mHeap[j], e)) + { + mHeap[i] = mHeap[j]; + mHeap[i]->heapPos = (int32_t)i; + i = j; + } + else + { + break; + } + } + mHeap[i] = e; + mHeap[i]->heapPos = (int32_t)i; +} + + + +void ApexQuadricSimplifier::heapSift(uint32_t i) +{ + const uint32_t num = mHeap.size() - mNumDeletedHeapElements; + PX_ASSERT(1 <= i && i < num); + QuadricEdge* e = mHeap[i]; + while ((i << 1) < num) + { + uint32_t j = i << 1; + if (j + 1 < num && heapElementSmaller(mHeap[j + 1], mHeap[j])) + { + j++; + } + + if (heapElementSmaller(mHeap[j], e)) + { + mHeap[i] = mHeap[j]; + mHeap[i]->heapPos = (int32_t)i; + i = j; + } + else + { + break; + } + } + + mHeap[i] = e; + mHeap[i]->heapPos = (int32_t)i; +} + + + +void ApexQuadricSimplifier::heapRemove(uint32_t i, bool append) +{ + const uint32_t num = mHeap.size() - mNumDeletedHeapElements; + PX_ASSERT(1 <= i && i < num); + QuadricEdge* e = mHeap[i]; + mHeap[i]->heapPos = -1; + mHeap[i] = mHeap[num - 1]; + if (append) + { + mHeap[num - 1] = e; + mNumDeletedHeapElements++; + } + else if (mNumDeletedHeapElements > 0) + { + mHeap[num - 1] = mHeap.back(); + mHeap[num - 1]->heapPos = -1; + mHeap.popBack(); + } + else + { + mHeap.popBack(); + } + + PX_ASSERT(e->heapPos == -1); + if (i < num - 1) + { + mHeap[i]->heapPos = (int32_t)i; + heapUpdate(i); + } + PX_ASSERT(e->heapPos == -1); +} + + + +void ApexQuadricSimplifier::testHeap() +{ + const uint32_t num = mHeap.size() - mNumDeletedHeapElements; + for (uint32_t i = 1; i < num; i++) + { + PX_ASSERT(mHeap[i]->heapPos == (int32_t) i); + if ((i << 1) < num) + { + uint32_t j = i << 1; + if (j + 1 < num && heapElementSmaller(mHeap[j + 1], mHeap[j])) + { + j++; + } + PX_ASSERT(!heapElementSmaller(mHeap[j], mHeap[i])); + } + } + + for (uint32_t i = num; i < mHeap.size(); i++) + { + PX_ASSERT(mHeap[i]->heapPos == -1); + } +} + + + +void ApexQuadricSimplifier::testMesh() +{ + for (uint32_t i = 0; i < mVertices.size(); i++) + { + QuadricVertex* v = mVertices[i]; + if (v->bDeleted == 1) + { + continue; + } + + for (uint32_t j = 0; j < v->mEdges.size(); j++) + { + uint32_t eNr = v->mEdges[j]; + PX_ASSERT(eNr < mEdges.size()); + QuadricEdge& e = mEdges[eNr]; + PX_ASSERT(e.vertexNr[0] == i || e.vertexNr[1] == i); + PX_ASSERT(!e.deleted); + PX_UNUSED(e); + } + + for (uint32_t j = 0; j < (uint32_t)v->mTriangles.size(); j++) + { + uint32_t tNr = v->mTriangles[j]; + PX_ASSERT(tNr < mTriangles.size()); + QuadricTriangle& t = mTriangles[tNr]; + PX_ASSERT(t.vertexNr[0] == i || t.vertexNr[1] == i || t.vertexNr[2] == i); + PX_ASSERT(!t.deleted); + PX_UNUSED(t); + } + } + + for (uint32_t i = 0; i < mEdges.size(); i++) + { + QuadricEdge& e = mEdges[i]; + if (e.deleted) + { + continue; + } + + for (uint32_t j = 0; j < 2; j++) + { + uint32_t vNr = e.vertexNr[j]; + PX_ASSERT(vNr < mVertices.size()); + QuadricVertex* v = mVertices[vNr]; + PX_ASSERT(v->bDeleted == 0); + uint32_t found = 0; + for (uint32_t k = 0; k < v->mEdges.size(); k++) + { + found += (v->mEdges[k] == i) ? 1 : 0; + } + + PX_ASSERT(found == 1); + } + } + for (uint32_t i = 0; i < mTriangles.size(); i++) + { + QuadricTriangle& t = mTriangles[i]; + if (t.deleted) + { + continue; + } + + for (uint32_t j = 0; j < 3; j++) + { + uint32_t vNr = t.vertexNr[j]; + PX_ASSERT(vNr < mVertices.size()); + QuadricVertex* v = mVertices[vNr]; + PX_ASSERT(v->bDeleted == 0); + uint32_t found = 0; + for (uint32_t k = 0; k < v->mTriangles.size(); k++) + { + found += (v->mTriangles[k] == i) ? 1 : 0; + } + + PX_ASSERT(found == 1); + } + } +} + + + + +// --- Quadric Vertex ----------------------------------------- + +void ApexQuadricSimplifier::QuadricVertex::removeEdge(int32_t edgeNr) +{ + for (int32_t i = (int32_t)mEdges.size() - 1; i >= 0; i--) + { + if (mEdges[(uint32_t)i] == (uint32_t) edgeNr) + { + mEdges.replaceWithLast((uint32_t)i); + } + } +} + + + +void ApexQuadricSimplifier::QuadricVertex::addTriangle(int32_t triangleNr) +{ + for (uint32_t i = 0; i < mTriangles.size(); i++) + { + if (mTriangles[i] == (uint32_t) triangleNr) + { + return; + } + } + mTriangles.pushBack((uint32_t)triangleNr); +} + + + +void ApexQuadricSimplifier::QuadricVertex::removeTriangle(int32_t triangleNr) +{ + uint32_t found = 0; + for (int32_t i = (int32_t)mTriangles.size() - 1; i >= 0; i--) + { + if (mTriangles[(uint32_t)i] == (uint32_t) triangleNr) + { + mTriangles.replaceWithLast((uint32_t)i); + found++; + } + } + PX_ASSERT(found == 1); +} + +} +} // end namespace nvidia::apex + diff --git a/APEX_1.4/common/src/ApexRWLockable.cpp b/APEX_1.4/common/src/ApexRWLockable.cpp new file mode 100644 index 00000000..280a640b --- /dev/null +++ b/APEX_1.4/common/src/ApexRWLockable.cpp @@ -0,0 +1,176 @@ +/* + * 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. + */ + +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ApexRWLockable.h" +#include "PsThread.h" +#include "PsAtomic.h" +#include "ApexSDKIntl.h" + +namespace nvidia +{ +namespace apex +{ + +ApexRWLockable::ApexRWLockable() + : + mEnabled (true) + , mCurrentWriter(0) + , mRWLock() + , mConcurrentWriteCount (0) + , mConcurrentReadCount (0) + , mConcurrentErrorCount (0) +{ +} + +ApexRWLockable::~ApexRWLockable() +{ +} + +void ApexRWLockable::setEnabled(bool e) +{ + mEnabled = e; +} + +bool ApexRWLockable::isEnabled() const +{ + return mEnabled; +} + +void ApexRWLockable::acquireReadLock(const char*, uint32_t) const +{ + mRWLock.lockReader(); +} + +void ApexRWLockable::acquireWriteLock(const char*, uint32_t) const +{ + mRWLock.lockWriter(); + mCurrentWriter = Thread::getId(); +} + +void ApexRWLockable::releaseReadLock(void) const +{ + mRWLock.unlockReader(); +} + +void ApexRWLockable::releaseWriteLock(void) const +{ + mCurrentWriter = 0; + mRWLock.unlockWriter(); +} + +bool ApexRWLockable::startWrite(bool allowReentry) +{ + PX_COMPILE_TIME_ASSERT(sizeof(ThreadReadWriteCount) == 4); + mDataLock.lock(); + bool error = false; + + ThreadReadWriteCount& rwc = mData[Thread::getId()]; + // check that we are the only thread reading (this allows read->write order on a single thread) + error |= mConcurrentReadCount != rwc.counters.readDepth; + + // check no other threads are writing + error |= mConcurrentWriteCount != rwc.counters.writeDepth; + + // increment shared write counter + shdfnd::atomicIncrement(&mConcurrentWriteCount); + + // in the normal case (re-entry is allowed) then we simply increment + // the writeDepth by 1, otherwise (re-entry is not allowed) increment + // by 2 to force subsequent writes to fail by creating a mismatch between + // the concurrent write counter and the local counter, any value > 1 will do + if (allowReentry) + rwc.counters.writeDepth++; + else + rwc.counters.writeDepth += 2; + mDataLock.unlock(); + + if (error) + shdfnd::atomicIncrement(&mConcurrentErrorCount); + + return !error; +} + +void ApexRWLockable::stopWrite(bool allowReentry) +{ + shdfnd::atomicDecrement(&mConcurrentWriteCount); + + // decrement depth of writes for this thread + mDataLock.lock(); + nvidia::ThreadImpl::Id id = nvidia::Thread::getId(); + + // see comment in startWrite() + if (allowReentry) + mData[id].counters.writeDepth--; + else + mData[id].counters.writeDepth -= 2; + + mDataLock.unlock(); +} + +nvidia::Thread::Id ApexRWLockable::getCurrentWriter() const +{ + return mCurrentWriter; +} + +bool ApexRWLockable::startRead() const +{ + shdfnd::atomicIncrement(&mConcurrentReadCount); + + mDataLock.lock(); + nvidia::ThreadImpl::Id id = nvidia::Thread::getId(); + ThreadReadWriteCount& rwc = mData[id]; + rwc.counters.readDepth++; + bool success = (rwc.counters.writeDepth > 0 || mConcurrentWriteCount == 0); + mDataLock.unlock(); + + if (!success) + shdfnd::atomicIncrement(&mConcurrentErrorCount); + + return success; +} + +void ApexRWLockable::stopRead() const +{ + shdfnd::atomicDecrement(&mConcurrentReadCount); + mDataLock.lock(); + nvidia::ThreadImpl::Id id = nvidia::Thread::getId(); + mData[id].counters.readDepth--; + mDataLock.unlock(); +} + +uint32_t ApexRWLockable::getReadWriteErrorCount() const +{ + return static_cast<uint32_t>(mConcurrentErrorCount); +} + +ApexRWLockableScopedDisable::ApexRWLockableScopedDisable(RWLockable* rw) : mLockable(static_cast<ApexRWLockable*>(rw)) +{ + mLockable->setEnabled(false); +} + +ApexRWLockableScopedDisable::~ApexRWLockableScopedDisable() +{ + mLockable->setEnabled(true); +} + +ApexRWLockableScopedDisable::ApexRWLockableScopedDisable(const ApexRWLockableScopedDisable& o) : mLockable(o.mLockable) +{ +} + +ApexRWLockableScopedDisable& ApexRWLockableScopedDisable::operator=(const ApexRWLockableScopedDisable&) +{ + return *this; +} + +} +}
\ No newline at end of file diff --git a/APEX_1.4/common/src/ApexResource.cpp b/APEX_1.4/common/src/ApexResource.cpp new file mode 100644 index 00000000..e0e0aa3d --- /dev/null +++ b/APEX_1.4/common/src/ApexResource.cpp @@ -0,0 +1,36 @@ +/* + * 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. + */ + + +#include "ApexResource.h" +#include "ApexSDKHelpers.h" + +namespace nvidia +{ +namespace apex +{ + +ApexResource::~ApexResource() +{ + removeSelf(); +} + +void ApexResource::removeSelf() +{ + if (m_list && m_listIndex != 0xFFFFFFFF) + { + m_list->remove(m_listIndex); + } + m_list = NULL; + m_listIndex = 0xFFFFFFFF; +} + +} // namespace apex +} // namespace nvidia diff --git a/APEX_1.4/common/src/ApexSDKCachedDataImpl.cpp b/APEX_1.4/common/src/ApexSDKCachedDataImpl.cpp new file mode 100644 index 00000000..8212c653 --- /dev/null +++ b/APEX_1.4/common/src/ApexSDKCachedDataImpl.cpp @@ -0,0 +1,116 @@ +/* + * 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. + */ + + +#include "ApexSDKCachedDataImpl.h" +#include "nvparameterized/NvParameterized.h" + +namespace nvidia +{ +namespace apex +{ + +bool ApexSDKCachedDataImpl::registerModuleDataCache(ModuleCachedDataIntl* cache) +{ + if (cache == NULL) + { + return false; + } + + for (uint32_t i = 0; i < mModuleCaches.size(); ++i) + { + if (cache == mModuleCaches[i]) + { + return false; + } + } + + mModuleCaches.pushBack(cache); + + return true; +} + +bool ApexSDKCachedDataImpl::unregisterModuleDataCache(ModuleCachedDataIntl* cache) +{ + if (cache == NULL) + { + return false; + } + + for (uint32_t i = mModuleCaches.size(); i--;) + { + if (cache == mModuleCaches[i]) + { + mModuleCaches.replaceWithLast(i); + break; + } + } + + return false; +} + +ApexSDKCachedDataImpl::ApexSDKCachedDataImpl() +{ +} + +ApexSDKCachedDataImpl::~ApexSDKCachedDataImpl() +{ +} + +ModuleCachedData* ApexSDKCachedDataImpl::getCacheForModule(AuthObjTypeID moduleID) +{ + for (uint32_t i = 0; i < mModuleCaches.size(); ++i) + { + if (moduleID == mModuleCaches[i]->getModuleID()) + { + return mModuleCaches[i]; + } + } + + return NULL; +} + +PxFileBuf& ApexSDKCachedDataImpl::serialize(PxFileBuf& stream) const +{ + stream.storeDword((uint32_t)Version::Current); + + for (uint32_t i = 0; i < mModuleCaches.size(); ++i) + { + mModuleCaches[i]->serialize(stream); + } + + return stream; +} + +PxFileBuf& ApexSDKCachedDataImpl::deserialize(PxFileBuf& stream) +{ + clear(false); // false => don't delete cached data for referenced sets + + /*const uint32_t version =*/ + stream.readDword(); // Original version + + for (uint32_t i = 0; i < mModuleCaches.size(); ++i) + { + mModuleCaches[i]->deserialize(stream); + } + + return stream; +} + +void ApexSDKCachedDataImpl::clear(bool force) +{ + for (uint32_t i = mModuleCaches.size(); i--;) + { + mModuleCaches[i]->clear(force); + } +} + +} +} // end namespace nvidia::apex diff --git a/APEX_1.4/common/src/ApexSDKHelpers.cpp b/APEX_1.4/common/src/ApexSDKHelpers.cpp new file mode 100644 index 00000000..2cccff31 --- /dev/null +++ b/APEX_1.4/common/src/ApexSDKHelpers.cpp @@ -0,0 +1,310 @@ +/* + * 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. + */ + + +#include "Apex.h" +#include "ApexSDKIntl.h" +#include "ApexResource.h" +#include "ApexSDKHelpers.h" +#include "AuthorableObjectIntl.h" + +#include "ApexPvdClient.h" + +#ifndef WITHOUT_PVD +#include "PxPvdDataStream.h" +#endif + +namespace nvidia +{ +using namespace physx::pvdsdk; + +namespace apex +{ + +/* + ResourceList functions + */ +ResourceList::~ResourceList() +{ + clear(); +} + +void ResourceList::clear() +{ + ScopedWriteLock scopedLock(mRWLock); + + uint32_t s = mArray.size(); + while (s--) + { + mArray.back()->release(); + if (mArray.size() != s) + { + PX_ASSERT(!"Error - resource did not remove itself from list upon release\n"); + if (mArray.size()) + { + mArray.popBack(); // Force removal + } + } + } +} + +#ifndef WITHOUT_PVD +void ResourceList::setupForPvd(const void* owner, const char* listName, const char* entryName) +{ + mOwner = owner; + mListName = listName; + mEntryName = entryName; +} + + +void ResourceList::initPvdInstances(PvdDataStream& pvdStream) +{ + PX_UNUSED(pvdStream); + ScopedWriteLock scopedLock(mRWLock); + + for (uint32_t i = 0; i < mArray.size(); ++i) + { + const void* entry = (const void*)mArray[i]; + pvdStream.createInstance(NamespacedName(APEX_PVD_NAMESPACE, mEntryName.c_str()), entry); + pvdStream.pushBackObjectRef(mOwner, mListName.c_str(), entry); + mArray[i]->initPvdInstances(pvdStream); + } +} +#endif + +void ResourceList::add(ApexResourceInterface& resource) +{ + ScopedWriteLock scopedLock(mRWLock); + + if (resource.getListIndex() != 0xFFFFFFFF) + { + PX_ASSERT(!"Error - attempting to add a resource to a list twice"); + return; + } + resource.setListIndex(*this, mArray.size()); + mArray.pushBack(&resource); + + // add to pvd + if (mOwner != NULL) + { + ApexPvdClient* client = GetInternalApexSDK()->getApexPvdClient(); + if (client != NULL) + { + if (client->isConnected() && client->getPxPvd().getInstrumentationFlags() & PxPvdInstrumentationFlag::eDEBUG) + { + PvdDataStream* pvdStream = client->getDataStream(); + { + if (pvdStream != NULL) + { + pvdStream->createInstance(NamespacedName(APEX_PVD_NAMESPACE, mEntryName.c_str()), &resource); + pvdStream->pushBackObjectRef(mOwner, mListName.c_str(), &resource); + resource.initPvdInstances(*pvdStream); + } + } + } + } + } +} + +void ResourceList::remove(uint32_t index) +{ + ScopedWriteLock scopedLock(mRWLock); + + PX_ASSERT(index < mArray.size()); + + // remove from pvd + if (mOwner != NULL) + { + ApexPvdClient* client = GetInternalApexSDK()->getApexPvdClient(); + if (client != NULL) + { + if (client->isConnected() && client->getPxPvd().getInstrumentationFlags() & PxPvdInstrumentationFlag::eDEBUG) + { + PvdDataStream* pvdStream = client->getDataStream(); + { + if (pvdStream != NULL) + { + // would be nice to be able to call resource->destroyPvdInstances() here, + // but the resource has already been destroyed, so it's too late here + ApexResourceInterface* resource = mArray[index]; + pvdStream->removeObjectRef(mOwner, mListName.c_str(), resource); + pvdStream->destroyInstance(resource); + } + } + } + } + } + + mArray.replaceWithLast(index); + if (index < mArray.size()) + { + mArray[index]->setListIndex(*this, index); + } +} + +#if 0 +// these are poison +void writeStreamHeader(PxFileBuf& stream, ApexSimpleString& streamName, uint32_t versionStamp) +{ + uint32_t streamStamp = GetStamp(streamName); + + stream.storeDword(versionStamp); + stream.storeDword(streamStamp); +} + +uint32_t readStreamHeader(const PxFileBuf& stream, ApexSimpleString& streamName) +{ + uint32_t version = stream.readDword(); + + uint32_t streamStamp = stream.readDword(); + if (streamStamp != GetStamp(streamName)) + { + APEX_INVALID_PARAMETER("Wrong input stream. The provided stream has to contain %s.", streamName.c_str()); + return (uint32_t) - 1; + } + + return version; +} +#endif + +/****************************************************************************** + * Helper function for loading assets + * + * This method's purpose is to generalize this process: + * + * 1. Get the module's namespace resource ID (this also checks that the module is loaded) + * 2. If the asset has not been created yet, it will call createResource() + * 3. It will call getResource() and return the result + * + * This allows both the asset's forceLoad method AND the actors init methods to get + * an asset pointer. + * + * This also allows the forceLoad method to call this method repeatedly until getResource() + * returns a valid pointer (for async loading). + + *****************************************************************************/ +void* ApexAssetHelper::getAssetFromName(ApexSDKIntl* sdk, + const char* authoringTypeName, + const char* assetName, + ResID& inOutResID, + ResID optionalPsID) +{ + /* Get the NRP */ + ResourceProviderIntl* nrp = sdk->getInternalResourceProvider(); + + /* Get the asset namespace ID */ + ResID typePsID = INVALID_RESOURCE_ID; + if (optionalPsID == INVALID_RESOURCE_ID) + { + AuthorableObjectIntl* ao = sdk->getAuthorableObject(authoringTypeName); + if (ao) + { + typePsID = ao->getResID(); + } + else + { + APEX_INTERNAL_ERROR("Unknown authorable type: %s, please load all required modules.", authoringTypeName); + return NULL; + } + } + else + { + typePsID = optionalPsID; + } + + if (inOutResID == INVALID_RESOURCE_ID) + { + if (optionalPsID == sdk->getOpaqueMeshNameSpace()) + { + AuthorableObjectIntl* ao = sdk->getAuthorableObject(RENDER_MESH_AUTHORING_TYPE_NAME); + if (ao) + { + typePsID = ao->getResID(); + } + + ResID opaqueMesh = nrp->createResource(optionalPsID, assetName); + inOutResID = nrp->createResource(typePsID, assetName); + if (!nrp->checkResource(inOutResID)) + { + UserOpaqueMesh* om = (UserOpaqueMesh*)nrp->getResource(opaqueMesh); + Asset* asset = sdk->createAsset(assetName, om); + nrp->setResource(authoringTypeName, assetName, asset, true, false); + } + } + else + { + inOutResID = nrp->createResource(typePsID, assetName); + } + } + + // If resID is valid, get the resource + if (inOutResID != INVALID_RESOURCE_ID) + { + return nrp->getResource(inOutResID); + } + else + { + APEX_DEBUG_INFO("ApexAssetHelper::getAssetFromName: Could not create resource ID asset: %s", assetName); + return NULL; + } + +} + + + +/* getAssetFromNameList + * + * This method's purpose is to generalize this process: + * + * 1. Find the asset name in this asset type's name list + * 2. Call the SDK's helper method, getAssetFromName + * + * This allows both the asset's forceLoad method AND the actors init methods to get + * an asset pointer. + * + * This also allows the forceLoad method to call this method repeatedly until getResource() + * returns a valid pointer (for async loading). + */ +void* ApexAssetHelper::getAssetFromNameList(ApexSDKIntl* sdk, + const char* authoringTypeName, + physx::Array<AssetNameIDMapping*>& nameIdList, + const char* assetName, + ResID assetPsId) +{ + // find the index of the asset name in our list of name->resID maps + uint32_t assetIdx = 0; + for (assetIdx = 0; assetIdx < nameIdList.size(); assetIdx++) + { + if (nameIdList[assetIdx]->assetName == assetName) + { + break; + } + } + + // This can't ever happen + PX_ASSERT(assetIdx < nameIdList.size()); + + if (assetIdx < nameIdList.size()) + { + return ApexAssetHelper::getAssetFromName(sdk, + authoringTypeName, + assetName, + nameIdList[assetIdx]->resID, + assetPsId); + } + else + { + APEX_DEBUG_WARNING("Request for asset %s of type %s not registered in asset tracker's list", assetName, authoringTypeName); + return NULL; + } +} + +} +} // end namespace nvidia::apex diff --git a/APEX_1.4/common/src/ApexShape.cpp b/APEX_1.4/common/src/ApexShape.cpp new file mode 100644 index 00000000..30bb398d --- /dev/null +++ b/APEX_1.4/common/src/ApexShape.cpp @@ -0,0 +1,402 @@ +/* + * 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. + */ + + +#include "ApexShape.h" +#include "RenderDebugInterface.h" + +#include "PsMathUtils.h" + +namespace nvidia +{ +namespace apex +{ + +bool isBoundsEmpty(PxBounds3 bounds) +{ + return bounds.minimum.x >= bounds.maximum.x || bounds.minimum.y >= bounds.maximum.y || bounds.minimum.z >= bounds.maximum.z; +} + + +ApexSphereShape::ApexSphereShape() +{ + mRadius = 0.5; + mTransform4x4 = PxMat44(PxIdentity); + mOldTransform4x4 = PxMat44(PxIdentity); + calculateAABB(); +} + +void ApexSphereShape::setRadius(float radius) +{ + mRadius = radius; + calculateAABB(); +}; + +void ApexSphereShape::setPose(PxMat44 pose) +{ + mOldTransform4x4 = mTransform4x4; + mTransform4x4 = pose; + calculateAABB(); +}; + +void ApexSphereShape::calculateAABB() +{ + //find extreme points of the (transformed by mPose and mTranslation) sphere and construct an AABB from it + PxVec3 points[6]; + points[0] = PxVec3( mRadius, 0.0f, 0.0f); + points[1] = PxVec3(-mRadius, 0.0f, 0.0f); + points[2] = PxVec3(0.0f, mRadius, 0.0f); + points[3] = PxVec3(0.0f, -mRadius, 0.0f); + points[4] = PxVec3(0.0f, 0.0f, mRadius); + points[5] = PxVec3(0.0f, 0.0f, -mRadius); + + PxVec3 maxBounds(0.0f), minBounds(0.0f); + for (int i = 0; i < 6; i++) + { + PxVec3 tempPoint = mTransform4x4.transform(points[i]); + if (i == 0) + { + minBounds = maxBounds = tempPoint; + } + else + { + if (tempPoint.x < minBounds.x) + { + minBounds.x = tempPoint.x; + } + if (tempPoint.x > maxBounds.x) + { + maxBounds.x = tempPoint.x; + } + if (tempPoint.y < minBounds.y) + { + minBounds.y = tempPoint.y; + } + if (tempPoint.y > maxBounds.y) + { + maxBounds.y = tempPoint.y; + } + if (tempPoint.z < minBounds.z) + { + minBounds.z = tempPoint.z; + } + if (tempPoint.z > maxBounds.z) + { + maxBounds.z = tempPoint.z; + } + } + } + mBounds = PxBounds3(minBounds, maxBounds); +} + +bool ApexSphereShape::intersectAgainstAABB(PxBounds3 bounds) +{ + if (!isBoundsEmpty(mBounds) && !isBoundsEmpty(bounds)) //if the bounds have some real volume then check bounds intersection + { + return bounds.intersects(mBounds); + } + else + { + return false; //if the bounds had no volume then return false + } +} + +void ApexSphereShape::visualize(RenderDebugInterface* renderer) const +{ + const physx::PxMat44& savedPose = *RENDER_DEBUG_IFACE(renderer)->getPoseTyped(); + RENDER_DEBUG_IFACE(renderer)->setIdentityPose(); + RENDER_DEBUG_IFACE(renderer)->debugSphere(mTransform4x4.getPosition(), 2); + RENDER_DEBUG_IFACE(renderer)->setPose(savedPose); +} + +ApexCapsuleShape::ApexCapsuleShape() +{ + mRadius = 1.0f; + mHeight = 1.0f; + mTransform4x4 = PxMat44(PxIdentity); + mOldTransform4x4 = PxMat44(PxIdentity); + calculateAABB(); +} + +void ApexCapsuleShape::setDimensions(float height, float radius) +{ + mRadius = radius; + mHeight = height; + calculateAABB(); +}; + +void ApexCapsuleShape::setPose(PxMat44 pose) +{ + mOldTransform4x4 = mTransform4x4; + mTransform4x4 = pose; + calculateAABB(); +}; + +void ApexCapsuleShape::calculateAABB() +{ + //find extreme points of the (transformed by mPose and mTranslation) capsule and construct an AABB from it + PxVec3 points[6]; + points[0] = PxVec3(mRadius, 0.0f, 0.0f); + points[1] = PxVec3(-mRadius, 0.0f, 0.0f); + points[2] = PxVec3(0.0f, mRadius + mHeight / 2.0f, 0.0f); + points[3] = PxVec3(0.0f, -(mRadius + mHeight / 2.0f), 0.0f); + points[4] = PxVec3(0.0f, 0.0f, mRadius); + points[5] = PxVec3(0.0f, 0.0f, -mRadius); + + PxVec3 maxBounds(0.0f), minBounds(0.0f); + for (int i = 0; i < 6; i++) + { + PxVec3 tempPoint = mTransform4x4.transform(points[i]); + if (i == 0) + { + minBounds = maxBounds = tempPoint; + } + else + { + if (tempPoint.x < minBounds.x) + { + minBounds.x = tempPoint.x; + } + if (tempPoint.x > maxBounds.x) + { + maxBounds.x = tempPoint.x; + } + if (tempPoint.y < minBounds.y) + { + minBounds.y = tempPoint.y; + } + if (tempPoint.y > maxBounds.y) + { + maxBounds.y = tempPoint.y; + } + if (tempPoint.z < minBounds.z) + { + minBounds.z = tempPoint.z; + } + if (tempPoint.z > maxBounds.z) + { + maxBounds.z = tempPoint.z; + } + } + } + mBounds = PxBounds3(minBounds, maxBounds); +} + +bool ApexCapsuleShape::intersectAgainstAABB(PxBounds3 bounds) +{ + if (!isBoundsEmpty(mBounds) && !isBoundsEmpty(bounds)) //if the bounds have some real volume then check bounds intersection + { + return bounds.intersects(mBounds); + } + else + { + return false; //if the bounds had no volume then return false + } +} + +void ApexCapsuleShape::visualize(RenderDebugInterface* renderer) const +{ + const physx::PxMat44& savedPose = *RENDER_DEBUG_IFACE(renderer)->getPoseTyped(); + RENDER_DEBUG_IFACE(renderer)->setPose(&mTransform4x4.column0.x); + RENDER_DEBUG_IFACE(renderer)->debugCapsule(mRadius, mHeight); + RENDER_DEBUG_IFACE(renderer)->setPose(savedPose); +} + +ApexBoxShape::ApexBoxShape() +{ + mSize = PxVec3(1, 1, 1); + mTransform4x4 = PxMat44(PxIdentity); + mOldTransform4x4 = PxMat44(PxIdentity); + calculateAABB(); +} + +void ApexBoxShape::setSize(PxVec3 size) +{ + mSize = size; + calculateAABB(); +}; + +void ApexBoxShape::setPose(PxMat44 pose) +{ + mOldTransform4x4 = mTransform4x4; + mTransform4x4 = pose; + calculateAABB(); +}; + +void ApexBoxShape::calculateAABB() +{ + //find extreme points of the (transformed by mPose and mTranslation) box and construct an AABB from it + PxVec3 points[8]; + PxVec3 sizeHalf = PxVec3(mSize.x / 2.0f, mSize.y / 2.0f, mSize.z / 2.0f); + points[0] = PxVec3(sizeHalf.x, sizeHalf.y, sizeHalf.z); + points[1] = PxVec3(-sizeHalf.x, sizeHalf.y, sizeHalf.z); + points[2] = PxVec3(sizeHalf.x, -sizeHalf.y, sizeHalf.z); + points[3] = PxVec3(sizeHalf.x, sizeHalf.y, -sizeHalf.z); + points[4] = PxVec3(sizeHalf.x, -sizeHalf.y, -sizeHalf.z); + points[5] = PxVec3(-sizeHalf.x, -sizeHalf.y, sizeHalf.z); + points[6] = PxVec3(-sizeHalf.x, sizeHalf.y, -sizeHalf.z); + points[7] = PxVec3(-sizeHalf.x, -sizeHalf.y, -sizeHalf.z); + + PxVec3 maxBounds(0.0f), minBounds(0.0f); + for (int i = 0; i < 8; i++) + { + PxVec3 tempPoint = mTransform4x4.transform(points[i]); + if (i == 0) + { + minBounds = maxBounds = tempPoint; + } + else + { + if (tempPoint.x < minBounds.x) + { + minBounds.x = tempPoint.x; + } + if (tempPoint.x > maxBounds.x) + { + maxBounds.x = tempPoint.x; + } + if (tempPoint.y < minBounds.y) + { + minBounds.y = tempPoint.y; + } + if (tempPoint.y > maxBounds.y) + { + maxBounds.y = tempPoint.y; + } + if (tempPoint.z < minBounds.z) + { + minBounds.z = tempPoint.z; + } + if (tempPoint.z > maxBounds.z) + { + maxBounds.z = tempPoint.z; + } + } + } + mBounds = PxBounds3(minBounds, maxBounds); +} + +bool ApexBoxShape::intersectAgainstAABB(PxBounds3 bounds) +{ + if (!isBoundsEmpty(mBounds) && !isBoundsEmpty(bounds)) //if the bounds have some real volume then check bounds intersection + { + return bounds.intersects(mBounds); + } + else + { + return false; //if the bounds had no volume then return false + } +} + +void ApexBoxShape::visualize(RenderDebugInterface* renderer) const +{ + PxVec3 halfSize = mSize / 2; + const PxBounds3 bounds(-halfSize, halfSize); + const physx::PxMat44& savedPose = *RENDER_DEBUG_IFACE(renderer)->getPoseTyped(); + RENDER_DEBUG_IFACE(renderer)->setPose(&mTransform4x4.column0.x); + RENDER_DEBUG_IFACE(renderer)->debugBound(bounds); + RENDER_DEBUG_IFACE(renderer)->setPose(savedPose); +} + +bool ApexHalfSpaceShape::isPointInside(PxVec3 pos) +{ + PxVec3 vecToPos = pos - mOrigin; + if (mNormal.dot(vecToPos) < 0.0f) + { + return true; + } + return false; +} + +//if any of the corners of the bounds is inside then the bounds is inside +bool ApexHalfSpaceShape::intersectAgainstAABB(PxBounds3 bounds) +{ + PxVec3 center = bounds.getCenter(); + PxVec3 sizeHalf = bounds.getDimensions() / 2.0f; + + if (isPointInside(PxVec3(center.x + sizeHalf.x, center.y + sizeHalf.y, center.z + sizeHalf.z))) + { + return true; + } + if (isPointInside(PxVec3(center.x - sizeHalf.x, center.y + sizeHalf.y, center.z + sizeHalf.z))) + { + return true; + } + if (isPointInside(PxVec3(center.x + sizeHalf.x, center.y - sizeHalf.y, center.z + sizeHalf.z))) + { + return true; + } + if (isPointInside(PxVec3(center.x + sizeHalf.x, center.y + sizeHalf.y, center.z - sizeHalf.z))) + { + return true; + } + if (isPointInside(PxVec3(center.x + sizeHalf.x, center.y - sizeHalf.y, center.z - sizeHalf.z))) + { + return true; + } + if (isPointInside(PxVec3(center.x - sizeHalf.x, center.y - sizeHalf.y, center.z + sizeHalf.z))) + { + return true; + } + if (isPointInside(PxVec3(center.x - sizeHalf.x, center.y + sizeHalf.y, center.z - sizeHalf.z))) + { + return true; + } + if (isPointInside(PxVec3(center.x - sizeHalf.x, center.y - sizeHalf.y, center.z - sizeHalf.z))) + { + return true; + } + + return false; + +} + +ApexHalfSpaceShape::ApexHalfSpaceShape() +{ + mOrigin = mPreviousOrigin = mNormal = mPreviousNormal = PxVec3(0, 0, 0); +} + +void ApexHalfSpaceShape::setOriginAndNormal(PxVec3 origin, PxVec3 normal) +{ + mPreviousOrigin = mOrigin; + mOrigin = origin; + mPreviousNormal = mNormal; + mNormal = normal; +}; + +void ApexHalfSpaceShape::visualize(RenderDebugInterface* renderer) const +{ + float radius = 2 * mNormal.magnitude(); + const PxPlane plane(mOrigin, mNormal); + const physx::PxMat44& savedPose = *RENDER_DEBUG_IFACE(renderer)->getPoseTyped(); + RENDER_DEBUG_IFACE(renderer)->setIdentityPose(); + RENDER_DEBUG_IFACE(renderer)->debugPlane(plane, radius, radius); + RENDER_DEBUG_IFACE(renderer)->debugRay(mOrigin, mOrigin + mNormal); + RENDER_DEBUG_IFACE(renderer)->setPose(savedPose); +} + +static PX_INLINE PxMat44 OriginAndNormalToPose(const PxVec3& origin, const PxVec3& normal) +{ + return PxMat44(physx::shdfnd::rotFrom2Vectors(PxVec3(0, 1, 0), normal), origin); +} + +PxMat44 ApexHalfSpaceShape::getPose() const +{ + return OriginAndNormalToPose(mOrigin, mNormal); +} + +PxMat44 ApexHalfSpaceShape::getPreviousPose() const +{ + return OriginAndNormalToPose(mPreviousOrigin, mPreviousNormal); +} + +} +} // end namespace nvidia::apex + diff --git a/APEX_1.4/common/src/ApexSharedUtils.cpp b/APEX_1.4/common/src/ApexSharedUtils.cpp new file mode 100644 index 00000000..9cdc2a30 --- /dev/null +++ b/APEX_1.4/common/src/ApexSharedUtils.cpp @@ -0,0 +1,4177 @@ +/* + * 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. + */ + + +#include "Apex.h" +#include "ApexSharedUtils.h" +#include "ApexRand.h" +#include "PsMemoryBuffer.h" + +#if PX_PHYSICS_VERSION_MAJOR == 3 +#include "PxPhysics.h" +#include "cooking/PxConvexMeshDesc.h" +#include "cooking/PxCooking.h" +#include <PxShape.h> +#endif + +#include "PsSort.h" +#include "PsBitUtils.h" + +#include "ApexSDKIntl.h" + +#include "Cof44.h" +#include "PxPlane.h" + +#include "PsVecMath.h" +#include "PsMathUtils.h" + +#include "../../shared/general/stan_hull/include/StanHull.h" + +#define REDUCE_CONVEXHULL_INPUT_POINT_SET 0 + +namespace nvidia +{ +namespace apex +{ +// Local utilities + +// copied from //sw/physx/PhysXSDK/3.3/trunk/Samples/SampleBase/RenderClothActor.cpp +float det(PxVec4 v0, PxVec4 v1, PxVec4 v2, PxVec4 v3) +{ + const PxVec3& d0 = reinterpret_cast<const PxVec3&>(v0); + const PxVec3& d1 = reinterpret_cast<const PxVec3&>(v1); + const PxVec3& d2 = reinterpret_cast<const PxVec3&>(v2); + const PxVec3& d3 = reinterpret_cast<const PxVec3&>(v3); + + return v0.w * d1.cross(d2).dot(d3) + - v1.w * d0.cross(d2).dot(d3) + + v2.w * d0.cross(d1).dot(d3) + - v3.w * d0.cross(d1).dot(d2); +} + +PX_INLINE float det(const PxMat44& m) +{ + return det(m.column0, m.column1, m.column2, m.column3); +} + +PX_INLINE float extentDistance(float min0, float max0, float min1, float max1) +{ + return PxMax(min0 - max1, min1 - max0); +} + +PX_INLINE float extentDistanceAndNormalDirection(float min0, float max0, float min1, float max1, float& midpoint, bool& normalPointsFrom0to1) +{ + const float d01 = min1 - max0; + const float d10 = min0 - max1; + + normalPointsFrom0to1 = d01 > d10; + + if (normalPointsFrom0to1) + { + midpoint = 0.5f*(min1 + max0); + return d01; + } + else + { + midpoint = 0.5f*(min0 + max1); + return d10; + } +} + +PX_INLINE void extentOverlapTimeInterval(float& tIn, float& tOut, float vel0, float min0, float max0, float min1, float max1) +{ + if (PxAbs(vel0) < PX_EPS_F32) + { + // Not moving + if (extentDistance(min0, max0, min1, max1) < 0.0f) + { + // Always overlap + tIn = -PX_MAX_F32; + tOut = PX_MAX_F32; + } + else + { + // Never overlap + tIn = PX_MAX_F32; + tOut = -PX_MAX_F32; + } + return; + } + + const float recipVel0 = 1.0f / vel0; + + if (vel0 > 0.0f) + { + tIn = (min1 - max0) * recipVel0; + tOut = (max1 - min0) * recipVel0; + } + else + { + tIn = (max1 - min0) * recipVel0; + tOut = (min1 - max0) * recipVel0; + } +} + +PX_INLINE bool updateTimeIntervalAndNormal(float& in, float& out, float& tNormal, PxVec3* normal, float tIn, float tOut, const PxVec3& testNormal) +{ + if (tIn >= out || tOut <= in) + { + return false; // No intersection will occur + } + + if (normal != NULL && tIn > tNormal) + { + tNormal = tIn; + *normal = testNormal; + } + + in = PxMax(tIn, in); + out = PxMin(tOut, out); + + return true; +} + +PX_INLINE void getExtent(float& min, float& max, const void* points, uint32_t pointCount, const uint32_t pointByteStride, const PxVec3& normal) +{ + min = PX_MAX_F32; + max = -PX_MAX_F32; + const PxVec3* p = (const PxVec3*)points; + for (uint32_t i = 0; i < pointCount; ++i, p = (const PxVec3*)(((const uint8_t*)p) + pointByteStride)) + { + const float d = normal.dot(*p); + if (d < min) + { + min = d; + } + if (d > max) + { + max = d; + } + } +} + +PX_INLINE bool intersectPlanes(PxVec3& point, const PxPlane& p0, const PxPlane& p1, const PxPlane& p2) +{ + const PxVec3 p1xp2 = p1.n.cross(p2.n); + const float det = p0.n.dot(p1xp2); + if (PxAbs(det) < 1.0e-18) + { + return false; // No intersection + } + point = (-p0.d * p1xp2 - p1.d * (p2.n.cross(p0.n)) - p2.d * (p0.n.cross(p1.n))) / det; + return true; +} + +PX_INLINE bool pointInsidePlanes(const PxVec3& point, const PxPlane* planes, uint32_t numPlanes, float eps) +{ + for (uint32_t i = 0; i < numPlanes; ++i) + { + const PxPlane& plane = planes[i]; + if (plane.distance(point) > eps) + { + return false; + } + } + return true; +} + +static void findPrincipleComponents(PxVec3& mean, PxVec3& variance, PxMat33& axes, const PxVec3* points, uint32_t pointCount) +{ + // Find the average of the points + mean = PxVec3(0.0f); + for (uint32_t i = 0; i < pointCount; ++i) + { + mean += points[i]; + } + + PxMat33 cov = PxMat33(PxZero); + + if (pointCount > 0) + { + mean *= 1.0f/pointCount; + + // Now subtract the mean and add to the covariance matrix cov + for (uint32_t i = 0; i < pointCount; ++i) + { + const PxVec3 relativePoint = points[i] - mean; + for (uint32_t j = 0; j < 3; ++j) + { + for (uint32_t k = 0; k < 3; ++k) + { + cov(j,k) += relativePoint[j]*relativePoint[k]; + } + } + } + cov *= 1.0f/pointCount; + } + + // Now diagonalize cov + variance = diagonalizeSymmetric(axes, cov); +} + +#if 0 +/* John's k-means clustering function, slightly specialized */ +static uint32_t kmeans_cluster(const PxVec3* input, + uint32_t inputCount, + PxVec3* clusters, + uint32_t clumpCount, + float threshold = 0.01f, // controls how long it works to converge towards a least errors solution. + float collapseDistance = 0.0001f) // distance between clumps to consider them to be essentially equal. +{ + uint32_t convergeCount = 64; // maximum number of iterations attempting to converge to a solution.. + physx::Array<uint32_t> counts(clumpCount); + float error = 0.0f; + if (inputCount <= clumpCount) // if the number of input points is less than our clumping size, just return the input points. + { + clumpCount = inputCount; + for (uint32_t i = 0; i < inputCount; ++i) + { + clusters[i] = input[i]; + counts[i] = 1; + } + } + else + { + physx::Array<PxVec3> centroids(clumpCount); + + // Take a sampling of the input points as initial centroid estimates. + for (uint32_t i = 0; i < clumpCount; ++i) + { + uint32_t index = (i*inputCount)/clumpCount; + PX_ASSERT( index < inputCount ); + clusters[i] = input[index]; + } + + // Here is the main convergence loop + float old_error = PX_MAX_F32; // old and initial error estimates are max float + error = PX_MAX_F32; + do + { + old_error = error; // preserve the old error + // reset the counts and centroids to current cluster location + for (uint32_t i = 0; i < clumpCount; ++i) + { + counts[i] = 0; + centroids[i] = PxVec3(0.0f); + } + error = 0.0f; + // For each input data point, figure out which cluster it is closest too and add it to that cluster. + for (uint32_t i = 0; i < inputCount; ++i) + { + float min_distance2 = PX_MAX_F32; + uint32_t j_nearest = 0; + // find the nearest clump to this point. + for (uint32_t j = 0; j < clumpCount; ++j) + { + const float distance2 = (input[i]-clusters[j]).magnitudeSquared(); + if (distance2 < min_distance2) + { + min_distance2 = distance2; + j_nearest = j; // save which clump this point indexes + } + } + centroids[j_nearest] += input[i]; + ++counts[j_nearest]; // increment the counter indicating how many points are in this clump. + error += min_distance2; // save the error accumulation + } + // Now, for each clump, compute the mean and store the result. + for (uint32_t i = 0; i < clumpCount; ++i) + { + if (counts[i]) // if this clump got any points added to it... + { + centroids[i] /= (float)counts[i]; // compute the average center of the points in this clump. + clusters[i] = centroids[i]; // store it as the new cluster. + } + } + // decrement the convergence counter and bail if it is taking too long to converge to a solution. + --convergeCount; + if (convergeCount == 0) + { + break; + } + if (error < threshold) // early exit if our first guess is already good enough (if all input points are the same) + { + break; + } + } while (PxAbs(error - old_error) > threshold); // keep going until the error is reduced by this threshold amount. + } + + // ok..now we prune the clumps if necessary. + // The rules are; first, if a clump has no 'counts' then we prune it as it's unused. + // The second, is if the centroid of this clump is essentially the same (based on the distance tolerance) + // as an existing clump, then it is pruned and all indices which used to point to it, now point to the one + // it is closest too. + uint32_t outCount = 0; // number of clumps output after pruning performed. + float d2 = collapseDistance*collapseDistance; // squared collapse distance. + for (uint32_t i = 0; i < clumpCount; ++i) + { + if (counts[i] == 0) // if no points ended up in this clump, eliminate it. + { + continue; + } + // see if this clump is too close to any already accepted clump. + bool add = true; + for (uint32_t j = 0; j < outCount; ++j) + { + if ((clusters[i]-clusters[j]).magnitudeSquared() < d2) + { + add = false; // we do not add this clump + break; + } + } + if (add) + { + clusters[outCount++] = clusters[i]; + } + } + + clumpCount = outCount; + + return clumpCount; +}; +#endif + +// barycentric utilities +void generateBarycentricCoordinatesTri(const PxVec3& pa, const PxVec3& pb, const PxVec3& pc, const PxVec3& p, float& s, float& t) +{ + // pe = pb-ba, pf = pc-pa + // Barycentric coordinates: (s,t) -> pa + s*pe + t*pf + // Minimize: [s*pe + t*pf - (p-pa)]^2 + // Minimize: a s^2 + 2 b s t + c t^2 + 2 d s + 2 e t + f + // Minimization w.r.t s and t : + // as + bt = -d + // bs + ct = -e + + PxVec3 pe = pb - pa; + PxVec3 pf = pc - pa; + PxVec3 pg = pa - p; + + float a = pe.dot(pe); + float b = pe.dot(pf); + float c = pf.dot(pf); + float d = pe.dot(pg); + float e = pf.dot(pg); + + float det = a * c - b * b; + if (det != 0.0f) + { + s = (b * e - c * d) / det; + t = (b * d - a * e) / det; + } +} + +void generateBarycentricCoordinatesTet(const PxVec3& pa, const PxVec3& pb, const PxVec3& pc, const PxVec3& pd, const PxVec3& p, PxVec3& bary) +{ + PxVec3 q = p - pd; + PxVec3 q0 = pa - pd; + PxVec3 q1 = pb - pd; + PxVec3 q2 = pc - pd; + PxMat33 m(q0,q1,q2); + float det = m.getDeterminant(); + m.column0 = q; + bary.x = m.getDeterminant(); + m.column0 = q0; + m.column1 = q; + bary.y = m.getDeterminant(); + m.column1 = q1; + m.column2 = q; + bary.z = m.getDeterminant(); + if (det != 0.0f) + { + bary /= det; + } +} + +/* TriangleFrame */ + +size_t TriangleFrame::s_offsets[TriangleFrame::VertexFieldCount]; + +static TriangleFrameBuilder sTriangleFrameBuilder; + +/* + File-local functions and definitions + */ + +struct Marker +{ + float pos; + uint32_t id; // lsb = type (0 = max, 1 = min), other bits used for object index + + void set(float _pos, int32_t _id) + { + pos = _pos; + id = (uint32_t)_id; + } +}; + +static int compareMarkers(const void* A, const void* B) +{ + // Sorts by value. If values equal, sorts min types greater than max types, to reduce the # of overlaps + const float delta = ((Marker*)A)->pos - ((Marker*)B)->pos; + return delta != 0 ? (delta < 0 ? -1 : 1) : ((int)(((Marker*)A)->id & 1) - (int)(((Marker*)B)->id & 1)); +} + +struct EdgeWithFace +{ + EdgeWithFace() {} + EdgeWithFace(uint32_t v0, uint32_t v1, uint32_t face, uint32_t oppositeFace, bool sort = false) : faceIndex(face), v0Index(v0), v1Index(v1), opposite(oppositeFace) + { + if (sort) + { + if (v1Index < v0Index) + { + nvidia::swap(v0Index, v1Index); + } + } + } + + bool operator == (const EdgeWithFace& e) + { + return v0Index == e.v0Index && v1Index == e.v1Index; + } + + uint32_t faceIndex; + uint32_t v0Index; + uint32_t v1Index; + uint32_t opposite; +}; + +/* + Global utilities + */ + +void boundsCalculateOverlaps(physx::Array<IntPair>& overlaps, Bounds3Axes axesToUse, const BoundsRep* bounds, uint32_t boundsCount, uint32_t boundsByteStride, + const BoundsInteractions& interactions, bool append) +{ + if (!append) + { + overlaps.reset(); + } + + uint32_t D = 0; + uint32_t axisNums[3]; + for (unsigned i = 0; i < 3; ++i) + { + if ((axesToUse >> i) & 1) + { + axisNums[D++] = i; + } + } + + if (D == 0 || D > 3) + { + return; + } + + physx::Array< physx::Array<Marker> > axes; + axes.resize(D); + uint32_t overlapCount[3]; + + for (uint32_t n = 0; n < D; ++n) + { + const uint32_t axisNum = axisNums[n]; + physx::Array<Marker>& axis = axes[n]; + overlapCount[n] = 0; + axis.resize(2 * boundsCount); + uint8_t* boundsPtr = (uint8_t*)bounds; + for (uint32_t i = 0; i < boundsCount; ++i, boundsPtr += boundsByteStride) + { + const BoundsRep& boundsRep = *(const BoundsRep*)boundsPtr; + const PxBounds3& box = boundsRep.aabb; + float min = box.minimum[axisNum]; + float max = box.maximum[axisNum]; + if (min >= max) + { + const float mid = 0.5f * (min + max); + float pad = 0.000001f * fabsf(mid); + min = mid - pad; + max = mid + pad; + } + axis[i << 1].set(min, (int32_t)i << 1 | 1); + axis[i << 1 | 1].set(max, (int32_t)i << 1); + } + qsort(axis.begin(), axis.size(), sizeof(Marker), compareMarkers); + uint32_t localOverlapCount = 0; + for (uint32_t i = 0; i < axis.size(); ++i) + { + Marker& marker = axis[i]; + if (marker.id & 1) + { + overlapCount[n] += localOverlapCount; + ++localOverlapCount; + } + else + { + --localOverlapCount; + } + } + } + + unsigned int axis0; + unsigned int axis1; + unsigned int axis2; + unsigned int maxBin; + if (D == 1) + { + maxBin = 0; + axis0 = axisNums[0]; + axis1 = axis0; + axis2 = axis0; + } + else if (D == 2) + { + if (overlapCount[0] < overlapCount[1]) + { + maxBin = 0; + axis0 = axisNums[0]; + axis1 = axisNums[1]; + axis2 = axis0; + } + else + { + maxBin = 1; + axis0 = axisNums[1]; + axis1 = axisNums[0]; + axis2 = axis0; + } + } + else + { + maxBin = overlapCount[0] < overlapCount[1] ? (overlapCount[0] < overlapCount[2] ? 0U : 2U) : (overlapCount[1] < overlapCount[2] ? 1U : 2U); + axis0 = axisNums[maxBin]; + axis1 = (axis0 + 1) % 3; + axis2 = (axis0 + 2) % 3; + } + + const uint64_t interactionBits = interactions.bits; + + IndexBank<uint32_t> localOverlaps(boundsCount); + physx::Array<Marker>& axis = axes[maxBin]; + float boxMin1 = 0.0f; + float boxMax1 = 0.0f; + float boxMin2 = 0.0f; + float boxMax2 = 0.0f; + + for (uint32_t i = 0; i < axis.size(); ++i) + { + Marker& marker = axis[i]; + const uint32_t index = marker.id >> 1; + if (marker.id & 1) + { + const BoundsRep& boundsRep = *(const BoundsRep*)((uint8_t*)bounds + index*boundsByteStride); + const uint8_t interaction = (uint8_t)((interactionBits >> (boundsRep.type << 3)) & 0xFF); + const PxBounds3& box = boundsRep.aabb; + // These conditionals compile out with optimization: + if (D > 1) + { + boxMin1 = box.minimum[axis1]; + boxMax1 = box.maximum[axis1]; + if (D == 3) + { + boxMin2 = box.minimum[axis2]; + boxMax2 = box.maximum[axis2]; + } + } + const uint32_t localOverlapCount = localOverlaps.usedCount(); + const uint32_t* localOverlapIndices = localOverlaps.usedIndices(); + for (uint32_t j = 0; j < localOverlapCount; ++j) + { + const uint32_t overlapIndex = localOverlapIndices[j]; + const BoundsRep& overlapBoundsRep = *(const BoundsRep*)((uint8_t*)bounds + overlapIndex*boundsByteStride); + if ((interaction >> overlapBoundsRep.type) & 1) + { + const PxBounds3& overlapBox = overlapBoundsRep.aabb; + // These conditionals compile out with optimization: + if (D > 1) + { + if (boxMin1 >= overlapBox.maximum[axis1] || boxMax1 <= overlapBox.minimum[axis1]) + { + continue; + } + if (D == 3) + { + if (boxMin2 >= overlapBox.maximum[axis2] || boxMax2 <= overlapBox.minimum[axis2]) + { + continue; + } + } + } + // Add overlap + IntPair& pair = overlaps.insert(); + pair.i0 = (int32_t)index; + pair.i1 = (int32_t)overlapIndex; + } + } + PX_ASSERT(localOverlaps.isValid(index)); + PX_ASSERT(!localOverlaps.isUsed(index)); + localOverlaps.use(index); + } + else + { + // Remove local overlap + PX_ASSERT(localOverlaps.isValid(index)); + localOverlaps.free(index); + } + } +} + + +/* + ConvexHullImpl functions + */ + +ConvexHullImpl::ConvexHullImpl() : mParams(NULL), mOwnsParams(false) +{ +} + +ConvexHullImpl::~ConvexHullImpl() +{ + term(); +} + +// If params == NULL, ConvexHullImpl will build (and own) its own internal parameters +void ConvexHullImpl::init(NvParameterized::Interface* params) +{ + term(); + + if (params != NULL) + { + mParams = DYNAMIC_CAST(ConvexHullParameters*)(params); + if (mParams == NULL) + { + PX_ASSERT(!"params must be ConvexHullParameters"); + } + } + else + { + NvParameterized::Traits* traits = GetInternalApexSDK()->getParameterizedTraits(); + mParams = DYNAMIC_CAST(ConvexHullParameters*)(traits->createNvParameterized(ConvexHullParameters::staticClassName())); + PX_ASSERT(mParams != NULL); + if (mParams != NULL) + { + mOwnsParams = true; + setEmpty(); + } + } +} + + +NvParameterized::Interface* ConvexHullImpl::giveOwnersipOfNvParameters() +{ + if (mOwnsParams) + { + mOwnsParams = false; + return mParams; + } + + return NULL; +} + +// Releases parameters if it owns them +void ConvexHullImpl::term() +{ + if (mParams != NULL && mOwnsParams) + { + mParams->destroy(); + } + mParams = NULL; + mOwnsParams = false; +} + +static int compareEdgesWithFaces(const void* A, const void* B) +{ + const EdgeWithFace& eA = *(const EdgeWithFace*)A; + const EdgeWithFace& eB = *(const EdgeWithFace*)B; + if (eA.v0Index != eB.v0Index) + { + return (int)eA.v0Index - (int)eB.v0Index; + } + if (eA.v1Index != eB.v1Index) + { + return (int)eA.v1Index - (int)eB.v1Index; + } + return (int)eA.faceIndex - (int)eB.faceIndex; +} + +void ConvexHullImpl::buildFromDesc(const ConvexHullDesc& desc) +{ + if (!desc.isValid()) + { + setEmpty(); + return; + } + + setEmpty(); + + Array<PxPlane> uniquePlanes; + Array<uint32_t> edges; + Array<float> widths; + + NvParameterized::Handle handle(*mParams); + + // Copy vertices and compute bounds + mParams->getParameterHandle("vertices", handle); + mParams->resizeArray(handle, (int32_t)desc.numVertices); + const uint8_t* vertPointer = (const uint8_t*)desc.vertices; + for (uint32_t i = 0; i < desc.numVertices; ++i, vertPointer += desc.vertexStrideBytes) + { + mParams->vertices.buf[i] = *(const PxVec3*)vertPointer; + mParams->bounds.include(mParams->vertices.buf[i]); + } + + PxVec3 center; + center = mParams->bounds.getCenter(); + + // Find faces and compute volume + physx::Array<EdgeWithFace> fEdges; + const uint32_t* indices = desc.indices; + mParams->volume = 0.0f; + for (uint32_t fI = 0; fI < desc.numFaces; ++fI) + { + const uint32_t indexCount = (const uint32_t)desc.faceIndexCounts[fI]; + const PxVec3& vI0 = mParams->vertices.buf[indices[0]]; + PxVec3 normal(0.0f); + for (uint32_t pI = 1; pI < indexCount-1; ++pI) + { + const uint32_t i1 = indices[pI]; + const uint32_t i2 = indices[pI+1]; + const PxVec3& vI1 = mParams->vertices.buf[i1]; + const PxVec3& vI2 = mParams->vertices.buf[i2]; + const PxVec3 eI1 = vI1 - vI0; + const PxVec3 eI2 = vI2 - vI0; + PxVec3 normalI = eI1.cross(eI2); + mParams->volume += (normalI.dot(vI0 - center)); + normal += normalI; + } + + const bool normalValid = normal.normalize() > 0.0f; + + float d = 0.0f; + for (uint32_t pI = 0; pI < indexCount; ++pI) + { + d -= normal.dot(mParams->vertices.buf[indices[pI]]); + } + d /= indexCount; + + uint32_t oppositeFace = 0; + uint32_t faceN = uniquePlanes.size(); // unless we find an opposite + + if (normalValid) + { + // See if this face is opposite an existing one + for (uint32_t j = 0; j < uniquePlanes.size(); ++j) + { + if (normal.dot(uniquePlanes[j].n) < -0.999999f && widths[j] == PX_MAX_F32) + { + oppositeFace = 1; + faceN = j; + widths[j] = -d - uniquePlanes[j].d; + break; + } + } + } + + if (!oppositeFace) + { + uniquePlanes.pushBack(PxPlane(normal, d)); + widths.pushBack(PX_MAX_F32); + } + + for (uint32_t pI = 0; pI < indexCount; ++pI) + { + fEdges.pushBack(EdgeWithFace(indices[pI], indices[(pI+1)%indexCount], faceN, oppositeFace, true)); + } + + indices += indexCount; + } + + // Create a map from current plane indices to the arrangement we'll have after the following loop + // This map will be its inverse + physx::Array<uint32_t> invMap; + invMap.resize(2*widths.size()); + for (uint32_t i = 0; i < invMap.size(); ++i) + { + invMap[i] = i; + } + + // This ensures that all the slabs are represented first in the planes list + uint32_t slabCount = widths.size(); + for (uint32_t i = widths.size(); i--;) + { + if (widths[i] == PX_MAX_F32) + { + --slabCount; + nvidia::swap(widths[i], widths[slabCount]); + nvidia::swap(uniquePlanes[i], uniquePlanes[slabCount]); + nvidia::swap(invMap[i], invMap[slabCount]); + } + } + + // Now invert invMap to get the map + physx::Array<uint32_t> map; + map.resize(invMap.size()); + for (uint32_t i = 0; i < map.size(); ++i) + { + map[invMap[i]] = i; + } + + // Plane indices have been rearranged, need to make corresponding rearrangements to fEdges' faceIndex value. + for (uint32_t i = 0; i < fEdges.size(); ++i) + { + fEdges[i].faceIndex = map[fEdges[i].faceIndex]; + if (fEdges[i].opposite) + { + fEdges[i].faceIndex += uniquePlanes.size(); + } + } + + // Total plane count, with non-unique (back-facing) normals + mParams->planeCount = uniquePlanes.size() + slabCount; + + // Fill in remaining widths + for (uint32_t fI = mParams->planeCount - uniquePlanes.size(); fI < widths.size(); ++fI) + { + widths[fI] = 0.0f; + for (uint32_t vI = 0; vI < desc.numVertices; ++vI) + { + const float vDepth = -uniquePlanes[fI].distance(mParams->vertices.buf[vI]); + if (vDepth > widths[fI]) + { + widths[fI] = vDepth; + } + } + } + + // Record faceIndex pairs in adjacentFaces + physx::Array<uint32_t> adjacentFaces; + + // Eliminate redundant edges + qsort(fEdges.begin(), fEdges.size(), sizeof(EdgeWithFace), compareEdgesWithFaces); + for (uint32_t eI = 0; eI < fEdges.size();) + { + uint32_t i0 = fEdges[eI].v0Index; + uint32_t i1 = fEdges[eI].v1Index; + PX_ASSERT(i0 < 65536 && i1 < 65536); + if (eI < fEdges.size() - 1 && fEdges[eI] == fEdges[eI + 1]) + { + if (fEdges[eI].faceIndex != fEdges[eI + 1].faceIndex) + { + edges.pushBack(i0 << 16 | i1); + adjacentFaces.pushBack(fEdges[eI].faceIndex << 16 | fEdges[eI + 1].faceIndex); + } + eI += 2; + } + else + { + edges.pushBack(i0 << 16 | i1); + adjacentFaces.pushBack(0xFFFF0000 | fEdges[eI].faceIndex); + ++eI; + } + } + + // Find unique edge directions, put them at the front of the edge list + const uint32_t edgeCount = edges.size(); + if (edgeCount > 0) + { + mParams->uniqueEdgeDirectionCount = 1; + for (uint32_t testEdgeIndex = 1; testEdgeIndex < edgeCount; ++testEdgeIndex) + { + const uint32_t testEdgeEndpointIndices = edges[testEdgeIndex]; + const PxVec3 testEdge = mParams->vertices.buf[testEdgeEndpointIndices & 0xFFFF] - mParams->vertices.buf[testEdgeEndpointIndices >> 16]; + const float testEdge2 = testEdge.magnitudeSquared(); + uint32_t uniqueEdgeIndex = 0; + for (; uniqueEdgeIndex < mParams->uniqueEdgeDirectionCount; ++uniqueEdgeIndex) + { + const uint32_t uniqueEdgeEndpointIndices = edges[uniqueEdgeIndex]; + const PxVec3 uniqueEdge = mParams->vertices.buf[uniqueEdgeEndpointIndices & 0xFFFF] - mParams->vertices.buf[uniqueEdgeEndpointIndices >> 16]; + if (uniqueEdge.cross(testEdge).magnitudeSquared() < testEdge2 * uniqueEdge.magnitudeSquared()*PX_EPS_F32 * PX_EPS_F32) + { + break; + } + } + if (uniqueEdgeIndex == mParams->uniqueEdgeDirectionCount) + { + nvidia::swap(edges[mParams->uniqueEdgeDirectionCount], edges[testEdgeIndex]); + nvidia::swap(adjacentFaces[mParams->uniqueEdgeDirectionCount], adjacentFaces[testEdgeIndex]); + ++mParams->uniqueEdgeDirectionCount; + } + } + } + + mParams->volume *= 0.1666666667f; + + // Transfer from temporary arrays into mParams + + // uniquePlanes + mParams->getParameterHandle("uniquePlanes", handle); + mParams->resizeArray(handle, (int32_t)uniquePlanes.size()); + for (uint32_t i = 0; i < uniquePlanes.size(); ++i) + { + PxPlane& plane = uniquePlanes[i]; + ConvexHullParametersNS::Plane_Type& paramPlane = mParams->uniquePlanes.buf[i]; + paramPlane.normal = plane.n; + paramPlane.d = plane.d; + } + + // edges + mParams->getParameterHandle("edges", handle); + mParams->resizeArray(handle, (int32_t)edges.size()); + mParams->setParamU32Array(handle, edges.begin(), (int32_t)edges.size()); + + // adjacentFaces + mParams->getParameterHandle("adjacentFaces", handle); + mParams->resizeArray(handle, (int32_t)adjacentFaces.size()); + mParams->setParamU32Array(handle, adjacentFaces.begin(), (int32_t)adjacentFaces.size()); + + // widths + mParams->getParameterHandle("widths", handle); + mParams->resizeArray(handle, (int32_t)widths.size()); + mParams->setParamF32Array(handle, widths.begin(), (int32_t)widths.size()); + +} + +void ConvexHullImpl::buildFromPoints(const void* points, uint32_t numPoints, uint32_t pointStrideBytes) +{ + if (numPoints < 4) + { + setEmpty(); + return; + } + + // First try stanhull + + // We will transform the points of the hull such that they fit well into a rotated 2x2x2 cube. + // To find a suitable set of axes for this rotated cube, we will find the of the point distribution. + + // Create an array of points which we will transform + physx::Array<PxVec3> transformedPoints(numPoints); + uint8_t* pointPtr = (uint8_t*)points; + for (uint32_t i = 0; i < numPoints; ++i, pointPtr += pointStrideBytes) + { + transformedPoints[i] = *(PxVec3*)pointPtr; + } + + // Optionally reduce the point set +#if REDUCE_CONVEXHULL_INPUT_POINT_SET + const uint32_t maxSetSize = 400; + physx::Array<PxVec3> reducedPointSet(numPoints); + const uint32_t reducedSetSize = kmeans_cluster(&transformedPoints[0], numPoints, &reducedPointSet[0], maxSetSize); + PX_ASSERT(reducedSetSize <= 400 && reducedSetSize >= 4); + transformedPoints = reducedPointSet; +#endif // REDUCE_CONVEXHULL_INPUT_POINT_SET + + PxVec3 mean; + PxVec3 variance; + PxMat33 axes; + findPrincipleComponents(mean, variance, axes, &transformedPoints[0], numPoints); + + // Now subtract the mean from the points and rotate the points into the frame of the axes + for (uint32_t i = 0; i < numPoints; ++i) + { + transformedPoints[i] = axes.transformTranspose(transformedPoints[i] - mean); + } + + // Finally find a scale such that the maximum absolute coordinate on each axis is 1 + PxVec3 scale(0.0f); + for (uint32_t i = 0; i < numPoints; ++i) + { + for (unsigned j = 0; j < 3; ++j) + { + scale[j] = PxMax(scale[j], PxAbs(transformedPoints[i][j])); + } + } + if (scale.x*scale.y*scale.z == 0.0f) + { + // We have a degeneracy, planar (or colinear or coincident) points + setEmpty(); + return; + } + + // Now scale the points + const PxVec3 recipScale(1.0f/scale.x, 1.0f/scale.y, 1.0f/scale.z); + for (uint32_t i = 0; i < numPoints; ++i) + { + transformedPoints[i] = transformedPoints[i].multiply(recipScale); + } + + // The points are transformed, and for completeness let us now build the transform which restores them + const PxMat44 tm(axes.column0*scale.x, axes.column1*scale.y, axes.column2*scale.z, mean); + +#if 0 + // Now find the convex hull of the transformed points + physx::stanhull::HullDesc hullDesc; + hullDesc.mVertices = (const float*)&transformedPoints[0]; + hullDesc.mVcount = numPoints; + hullDesc.mVertexStride = sizeof(PxVec3); + hullDesc.mSkinWidth = 0.0f; + + physx::stanhull::HullLibrary hulllib; + physx::stanhull::HullResult hull; + if (physx::stanhull::QE_OK == hulllib.CreateConvexHull(hullDesc, hull)) + { // Success + ConvexHullDesc desc; + desc.vertices = hull.mOutputVertices; + desc.numVertices = hull.mNumOutputVertices; + desc.vertexStrideBytes = 3*sizeof(float); + desc.indices = hull.mIndices; + desc.numIndices = hull.mNumIndices; + desc.faceIndexCounts = hull.mFaces; + desc.numFaces = hull.mNumFaces; + buildFromDesc(desc); + hulllib.ReleaseResult(hull); + + // Transform our hull back before returning + applyTransformation(tm); + + return; + } +#endif + +#if PX_PHYSICS_VERSION_MAJOR == 3 + // Stanhull failed, try physx sdk cooker + ApexSDK* sdk = GetApexSDK(); + if (sdk && sdk->getCookingInterface() && sdk->getPhysXSDK()) + { + + physx::PxConvexMeshDesc convexMeshDesc; + convexMeshDesc.points.count = numPoints; + convexMeshDesc.points.data = &transformedPoints[0]; + convexMeshDesc.points.stride = pointStrideBytes; + convexMeshDesc.flags = physx::PxConvexFlag::eCOMPUTE_CONVEX; + + nvidia::PsMemoryBuffer stream; + stream.setEndianMode(PxFileBuf::ENDIAN_NONE); + PxStreamFromFileBuf nvs(stream); + if (sdk->getCookingInterface()->cookConvexMesh(convexMeshDesc, nvs)) + { + physx::PxConvexMesh* mesh = sdk->getPhysXSDK()->createConvexMesh(nvs); + if (mesh) + { + buildFromConvexMesh(mesh); + mesh->release(); + // Transform our hull back before returning + applyTransformation(tm); + return; + } + } + } +#endif + + // No luck + setEmpty(); +} + +void ConvexHullImpl::buildFromPlanes(const PxPlane* planes, uint32_t numPlanes, float eps) +{ + physx::Array<PxVec3> points; + points.reserve((numPlanes * (numPlanes - 1) * (numPlanes - 2)) / 6); + for (uint32_t i = 0; i < numPlanes; ++i) + { + const PxPlane& planeI = planes[i]; + for (uint32_t j = i + 1; j < numPlanes; ++j) + { + const PxPlane& planeJ = planes[j]; + for (uint32_t k = j + 1; k < numPlanes; ++k) + { + const PxPlane& planeK = planes[k]; + PxVec3 point; + if (intersectPlanes(point, planeI, planeJ, planeK)) + { + if (pointInsidePlanes(point, planes, i, eps) && + pointInsidePlanes(point, planes + i + 1, j - (i + 1), eps) && + pointInsidePlanes(point, planes + j + 1, k - (j + 1), eps) && + pointInsidePlanes(point, planes + k + 1, numPlanes - (k + 1), eps)) + { + uint32_t m = 0; + for (; m < points.size(); ++m) + { + if ((point - points[m]).magnitudeSquared() < eps) + { + break; + } + } + if (m == points.size()) + { + points.pushBack(point); + } + } + } + } + } + } + + buildFromPoints(points.begin(), points.size(), sizeof(PxVec3)); +} + + +#if PX_PHYSICS_VERSION_MAJOR == 3 + +void ConvexHullImpl::buildFromConvexMesh(const ConvexMesh* mesh) +{ + setEmpty(); + + if (!mesh) + { + return; + } + + const uint32_t numVerts = mesh->getNbVertices(); + const PxVec3* verts = (const PxVec3*)mesh->getVertices(); + const uint32_t numPolygons = mesh->getNbPolygons(); + const uint8_t* index = (const uint8_t*)mesh->getIndexBuffer(); + + + Array<PxPlane> uniquePlanes; + Array<uint32_t> edges; + Array<float> widths; + + NvParameterized::Handle handle(*mParams); + + // Copy vertices and compute bounds + mParams->getParameterHandle("vertices", handle); + mParams->resizeArray(handle, (int32_t)numVerts); + for (uint32_t i = 0; i < numVerts; ++i) + { + mParams->vertices.buf[i] = verts[i]; + mParams->bounds.include(verts[i]); + } + + PxVec3 center; + center = mParams->bounds.getCenter(); + + PxMat33 localInertia; + PxVec3 localCenterOfMass; + mesh->getMassInformation(mParams->volume, localInertia, localCenterOfMass); + + // Find planes and compute volume + physx::Array<EdgeWithFace> fEdges; + + for (uint32_t i = 0; i < numPolygons; i++) + { + physx::PxHullPolygon data; + bool status = mesh->getPolygonData(i, data); + PX_ASSERT(status); + if (!status) + { + continue; + } + + const uint32_t numIndices = data.mNbVerts; + for (uint32_t vI = 0; vI < numIndices; ++vI) + { + const uint16_t i0 = (uint16_t)index[data.mIndexBase + vI]; + const uint16_t i1 = (uint16_t)index[data.mIndexBase + ((vI+1)%numIndices)]; + const PxVec3& vI0 = verts[i0]; + const PxVec3 normal(data.mPlane[0], data.mPlane[1], data.mPlane[2]); + PX_ASSERT(PxEquals(normal.magnitudeSquared(), 1.0f, 0.000001f)); + uint32_t faceIndex; + uint32_t oppositeFace = 0; + for (faceIndex = 0; faceIndex < uniquePlanes.size(); ++faceIndex) + { + const float cosTheta = normal.dot(uniquePlanes[faceIndex].n); + oppositeFace = (uint32_t)(cosTheta < 0.0f); + if (cosTheta * cosTheta > 0.999999f) + { + break; + } + } + if (faceIndex == uniquePlanes.size()) + { + uniquePlanes.pushBack(PxPlane(vI0, normal)); + widths.pushBack(PX_MAX_F32); + oppositeFace = 0; + } + else if (oppositeFace && widths[faceIndex] == PX_MAX_F32) + { + // New slab + widths[faceIndex] = -uniquePlanes[faceIndex].distance(vI0); + } + fEdges.pushBack(EdgeWithFace(i0, i1, faceIndex, oppositeFace, true)); + } + } + + // Create a map from current plane indices to the arrangement we'll have after the following loop + // This map will be its inverse + physx::Array<uint32_t> invMap; + invMap.resize(2*widths.size()); + for (uint32_t i = 0; i < invMap.size(); ++i) + { + invMap[i] = i; + } + + // This ensures that all the slabs are represented first in the planes list + uint32_t slabCount = widths.size(); + for (uint32_t i = widths.size(); i--;) + { + if (widths[i] == PX_MAX_F32) + { + --slabCount; + nvidia::swap(widths[i], widths[slabCount]); + nvidia::swap(uniquePlanes[i], uniquePlanes[slabCount]); + nvidia::swap(invMap[i], invMap[slabCount]); + } + } + + // Now invert invMap to get the map + physx::Array<uint32_t> map; + map.resize(invMap.size()); + for (uint32_t i = 0; i < map.size(); ++i) + { + map[invMap[i]] = i; + } + + // Plane indices have been rearranged, need to make corresponding rearrangements to fEdges' faceIndex value. + for (uint32_t i = 0; i < fEdges.size(); ++i) + { + fEdges[i].faceIndex = map[fEdges[i].faceIndex]; + if (fEdges[i].opposite) + { + fEdges[i].faceIndex += uniquePlanes.size(); + } + } + + // Total plane count, with non-unique (back-facing) normals + mParams->planeCount = uniquePlanes.size() + slabCount; + + // Fill in remaining widths + for (uint32_t pI = mParams->planeCount - uniquePlanes.size(); pI < widths.size(); ++pI) + { + widths[pI] = 0.0f; + for (uint32_t i = 0; i < numPolygons; i++) + { + physx::PxHullPolygon data; + bool status = mesh->getPolygonData(i, data); + PX_ASSERT(status); + if (!status) + { + continue; + } + + const uint32_t numIndices = (uint32_t)data.mNbVerts - 2; + for (uint32_t vI = 0; vI < numIndices; ++vI) + { + const float vDepth = -uniquePlanes[pI].distance(verts[index[data.mIndexBase + vI]]); + if (vDepth > widths[pI]) + { + widths[pI] = vDepth; + } + } + } + } + + // Record faceIndex pairs in adjacentFaces + physx::Array<uint32_t> adjacentFaces; + + // Eliminate redundant edges + qsort(fEdges.begin(), fEdges.size(), sizeof(EdgeWithFace), compareEdgesWithFaces); + for (uint32_t eI = 0; eI < fEdges.size();) + { + uint32_t i0 = fEdges[eI].v0Index; + uint32_t i1 = fEdges[eI].v1Index; + PX_ASSERT(i0 < 65536 && i1 < 65536); + if (eI < fEdges.size() - 1 && fEdges[eI] == fEdges[eI + 1]) + { + if (fEdges[eI].faceIndex != fEdges[eI + 1].faceIndex) + { + edges.pushBack(i0 << 16 | i1); + adjacentFaces.pushBack(fEdges[eI].faceIndex << 16 | fEdges[eI + 1].faceIndex); + } + eI += 2; + } + else + { + edges.pushBack(i0 << 16 | i1); + adjacentFaces.pushBack(0xFFFF0000 | fEdges[eI].faceIndex); + ++eI; + } + } + + // Find unique edge directions, put them at the front of the edge list + const uint32_t edgeCount = edges.size(); + if (edgeCount > 0) + { + mParams->uniqueEdgeDirectionCount = 1; + for (uint32_t testEdgeIndex = 1; testEdgeIndex < edgeCount; ++testEdgeIndex) + { + const PxVec3 testEdge = verts[edges[testEdgeIndex] & 0xFFFF] - verts[edges[testEdgeIndex] >> 16]; + const float testEdge2 = testEdge.magnitudeSquared(); + uint32_t uniqueEdgeIndex = 0; + for (; uniqueEdgeIndex < mParams->uniqueEdgeDirectionCount; ++uniqueEdgeIndex) + { + const PxVec3 uniqueEdge = verts[edges[uniqueEdgeIndex] & 0xFFFF] - verts[edges[uniqueEdgeIndex] >> 16]; + if (uniqueEdge.cross(testEdge).magnitudeSquared() < testEdge2 * uniqueEdge.magnitudeSquared()*PX_EPS_F32 * PX_EPS_F32) + { + break; + } + } + if (uniqueEdgeIndex == mParams->uniqueEdgeDirectionCount) + { + nvidia::swap(edges[mParams->uniqueEdgeDirectionCount], edges[testEdgeIndex]); + nvidia::swap(adjacentFaces[mParams->uniqueEdgeDirectionCount], adjacentFaces[testEdgeIndex]); + ++mParams->uniqueEdgeDirectionCount; + } + } + } + + // Transfer from temporary arrays into mParams + + // uniquePlanes + mParams->getParameterHandle("uniquePlanes", handle); + mParams->resizeArray(handle, (int32_t)uniquePlanes.size()); + for (uint32_t i = 0; i < uniquePlanes.size(); ++i) + { + PxPlane& plane = uniquePlanes[i]; + ConvexHullParametersNS::Plane_Type& paramPlane = mParams->uniquePlanes.buf[i]; + paramPlane.normal = plane.n; + paramPlane.d = plane.d; + } + + // edges + mParams->getParameterHandle("edges", handle); + mParams->resizeArray(handle, (int32_t)edges.size()); + mParams->setParamU32Array(handle, edges.begin(), (int32_t)edges.size()); + + // adjacentFaces + mParams->getParameterHandle("adjacentFaces", handle); + mParams->resizeArray(handle, (int32_t)adjacentFaces.size()); + mParams->setParamU32Array(handle, adjacentFaces.begin(), (int32_t)adjacentFaces.size()); + + // widths + mParams->getParameterHandle("widths", handle); + mParams->resizeArray(handle, (int32_t)widths.size()); + mParams->setParamF32Array(handle, widths.begin(), (int32_t)widths.size()); +} +#endif + + + +void ConvexHullImpl::buildFromAABB(const PxBounds3& aabb) +{ + PxVec3 center, extent; + center = aabb.getCenter(); + extent = aabb.getExtents(); + + NvParameterized::Handle handle(*mParams); + + // Copy vertices and compute bounds + mParams->getParameterHandle("vertices", handle); + mParams->resizeArray(handle, 8); + for (int i = 0; i < 8; ++i) + { + mParams->vertices.buf[i] = center + PxVec3((2.0f * (i & 1) - 1.0f) * extent.x, ((float)(i & 2) - 1.0f) * extent.y, (0.5f * (i & 4) - 1.0f) * extent.z); + } + + mParams->getParameterHandle("uniquePlanes", handle); + mParams->resizeArray(handle, 3); + mParams->getParameterHandle("widths", handle); + mParams->resizeArray(handle, 3); + for (unsigned i = 0; i < 3; ++i) + { + ConvexHullParametersNS::Plane_Type& plane = mParams->uniquePlanes.buf[i]; + plane.normal = PxVec3(0.0f); + plane.normal[i] = -1.0f; + plane.d = -aabb.maximum[i]; + mParams->widths.buf[i] = aabb.maximum[i] - aabb.minimum[i]; + } + mParams->planeCount = 6; + + mParams->getParameterHandle("edges", handle); + mParams->resizeArray(handle, 12); + mParams->getParameterHandle("adjacentFaces", handle); + mParams->resizeArray(handle, 12); + for (uint32_t i = 0; i < 3; ++i) + { + uint32_t i1 = ((i + 1) % 3); + uint32_t i2 = ((i + 2) % 3); + uint32_t vOffset1 = 1u << i1; + uint32_t vOffset2 = 1u << i2; + for (int j = 0; j < 4; ++j) + { + uint32_t v0 = 0; + uint32_t v1 = 1u << i; + uint32_t f0 = i1; + uint32_t f1 = i2; + if (j & 1) + { + v0 += vOffset1; + v1 += vOffset1; + f0 += 3; + } + if (j & 2) + { + v0 += vOffset2; + v1 += vOffset2; + f1 += 3; + } + if (j == 1 || j == 2) + { + nvidia::swap(f0, f1); + } + uint32_t index = i + 3 * j; + mParams->edges.buf[index] = v0 << 16 | v1; + mParams->adjacentFaces.buf[i] = f0 << 16 | f1; + } + } + mParams->uniqueEdgeDirectionCount = 3; + + mParams->bounds = aabb; + + const PxVec3 diag = mParams->bounds.maximum - mParams->bounds.minimum; + mParams->volume = diag.x * diag.y * diag.z; +} + +void ConvexHullImpl::buildKDOP(const void* points, uint32_t numPoints, uint32_t pointStrideBytes, const PxVec3* directions, uint32_t numDirections) +{ + setEmpty(); + + if (numPoints == 0) + { + return; + } + + float size = 0; + + physx::Array<PxPlane> planes; + planes.reserve(2 * numDirections); + for (uint32_t i = 0; i < numDirections; ++i) + { + PxVec3 dir = directions[i]; + dir.normalize(); + float min, max; + getExtent(min, max, points, numPoints, pointStrideBytes, dir); + planes.pushBack(PxPlane(dir, -max)); + planes.pushBack(PxPlane(-dir, min)); + size = PxMax(size, max - min); + } + + buildFromPlanes(planes.begin(), planes.size(), 0.00001f * size); +} + +void ConvexHullImpl::intersectPlaneSide(const PxPlane& plane) +{ + const float size = (mParams->bounds.maximum - mParams->bounds.minimum).magnitude(); + const float eps = 0.00001f * size; + + const uint32_t oldVertexCount = (uint32_t)mParams->vertices.arraySizes[0]; + + Array<PxVec3> vertices; + vertices.resize(oldVertexCount); + NvParameterized::Handle handle(*mParams); + mParams->getParameterHandle("vertices", handle); + mParams->getParamVec3Array(handle, vertices.begin(), (int32_t)vertices.size()); + + // Throw out all vertices on the + side of the plane + for (uint32_t i = oldVertexCount; i--;) + { + if (plane.distance(vertices[i]) > eps) + { + vertices.replaceWithLast(i); + } + } + + if (vertices.size() == 0) + { + // plane excludes this whole hull. Delete everything. + setEmpty(); + return; + } + + if (vertices.size() == oldVertexCount) + { + // plane includes this whole hull. Do nothing. + return; + } + + // Intersect new plane with all pairs of old planes. + const uint32_t planeCount = getPlaneCount(); + for (uint32_t j = 1; j < planeCount; ++j) + { + const PxPlane planeJ = getPlane(j); + for (uint32_t i = 0; i < j; ++i) + { + const PxPlane planeI = getPlane(i); + PxVec3 point; + if (intersectPlanes(point, plane, planeI, planeJ)) + { + // See if point is excluded by any of the other planes + uint32_t k; + for (k = 0; k < planeCount; ++k) + { + if (k != i && k != j && getPlane(k).distance(point) > eps) + { + break; + } + } + if (k == planeCount) + { + // Point is part of the new intersected hull + uint32_t m = 0; + for (; m < vertices.size(); ++m) + { + if ((point - vertices[m]).magnitudeSquared() < eps) + { + break; + } + } + if (m == vertices.size()) + { + vertices.pushBack(point); + } + } + } + } + } + + buildFromPoints(vertices.begin(), vertices.size(), sizeof(vertices[0])); +} + +void ConvexHullImpl::intersectHull(const ConvexHullImpl& hull) +{ + if (hull.getPlaneCount() == 0) + { + setEmpty(); + return; + } + + physx::Array<PxPlane> planes; + planes.reserve(getPlaneCount() + hull.getPlaneCount()); + for (uint32_t planeN = 0; planeN < getPlaneCount(); ++planeN) + { + planes.pushBack(getPlane(planeN)); + } + for (uint32_t planeN = 0; planeN < hull.getPlaneCount(); ++planeN) + { + planes.pushBack(hull.getPlane(planeN)); + } + + const float size = (mParams->bounds.maximum - mParams->bounds.minimum).magnitude(); + + buildFromPlanes(planes.begin(), planes.size(), 0.00001f * size); +} + +// Returns true iff distance does not exceed result and maxDistance +PX_INLINE bool updateResultAndSeparation(bool& updated, bool& normalPointsFrom0to1, float& result, ConvexHullImpl::Separation* separation, + float min0, float max0, float min1, float max1, const PxVec3& n, float maxDistance) +{ + float midpoint; + const float dist = extentDistanceAndNormalDirection(min0, max0, min1, max1, midpoint, normalPointsFrom0to1); + updated = dist > result; + if (updated) + { + if (dist > maxDistance) + { + return false; + } + result = dist; + if (separation != NULL) + { + if (normalPointsFrom0to1) + { + separation->plane = PxPlane(n, -midpoint); + separation->min0 = min0; + separation->max0 = max0; + separation->min1 = min1; + separation->max1 = max1; + } + else + { + separation->plane = PxPlane(-n, midpoint); + separation->min0 = -max0; + separation->max0 = -min0; + separation->min1 = -max1; + separation->max1 = -min1; + } + } + } + return true; +} + + +namespace GjkInternal +{ +using namespace physx::aos; + +PX_NOALIAS PX_FORCE_INLINE BoolV PointOutsideOfPlane4(const Vec3VArg _a, const Vec3VArg _b, const Vec3VArg _c, const Vec3VArg _d) +{ + // this is not 0 because of the following scenario: + // All the points lie on the same plane and the plane goes through the origin (0,0,0). + // On the Wii U, the math below has the problem that when point A gets projected on the + // plane cumputed by A, B, C, the distance to the plane might not be 0 for the mentioned + // scenario but a small positive or negative value. This can lead to the wrong boolean + // results. Using a small negative value as threshold is more conservative but safer. + const Vec4V zero = V4Load(-1e-6); + + const Vec3V ab = V3Sub(_b, _a); + const Vec3V ac = V3Sub(_c, _a); + const Vec3V ad = V3Sub(_d, _a); + const Vec3V bd = V3Sub(_d, _b); + const Vec3V bc = V3Sub(_c, _b); + + const Vec3V v0 = V3Cross(ab, ac); + const Vec3V v1 = V3Cross(ac, ad); + const Vec3V v2 = V3Cross(ad, ab); + const Vec3V v3 = V3Cross(bd, bc); + + const FloatV signa0 = V3Dot(v0, _a); + const FloatV signa1 = V3Dot(v1, _a); + const FloatV signa2 = V3Dot(v2, _a); + const FloatV signd3 = V3Dot(v3, _a); + + const FloatV signd0 = V3Dot(v0, _d); + const FloatV signd1 = V3Dot(v1, _b); + const FloatV signd2 = V3Dot(v2, _c); + const FloatV signa3 = V3Dot(v3, _b); + + const Vec4V signa = V4Merge(signa0, signa1, signa2, signa3); + const Vec4V signd = V4Merge(signd0, signd1, signd2, signd3); + return V4IsGrtrOrEq(V4Mul(signa, signd), zero);//same side, outside of the plane +} + +PX_NOALIAS PX_FORCE_INLINE Vec3V closestPtPointSegment(const Vec3VArg a, const Vec3VArg b) +{ + const FloatV zero = FZero(); + const FloatV one = FOne(); + + //Test degenerated case + const Vec3V ab = V3Sub(b, a); + const FloatV denom = V3Dot(ab, ab); + const Vec3V ap = V3Neg(a);//V3Sub(origin, a); + const FloatV nom = V3Dot(ap, ab); + const BoolV con = FIsEq(denom, zero); + const FloatV tValue = FClamp(FDiv(nom, denom), zero, one); + const FloatV t = FSel(con, zero, tValue); + + return V3Sel(con, a, V3ScaleAdd(ab, t, a)); +} + +PX_NOALIAS PX_FORCE_INLINE Vec3V closestPtPointSegment(const Vec3VArg Q0, const Vec3VArg Q1, const Vec3VArg A0, const Vec3VArg A1, + const Vec3VArg B0, const Vec3VArg B1, PxU32& size, Vec3V& closestA, Vec3V& closestB) +{ + const Vec3V a = Q0; + const Vec3V b = Q1; + + const BoolV bTrue = BTTTT(); + const FloatV zero = FZero(); + const FloatV one = FOne(); + + //Test degenerated case + const Vec3V ab = V3Sub(b, a); + const FloatV denom = V3Dot(ab, ab); + const Vec3V ap = V3Neg(a);//V3Sub(origin, a); + const FloatV nom = V3Dot(ap, ab); + const BoolV con = FIsEq(denom, zero); + + if (BAllEq(con, bTrue)) + { + size = 1; + closestA = A0; + closestB = B0; + return Q0; + } + + const Vec3V v = V3Sub(A1, A0); + const Vec3V w = V3Sub(B1, B0); + const FloatV tValue = FClamp(FDiv(nom, denom), zero, one); + const FloatV t = FSel(con, zero, tValue); + + const Vec3V tempClosestA = V3ScaleAdd(v, t, A0); + const Vec3V tempClosestB = V3ScaleAdd(w, t, B0); + closestA = tempClosestA; + closestB = tempClosestB; + return V3Sub(tempClosestA, tempClosestB); +} + +PX_NOALIAS Vec3V closestPtPointSegmentTesselation(const Vec3VArg Q0, const Vec3VArg Q1, const Vec3VArg A0, const Vec3VArg A1, + const Vec3VArg B0, const Vec3VArg B1, PxU32& size, Vec3V& closestA, Vec3V& closestB) +{ + const FloatV half = FHalf(); + + const FloatV targetSegmentLengthSq = FLoad(10000.f);//100 unit + + Vec3V q0 = Q0; + Vec3V q1 = Q1; + Vec3V a0 = A0; + Vec3V a1 = A1; + Vec3V b0 = B0; + Vec3V b1 = B1; + + do + { + const Vec3V midPoint = V3Scale(V3Add(q0, q1), half); + const Vec3V midA = V3Scale(V3Add(a0, a1), half); + const Vec3V midB = V3Scale(V3Add(b0, b1), half); + + const Vec3V v = V3Sub(midPoint, q0); + const FloatV sqV = V3Dot(v, v); + if (FAllGrtr(targetSegmentLengthSq, sqV)) + break; + //split the segment into half + const Vec3V tClos0 = closestPtPointSegment(q0, midPoint); + const FloatV sqDist0 = V3Dot(tClos0, tClos0); + + const Vec3V tClos1 = closestPtPointSegment(q1, midPoint); + const FloatV sqDist1 = V3Dot(tClos1, tClos1); + //const BoolV con = FIsGrtr(sqDist0, sqDist1); + if (FAllGrtr(sqDist0, sqDist1)) + { + //segment [m, q1] + q0 = midPoint; + a0 = midA; + b0 = midB; + } + else + { + //segment [q0, m] + q1 = midPoint; + a1 = midA; + b1 = midB; + } + + } while (1); + + return closestPtPointSegment(q0, q1, a0, a1, b0, b1, size, closestA, closestB); +} + +PX_NOALIAS Vec3V closestPtPointTriangleTesselation(const Vec3V* PX_RESTRICT Q, const Vec3V* PX_RESTRICT A, const Vec3V* PX_RESTRICT B, const PxU32* PX_RESTRICT indices, PxU32& size, Vec3V& closestA, Vec3V& closestB) +{ + size = 3; + const FloatV zero = FZero(); + const FloatV eps = FEps(); + const FloatV half = FHalf(); + const BoolV bTrue = BTTTT(); + const FloatV four = FLoad(4.f); + const FloatV sixty = FLoad(100.f); + + const PxU32 ind0 = indices[0]; + const PxU32 ind1 = indices[1]; + const PxU32 ind2 = indices[2]; + + const Vec3V a = Q[ind0]; + const Vec3V b = Q[ind1]; + const Vec3V c = Q[ind2]; + + Vec3V ab_ = V3Sub(b, a); + Vec3V ac_ = V3Sub(c, a); + Vec3V bc_ = V3Sub(b, c); + + const FloatV dac_ = V3Dot(ac_, ac_); + const FloatV dbc_ = V3Dot(bc_, bc_); + if (FAllGrtrOrEq(eps, FMin(dac_, dbc_))) + { + //degenerate + size = 2; + return closestPtPointSegment(Q[ind0], Q[ind1], A[ind0], A[ind1], B[ind0], B[ind1], size, closestA, closestB); + } + + Vec3V ap = V3Neg(a); + Vec3V bp = V3Neg(b); + Vec3V cp = V3Neg(c); + + FloatV d1 = V3Dot(ab_, ap); // snom + FloatV d2 = V3Dot(ac_, ap); // tnom + FloatV d3 = V3Dot(ab_, bp); // -sdenom + FloatV d4 = V3Dot(ac_, bp); // unom = d4 - d3 + FloatV d5 = V3Dot(ab_, cp); // udenom = d5 - d6 + FloatV d6 = V3Dot(ac_, cp); // -tdenom + /* FloatV unom = FSub(d4, d3); + FloatV udenom = FSub(d5, d6);*/ + + FloatV va = FNegScaleSub(d5, d4, FMul(d3, d6));//edge region of BC + FloatV vb = FNegScaleSub(d1, d6, FMul(d5, d2));//edge region of AC + FloatV vc = FNegScaleSub(d3, d2, FMul(d1, d4));//edge region of AB + + //check if p in vertex region outside a + const BoolV con00 = FIsGrtrOrEq(zero, d1); // snom <= 0 + const BoolV con01 = FIsGrtrOrEq(zero, d2); // tnom <= 0 + const BoolV con0 = BAnd(con00, con01); // vertex region a + if (BAllEq(con0, bTrue)) + { + //size = 1; + closestA = A[ind0]; + closestB = B[ind0]; + return Q[ind0]; + } + + //check if p in vertex region outside b + const BoolV con10 = FIsGrtrOrEq(d3, zero); + const BoolV con11 = FIsGrtrOrEq(d3, d4); + const BoolV con1 = BAnd(con10, con11); // vertex region b + if (BAllEq(con1, bTrue)) + { + /*size = 1; + indices[0] = ind1;*/ + closestA = A[ind1]; + closestB = B[ind1]; + return Q[ind1]; + } + + + //check if p in vertex region outside of c + const BoolV con20 = FIsGrtrOrEq(d6, zero); + const BoolV con21 = FIsGrtrOrEq(d6, d5); + const BoolV con2 = BAnd(con20, con21); // vertex region c + if (BAllEq(con2, bTrue)) + { + closestA = A[ind2]; + closestB = B[ind2]; + return Q[ind2]; + } + + //check if p in edge region of AB + const BoolV con30 = FIsGrtrOrEq(zero, vc); + const BoolV con31 = FIsGrtrOrEq(d1, zero); + const BoolV con32 = FIsGrtrOrEq(zero, d3); + const BoolV con3 = BAnd(con30, BAnd(con31, con32)); + + if (BAllEq(con3, bTrue)) + { + //size = 2; + //p in edge region of AB, split AB + return closestPtPointSegmentTesselation(Q[ind0], Q[ind1], A[ind0], A[ind1], B[ind0], B[ind1], size, closestA, closestB); + } + + //check if p in edge region of BC + const BoolV con40 = FIsGrtrOrEq(zero, va); + const BoolV con41 = FIsGrtrOrEq(d4, d3); + const BoolV con42 = FIsGrtrOrEq(d5, d6); + const BoolV con4 = BAnd(con40, BAnd(con41, con42)); + + if (BAllEq(con4, bTrue)) + { + //p in edge region of BC, split BC + return closestPtPointSegmentTesselation(Q[ind1], Q[ind2], A[ind1], A[ind2], B[ind1], B[ind2], size, closestA, closestB); + } + + //check if p in edge region of AC + const BoolV con50 = FIsGrtrOrEq(zero, vb); + const BoolV con51 = FIsGrtrOrEq(d2, zero); + const BoolV con52 = FIsGrtrOrEq(zero, d6); + const BoolV con5 = BAnd(con50, BAnd(con51, con52)); + + if (BAllEq(con5, bTrue)) + { + //p in edge region of AC, split AC + return closestPtPointSegmentTesselation(Q[ind0], Q[ind2], A[ind0], A[ind2], B[ind0], B[ind2], size, closestA, closestB); + } + + size = 3; + + Vec3V q0 = Q[ind0]; + Vec3V q1 = Q[ind1]; + Vec3V q2 = Q[ind2]; + Vec3V a0 = A[ind0]; + Vec3V a1 = A[ind1]; + Vec3V a2 = A[ind2]; + Vec3V b0 = B[ind0]; + Vec3V b1 = B[ind1]; + Vec3V b2 = B[ind2]; + + do + { + + const Vec3V ab = V3Sub(q1, q0); + const Vec3V ac = V3Sub(q2, q0); + const Vec3V bc = V3Sub(q2, q1); + + const FloatV dab = V3Dot(ab, ab); + const FloatV dac = V3Dot(ac, ac); + const FloatV dbc = V3Dot(bc, bc); + + const FloatV fMax = FMax(dab, FMax(dac, dbc)); + const FloatV fMin = FMin(dab, FMin(dac, dbc)); + + const Vec3V w = V3Cross(ab, ac); + + const FloatV area = V3Length(w); + const FloatV ratio = FDiv(FSqrt(fMax), FSqrt(fMin)); + if (FAllGrtr(four, ratio) && FAllGrtr(sixty, area)) + break; + + //calculate the triangle normal + const Vec3V triNormal = V3Normalize(w); + + PX_ASSERT(V3AllEq(triNormal, V3Zero()) == 0); + + + //split the longest edge + if (FAllGrtrOrEq(dab, dac) && FAllGrtrOrEq(dab, dbc)) + { + //split edge q0q1 + const Vec3V midPoint = V3Scale(V3Add(q0, q1), half); + const Vec3V midA = V3Scale(V3Add(a0, a1), half); + const Vec3V midB = V3Scale(V3Add(b0, b1), half); + + const Vec3V v = V3Sub(midPoint, q2); + const Vec3V n = V3Normalize(V3Cross(v, triNormal)); + + const FloatV d = FNeg(V3Dot(n, midPoint)); + const FloatV dp = FAdd(V3Dot(n, q0), d); + const FloatV sum = FMul(d, dp); + + if (FAllGrtr(sum, zero)) + { + //q0 and origin at the same side, split triangle[q0, m, q2] + q1 = midPoint; + a1 = midA; + b1 = midB; + } + else + { + //q1 and origin at the same side, split triangle[m, q1, q2] + q0 = midPoint; + a0 = midA; + b0 = midB; + } + + } + else if (FAllGrtrOrEq(dac, dbc)) + { + //split edge q0q2 + const Vec3V midPoint = V3Scale(V3Add(q0, q2), half); + const Vec3V midA = V3Scale(V3Add(a0, a2), half); + const Vec3V midB = V3Scale(V3Add(b0, b2), half); + + const Vec3V v = V3Sub(midPoint, q1); + const Vec3V n = V3Normalize(V3Cross(v, triNormal)); + + const FloatV d = FNeg(V3Dot(n, midPoint)); + const FloatV dp = FAdd(V3Dot(n, q0), d); + const FloatV sum = FMul(d, dp); + + if (FAllGrtr(sum, zero)) + { + //q0 and origin at the same side, split triangle[q0, q1, m] + q2 = midPoint; + a2 = midA; + b2 = midB; + } + else + { + //q2 and origin at the same side, split triangle[m, q1, q2] + q0 = midPoint; + a0 = midA; + b0 = midB; + } + } + else + { + //split edge q1q2 + const Vec3V midPoint = V3Scale(V3Add(q1, q2), half); + const Vec3V midA = V3Scale(V3Add(a1, a2), half); + const Vec3V midB = V3Scale(V3Add(b1, b2), half); + + const Vec3V v = V3Sub(midPoint, q0); + const Vec3V n = V3Normalize(V3Cross(v, triNormal)); + + const FloatV d = FNeg(V3Dot(n, midPoint)); + const FloatV dp = FAdd(V3Dot(n, q1), d); + const FloatV sum = FMul(d, dp); + + if (FAllGrtr(sum, zero)) + { + //q1 and origin at the same side, split triangle[q0, q1, m] + q2 = midPoint; + a2 = midA; + b2 = midB; + } + else + { + //q2 and origin at the same side, split triangle[q0, m, q2] + q1 = midPoint; + a1 = midA; + b1 = midB; + } + + + } + } while (1); + + //P must project inside face region. Compute Q using Barycentric coordinates + ab_ = V3Sub(q1, q0); + ac_ = V3Sub(q2, q0); + ap = V3Neg(q0); + bp = V3Neg(q1); + cp = V3Neg(q2); + + d1 = V3Dot(ab_, ap); // snom + d2 = V3Dot(ac_, ap); // tnom + d3 = V3Dot(ab_, bp); // -sdenom + d4 = V3Dot(ac_, bp); // unom = d4 - d3 + d5 = V3Dot(ab_, cp); // udenom = d5 - d6 + d6 = V3Dot(ac_, cp); // -tdenom + + va = FNegScaleSub(d5, d4, FMul(d3, d6));//edge region of BC + vb = FNegScaleSub(d1, d6, FMul(d5, d2));//edge region of AC + vc = FNegScaleSub(d3, d2, FMul(d1, d4));//edge region of AB + + const FloatV toRecipD = FAdd(va, FAdd(vb, vc)); + const FloatV denom = FRecip(toRecipD);//V4GetW(recipTmp); + const Vec3V v0 = V3Sub(a1, a0); + const Vec3V v1 = V3Sub(a2, a0); + const Vec3V w0 = V3Sub(b1, b0); + const Vec3V w1 = V3Sub(b2, b0); + + const FloatV t = FMul(vb, denom); + const FloatV w = FMul(vc, denom); + const Vec3V vA1 = V3Scale(v1, w); + const Vec3V vB1 = V3Scale(w1, w); + const Vec3V tempClosestA = V3Add(a0, V3ScaleAdd(v0, t, vA1)); + const Vec3V tempClosestB = V3Add(b0, V3ScaleAdd(w0, t, vB1)); + closestA = tempClosestA; + closestB = tempClosestB; + return V3Sub(tempClosestA, tempClosestB); +} + +PX_NOALIAS Vec3V closestPtPointTetrahedronTesselation(Vec3V* PX_RESTRICT Q, Vec3V* PX_RESTRICT A, Vec3V* PX_RESTRICT B, PxU32& size, Vec3V& closestA, Vec3V& closestB) +{ + const FloatV eps = FEps(); + const Vec3V zeroV = V3Zero(); + PxU32 tempSize = size; + + FloatV bestSqDist = FLoad(PX_MAX_REAL); + const Vec3V a = Q[0]; + const Vec3V b = Q[1]; + const Vec3V c = Q[2]; + const Vec3V d = Q[3]; + const BoolV bTrue = BTTTT(); + const BoolV bFalse = BFFFF(); + + //degenerated + const Vec3V ad = V3Sub(d, a); + const Vec3V bd = V3Sub(d, b); + const Vec3V cd = V3Sub(d, c); + const FloatV dad = V3Dot(ad, ad); + const FloatV dbd = V3Dot(bd, bd); + const FloatV dcd = V3Dot(cd, cd); + const FloatV fMin = FMin(dad, FMin(dbd, dcd)); + if (FAllGrtr(eps, fMin)) + { + size = 3; + PxU32 tempIndices[] = { 0, 1, 2 }; + return closestPtPointTriangleTesselation(Q, A, B, tempIndices, size, closestA, closestB); + } + + Vec3V _Q[] = { Q[0], Q[1], Q[2], Q[3] }; + Vec3V _A[] = { A[0], A[1], A[2], A[3] }; + Vec3V _B[] = { B[0], B[1], B[2], B[3] }; + + PxU32 indices[3] = { 0, 1, 2 }; + + const BoolV bIsOutside4 = PointOutsideOfPlane4(a, b, c, d); + + if (BAllEq(bIsOutside4, bFalse)) + { + //origin is inside the tetrahedron, we are done + return zeroV; + } + + Vec3V result = zeroV; + Vec3V tempClosestA, tempClosestB; + + if (BAllEq(BGetX(bIsOutside4), bTrue)) + { + + PxU32 tempIndices[] = { 0, 1, 2 }; + PxU32 _size = 3; + + result = closestPtPointTriangleTesselation(_Q, _A, _B, tempIndices, _size, tempClosestA, tempClosestB); + + const FloatV sqDist = V3Dot(result, result); + bestSqDist = sqDist; + + indices[0] = tempIndices[0]; + indices[1] = tempIndices[1]; + indices[2] = tempIndices[2]; + + tempSize = _size; + closestA = tempClosestA; + closestB = tempClosestB; + } + + if (BAllEq(BGetY(bIsOutside4), bTrue)) + { + + PxU32 tempIndices[] = { 0, 2, 3 }; + + PxU32 _size = 3; + + const Vec3V q = closestPtPointTriangleTesselation(_Q, _A, _B, tempIndices, _size, tempClosestA, tempClosestB); + + const FloatV sqDist = V3Dot(q, q); + const BoolV con = FIsGrtr(bestSqDist, sqDist); + if (BAllEq(con, bTrue)) + { + result = q; + bestSqDist = sqDist; + indices[0] = tempIndices[0]; + indices[1] = tempIndices[1]; + indices[2] = tempIndices[2]; + + tempSize = _size; + closestA = tempClosestA; + closestB = tempClosestB; + } + } + + if (BAllEq(BGetZ(bIsOutside4), bTrue)) + { + + PxU32 tempIndices[] = { 0, 3, 1 }; + PxU32 _size = 3; + + const Vec3V q = closestPtPointTriangleTesselation(_Q, _A, _B, tempIndices, _size, tempClosestA, tempClosestB); + + const FloatV sqDist = V3Dot(q, q); + const BoolV con = FIsGrtr(bestSqDist, sqDist); + if (BAllEq(con, bTrue)) + { + result = q; + bestSqDist = sqDist; + indices[0] = tempIndices[0]; + indices[1] = tempIndices[1]; + indices[2] = tempIndices[2]; + tempSize = _size; + closestA = tempClosestA; + closestB = tempClosestB; + } + + } + + if (BAllEq(BGetW(bIsOutside4), bTrue)) + { + + PxU32 tempIndices[] = { 1, 3, 2 }; + PxU32 _size = 3; + + const Vec3V q = closestPtPointTriangleTesselation(_Q, _A, _B, tempIndices, _size, tempClosestA, tempClosestB); + + const FloatV sqDist = V3Dot(q, q); + const BoolV con = FIsGrtr(bestSqDist, sqDist); + + if (BAllEq(con, bTrue)) + { + result = q; + bestSqDist = sqDist; + + indices[0] = tempIndices[0]; + indices[1] = tempIndices[1]; + indices[2] = tempIndices[2]; + + tempSize = _size; + closestA = tempClosestA; + closestB = tempClosestB; + } + } + + A[0] = _A[indices[0]]; A[1] = _A[indices[1]]; A[2] = _A[indices[2]]; + B[0] = _B[indices[0]]; B[1] = _B[indices[1]]; B[2] = _B[indices[2]]; + Q[0] = _Q[indices[0]]; Q[1] = _Q[indices[1]]; Q[2] = _Q[indices[2]]; + + + size = tempSize; + return result; +} + +PX_NOALIAS PX_FORCE_INLINE Vec3V doTesselation(Vec3V* PX_RESTRICT Q, Vec3V* PX_RESTRICT A, Vec3V* PX_RESTRICT B, + const Vec3VArg support, const Vec3VArg supportA, const Vec3VArg supportB, PxU32& size, Vec3V& closestA, Vec3V& closestB) +{ + switch (size) + { + case 1: + { + closestA = supportA; + closestB = supportB; + return support; + } + case 2: + { + return closestPtPointSegmentTesselation(Q[0], support, A[0], supportA, B[0], supportB, size, closestA, closestB); + } + case 3: + { + + PxU32 tempIndices[3] = { 0, 1, 2 }; + return closestPtPointTriangleTesselation(Q, A, B, tempIndices, size, closestA, closestB); + } + case 4: + { + return closestPtPointTetrahedronTesselation(Q, A, B, size, closestA, closestB); + } + default: + PX_ASSERT(0); + } + return support; +} + +struct ConvexV +{ + void calcExtent(const Vec3V& dir, PxF32& minOut, PxF32& maxOut) const + { + // Expand + const Vec4V x = Vec4V_From_FloatV(V3GetX(dir)); + const Vec4V y = Vec4V_From_FloatV(V3GetY(dir)); + const Vec4V z = Vec4V_From_FloatV(V3GetZ(dir)); + + const Vec4V* src = mAovVertices; + const Vec4V* end = src + mNumAovVertices * 3; + + // Do first step + Vec4V max = V4MulAdd(x, src[0], V4MulAdd(y, src[1], V4Mul(z, src[2]))); + Vec4V min = max; + src += 3; + // Do the rest + for (; src < end; src += 3) + { + const Vec4V dot = V4MulAdd(x, src[0], V4MulAdd(y, src[1], V4Mul(z, src[2]))); + max = V4Max(dot, max); + min = V4Min(dot, min); + } + FStore(V4ExtractMax(max), &maxOut); + FStore(V4ExtractMin(min), &minOut); + } + Vec3V calcSupport(const Vec3V& dir) const + { + // Expand + const Vec4V x = Vec4V_From_FloatV(V3GetX(dir)); + const Vec4V y = Vec4V_From_FloatV(V3GetY(dir)); + const Vec4V z = Vec4V_From_FloatV(V3GetZ(dir)); + + PX_ALIGN(16, static const PxF32 index4const[]) = { 0.0f, 1.0f, 2.0f, 3.0f }; + Vec4V index4 = *(const Vec4V*)index4const; + PX_ALIGN(16, static const PxF32 delta4const[]) = { 4.0f, 4.0f, 4.0f, 4.0f }; + const Vec4V delta4 = *(const Vec4V*)delta4const; + + const Vec4V* src = mAovVertices; + const Vec4V* end = src + mNumAovVertices * 3; + + // Do first step + Vec4V max = V4MulAdd(x, src[0], V4MulAdd(y, src[1], V4Mul(z, src[2]))); + Vec4V maxIndex = index4; + index4 = V4Add(index4, delta4); + src += 3; + // Do the rest + for (; src < end; src += 3) + { + const Vec4V dot = V4MulAdd(x, src[0], V4MulAdd(y, src[1], V4Mul(z, src[2]))); + const BoolV cmp = V4IsGrtr(dot, max); + max = V4Max(dot, max); + maxIndex = V4Sel(cmp, index4, maxIndex); + index4 = V4Add(index4, delta4); + } + Vec4V horiMax = Vec4V_From_FloatV(V4ExtractMax(max)); + PxU32 mask = BGetBitMask(V4IsEq(horiMax, max)); + const PxU32 simdIndex = (0x12131210 >> (mask + mask)) & PxU32(3); + + /// NOTE! Could be load hit store + /// Would be better to have all simd. + PX_ALIGN(16, PxF32 f[4]); + V4StoreA(maxIndex, f); + PxU32 index = PxU32(PxI32(f[simdIndex])); + + const Vec4V* aovIndex = (mAovVertices + (index >> 2) * 3); + const PxF32* aovOffset = ((const PxF32*)aovIndex) + (index & 3); + + return Vec3V_From_Vec4V(V4LoadXYZW(aovOffset[0], aovOffset[4], aovOffset[8], 1.0f)); + } + + const Vec4V* mAovVertices; ///< Vertices storex x,x,x,x, y,y,y,y, z,z,z,z + PxU32 mNumAovVertices; ///< Number of groups of 4 of vertices +}; + +struct Output +{ + /// Get the normal to push apart in direction from A to B + PX_FORCE_INLINE Vec3V getNormal() const { const Vec3V dir = V3Sub(mClosestB, mClosestA); if (!V3AllGrtr(V3Eps(), V3Abs(dir))) return V3Normalize(dir); return V3Zero(); } + Vec3V mClosestA; ///< Closest point on A + Vec3V mClosestB; ///< Closest point on B + FloatV mDistSq; +}; + +enum Status +{ + STATUS_NON_INTERSECT, + STATUS_CONTACT, + STATUS_DEGENERATE, +}; + +Status Collide(const Vec3V& initialDir, const ConvexV& convexA, const Mat34V& bToA, const ConvexV& convexB, Output& out) +{ + Vec3V Q[4]; + Vec3V A[4]; + Vec3V B[4]; + + Mat33V aToB = M34Trnsps33(bToA); + + PxU32 size = 0; + + const Vec3V zeroV = V3Zero(); + const BoolV bTrue = BTTTT(); + + //Vec3V v = V3UnitX(); + Vec3V v = V3Sel(FIsGrtr(V3Dot(initialDir, initialDir), FZero()), initialDir, V3UnitX()); + + //const FloatV minMargin = zero; + //const FloatV eps2 = FMul(minMargin, FLoad(0.01f)); + //FloatV eps2 = zero; + FloatV eps2 = FLoad(1e-6f); + const FloatV epsRel = FLoad(0.000225f); + + Vec3V closA(zeroV), closB(zeroV); + FloatV sDist = FMax(); + FloatV minDist = sDist; + Vec3V closAA = zeroV; + Vec3V closBB = zeroV; + + BoolV bNotTerminated = bTrue; + BoolV bCon = bTrue; + + do + { + minDist = sDist; + closAA = closA; + closBB = closB; + + PxU32 index = size++; + PX_ASSERT(index < 4); + + const Vec3V supportA = convexA.calcSupport(V3Neg(v)); + const Vec3V supportB = M34MulV3(bToA, convexB.calcSupport(M33MulV3(aToB, v))); + const Vec3V support = Vec3V_From_Vec4V(Vec4V_From_Vec3V(V3Sub(supportA, supportB))); + + A[index] = supportA; + B[index] = supportB; + Q[index] = support; + + const FloatV signDist = V3Dot(v, support); + const FloatV tmp0 = FSub(sDist, signDist); + if (FAllGrtr(FMul(epsRel, sDist), tmp0)) + { + out.mClosestA = closA; + out.mClosestB = closB; + out.mDistSq = sDist; + return STATUS_NON_INTERSECT; + } + + //calculate the closest point between two convex hull + v = doTesselation(Q, A, B, support, supportA, supportB, size, closA, closB); + sDist = V3Dot(v, v); + bCon = FIsGrtr(minDist, sDist); + + bNotTerminated = BAnd(FIsGrtr(sDist, eps2), bCon); + } while (BAllEq(bNotTerminated, bTrue)); + + out.mClosestA = V3Sel(bCon, closA, closAA); + out.mClosestB = V3Sel(bCon, closB, closBB); + out.mDistSq = FSel(bCon, sDist, minDist); + return Status(BAllEq(bCon, bTrue) == 1 ? STATUS_CONTACT : STATUS_DEGENERATE); +} + +Status CollidePoint(const ConvexV& convexA, const Vec3V& pointB, Output& out) +{ + Vec3V Q[4]; + Vec3V A[4]; + Vec3V B[4]; + + PxU32 size = 0; + + const Vec3V zeroV = V3Zero(); + //const FloatV zero = FZero(); + const BoolV bTrue = BTTTT(); + + Vec3V v = V3UnitX(); + + //const FloatV minMargin = zero; + //const FloatV eps2 = FMul(minMargin, FLoad(0.01f)); + //FloatV eps2 = zero; + FloatV eps2 = FLoad(1e-6f); + const FloatV epsRel = FLoad(0.000225f); + + Vec3V closA(zeroV), closB(zeroV); + FloatV sDist = FMax(); + FloatV minDist = sDist; + Vec3V closAA = zeroV; + Vec3V closBB = zeroV; + + BoolV bNotTerminated = bTrue; + BoolV bCon = bTrue; + + do + { + minDist = sDist; + closAA = closA; + closBB = closB; + + PxU32 index = size++; + PX_ASSERT(index < 4); + + const Vec3V supportA = convexA.calcSupport(V3Neg(v)); + const Vec3V supportB = pointB; + const Vec3V support = Vec3V_From_Vec4V(Vec4V_From_Vec3V(V3Sub(supportA, supportB))); + + A[index] = supportA; + B[index] = supportB; + Q[index] = support; + + const FloatV signDist = V3Dot(v, support); + const FloatV tmp0 = FSub(sDist, signDist); + if (FAllGrtr(FMul(epsRel, sDist), tmp0)) + { + out.mClosestA = closA; + out.mClosestB = closB; + out.mDistSq = sDist; + return STATUS_NON_INTERSECT; + } + + //calculate the closest point between two convex hull + v = doTesselation(Q, A, B, support, supportA, supportB, size, closA, closB); + sDist = V3Dot(v, v); + bCon = FIsGrtr(minDist, sDist); + + bNotTerminated = BAnd(FIsGrtr(sDist, eps2), bCon); + } while (BAllEq(bNotTerminated, bTrue)); + + out.mClosestA = V3Sel(bCon, closA, closAA); + out.mClosestB = V3Sel(bCon, closB, closBB); + out.mDistSq = FSel(bCon, sDist, minDist); + return Status(BAllEq(bCon, bTrue) == 1 ? STATUS_CONTACT : STATUS_DEGENERATE); +} + + +} // namespace GjkInternal + +static void _arrayVec3ToVec4(const PxVec3* src, physx::aos::Vec4V* dst, PxU32 num) +{ + using namespace physx::aos; + const PxU32 num4 = num >> 2; + for (PxU32 i = 0; i < num4; i++, dst += 3, src += 4) + { + Vec3V v0 = V3LoadU(&src[0].x); + Vec3V v1 = V3LoadU(&src[1].x); + Vec3V v2 = V3LoadU(&src[2].x); + Vec3V v3 = V3LoadU(&src[3].x); + // Transpose + V4Transpose(v0, v1, v2, v3); + // Save + dst[0] = v0; + dst[1] = v1; + dst[2] = v2; + } + const PxU32 remain = num & 3; + if (remain) + { + Vec3V work[4]; + PxU32 i = 0; + for (; i < remain; i++) work[i] = V3LoadU(&src[i].x); + for (; i < 4; i++) work[i] = work[remain - 1]; + V4Transpose(work[0], work[1], work[2], work[3]); + dst[0] = work[0]; + dst[1] = work[1]; + dst[2] = work[2]; + } +} + +static void _arrayVec3ToVec4(const PxVec3* src, const physx::aos::Vec3V& scale, physx::aos::Vec4V* dst, PxU32 num) +{ + using namespace physx::aos; + + // If no scale - use the faster version + if (V3AllEq(scale, V3One())) + { + return _arrayVec3ToVec4(src, dst, num); + } + + const PxU32 num4 = num >> 2; + for (PxU32 i = 0; i < num4; i++, dst += 3, src += 4) + { + Vec3V v0 = V3Mul(scale, V3LoadU(&src[0].x)); + Vec3V v1 = V3Mul(scale, V3LoadU(&src[1].x)); + Vec3V v2 = V3Mul(scale, V3LoadU(&src[2].x)); + Vec3V v3 = V3Mul(scale, V3LoadU(&src[3].x)); + // Transpose + V4Transpose(v0, v1, v2, v3); + // Save + dst[0] = v0; + dst[1] = v1; + dst[2] = v2; + } + const PxU32 remain = num & 3; + if (remain) + { + Vec3V work[4]; + PxU32 i = 0; + for (; i < remain; i++) work[i] = V3Mul(scale, V3LoadU(&src[i].x)); + for (; i < 4; i++) work[i] = work[remain - 1]; + V4Transpose(work[0], work[1], work[2], work[3]); + dst[0] = work[0]; + dst[1] = work[1]; + dst[2] = work[2]; + } +} + +static void _calcSeparation(const GjkInternal::ConvexV& convexA, const physx::PxTransform& aToWorldIn, const physx::aos::Mat34V& bToA, const GjkInternal::ConvexV& convexB, const GjkInternal::Output& out, ConvexHullImpl::Separation& sep) +{ + using namespace physx::aos; + + Mat33V aToB = M34Trnsps33(bToA); + Vec3V normalA = out.getNormal(); + + convexA.calcExtent(normalA, sep.min0, sep.max0); + Vec3V normalB = M33MulV3(aToB, normalA); + convexB.calcExtent(normalB, sep.min1, sep.max1); + + { + // Offset the min max taking into account transform + // Distance of origin from B's space in As space in direction of the normal in As space should fix it... + PxF32 fix; + FStore(V3Dot(bToA.col3, normalA), &fix); + sep.min1 += fix; + sep.max1 += fix; + } + + // Looks like it's the plane at the midpoint + Vec3V center = V3Scale(V3Add(out.mClosestA, out.mClosestB), FLoad(0.5f)); + // Transform to world space + Mat34V aToWorld; + *(PxMat44*)&aToWorld = aToWorldIn; + // Put the normal in world space + Vec3V worldCenter = M34MulV3(aToWorld, center); + Vec3V worldNormal = M34Mul33V3(aToWorld, normalA); + + FloatV dist = V3Dot(worldNormal, worldCenter); + V3StoreU(worldNormal, sep.plane.n); + FStore(dist, &sep.plane.d); + sep.plane.d = -sep.plane.d; +} + +bool ConvexHullImpl::hullsInProximity(const ConvexHullImpl& hull0, const physx::PxTransform& localToWorldRT0In, const physx::PxVec3& scale0In, + const ConvexHullImpl& hull1, const physx::PxTransform& localToWorldRT1In, const physx::PxVec3& scale1In, + physx::PxF32 maxDistance, Separation* separation) +{ + using namespace physx::aos; + + const PxU32 numVerts0 = hull0.getVertexCount(); + const PxU32 numVerts1 = hull1.getVertexCount(); + const PxU32 numAov0 = (numVerts0 + 3) >> 2; + const PxU32 numAov1 = (numVerts1 + 3) >> 2; + +#if PX_ANDROID == 1 + Vec4V* verts0 = (Vec4V*)PxAllocaAligned((numAov0 + numAov1) * sizeof(Vec4V) * 3, 16); +#else + Vec4V* verts0 = (Vec4V*)PxAlloca((numAov0 + numAov1) * sizeof(Vec4V) * 3); +#endif + // Make sure it's aligned + PX_ASSERT((size_t(verts0) & 0xf) == 0); + + Vec4V* verts1 = verts0 + (numAov0 * 3); + + const Vec3V scale0 = V3LoadU(&scale0In.x); + const Vec3V scale1 = V3LoadU(&scale1In.x); + + _arrayVec3ToVec4(hull0.mParams->vertices.buf, scale0, verts0, numVerts0); + _arrayVec3ToVec4(hull1.mParams->vertices.buf, scale1, verts1, numVerts1); + + const PxTransform trans1To0 = localToWorldRT0In.transformInv(localToWorldRT1In); + + // Load into simd mat + Mat34V bToA; + *(PxMat44*)&bToA = trans1To0; + (*(PxMat44*)&bToA).column3.w = 0.0f; // AOS wants the 4th component of Vec3V to be 0 to work properly + + GjkInternal::ConvexV convexA; + GjkInternal::ConvexV convexB; + + convexA.mNumAovVertices = numAov0; + convexA.mAovVertices = verts0; + + convexB.mNumAovVertices = numAov1; + convexB.mAovVertices = verts1; + + // Take the origin of B in As space as the inital direction as it is 'the difference in transform origins B-A in A's space' + // Should be a good first guess + const Vec3V initialDir = bToA.col3; + GjkInternal::Output output; + GjkInternal::Status status = GjkInternal::Collide(initialDir, convexA, bToA, convexB, output); + + if (status == GjkInternal::STATUS_DEGENERATE) + { + // Calculate the tolerance from the extents + const PxVec3 extents0 = hull0.getBounds().getExtents(); + const PxVec3 extents1 = hull1.getBounds().getExtents(); + + const FloatV tolerance0 = V3ExtractMin(V3Mul(V3LoadU(&extents0.x), scale0)); + const FloatV tolerance1 = V3ExtractMin(V3Mul(V3LoadU(&extents1.x), scale1)); + + const FloatV tolerance = FMul(FAdd(tolerance0, tolerance1), FLoad(0.01f)); + const FloatV sqTolerance = FMul(tolerance, tolerance); + + status = FAllGrtr(sqTolerance, output.mDistSq) ? GjkInternal::STATUS_CONTACT : GjkInternal::STATUS_NON_INTERSECT; + } + + switch (status) + { + case GjkInternal::STATUS_CONTACT: + { + if (separation) + { + _calcSeparation(convexA, localToWorldRT0In, bToA, convexB, output, *separation); + } + return true; + } + default: + case GjkInternal::STATUS_NON_INTERSECT: + { + if (separation) + { + _calcSeparation(convexA, localToWorldRT0In, bToA, convexB, output, *separation); + } + PxF32 val; + FStore(output.mDistSq, &val); + return val < (maxDistance * maxDistance); + } + } +} + +static void _calcSeparation(const GjkInternal::ConvexV& convex, const physx::PxTransform& hullLocalToWorldRT, const PxVec3& sphereCenterWorld, PxF32 sphereRadius, const GjkInternal::Output& gjkOut, ConvexHullImpl::Separation& sep) +{ + using namespace physx::aos; + + const Vec3V normal = gjkOut.getNormal(); + + // Calc the plane normal (in world space) + V3StoreU(normal, sep.plane.n); + sep.plane.n = hullLocalToWorldRT.rotate(sep.plane.n); + + const PxF32 originOffset = sep.plane.n.dot(hullLocalToWorldRT.p); + convex.calcExtent(normal, sep.min0, sep.max0); + // The min and mix are in local space -> fix by taking into account world space origin + sep.min0 += originOffset; + sep.max0 += originOffset; + + // Calculate the sphere radius + const PxF32 centerDist = sep.plane.n.dot(sphereCenterWorld); + // Work out the spheres + sep.min1 = centerDist - sphereRadius; + sep.max1 = centerDist + sphereRadius; + // Set the plane distance + sep.plane.d = (sep.max0 + sep.min1) * -0.5f; +} + +bool ConvexHullImpl::sphereInProximity(const physx::PxTransform& hullLocalToWorldRT, const physx::PxVec3& hullScaleIn, + const physx::PxVec3& sphereWorldCenterIn, physx::PxF32 sphereRadius, + physx::PxF32 maxDistance, Separation* separation) +{ + using namespace physx::aos; + + const PxU32 numVerts = getVertexCount(); + const PxU32 numAov = (numVerts + 3) >> 2; + +#if PX_ANDROID == 1 + Vec4V* verts = (Vec4V*)PxAllocaAligned(numAov * sizeof(Vec4V) * 3, 16); +#else + Vec4V* verts = (Vec4V*)PxAlloca(numAov * sizeof(Vec4V) * 3); +#endif + // Make sure it's aligned + PX_ASSERT((size_t(verts) & 0xf) == 0); + + const Vec3V hullScale = V3LoadU(&hullScaleIn.x); + + _arrayVec3ToVec4(mParams->vertices.buf, hullScale, verts, numVerts); + + // Put the spheres center in the hulls space + Vec3V sphereCenterLocal = V3LoadU(hullLocalToWorldRT.transformInv(sphereWorldCenterIn)); + + // Load into simd mat + GjkInternal::ConvexV convex; + + convex.mNumAovVertices = numAov; + convex.mAovVertices = verts; + + // Calculate distance to center of sphere + GjkInternal::Output gjkOutput; + GjkInternal::Status status = GjkInternal::CollidePoint(convex, sphereCenterLocal, gjkOutput); + + if (status == GjkInternal::STATUS_DEGENERATE) + { + FloatV sphereRadiusV = FLoad(sphereRadius); + status = FAllGrtr(gjkOutput.mDistSq, FMul(sphereRadiusV, sphereRadiusV)) ? GjkInternal::STATUS_NON_INTERSECT : GjkInternal::STATUS_CONTACT; + } + + switch (status) + { + case GjkInternal::STATUS_CONTACT: + { + if (separation) + { + _calcSeparation(convex, hullLocalToWorldRT, sphereWorldCenterIn, sphereRadius, gjkOutput, *separation); + } + return true; + } + default: + case GjkInternal::STATUS_NON_INTERSECT: + { + if (separation) + { + _calcSeparation(convex, hullLocalToWorldRT, sphereWorldCenterIn, sphereRadius, gjkOutput, *separation); + } + const PxF32 maxTotal = maxDistance + sphereRadius; + PxF32 val; + FStore(gjkOutput.mDistSq, &val); + return val < (maxTotal * maxTotal); + } + } +} + +#if PX_PHYSICS_VERSION_MAJOR == 3 +bool ConvexHullImpl::intersects(const PxShape& shape, const PxTransform& localToWorldRT, const PxVec3& scale, float padding) const +{ + PX_UNUSED(localToWorldRT); + PX_UNUSED(scale); + PX_UNUSED(padding); + + switch (shape.getGeometryType()) + { + case PxGeometryType::ePLANE: // xxx todo - implement + return true; + case PxGeometryType::eSPHERE: // xxx todo - implement + return true; + case PxGeometryType::eBOX: // xxx todo - implement + return true; + case PxGeometryType::eCAPSULE: // xxx todo - implement + return true; + case PxGeometryType::eCONVEXMESH: // xxx todo - implement + return true; + case PxGeometryType::eTRIANGLEMESH: // xxx todo - implement + return true; + case PxGeometryType::eHEIGHTFIELD: // xxx todo - implement + return true; + default: + return false; + } +} +#endif + + + +bool ConvexHullImpl::rayCast(float& in, float& out, const PxVec3& orig, const PxVec3& dir, const PxTransform& localToWorldRT, const PxVec3& scale, PxVec3* normal) const +{ + const uint32_t numSlabs = getUniquePlaneNormalCount(); + + if (numSlabs == 0) + { + return false; + } + + // Create hull-local ray + PxVec3 localorig, localdir; + if (!worldToLocalRay(localorig, localdir, orig, dir, localToWorldRT, scale)) + { + return false; + } + + // Intersect with hull - local and world intersect 'times' are equal + + if (normal != NULL) + { + *normal = PxVec3(0.0f); // This will be the value if the ray origin is within the hull + } + + const float tol2 = (float)1.0e-14 * localdir.magnitudeSquared(); + + for (uint32_t slabN = 0; slabN < numSlabs; ++slabN) + { + ConvexHullParametersNS::Plane_Type& paramPlane = mParams->uniquePlanes.buf[slabN]; + const PxPlane plane(paramPlane.normal, paramPlane.d); + const float num0 = -plane.distance(localorig); + const float num1 = mParams->widths.buf[slabN] - num0; + const float den = localdir.dot(plane.n); + if (den* den <= tol2) // Needs to be <=, so that localdir = (0,0,0) will act as a point check + { + if (num0 < 0 || num1 < 0) + { + return false; + } + } + else + { + if (den > 0) + { + if (num0 < in * den || num1 < out * (-den)) + { + return false; + } + const float recipDen = 1.0f / den; + const float slabIn = -num1 * recipDen; + if (slabIn > in) + { + in = slabIn; + if (normal != NULL) + { + *normal = -plane.n; + } + } + out = PxMin(num0 * recipDen, out); + } + else + { + if (num0 < out * den || num1 < in * (-den)) + { + return false; + } + const float recipDen = 1.0f / den; + const float slabIn = num0 * recipDen; + if (slabIn > in) + { + in = slabIn; + if (normal != NULL) + { + *normal = plane.n; + } + } + out = PxMin(-num1 * recipDen, out); + } + } + } + + if (normal != NULL) + { + Cof44 cof(localToWorldRT, scale); + *normal = cof.getBlock33().transform(*normal); + normal->normalize(); + } + + return true; +} + +bool ConvexHullImpl::obbSweep(float& in, float& out, const PxVec3& worldBoxCenter, const PxVec3& worldBoxExtents, const PxVec3 worldBoxAxes[3], + const PxVec3& worldDisp, const PxTransform& localToWorldRT, const PxVec3& scale, PxVec3* normal) const +{ + float tNormal = -PX_MAX_F32; + + // Leave hull untransformed + const uint32_t numHullSlabs = getUniquePlaneNormalCount(); + const uint32_t numHullVerts = getVertexCount(); + ConvexHullParametersNS::Plane_Type* paramPlanes = mParams->uniquePlanes.buf; + const PxVec3* hullVerts = mParams->vertices.buf; + const float* hullWidths = mParams->widths.buf; + + // Create inverse transform for box and displacement + 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; + const PxVec3 invS(scale.y * scale.z * recipDetS, scale.z * scale.x * recipDetS, scale.x * scale.y * recipDetS); + + // Create box directions and displacement - the box will become a parallelepiped in general. For brevity we'll still call it a box. + PxVec3 disp = localToWorldRT.rotateInv(worldDisp); + disp = invS.multiply(disp); + + PxVec3 boxCenter = localToWorldRT.transformInv(worldBoxCenter); + boxCenter = invS.multiply(boxCenter); + + PxVec3 boxAxes[3]; // Will not be orthonormal in general + for (uint32_t i = 0; i < 3; ++i) + { + boxAxes[i] = localToWorldRT.rotateInv(worldBoxAxes[i]); + boxAxes[i] *= worldBoxExtents[i]; + boxAxes[i] = invS.multiply(boxAxes[i]); + } + + PxVec3 boxFaceNormals[3]; + float boxRadii[3]; + const float octantVol = boxAxes[0].dot(boxAxes[1].cross(boxAxes[2])); + for (uint32_t i = 0; i < 3; ++i) + { + boxFaceNormals[i] = boxAxes[(1 << i) & 3].cross(boxAxes[(3 >> i) ^ 1]); + const float norm = PxRecipSqrt(boxFaceNormals[i].magnitudeSquared()); + boxFaceNormals[i] *= norm; + boxRadii[i] = octantVol * norm; + } + + // Test box against slabs of hull + for (uint32_t slabIndex = 0; slabIndex < numHullSlabs; ++slabIndex) + { + ConvexHullParametersNS::Plane_Type& paramPlane = paramPlanes[slabIndex]; + const PxPlane plane(paramPlane.normal, paramPlane.d); + const float projectedRadius = PxAbs(plane.n.dot(boxAxes[0])) + PxAbs(plane.n.dot(boxAxes[1])) + PxAbs(plane.n.dot(boxAxes[2])); + const float projectedCenter = plane.n.dot(boxCenter); + const float vel0 = disp.dot(plane.n); + float tIn, tOut; + extentOverlapTimeInterval(tIn, tOut, vel0, projectedCenter - projectedRadius, projectedCenter + projectedRadius, -plane.d - hullWidths[slabIndex], -plane.d); + if (!updateTimeIntervalAndNormal(in, out, tNormal, normal, tIn, tOut, (-PxSign(vel0))*plane.n)) + { + return false; // No intersection within the input time interval + } + } + + // Test hull against box face directions + for (uint32_t faceIndex = 0; faceIndex < 3; ++faceIndex) + { + const PxVec3& faceNormal = boxFaceNormals[faceIndex]; + float min, max; + getExtent(min, max, hullVerts, numHullVerts, sizeof(PxVec3), faceNormal); + const float projectedRadius = boxRadii[faceIndex]; + const float projectedCenter = faceNormal.dot(boxCenter); + const float vel0 = disp.dot(faceNormal); + float tIn, tOut; + extentOverlapTimeInterval(tIn, tOut, vel0, projectedCenter - projectedRadius, projectedCenter + projectedRadius, min, max); + PxVec3 testFace = (-PxSign(vel0)) * faceNormal; + if (!updateTimeIntervalAndNormal(in, out, tNormal, normal, tIn, tOut, testFace)) + { + return false; // No intersection within the input time interval + } + } + + // Test hulls against cross-edge planes + const uint32_t numHullEdges = getUniqueEdgeDirectionCount(); + for (uint32_t hullEdgeIndex = 0; hullEdgeIndex < numHullEdges; ++hullEdgeIndex) + { + const PxVec3 hullEdge = getEdgeDirection(hullEdgeIndex); + for (uint32_t boxEdgeIndex = 0; boxEdgeIndex < 3; ++boxEdgeIndex) + { + PxVec3 n = hullEdge.cross(boxAxes[boxEdgeIndex]); + const float n2 = n.magnitudeSquared(); + if (n2 < PX_EPS_F32 * PX_EPS_F32) + { + continue; + } + n *= nvidia::recipSqrtFast(n2); + float vel0 = disp.dot(n); + // Choose normal direction such that the normal component of velocity is negative + if (vel0 > 0.0f) + { + vel0 = -vel0; + n *= -1.0f; + } + const float projectedRadius = PxAbs(n.dot(boxAxes[(1 << boxEdgeIndex) & 3])) + + PxAbs(n.dot(boxAxes[(3 >> boxEdgeIndex) ^ 1])); + const float projectedCenter = n.dot(boxCenter); + float min, max; + getExtent(min, max, hullVerts, numHullVerts, sizeof(PxVec3), n); + float tIn, tOut; + extentOverlapTimeInterval(tIn, tOut, vel0, projectedCenter - projectedRadius, projectedCenter + projectedRadius, min, max); + if (!updateTimeIntervalAndNormal(in, out, tNormal, normal, tIn, tOut, n)) + { + return false; // No intersection within the input time interval + } + } + } + + if (normal != NULL) + { + Cof44 cof(localToWorldRT, scale); + *normal = cof.getBlock33().transform(*normal); + normal->normalize(); + } + + return true; +} + +void ConvexHullImpl::extent(float& min, float& max, const PxVec3& normal) const +{ + getExtent(min, max, mParams->vertices.buf, (uint32_t)mParams->vertices.arraySizes[0], sizeof(PxVec3), normal); +} + +void ConvexHullImpl::fill(physx::Array<PxVec3>& outPoints, const PxTransform& localToWorldRT, const PxVec3& scale, + float spacing, float jitter, uint32_t maxPoints, bool adjustSpacing) const +{ + if (!maxPoints) + { + return; + } + + const uint32_t numPlanes = getPlaneCount(); + PxPlane* hull = (PxPlane*)PxAlloca(numPlanes * sizeof(PxPlane)); + float* recipDens = (float*)PxAlloca(numPlanes * sizeof(float)); + + const Cof44 cof(localToWorldRT, scale); // matrix of cofactors to correctly transform planes + PxMat44 srt(localToWorldRT); + srt.scale(PxVec4(scale, 1.f)); + + PxBounds3 bounds; + bounds.setEmpty(); + for (uint32_t vertN = 0; vertN < getVertexCount(); ++vertN) + { + PxVec3 worldVert = srt.transform(getVertex(vertN)); + bounds.include(worldVert); + } + + PxVec3 center, extents; + center = bounds.getCenter(); + extents = bounds.getExtents(); + + const PxVec3 areas(extents[1]*extents[2], extents[2]*extents[0], extents[0]*extents[1]); + const uint32_t axisN = areas[0] < areas[1] ? (areas[0] < areas[2] ? 0u : 2u) : (areas[1] < areas[2] ? 1u : 2u); + const uint32_t axisN1 = (axisN + 1) % 3; + const uint32_t axisN2 = (axisN + 2) % 3; + + if (adjustSpacing) + { + const float boxVolume = 8 * extents[0] * areas[0]; + const float cellVolume = spacing * spacing * spacing; + if (boxVolume > maxPoints * cellVolume) + { + spacing = PxPow(boxVolume / maxPoints, 0.333333333f); + } + } + + for (uint32_t planeN = 0; planeN < numPlanes; ++planeN) + { + cof.transform(getPlane(planeN), hull[planeN]); + recipDens[planeN] = PxAbs(hull[planeN].n[axisN]) > 1.0e-7 ? 1.0f / hull[planeN].n[axisN] : 0.0f; + } + + const float recipSpacing = 1.0f / spacing; + const int32_t num1 = (int32_t)(extents[axisN1] * recipSpacing); + const int32_t num2 = (int32_t)(extents[axisN2] * recipSpacing); + + QDSRand rnd; + const float scaledJitter = jitter * spacing; + + PxVec3 orig; + for (int32_t i1 = -num1; i1 <= num1; ++i1) + { + orig[axisN1] = i1 * spacing + center[axisN1]; + for (int32_t i2 = -num2; i2 <= num2; ++i2) + { + orig[axisN2] = i2 * spacing + center[axisN2]; + + float out = extents[axisN]; + float in = -out; + + orig[axisN] = center[axisN]; + + uint32_t planeN; + for (planeN = 0; planeN < numPlanes; ++planeN) + { + const PxPlane& plane = hull[planeN]; + const float recipDen = recipDens[planeN]; + const float num = -plane.distance(orig); + if (recipDen == 0.0f) + { + if (num < 0) + { + break; + } + } + else + { + const float t = num * recipDen; + if (recipDen > 0) + { + if (t < in) + { + break; + } + out = PxMin(t, out); + } + else + { + if (t > out) + { + break; + } + in = PxMax(t, in); + } + } + } + + if (planeN == numPlanes) + { + const float depth = out - in; + const float stop = orig[axisN] + out; + orig[axisN] += in + 0.5f * (depth - spacing * (int)(depth * recipSpacing)); + do + { + outPoints.pushBack(orig + scaledJitter * PxVec3(rnd.getNext(), rnd.getNext(), rnd.getNext())); + if (!--maxPoints) + { + return; + } + } + while ((orig[axisN] += spacing) <= stop); + } + } + } +} + +#if PX_PHYSICS_VERSION_MAJOR == 3 +uint32_t ConvexHullImpl::calculateCookedSizes(uint32_t& vertexCount, uint32_t& faceCount, bool inflated) const +{ + PX_UNUSED(inflated); + vertexCount = 0; + faceCount = 0; + nvidia::PsMemoryBuffer memStream; + memStream.setEndianMode(PxFileBuf::ENDIAN_NONE); + PxStreamFromFileBuf nvs(memStream); + physx::PxConvexMeshDesc meshDesc; + meshDesc.points.count = getVertexCount(); + meshDesc.points.data = &getVertex(0); + meshDesc.points.stride = sizeof(PxVec3); + meshDesc.flags = physx::PxConvexFlag::eCOMPUTE_CONVEX; + const float skinWidth = GetApexSDK()->getCookingInterface() != NULL ? GetApexSDK()->getCookingInterface()->getParams().skinWidth : 0.0f; + if (inflated && skinWidth > 0.0f) + { + meshDesc.flags |= physx::PxConvexFlag::eINFLATE_CONVEX; + } + if (GetApexSDK()->getCookingInterface()->cookConvexMesh(meshDesc, nvs)) + { + PxConvexMesh* convexMesh = GetApexSDK()->getPhysXSDK()->createConvexMesh(nvs); + if (convexMesh != NULL) + { + vertexCount = convexMesh->getNbVertices(); + faceCount = convexMesh->getNbPolygons(); + } + convexMesh->release(); + } + + return memStream.getFileLength(); +} + +bool ConvexHullImpl::reduceByCooking() +{ + bool reduced = false; + nvidia::PsMemoryBuffer memStream; + memStream.setEndianMode(PxFileBuf::ENDIAN_NONE); + PxStreamFromFileBuf nvs(memStream); + physx::PxConvexMeshDesc meshDesc; + meshDesc.points.count = getVertexCount(); + meshDesc.points.data = &getVertex(0); + meshDesc.points.stride = sizeof(PxVec3); + meshDesc.flags = physx::PxConvexFlag::eCOMPUTE_CONVEX; + if (GetApexSDK()->getCookingInterface()->cookConvexMesh(meshDesc, nvs)) + { + PxConvexMesh* convexMesh = GetApexSDK()->getPhysXSDK()->createConvexMesh(nvs); + if (convexMesh != NULL) + { + const uint32_t vertexCount = convexMesh->getNbVertices(); + reduced = vertexCount < getVertexCount(); + if (reduced) + { + buildFromConvexMesh(convexMesh); + } + } + convexMesh->release(); + } + + return reduced; +} +#endif + +struct IndexedAngle +{ + float angle; + uint32_t index; + + struct LT + { + PX_INLINE bool operator()(const IndexedAngle& a, const IndexedAngle& b) const + { + return a.angle < b.angle; + } + }; +}; + +#if PX_PHYSICS_VERSION_MAJOR == 3 +bool ConvexHullImpl::reduceHull(uint32_t maxVertexCount, uint32_t maxEdgeCount, uint32_t maxFaceCount, bool inflated) +{ + // Translate max = 0 => max = "infinity" + if (maxVertexCount == 0) + { + maxVertexCount = 0xFFFFFFFF; + } + + if (maxEdgeCount == 0) + { + maxEdgeCount = 0xFFFFFFFF; + } + + if (maxFaceCount == 0) + { + maxFaceCount = 0xFFFFFFFF; + } + + while (reduceByCooking()) {} + + // Calculate sizes + uint32_t cookedVertexCount; + uint32_t cookedFaceCount; + calculateCookedSizes(cookedVertexCount, cookedFaceCount, inflated); + uint32_t cookedEdgeCount = cookedVertexCount + cookedFaceCount - 2; + + // Return successfully if we've met the required limits + if (cookedVertexCount <= maxVertexCount && cookedEdgeCount <= maxEdgeCount && cookedFaceCount <= maxFaceCount) + { + return true; + } + + NvParameterized::Handle handle(*mParams, "vertices"); + uint32_t vertexCount = 0; + mParams->getArraySize(handle, (int32_t&)vertexCount); + if (vertexCount < 4) + { + return false; + } + physx::Array<PxVec3> vertices(vertexCount); + mParams->getParamVec3Array(handle, &vertices[0], (int32_t)vertexCount); + + // We have at least four vertices in our hull. Find the tetrahedron with the largest volume. + PxMat44 tetrahedron; + float maxTetVolume = 0.0f; + uint32_t tetIndices[4] = {0,1,2,3}; + for (uint32_t i = 0; i < vertexCount-3; ++i) + { + tetrahedron.column0 = PxVec4(vertices[i], 1.0f); + for (uint32_t j = i+1; j < vertexCount-2; ++j) + { + tetrahedron.column1 = PxVec4(vertices[j], 1.0f); + for (uint32_t k = j+1; k < vertexCount-1; ++k) + { + tetrahedron.column2 = PxVec4(vertices[k], 1.0f); + for (uint32_t l = k+1; l < vertexCount; ++l) + { + tetrahedron.column3 = PxVec4(vertices[l], 1.0f); + const float v = PxAbs(det(tetrahedron)); + if (v > maxTetVolume) + { + maxTetVolume = v; + tetIndices[0] = i; + tetIndices[1] = j; + tetIndices[2] = k; + tetIndices[3] = l; + } + } + } + } + } + + // Remove tetradhedral vertices from vertices array, put in reducedVertices array + physx::Array<PxVec3> reducedVertices(4); + for (uint32_t vNum = 4; vNum--;) + { + reducedVertices[vNum] = vertices[tetIndices[vNum]]; + vertices.replaceWithLast(tetIndices[vNum]); // Works because tetIndices[i] < tetIndices[j] if i < j + } + buildFromPoints(&reducedVertices[0], 4, sizeof(PxVec3)); + + calculateCookedSizes(cookedVertexCount, cookedFaceCount, inflated); + cookedEdgeCount = cookedVertexCount + cookedFaceCount - 2; + if (cookedVertexCount > maxVertexCount || cookedEdgeCount > maxEdgeCount || cookedFaceCount > maxFaceCount) + { + return false; // Even a tetrahedron exceeds our limits + } + + physx::Array<uint32_t> faceEdges; + while (vertices.size()) + { + float maxVolumeIncrease = 0.0f; + uint32_t bestVertexIndex = 0; + for (uint32_t testVertexIndex = 0; testVertexIndex < vertices.size(); ++testVertexIndex) + { + const PxVec3& testVertex = vertices[testVertexIndex]; + float volumeIncrease = 0.0f; + PxMat44 addedTet; + addedTet.column0 = PxVec4(testVertex, 1.0f); + for (uint32_t planeIndex = 0; planeIndex < getPlaneCount(); ++planeIndex) + { + const PxPlane plane = getPlane(planeIndex); + if (plane.distance(testVertex) <= 0.0f) + { + continue; + } + faceEdges.resize(0); + PxVec3 faceCenter(0.0f); + for (uint32_t edgeIndex = 0; edgeIndex < getEdgeCount(); ++edgeIndex) + { + if (getEdgeAdjacentFaceIndex(edgeIndex, 0) == planeIndex || getEdgeAdjacentFaceIndex(edgeIndex, 1) == planeIndex) + { + faceCenter += getVertex(getEdgeEndpointIndex(edgeIndex, 0)) + getVertex(getEdgeEndpointIndex(edgeIndex, 1)); + faceEdges.pushBack(edgeIndex); + } + } + if (faceEdges.size()) + { + faceCenter *= 0.5f/(float)faceEdges.size(); + } + addedTet.column1 = PxVec4(faceCenter, 1.0f); + for (uint32_t edgeNum = 0; edgeNum < faceEdges.size(); ++edgeNum) + { + const uint32_t edgeIndex = faceEdges[edgeNum]; + addedTet.column2 = PxVec4(getVertex(getEdgeEndpointIndex(edgeIndex,0)), 1.0f); + addedTet.column3 = PxVec4(getVertex(getEdgeEndpointIndex(edgeIndex,1)), 1.0f); + volumeIncrease += PxAbs(det(addedTet)); + } + } + if (volumeIncrease > maxVolumeIncrease) + { + maxVolumeIncrease = volumeIncrease; + bestVertexIndex = testVertexIndex; + } + } + + reducedVertices.pushBack(vertices[bestVertexIndex]); + vertices.replaceWithLast(bestVertexIndex); + buildFromPoints(&reducedVertices[0], reducedVertices.size(), sizeof(PxVec3)); + + calculateCookedSizes(cookedVertexCount, cookedFaceCount, inflated); + cookedEdgeCount = cookedVertexCount + cookedFaceCount - 2; + if (cookedVertexCount > maxVertexCount || cookedEdgeCount > maxEdgeCount || cookedFaceCount > maxFaceCount) + { + // Exceeded limits, remove last + reducedVertices.popBack(); + buildFromPoints(&reducedVertices[0], reducedVertices.size(), sizeof(PxVec3)); + break; + } + } + + return true; +} +#endif + +bool ConvexHullImpl::createKDOPDirections(physx::Array<PxVec3>& directions, ConvexHullMethod::Enum method) +{ + uint32_t dirs; + switch (method) + { + case ConvexHullMethod::USE_6_DOP: + dirs = 0; + break; + case ConvexHullMethod::USE_10_DOP_X: + dirs = 1; + break; + case ConvexHullMethod::USE_10_DOP_Y: + dirs = 2; + break; + case ConvexHullMethod::USE_10_DOP_Z: + dirs = 4; + break; + case ConvexHullMethod::USE_14_DOP_XY: + dirs = 3; + break; + case ConvexHullMethod::USE_14_DOP_YZ: + dirs = 6; + break; + case ConvexHullMethod::USE_14_DOP_ZX: + dirs = 5; + break; + case ConvexHullMethod::USE_18_DOP: + dirs = 7; + break; + case ConvexHullMethod::USE_26_DOP: + dirs = 15; + break; + default: + return false; + } + directions.reset(); + directions.pushBack(PxVec3(1, 0, 0)); + directions.pushBack(PxVec3(0, 1, 0)); + directions.pushBack(PxVec3(0, 0, 1)); + if (dirs & 1) + { + directions.pushBack(PxVec3(0, 1, 1)); + directions.pushBack(PxVec3(0, -1, 1)); + } + if (dirs & 2) + { + directions.pushBack(PxVec3(1, 0, 1)); + directions.pushBack(PxVec3(1, 0, -1)); + } + if (dirs & 4) + { + directions.pushBack(PxVec3(1, 1, 0)); + directions.pushBack(PxVec3(-1, 1, 0)); + } + if (dirs & 8) + { + directions.pushBack(PxVec3(1, 1, 1)); + directions.pushBack(PxVec3(-1, 1, 1)); + directions.pushBack(PxVec3(1, -1, 1)); + directions.pushBack(PxVec3(1, 1, -1)); + } + return true; +} + + +void createIndexStartLookup(physx::Array<uint32_t>& lookup, int32_t indexBase, uint32_t indexRange, int32_t* indexSource, uint32_t indexCount, uint32_t indexByteStride) +{ + if (indexRange == 0) + { + lookup.resize(PxMax(indexRange + 1, 2u)); + lookup[0] = 0; + lookup[1] = indexCount; + } + else + { + lookup.resize(indexRange + 1); + uint32_t indexPos = 0; + for (uint32_t i = 0; i < indexRange; ++i) + { + for (; indexPos < indexCount; ++indexPos, indexSource = (int32_t*)((uintptr_t)indexSource + indexByteStride)) + { + if (*indexSource >= (int32_t)i + indexBase) + { + lookup[i] = indexPos; + break; + } + } + if (indexPos == indexCount) + { + lookup[i] = indexPos; + } + } + lookup[indexRange] = indexCount; + } +} + +void findIslands(physx::Array< physx::Array<uint32_t> >& islands, const physx::Array<IntPair>& overlaps, uint32_t indexRange) +{ + // symmetrize the overlap list + physx::Array<IntPair> symOverlaps; + symOverlaps.reserve(2 * overlaps.size()); + for (uint32_t i = 0; i < overlaps.size(); ++i) + { + const IntPair& pair = overlaps[i]; + symOverlaps.pushBack(pair); + IntPair& reversed = symOverlaps.insert(); + reversed.set(pair.i1, pair.i0); + } + // Sort symmetrized list + qsort(symOverlaps.begin(), symOverlaps.size(), sizeof(IntPair), IntPair::compare); + // Create overlap look-up + physx::Array<uint32_t> overlapStarts; + createIndexStartLookup(overlapStarts, 0, indexRange, &symOverlaps.begin()->i0, symOverlaps.size(), sizeof(IntPair)); + // Find islands + IndexBank<uint32_t> objectIndices(indexRange); + objectIndices.lockCapacity(true); + uint32_t seedIndex = UINT32_MAX; + while (objectIndices.useNextFree(seedIndex)) + { + physx::Array<uint32_t>& island = islands.insert(); + island.pushBack(seedIndex); + for (uint32_t i = 0; i < island.size(); ++i) + { + const uint32_t index = island[i]; + for (uint32_t j = overlapStarts[index]; j < overlapStarts[index + 1]; ++j) + { + PX_ASSERT(symOverlaps[j].i0 == (int32_t)index); + const uint32_t overlapIndex = (uint32_t)symOverlaps[j].i1; + if (objectIndices.use(overlapIndex)) + { + island.pushBack(overlapIndex); + } + } + } + } +} + + +// Neighbor-finding utility class + +void NeighborLookup::setBounds(const BoundsRep* bounds, uint32_t boundsCount, uint32_t boundsByteStride) +{ + m_neighbors.resize(0); + m_firstNeighbor.resize(0); + + if (bounds != NULL && boundsCount > 0 && boundsByteStride >= sizeof(BoundsRep)) + { + physx::Array<IntPair> overlaps; + boundsCalculateOverlaps(overlaps, Bounds3XYZ, bounds, boundsCount, boundsByteStride); + // symmetrize the overlap list + const uint32_t oldSize = overlaps.size(); + overlaps.resize(2 * oldSize); + for (uint32_t i = 0; i < oldSize; ++i) + { + const IntPair& pair = overlaps[i]; + overlaps[i+oldSize].set(pair.i1, pair.i0); + } + // Sort symmetrized list + qsort(overlaps.begin(), overlaps.size(), sizeof(IntPair), IntPair::compare); + // Create overlap look-up + if (overlaps.size() > 0) + { + createIndexStartLookup(m_firstNeighbor, 0, boundsCount, &overlaps[0].i0, overlaps.size(), sizeof(IntPair)); + m_neighbors.resize(overlaps.size()); + for (uint32_t i = 0; i < overlaps.size(); ++i) + { + m_neighbors[i] = (uint32_t)overlaps[i].i1; + } + } + } +} + +uint32_t NeighborLookup::getNeighborCount(const uint32_t index) const +{ + if (index + 1 >= m_firstNeighbor.size()) + { + return 0; // Invalid neighbor list or index + } + + return m_firstNeighbor[index+1] - m_firstNeighbor[index]; +} + +const uint32_t* NeighborLookup::getNeighbors(const uint32_t index) const +{ + if (index + 1 >= m_firstNeighbor.size()) + { + return 0; // Invalid neighbor list or index + } + + return &m_neighbors[m_firstNeighbor[index]]; +} + + +// Data conversion macros + +#define copyBuffer( _DstFormat, _SrcFormat ) \ + { \ + uint32_t _numVertices = numVertices; \ + int32_t* _invMap = invMap; \ + _DstFormat##_TYPE* _dst = (_DstFormat##_TYPE*)dst; \ + const _SrcFormat##_TYPE* _src = (const _SrcFormat##_TYPE*)src; \ + if( _invMap == NULL ) \ + { \ + while( _numVertices-- ) \ + { \ + convert_##_DstFormat##_from_##_SrcFormat( *_dst, *_src ); \ + ((uint8_t*&)_dst) += dstStride; \ + ((const uint8_t*&)_src) += srcStride; \ + } \ + } \ + else \ + { \ + while( _numVertices-- ) \ + { \ + const int32_t invMapValue = *_invMap++; \ + if (invMapValue >= 0) \ + { \ + _DstFormat##_TYPE* _dstElem = (_DstFormat##_TYPE*)((uint8_t*)_dst + invMapValue*dstStride); \ + convert_##_DstFormat##_from_##_SrcFormat( *_dstElem, *_src ); \ + } \ + ((const uint8_t*&)_src) += srcStride; \ + } \ + } \ + } + + +#define HANDLE_COPY1( _DstFormat, _SrcFormat ) \ + case RenderDataFormat::_DstFormat : \ + if( srcFormat == RenderDataFormat::_SrcFormat ) \ + { \ + copyBuffer( _DstFormat, _SrcFormat ); \ + } \ + break; + +#define HANDLE_COPY2( _DstFormat, _SrcFormat1, _SrcFormat2 ) \ + case RenderDataFormat::_DstFormat : \ + if( srcFormat == RenderDataFormat::_SrcFormat1 ) \ + { \ + copyBuffer( _DstFormat, _SrcFormat1 ); \ + } \ + else if( srcFormat == RenderDataFormat::_SrcFormat2 ) \ + { \ + copyBuffer( _DstFormat, _SrcFormat2 ); \ + } \ + break; + +#define HANDLE_COPY3( _DstFormat, _SrcFormat1, _SrcFormat2, _SrcFormat3 ) \ + case RenderDataFormat::_DstFormat : \ + if( srcFormat == RenderDataFormat::_SrcFormat1 ) \ + { \ + copyBuffer( _DstFormat, _SrcFormat1 ); \ + } \ + else if( srcFormat == RenderDataFormat::_SrcFormat2 ) \ + { \ + copyBuffer( _DstFormat, _SrcFormat2 ); \ + } \ + else if( srcFormat == RenderDataFormat::_SrcFormat3 ) \ + { \ + copyBuffer( _DstFormat, _SrcFormat3 ); \ + } \ + break; + +// ... etc. + + +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) +{ + const uint32_t dstDataSize = RenderDataFormat::getFormatDataSize(dstFormat); + if (dstStride == 0) + { + dstStride = dstDataSize; + } + + if (dstStride < dstDataSize) + { + return false; + } + + // advance src pointer + ((const uint8_t*&)src) += srcStart * srcStride; + + PX_ASSERT((dstStart == 0) || (invMap == NULL)); // can only be one of them, won't work if its both! + + if (dstFormat == srcFormat) + { + if (dstFormat != RenderDataFormat::UNSPECIFIED) + { + // Direct data copy + + // advance dst pointer + ((const uint8_t*&)dst) += dstStart * dstStride; + + if (invMap == NULL) + { + while (numVertices--) + { + memcpy(dst, src, dstDataSize); + ((uint8_t*&)dst) += dstStride; + ((const uint8_t*&)src) += srcStride; + } + } + else + { + while (numVertices--) + { + const int32_t invMapValue = *invMap++; + if (invMapValue >= 0) + { + void* mappedDst = (void*)((uint8_t*)dst + dstStride * (invMapValue)); + memcpy(mappedDst, src, dstDataSize); + } + ((const uint8_t*&)src) += srcStride; + } + } + } + + return true; + } + + // advance dst pointer + ((const uint8_t*&)dst) += dstStart * dstStride; + + switch (dstFormat) + { + case RenderDataFormat::UNSPECIFIED: + // Handle unspecified by doing nothing (still no error!) + break; + + // Put format converters here + HANDLE_COPY1(USHORT1, UINT1) + HANDLE_COPY1(USHORT2, UINT2) + HANDLE_COPY1(USHORT3, UINT3) + HANDLE_COPY1(USHORT4, UINT4) + + HANDLE_COPY1(UINT1, USHORT1) + HANDLE_COPY1(UINT2, USHORT2) + HANDLE_COPY1(UINT3, USHORT3) + HANDLE_COPY1(UINT4, USHORT4) + + HANDLE_COPY1(BYTE_SNORM1, FLOAT1) + HANDLE_COPY1(BYTE_SNORM2, FLOAT2) + HANDLE_COPY1(BYTE_SNORM3, FLOAT3) + HANDLE_COPY1(BYTE_SNORM4, FLOAT4) + HANDLE_COPY1(BYTE_SNORM4_QUATXYZW, FLOAT4_QUAT) + HANDLE_COPY1(SHORT_SNORM1, FLOAT1) + HANDLE_COPY1(SHORT_SNORM2, FLOAT2) + HANDLE_COPY1(SHORT_SNORM3, FLOAT3) + HANDLE_COPY1(SHORT_SNORM4, FLOAT4) + HANDLE_COPY1(SHORT_SNORM4_QUATXYZW, FLOAT4_QUAT) + + HANDLE_COPY2(FLOAT1, BYTE_SNORM1, SHORT_SNORM1) + HANDLE_COPY2(FLOAT2, BYTE_SNORM2, SHORT_SNORM2) + HANDLE_COPY2(FLOAT3, BYTE_SNORM3, SHORT_SNORM3) + HANDLE_COPY2(FLOAT4, BYTE_SNORM4, SHORT_SNORM4) + HANDLE_COPY2(FLOAT4_QUAT, BYTE_SNORM4_QUATXYZW, SHORT_SNORM4_QUATXYZW) + + HANDLE_COPY3(R8G8B8A8, B8G8R8A8, R32G32B32A32_FLOAT, B32G32R32A32_FLOAT) + HANDLE_COPY3(B8G8R8A8, R8G8B8A8, R32G32B32A32_FLOAT, B32G32R32A32_FLOAT) + HANDLE_COPY3(R32G32B32A32_FLOAT, R8G8B8A8, B8G8R8A8, B32G32R32A32_FLOAT) + HANDLE_COPY3(B32G32R32A32_FLOAT, R8G8B8A8, B8G8R8A8, R32G32B32A32_FLOAT) + + default: + { + PX_ALWAYS_ASSERT(); // Format conversion not handled + return false; + } + } + + + return true; +} + + + +/************************************************************************/ +// Convex Hull from Planes +/************************************************************************/ + +// copied from //sw/physx/PhysXSDK/3.2/trunk/Source/Common/src/CmMathUtils.cpp +PxQuat PxShortestRotation(const PxVec3& v0, const PxVec3& v1) +{ + const float d = v0.dot(v1); + const PxVec3 cross = v0.cross(v1); + + PxQuat q = d>-1 ? PxQuat(cross.x, cross.y, cross.z, 1+d) + : PxAbs(v0.x)<0.1f ? PxQuat(0.0f, v0.z, -v0.y, 0.0f) : PxQuat(v0.y, -v0.x, 0.0f, 0.0f); + + return q.getNormalized(); +} + +PxTransform PxTransformFromPlaneEquation(const PxPlane& plane) +{ + PxPlane p = plane; + p.normalize(); + + // special case handling for axis aligned planes + const float halfsqrt2 = 0.707106781; + PxQuat q; + if(2 == (p.n.x == 0.0f) + (p.n.y == 0.0f) + (p.n.z == 0.0f)) // special handling for axis aligned planes + { + if(p.n.x > 0) q = PxQuat(PxIdentity); + else if(p.n.x < 0) q = PxQuat(0, 0, 1, 0); + else q = PxQuat(0.0f, -p.n.z, p.n.y, 1) * halfsqrt2; + } + else q = PxShortestRotation(PxVec3(1,0,0), p.n); + + return PxTransform(-p.n * p.d, q); +} + + + +PxVec3 intersect(PxVec4 p0, PxVec4 p1, PxVec4 p2) +{ + const PxVec3& d0 = reinterpret_cast<const PxVec3&>(p0); + const PxVec3& d1 = reinterpret_cast<const PxVec3&>(p1); + const PxVec3& d2 = reinterpret_cast<const PxVec3&>(p2); + + return (p0.w * d1.cross(d2) + + p1.w * d2.cross(d0) + + p2.w * d0.cross(d1)) + / d0.dot(d2.cross(d1)); +} + +const uint16_t sInvalid = uint16_t(-1); + +// restriction: only supports a single patch per vertex. +struct HalfedgeMesh +{ + struct Halfedge + { + Halfedge(uint16_t vertex = sInvalid, uint16_t face = sInvalid, + uint16_t next = sInvalid, uint16_t prev = sInvalid) + : mVertex(vertex), mFace(face), mNext(next), mPrev(prev) + {} + + uint16_t mVertex; // to + uint16_t mFace; // left + uint16_t mNext; // ccw + uint16_t mPrev; // cw + }; + + HalfedgeMesh() : mNumTriangles(0) {} + + uint16_t findHalfedge(uint16_t v0, uint16_t v1) + { + uint16_t h = mVertices[v0], start = h; + while(h != sInvalid && mHalfedges[h].mVertex != v1) + { + h = mHalfedges[(uint32_t)h ^ 1].mNext; + if(h == start) + return sInvalid; + } + return h; + } + + void connect(uint16_t h0, uint16_t h1) + { + mHalfedges[h0].mNext = h1; + mHalfedges[h1].mPrev = h0; + } + + void addTriangle(uint16_t v0, uint16_t v1, uint16_t v2) + { + // add new vertices + uint16_t n = (uint16_t)(PxMax(v0, PxMax(v1, v2))+1); + if(mVertices.size() < n) + mVertices.resize(n, sInvalid); + + // collect halfedges, prev and next of triangle + uint16_t verts[] = { v0, v1, v2 }; + uint16_t handles[3], prev[3], next[3]; + for(uint16_t i=0; i<3; ++i) + { + uint16_t j = (uint16_t)((i+1)%3); + uint16_t h = findHalfedge(verts[i], verts[j]); + if(h == sInvalid) + { + // add new edge + PX_ASSERT(mHalfedges.size() <= UINT16_MAX); + h = (uint16_t)mHalfedges.size(); + mHalfedges.pushBack(Halfedge(verts[j])); + mHalfedges.pushBack(Halfedge(verts[i])); + } + handles[i] = h; + prev[i] = mHalfedges[h].mPrev; + next[i] = mHalfedges[h].mNext; + } + + // patch connectivity + for(uint16_t i=0; i<3; ++i) + { + uint16_t j = (uint16_t)((i+1)%3); + + PX_ASSERT(mFaces.size() <= UINT16_MAX); + mHalfedges[handles[i]].mFace = (uint16_t)mFaces.size(); + + // connect prev and next + connect(handles[i], handles[j]); + + if(next[j] == sInvalid) // new next edge, connect opposite + connect((uint32_t)(handles[j]^1), next[i]!=sInvalid ? next[i] : (uint32_t)(handles[i]^1)); + + if(prev[i] == sInvalid) // new prev edge, connect opposite + connect(prev[j]!=sInvalid ? prev[j] : (uint32_t)(handles[j]^1), (uint32_t)(handles[i]^1)); + + // prev is boundary, update middle vertex + if(mHalfedges[(uint32_t)(handles[i]^1)].mFace == sInvalid) + mVertices[verts[j]] = (uint32_t)(handles[i]^1); + } + + mFaces.pushBack(handles[2]); + ++mNumTriangles; + } + + uint16_t removeTriangle(uint16_t f) + { + uint16_t result = sInvalid; + + for(uint16_t i=0, h = mFaces[f]; i<3; ++i) + { + uint16_t v0 = mHalfedges[(uint32_t)(h^1)].mVertex; + uint16_t v1 = mHalfedges[h].mVertex; + + mHalfedges[h].mFace = sInvalid; + + if(mHalfedges[(uint32_t)(h^1)].mFace == sInvalid) // was boundary edge, remove + { + uint16_t v0Prev = mHalfedges[h ].mPrev; + uint16_t v0Next = mHalfedges[(uint32_t)(h^1)].mNext; + uint16_t v1Prev = mHalfedges[(uint32_t)(h^1)].mPrev; + uint16_t v1Next = mHalfedges[h ].mNext; + + // update halfedge connectivity + connect(v0Prev, v0Next); + connect(v1Prev, v1Next); + + // update vertex boundary or delete + mVertices[v0] = (v0Prev^1) == v0Next ? sInvalid : v0Next; + mVertices[v1] = (v1Prev^1) == v1Next ? sInvalid : v1Next; + } + else + { + mVertices[v0] = h; // update vertex boundary + result = v1; + } + + h = mHalfedges[h].mNext; + } + + mFaces[f] = sInvalid; + --mNumTriangles; + + return result; + } + + // true if vertex v is in front of face f + bool visible(uint16_t v, uint16_t f) + { + uint16_t h = mFaces[f]; + if(h == sInvalid) + return false; + + uint16_t v0 = mHalfedges[h].mVertex; + h = mHalfedges[h].mNext; + uint16_t v1 = mHalfedges[h].mVertex; + h = mHalfedges[h].mNext; + uint16_t v2 = mHalfedges[h].mVertex; + h = mHalfedges[h].mNext; + + return det(mPoints[v], mPoints[v0], mPoints[v1], mPoints[v2]) < -1e-5f; + } + + /* + void print() const + { + for(uint32_t i=0; i<mFaces.size(); ++i) + { + printf("f%u: ", i); + uint16_t h = mFaces[i]; + if(h == sInvalid) + { + printf("deleted\n"); + continue; + } + + for(int j=0; j<3; ++j) + { + printf("h%u -> v%u -> ", uint32_t(h), uint32_t(mHalfedges[h].mVertex)); + h = mHalfedges[h].mNext; + } + + printf("\n"); + } + + for(uint32_t i=0; i<mVertices.size(); ++i) + { + printf("v%u: ", i); + uint16_t h = mVertices[i]; + if(h == sInvalid) + { + printf("deleted\n"); + continue; + } + + uint16_t start = h; + do { + printf("h%u -> v%u, ", uint32_t(h), uint32_t(mHalfedges[h].mVertex)); + h = mHalfedges[h^1].mNext; + } while (h != start); + + printf("\n"); + } + + for(uint32_t i=0; i<mHalfedges.size(); ++i) + printf("h%u: v%u, f%u, p%u, n%u\n", i, uint32_t(mHalfedges[i].mVertex), + uint32_t(mHalfedges[i].mFace), uint32_t(mHalfedges[i].mPrev), uint32_t(mHalfedges[i].mNext)); + } + */ + + nvidia::Array<Halfedge> mHalfedges; + nvidia::Array<uint16_t> mVertices; // vertex -> (boundary) halfedge + nvidia::Array<uint16_t> mFaces; // face -> halfedge + nvidia::Array<PxVec4> mPoints; + uint16_t mNumTriangles; +}; + + + +void ConvexMeshBuilder::operator()(uint32_t planeMask, float scale) +{ + uint32_t numPlanes = shdfnd::bitCount(planeMask); + + if (numPlanes == 1) + { + PxTransform t = nvidia::apex::PxTransformFromPlaneEquation(reinterpret_cast<const PxPlane&>(mPlanes[lowestSetBit(planeMask)])); + + if (!t.isValid()) + return; + + const uint16_t indices[] = { 0, 1, 2, 0, 2, 3 }; + const PxVec3 vertices[] = { + PxVec3(0.0f, scale, scale), + PxVec3(0.0f, -scale, scale), + PxVec3(0.0f, -scale, -scale), + PxVec3(0.0f, scale, -scale) }; + + PX_ASSERT(mVertices.size() <= UINT16_MAX); + uint16_t baseIndex = (uint16_t)mVertices.size(); + + for (uint32_t i=0; i < 4; ++i) + mVertices.pushBack(t.transform(vertices[i])); + + for (uint32_t i=0; i < 6; ++i) + mIndices.pushBack((uint16_t)(indices[i] + baseIndex)); + + return; + } + + if(numPlanes < 4) + return; // todo: handle degenerate cases + + HalfedgeMesh mesh; + + // gather points (planes, that is) + mesh.mPoints.reserve(numPlanes); + for(; planeMask; planeMask &= planeMask-1) + mesh.mPoints.pushBack(mPlanes[shdfnd::lowestSetBit(planeMask)]); + + // initialize to tetrahedron + mesh.addTriangle(0, 1, 2); + mesh.addTriangle(0, 3, 1); + mesh.addTriangle(1, 3, 2); + mesh.addTriangle(2, 3, 0); + + // flip if inside-out + if(mesh.visible(3, 0)) + shdfnd::swap(mesh.mPoints[0], mesh.mPoints[1]); + + // iterate through remaining points + for(uint16_t i=4; i<mesh.mPoints.size(); ++i) + { + // remove any visible triangle + uint16_t v0 = sInvalid; + for(uint16_t j=0; j<mesh.mFaces.size(); ++j) + { + if(mesh.visible(i, j)) + v0 = PxMin(v0, mesh.removeTriangle(j)); + } + + if(v0 == sInvalid) + continue; // no triangle removed + + if(!mesh.mNumTriangles) + return; // empty mesh + + // find non-deleted boundary vertex + for(uint16_t h=0; mesh.mVertices[v0] == sInvalid; h+=2) + { + if ((mesh.mHalfedges[h ].mFace == sInvalid) ^ + (mesh.mHalfedges[(uint32_t)(h+1)].mFace == sInvalid)) + { + v0 = mesh.mHalfedges[h].mVertex; + } + } + + // tesselate hole + uint16_t start = v0; + do { + uint16_t h = mesh.mVertices[v0]; + uint16_t v1 = mesh.mHalfedges[h].mVertex; + if(mesh.mFaces.size() == UINT16_MAX) + break; // safety net + mesh.addTriangle(v0, v1, i); + v0 = v1; + } while(v0 != start); + + bool noHole = true; + for(uint16_t h=0; noHole && h < mesh.mHalfedges.size(); h+=2) + { + if ((mesh.mHalfedges[h ].mFace == sInvalid) ^ + (mesh.mHalfedges[(uint32_t)(h+1)].mFace == sInvalid)) + noHole = false; + } + + if(!noHole || mesh.mFaces.size() == UINT16_MAX) + { + mesh.mFaces.resize(0); + mesh.mVertices.resize(0); + break; + } + } + + // convert triangles to vertices (intersection of 3 planes) + nvidia::Array<uint32_t> face2Vertex(mesh.mFaces.size()); + for(uint32_t i=0; i<mesh.mFaces.size(); ++i) + { + face2Vertex[i] = mVertices.size(); + + uint16_t h = mesh.mFaces[i]; + if(h == sInvalid) + continue; + + uint16_t v0 = mesh.mHalfedges[h].mVertex; + h = mesh.mHalfedges[h].mNext; + uint16_t v1 = mesh.mHalfedges[h].mVertex; + h = mesh.mHalfedges[h].mNext; + uint16_t v2 = mesh.mHalfedges[h].mVertex; + + mVertices.pushBack(intersect(mesh.mPoints[v0], mesh.mPoints[v1], mesh.mPoints[v2])); + } + + // convert vertices to polygons (face one-ring) + for(uint32_t i=0; i<mesh.mVertices.size(); ++i) + { + uint16_t h = mesh.mVertices[i]; + if(h == sInvalid) + continue; + + uint32_t v0 = face2Vertex[mesh.mHalfedges[h].mFace]; + h = (uint16_t)(mesh.mHalfedges[h].mPrev^1); + uint32_t v1 = face2Vertex[mesh.mHalfedges[h].mFace]; + + while(true) + { + h = (uint16_t)(mesh.mHalfedges[h].mPrev^1); + uint32_t v2 = face2Vertex[mesh.mHalfedges[h].mFace]; + + if(v0 == v2) + break; + + PX_ASSERT(v0 <= UINT16_MAX); + PX_ASSERT(v1 <= UINT16_MAX); + PX_ASSERT(v2 <= UINT16_MAX); + mIndices.pushBack((uint16_t)v0); + mIndices.pushBack((uint16_t)v2); + mIndices.pushBack((uint16_t)v1); + + v1 = v2; + } + + } +} + +// Diagonalize a symmetric 3x3 matrix. Returns the eigenvectors in the first parameter, eigenvalues as the return value. +PxVec3 diagonalizeSymmetric(PxMat33& eigenvectors, const PxMat33& m) +{ + // jacobi rotation using quaternions (from an idea of Stan Melax, with fix for precision issues) + + const uint32_t MAX_ITERS = 24; + + PxQuat q = PxQuat(PxIdentity); + + PxMat33 d; + for(uint32_t i=0; i < MAX_ITERS;i++) + { + eigenvectors = PxMat33(q); + d = eigenvectors.getTranspose() * m * eigenvectors; + + const float d0 = PxAbs(d[1][2]), d1 = PxAbs(d[0][2]), d2 = PxAbs(d[0][1]); + const uint32_t a = d0 > d1 && d0 > d2 ? 0u : d1 > d2 ? 1u : 2u; // rotation axis index, from largest off-diagonal element + + const uint32_t a1 = (a+1)%3; + const uint32_t a2 = (a+2)%3; + if(d[a1][a2] == 0.0f || PxAbs(d[a1][a1]-d[a2][a2]) > 2e6*PxAbs(2.0f*d[a1][a2])) + { + break; + } + + const float w = (d[a1][a1]-d[a2][a2]) / (2.0f*d[a1][a2]); // cot(2 * phi), where phi is the rotation angle + const float absw = PxAbs(w); + + float c, s; + if(absw>1000) + { + c = 1; + s = 1/(4*w); // h will be very close to 1, so use small angle approx instead + } + else + { + float t = 1 / (absw + PxSqrt(w*w+1)); // absolute value of tan phi + float h = 1 / PxSqrt(t*t+1); // absolute value of cos phi + PX_ASSERT(h!=1); // |w|<1000 guarantees this with typical IEEE754 machine eps (approx 6e-8) + c = PxSqrt((1+h)/2); + s = PxSqrt((1-h)/2) * PxSign(w); + } + + PxQuat r(0,0,0,c); + reinterpret_cast<PxVec4&>(r)[a] = s; + + q = (q*r).getNormalized(); + } + + return PxVec3(d.column0.x, d.column1.y, d.column2.z); +} + + + +} +} // end namespace nvidia::apex diff --git a/APEX_1.4/common/src/ApexSubdivider.cpp b/APEX_1.4/common/src/ApexSubdivider.cpp new file mode 100644 index 00000000..072101ad --- /dev/null +++ b/APEX_1.4/common/src/ApexSubdivider.cpp @@ -0,0 +1,766 @@ +/* + * 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. + */ + + +#include "ApexSubdivider.h" + +#include "ApexSDKIntl.h" + +#include "PsSort.h" + +namespace nvidia +{ +namespace apex +{ + +ApexSubdivider::ApexSubdivider() : + mMarkedVertices(0), + mTriangleListEmptyElement(-1) +{ + mBound.setEmpty(); + mRand.setSeed(0); +} + + + + +void ApexSubdivider::clear() +{ + mBound.setEmpty(); + mRand.setSeed(0); +} + + + +void ApexSubdivider::registerVertex(const PxVec3& v, uint32_t bitFlagPayload) +{ + SubdividerVertex vertex(v, bitFlagPayload); + mVertices.pushBack(vertex); + mBound.include(v); +} + + + +void ApexSubdivider::registerTriangle(uint32_t i0, uint32_t i1, uint32_t i2) +{ + PX_ASSERT(i0 < mVertices.size()); + PX_ASSERT(i1 < mVertices.size()); + PX_ASSERT(i2 < mVertices.size()); + + SubdividerTriangle t; + t.init(i0, i1, i2); + uint32_t triangleNumber = mTriangles.size(); + mTriangles.pushBack(t); + addTriangleToVertex(i0, triangleNumber); + addTriangleToVertex(i1, triangleNumber); + addTriangleToVertex(i2, triangleNumber); +} + + + +void ApexSubdivider::endRegistration() +{ + +} + + + +void ApexSubdivider::mergeVertices(IProgressListener* progress) +{ + PX_UNUSED(progress); + if (mVertices.empty()) + { + APEX_INVALID_OPERATION("no vertices available"); + return; + } + + const float MERGE_THRESHOLD = 1.0e-6f; + + const float d = (mBound.minimum - mBound.maximum).magnitude() * MERGE_THRESHOLD; + const float d2 = d * d; + + Array<SubdividerVertexRef> refs; + refs.reserve(mVertices.size()); + + for (uint32_t i = 0; i < mVertices.size(); i++) + { + SubdividerVertex& v = mVertices[i]; + v.marked = false; + SubdividerVertexRef vr(v.pos, i); + refs.pushBack(vr); + } + mMarkedVertices = 0; + + shdfnd::sort(refs.begin(), refs.size(), SubdividerVertexRef()); + + for (uint32_t i = 0; i < refs.size() - 1; i++) + { + uint32_t iNr = refs[i].vertexNr; + SubdividerVertex& vi = mVertices[iNr]; + + if (vi.marked) + { + continue; + } + + const PxVec3 pos = refs[i].pos; + uint32_t j = i + 1; + while (j < refs.size() && fabs(refs[j].pos.x - pos.x) < MERGE_THRESHOLD) + { + if ((refs[j].pos - pos).magnitudeSquared() < d2) + { + uint32_t jNr = refs[j].vertexNr; + SubdividerVertex& vj = mVertices[jNr]; + + const uint32_t payload = vi.payload | vj.payload; + vi.payload = vj.payload = payload; + + if (vj.firstTriangle != -1) + { + // find vi's last triangle, add the others there + int32_t lastTri = vi.firstTriangle; + + for (;;) + { + TriangleList& t = mTriangleList[(uint32_t)lastTri]; + if (t.nextTriangle == -1) + { + break; + } + + PX_ASSERT(t.triangleNumber < mTriangles.size()); + + lastTri = t.nextTriangle; + } + PX_ASSERT(lastTri != -1); + PX_ASSERT((uint32_t)lastTri < mTriangleList.size()); + PX_ASSERT(mTriangleList[(uint32_t)lastTri].nextTriangle == -1); + mTriangleList[(uint32_t)lastTri].nextTriangle = vj.firstTriangle; + vj.firstTriangle = -1; + lastTri = mTriangleList[(uint32_t)lastTri].nextTriangle; + while (lastTri != -1) + { + uint32_t tNr = mTriangleList[(uint32_t)lastTri].triangleNumber; + PX_ASSERT(tNr < mTriangles.size()); + mTriangles[tNr].replaceVertex(refs[j].vertexNr, refs[i].vertexNr); + + lastTri = mTriangleList[(uint32_t)lastTri].nextTriangle; + } + } + vj.marked = true; + mMarkedVertices++; + } + j++; + } + } + + if (mMarkedVertices > 0) + { + compress(); + } +} + + + +void ApexSubdivider::closeMesh(IProgressListener* progress) +{ + PX_UNUSED(progress); + Array<SubdividerEdge> edges, borderEdges; + SubdividerEdge edge; + + edges.reserve(mTriangles.size() * 3); + + for (uint32_t i = 0; i < mTriangles.size(); i++) + { + SubdividerTriangle& t = mTriangles[i]; + edge.init(t.vertexNr[0], t.vertexNr[1], i); + edges.pushBack(edge); + edge.init(t.vertexNr[1], t.vertexNr[2], i); + edges.pushBack(edge); + edge.init(t.vertexNr[2], t.vertexNr[0], i); + edges.pushBack(edge); + } + + shdfnd::sort(edges.begin(), edges.size(), SubdividerEdge()); + + for (uint32_t i = 0; i < edges.size(); i++) + { + SubdividerEdge& ei = edges[i]; + uint32_t j = i + 1; + while (j < edges.size() && edges[j] == ei) + { + j++; + } + if (j == i + 1) + { + borderEdges.pushBack(ei); + } + i = j - 1; + } + + // find border circles + Array<uint32_t> borderVertices; + borderVertices.reserve(borderEdges.size()); + while (!borderEdges.empty()) + { + edge = borderEdges.back(); + borderEdges.popBack(); + + borderVertices.clear(); + // find orientation + + const SubdividerTriangle& triangle = mTriangles[edge.triangleNr]; + PX_ASSERT(triangle.containsVertex(edge.v0)); + PX_ASSERT(triangle.containsVertex(edge.v1)); + bool swap = triangle.vertexNr[0] == edge.v0 || (triangle.vertexNr[1] == edge.v0 && triangle.vertexNr[0] != edge.v1); + + if (swap) + { + borderVertices.pushBack(edge.v1); + borderVertices.pushBack(edge.v0); + } + else + { + borderVertices.pushBack(edge.v0); + borderVertices.pushBack(edge.v1); + } + uint32_t currentV = borderVertices.back(); + int32_t nextV = -1; + do + { + nextV = -1; + uint32_t i = 0; + for (; i < borderEdges.size(); i++) + { + SubdividerEdge& ei = borderEdges[i]; + if (ei.v0 == currentV) + { + nextV = (int32_t)ei.v1; + break; + } + else if (ei.v1 == currentV) + { + nextV = (int32_t)ei.v0; + break; + } + } + if (nextV < 0) + { + break; // chain ended + } + + PX_ASSERT(i < borderEdges.size()); + borderEdges.replaceWithLast(i); + borderVertices.pushBack((uint32_t)nextV); + currentV = (uint32_t)nextV; + } + while (nextV >= 0); + + if (borderVertices[0] == borderVertices[borderVertices.size() - 1]) + { + borderVertices.popBack(); + } + + closeHole(borderVertices.begin(), borderVertices.size()); + } +} + + + +void ApexSubdivider::subdivide(uint32_t subdivisionGridSize, IProgressListener*) +{ + PX_ASSERT(subdivisionGridSize > 0); + if (subdivisionGridSize == 0) + { + return; + } + + const float maxLength = (mBound.minimum - mBound.maximum).magnitude() / (float)subdivisionGridSize; + const float threshold = 2.0f * maxLength; + const float threshold2 = threshold * threshold; + + PX_ASSERT(threshold2 > 0.0f); + if (threshold2 <= 0.0f) + { + return; + } + + Array<SubdividerEdge> edges; + edges.reserve(mTriangles.size() * 3); + for (uint32_t i = 0; i < mTriangles.size(); i++) + { + SubdividerEdge edge; + SubdividerTriangle& t = mTriangles[i]; + edge.init(t.vertexNr[0], t.vertexNr[1], i); + edges.pushBack(edge); + edge.init(t.vertexNr[1], t.vertexNr[2], i); + edges.pushBack(edge); + edge.init(t.vertexNr[2], t.vertexNr[0], i); + edges.pushBack(edge); + } + + shdfnd::sort(edges.begin(), edges.size(), SubdividerEdge()); + + uint32_t i = 0; + while (i < edges.size()) + { + SubdividerEdge& ei = edges[i]; + uint32_t newI = i + 1; + while (newI < edges.size() && edges[newI] == ei) + { + newI++; + } + + const PxVec3 p0 = mVertices[ei.v0].pos; + const PxVec3 p1 = mVertices[ei.v1].pos; + const float d2 = (p0 - p1).magnitudeSquared(); + + if (d2 < threshold2) + { + i = newI; + continue; + } + + uint32_t newVertex = mVertices.size(); + const float eps = 1.0e-4f; + SubdividerVertex vertex; + vertex.pos = (p0 + p1) * 0.5f + PxVec3(mRand.getNext(), mRand.getNext(), mRand.getNext()) * eps; + vertex.payload = mVertices[ei.v0].payload | mVertices[ei.v1].payload; + mVertices.pushBack(vertex); + + uint32_t newEdgeNr = edges.size(); + for (uint32_t j = i; j < newI; j++) + { + SubdividerEdge ej = edges[j]; + SubdividerTriangle tj = mTriangles[ej.triangleNr]; + + uint32_t v2 = 0; // the vertex not contained in the edge + if (tj.vertexNr[1] != ej.v0 && tj.vertexNr[1] != ej.v1) + { + v2 = 1; + } + else if (tj.vertexNr[2] != ej.v0 && tj.vertexNr[2] != ej.v1) + { + v2 = 2; + } + + const uint32_t v0 = (v2 + 1) % 3; + const uint32_t v1 = (v0 + 1) % 3; + + // generate new triangle + const uint32_t newTriangle = mTriangles.size(); + + SubdividerTriangle tNew; + tNew.init(tj.vertexNr[v0], newVertex, tj.vertexNr[v2]); + mTriangles.pushBack(tNew); + addTriangleToVertex(tNew.vertexNr[0], newTriangle); + addTriangleToVertex(tNew.vertexNr[1], newTriangle); + addTriangleToVertex(tNew.vertexNr[2], newTriangle); + + // modify existing triangle + removeTriangleFromVertex(tj.vertexNr[v0], ej.triangleNr); + tj.vertexNr[v0] = newVertex; + mTriangles[ej.triangleNr].vertexNr[v0] = newVertex; + addTriangleToVertex(newVertex, ej.triangleNr); + + // update edges + int32_t k = binarySearchEdges(edges, tNew.vertexNr[2], tNew.vertexNr[0], ej.triangleNr); + PX_ASSERT(k >= 0); + edges[(uint32_t)k].triangleNr = newTriangle; + + SubdividerEdge edge; + edge.init(tj.vertexNr[v2], tj.vertexNr[v0], ej.triangleNr); + edges.pushBack(edge); + edge.init(tj.vertexNr[v0], tj.vertexNr[v1], ej.triangleNr); + edges.pushBack(edge); + edge.init(tNew.vertexNr[0], tNew.vertexNr[1], newTriangle); + edges.pushBack(edge); + edge.init(tNew.vertexNr[1], tNew.vertexNr[2], newTriangle); + edges.pushBack(edge); + } + i = newI; + PX_ASSERT(newEdgeNr < edges.size()); + + shdfnd::sort(edges.begin() + newEdgeNr, edges.size() - newEdgeNr, SubdividerEdge()); + } +} + + + +uint32_t ApexSubdivider::getNumVertices() const +{ + PX_ASSERT(mMarkedVertices == 0); + return mVertices.size(); +} + + + +uint32_t ApexSubdivider::getNumTriangles() const +{ + return mTriangles.size(); +} + + + +void ApexSubdivider::getVertex(uint32_t i, PxVec3& v, uint32_t& bitFlagPayload) const +{ + PX_ASSERT(mMarkedVertices == 0); + PX_ASSERT(i < mVertices.size()); + v = mVertices[i].pos; + bitFlagPayload = mVertices[i].payload; +} + + + +void ApexSubdivider::getTriangle(uint32_t i, uint32_t& i0, uint32_t& i1, uint32_t& i2) const +{ + PX_ASSERT(i < mTriangles.size()); + PX_ASSERT(mTriangles[i].isValid()); + + const SubdividerTriangle& t = mTriangles[i]; + i0 = t.vertexNr[0]; + i1 = t.vertexNr[1]; + i2 = t.vertexNr[2]; +} + + + +void ApexSubdivider::compress() +{ + Array<uint32_t> oldToNew; + oldToNew.resize(mVertices.size()); + Array<SubdividerVertex> newVertices; + newVertices.reserve(mVertices.size() - mMarkedVertices); + + for (uint32_t i = 0; i < mVertices.size(); i++) + { + if (mVertices[i].marked) + { + oldToNew[i] = (uint32_t) - 1; + mMarkedVertices--; + } + else + { + oldToNew[i] = newVertices.size(); + newVertices.pushBack(mVertices[i]); + } + } + + mVertices.resize(newVertices.size()); + for (uint32_t i = 0; i < newVertices.size(); i++) + { + mVertices[i] = newVertices[i]; + } + + for (uint32_t i = 0; i < mTriangles.size(); i++) + { + if (mTriangles[i].isValid()) + { + SubdividerTriangle& t = mTriangles[i]; + t.vertexNr[0] = oldToNew[t.vertexNr[0]]; + t.vertexNr[1] = oldToNew[t.vertexNr[1]]; + t.vertexNr[2] = oldToNew[t.vertexNr[2]]; + } + else + { + mTriangles.replaceWithLast(i); + i--; + } + } + + PX_ASSERT(mMarkedVertices == 0); +} + + + +void ApexSubdivider::closeHole(uint32_t* indices, uint32_t numIndices) +{ + if (numIndices < 3) + { + return; + } + + SubdividerTriangle triangle; + triangle.init(0, 0, 0); + + // fill hole + while (numIndices > 3) + { + PxVec3 normal(0.0f); + for (uint32_t i = 0; i < numIndices; i++) + { + const PxVec3& p0 = mVertices[indices[i]].pos; + const PxVec3& p1 = mVertices[indices[(i + 1) % numIndices]].pos; + const PxVec3& p2 = mVertices[indices[(i + 2) % numIndices]].pos; + + PxVec3 normalI = (p0 - p1).cross(p2 - p1); + normalI.normalize(); + normal += normalI; + } + normal.normalize(); + + float maxQuality = -1.0f; + int32_t bestI = -1; + for (uint32_t i = 0; i < numIndices; i++) + { + const uint32_t i2 = (i + 2) % numIndices; + + const uint32_t b0 = indices[i]; + const uint32_t b1 = indices[(i + 1) % numIndices]; + const uint32_t b2 = indices[i2]; + const uint32_t b3 = indices[(i + 3) % numIndices]; + const uint32_t b4 = indices[(i + 4) % numIndices]; + + if (getTriangleNr(b1, b2, b3) != -1) + { + continue; + } + + + // init best + //if (i == 0) + //{ + // t.init(b1,b2,b3); + // bestI = i2; + //} + + // check whether triangle is an ear + PxVec3 normalI = (mVertices[b1].pos - mVertices[b2].pos).cross(mVertices[b3].pos - mVertices[b2].pos); + normalI.normalize(); ///< \todo, remove again, only for debugging + if (normalI.dot(normal) < 0.0f) + { + continue; + } + + float quality = qualityOfTriangle(b1, b2, b3) - qualityOfTriangle(b0, b1, b2) - qualityOfTriangle(b2, b3, b4); + if (maxQuality < 0.0f || quality > maxQuality) + { + maxQuality = quality; + triangle.init(b1, b2, b3); + bestI = (int32_t)i2; + } + } + PX_ASSERT(bestI != -1); + PX_ASSERT(triangle.isValid()); + + + // remove ear vertex from temporary border + for (uint32_t i = (uint32_t)bestI; i < numIndices - 1; i++) + { + indices[i] = indices[i + 1]; + } + + numIndices--; + + // do we have the triangle already? + //if (getTriangleNr(triangle.vertexNr[0], triangle.vertexNr[1], triangle.vertexNr[2]) >= 0) + // continue; + + // TODO: triangle is potentially uninitialized. + // do we have to subdivide the triangle? + //PxVec3& p0 = mVertices[triangle.vertexNr[0]].pos; + //PxVec3& p2 = mVertices[triangle.vertexNr[2]].pos; + //PxVec3 dir = p2 - p0; + //float d = dir.normalize(); + uint32_t triangleNr = mTriangles.size(); + mTriangles.pushBack(triangle); + addTriangleToVertex(triangle.vertexNr[0], triangleNr); + addTriangleToVertex(triangle.vertexNr[1], triangleNr); + addTriangleToVertex(triangle.vertexNr[2], triangleNr); + } + + triangle.init(indices[0], indices[1], indices[2]); + if (getTriangleNr(triangle.vertexNr[0], triangle.vertexNr[1], triangle.vertexNr[2]) < 0) + { + uint32_t triangleNr = mTriangles.size(); + mTriangles.pushBack(triangle); + addTriangleToVertex(triangle.vertexNr[0], triangleNr); + addTriangleToVertex(triangle.vertexNr[1], triangleNr); + addTriangleToVertex(triangle.vertexNr[2], triangleNr); + } +} + + + +float ApexSubdivider::qualityOfTriangle(uint32_t v0, uint32_t v1, uint32_t v2) const +{ + const PxVec3& p0 = mVertices[v0].pos; + const PxVec3& p1 = mVertices[v1].pos; + const PxVec3& p2 = mVertices[v2].pos; + + const float a = (p0 - p1).magnitude(); + const float b = (p1 - p2).magnitude(); + const float c = (p2 - p0).magnitude(); + + if (a > b && a > c) // a is biggest + { + return b + c - a; + } + else if (b > c) + { + return a + c - b; // b is biggest + } + return a + b - c; // c is biggest +} + + + +int32_t ApexSubdivider::getTriangleNr(const uint32_t v0, const uint32_t v1, const uint32_t v2) const +{ + const uint32_t num = mVertices.size(); + if (v0 >= num || v1 >= num || v2 >= num) + { + return -1; + } + + int32_t triangleListIndex = mVertices[v0].firstTriangle; + while (triangleListIndex != -1) + { + const TriangleList& tl = mTriangleList[(uint32_t)triangleListIndex]; + const uint32_t triangleIndex = tl.triangleNumber; + const SubdividerTriangle& triangle = mTriangles[triangleIndex]; + if (triangle.containsVertex(v1) && triangle.containsVertex(v2)) + { + return (int32_t)triangleIndex; + } + + triangleListIndex = tl.nextTriangle; + } + + return -1; +} + + + +int32_t ApexSubdivider::binarySearchEdges(const Array<SubdividerEdge>& edges, uint32_t v0, uint32_t v1, uint32_t triangleNr) const +{ + if (edges.empty()) + { + return -1; + } + + SubdividerEdge edge; + edge.init(v0, v1, (uint32_t) - 1); + + uint32_t l = 0; + uint32_t r = edges.size() - 1; + int32_t m = 0; + while (l <= r) + { + m = (int32_t)(l + r) / 2; + if (edges[(uint32_t)m] < edge) + { + l = (uint32_t)m + 1; + } + else if (edge < edges[(uint32_t)m]) + { + r = (uint32_t)m - 1; + } + else + { + break; + } + } + if (!(edges[(uint32_t)m] == edge)) + { + return -1; + } + + while (m >= 0 && edges[(uint32_t)m] == edge) + { + m--; + } + m++; + + PX_ASSERT(m >= 0); + PX_ASSERT((uint32_t)m < edges.size()); + while ((uint32_t)m < edges.size() && edges[(uint32_t)m] == edge && edges[(uint32_t)m].triangleNr != triangleNr) + { + m++; + } + + if (edges[(uint32_t)m] == edge && edges[(uint32_t)m].triangleNr == triangleNr) + { + return m; + } + + return -1; +} + + + +void ApexSubdivider::addTriangleToVertex(uint32_t vertexNumber, uint32_t triangleNumber) +{ + PX_ASSERT(vertexNumber < mVertices.size()); + PX_ASSERT(triangleNumber < mTriangles.size()); + + TriangleList& t = allocateTriangleElement(); + + t.triangleNumber = triangleNumber; + t.nextTriangle = mVertices[vertexNumber].firstTriangle; + + mVertices[vertexNumber].firstTriangle = (int32_t)(&t - &mTriangleList[0]); + //int a = 0; +} + + + +void ApexSubdivider::removeTriangleFromVertex(uint32_t vertexNumber, uint32_t triangleNumber) +{ + PX_ASSERT(vertexNumber < mVertices.size()); + PX_ASSERT(triangleNumber < mTriangles.size()); + + int32_t* lastPointer = &mVertices[vertexNumber].firstTriangle; + int32_t triangleListIndex = *lastPointer; + while (triangleListIndex != -1) + { + if (mTriangleList[(uint32_t)triangleListIndex].triangleNumber == triangleNumber) + { + *lastPointer = mTriangleList[(uint32_t)triangleListIndex].nextTriangle; + + freeTriangleElement((uint32_t)triangleListIndex); + + break; + } + lastPointer = &mTriangleList[(uint32_t)triangleListIndex].nextTriangle; + triangleListIndex = *lastPointer; + } +} + + + +ApexSubdivider::TriangleList& ApexSubdivider::allocateTriangleElement() +{ + if (mTriangleListEmptyElement == -1) + { + return mTriangleList.insert(); + } + else + { + PX_ASSERT((uint32_t)mTriangleListEmptyElement < mTriangleList.size()); + TriangleList& elem = mTriangleList[(uint32_t)mTriangleListEmptyElement]; + mTriangleListEmptyElement = elem.nextTriangle; + elem.nextTriangle = -1; + + return elem; + } +} + + + +void ApexSubdivider::freeTriangleElement(uint32_t index) +{ + PX_ASSERT(index < mTriangleList.size()); + mTriangleList[index].nextTriangle = mTriangleListEmptyElement; + mTriangleListEmptyElement = (int32_t)index; +} + +} +} // end namespace nvidia::apex diff --git a/APEX_1.4/common/src/ApexTetrahedralizer.cpp b/APEX_1.4/common/src/ApexTetrahedralizer.cpp new file mode 100644 index 00000000..b1e8fda0 --- /dev/null +++ b/APEX_1.4/common/src/ApexTetrahedralizer.cpp @@ -0,0 +1,1266 @@ +/* + * 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. + */ + + +#include "ApexTetrahedralizer.h" + +#include "ApexSDKIntl.h" + +#include "ApexCollision.h" +#include "ApexMeshHash.h" +#include "ApexSharedUtils.h" + +#include "PsSort.h" + +namespace nvidia +{ +namespace apex +{ + +// check for sanity in between algorithms +#define TETRALIZER_SANITY_CHECKS 0 + +const uint32_t ApexTetrahedralizer::FullTetrahedron::sideIndices[4][3] = {{1, 2, 3}, {2, 0, 3}, {0, 1, 3}, {2, 1, 0}}; + +void ApexTetrahedralizer::TetraEdgeList::sort() +{ + shdfnd::sort(mEdges.begin(), mEdges.size(), TetraEdge()); +} + + +ApexTetrahedralizer::ApexTetrahedralizer(uint32_t subdivision) : + mMeshHash(NULL), + mSubdivision(subdivision), + mBoundDiagonal(0), + mFirstFarVertex(0), + mLastFarVertex(0) +{ + mBound.setEmpty(); +} + + + +ApexTetrahedralizer::~ApexTetrahedralizer() +{ + if (mMeshHash != NULL) + { + delete mMeshHash; + mMeshHash = NULL; + } +} + + + +void ApexTetrahedralizer::registerVertex(const PxVec3& pos) +{ + TetraVertex v; + v.init(pos, 0); + mVertices.pushBack(v); + mBound.include(pos); +} + + + +void ApexTetrahedralizer::registerTriangle(uint32_t i0, uint32_t i1, uint32_t i2) +{ + mIndices.pushBack(i0); + mIndices.pushBack(i1); + mIndices.pushBack(i2); +} + + + +void ApexTetrahedralizer::endRegistration(IProgressListener* progressListener) +{ + + // hash mesh triangles for faster access + mBoundDiagonal = (mBound.minimum - mBound.maximum).magnitude(); + + HierarchicalProgressListener progress(100, progressListener); + + if (mMeshHash == NULL) + { + mMeshHash = PX_NEW(ApexMeshHash)(); + } + + // clears the hash if it's not empty + mMeshHash->setGridSpacing(0.1f * mBoundDiagonal); + + weldVertices(); + + for (uint32_t i = 0; i < mIndices.size(); i += 3) + { + PxBounds3 triangleBound; + triangleBound.minimum = triangleBound.maximum = mVertices[mIndices[i]].pos; + triangleBound.include(mVertices[mIndices[i + 1]].pos); + triangleBound.include(mVertices[mIndices[i + 2]].pos); + mMeshHash->add(triangleBound, i); + } + + + progress.setSubtaskWork(10, "Delaunay Tetrahedralization"); + delaunayTetrahedralization(&progress); + progress.completeSubtask(); + + // neighbor info is gone from this point on! + compressTetrahedra(true); + +#if 1 // do we really need this? + uint32_t tetStart = 0; + uint32_t oldNumTets = mTetras.size(); + uint32_t numSwapped = 0; + uint32_t lastNumSwapped = mTetras.size() * 10; // just a number that is way too big + uint32_t iterations = 0; // this one is pure safety + int32_t fraction = 50; + do + { + fraction /= 2; + progress.setSubtaskWork(fraction, "Swapping Tetrahedra"); + lastNumSwapped = numSwapped; + numSwapped = swapTetrahedra(tetStart, &progress); + tetStart = oldNumTets; + oldNumTets = mTetras.size(); + progress.completeSubtask(); + } + while (numSwapped > 0 && (numSwapped < lastNumSwapped) && (iterations++ < 20)); + + + // just to fill the entire fraction up + progress.setSubtaskWork(fraction, "Swapping Tetrahedra"); + progress.completeSubtask(); +#endif + + + + progress.setSubtaskWork(-1, "Removing outer tetrahedra"); + removeOuterTetrahedra(&progress); + progress.completeSubtask(); + + compressTetrahedra(true); + compressVertices(); + + // release the indices + mIndices.clear(); +} + + + +void ApexTetrahedralizer::getVertices(PxVec3* data) +{ + for (uint32_t i = 0; i < mVertices.size(); i++) + { + data[i] = mVertices[i].pos; + } +} + + + +void ApexTetrahedralizer::getIndices(uint32_t* data) +{ + for (uint32_t i = 0; i < mTetras.size(); i++) + { + for (uint32_t j = 0; j < 4; j++) + { + data[i * 4 + j] = (uint32_t)mTetras[i].vertexNr[j]; + } + } +} + + + +void ApexTetrahedralizer::weldVertices() +{ + if (mSubdivision < 1) + { + return; + } + + const float relativeThreshold = 0.3f; + + const float d = mBoundDiagonal / mSubdivision * relativeThreshold; + const float d2 = d * d; + + uint32_t readIndex = 0; + uint32_t writeIndex = 0; + physx::Array<int32_t> old2new(mVertices.size(), -1); + while (readIndex < mVertices.size()) + { + for (uint32_t i = 0; i < writeIndex; i++) + { + if ((mVertices[i].pos - mVertices[readIndex].pos).magnitudeSquared() < d2) + { + old2new[readIndex] = (int32_t)i; + break; + } + } + + if (old2new[readIndex] == -1) + { + old2new[readIndex] = (int32_t)writeIndex; + mVertices[writeIndex] = mVertices[readIndex]; + writeIndex++; + } + readIndex++; + } + + if (readIndex == writeIndex) + { + return; + } + + for (uint32_t i = 0; i < mIndices.size(); i++) + { + PX_ASSERT(old2new[mIndices[i]] != -1); + mIndices[i] = (uint32_t)old2new[mIndices[i]]; + } +} + + +void ApexTetrahedralizer::delaunayTetrahedralization(IProgressListener* progress) +{ + physx::Array<TetraEdge> edges; + TetraEdge edge; + + // start with huge tetrahedron + mTetras.clear(); + + const float a = 3.0f * mBoundDiagonal; + const float x = 0.5f * a; + const float y0 = x / PxSqrt(3.0f); + const float y1 = x * PxSqrt(3.0f) - y0; + const float z0 = 0.25f * PxSqrt(6.0f) * a; + const float z1 = a * PxSqrt(6.0f) / 3.0f - z0; + + PxVec3 center = mBound.getCenter(); + + mFirstFarVertex = mVertices.size(); + PxVec3 p0(-x, -y0, -z1); + registerVertex(center + p0); + PxVec3 p1(x, -y0, -z1); + registerVertex(center + p1); + PxVec3 p2(0, y1, -z1); + registerVertex(center + p2); + PxVec3 p3(0, 0, z0); + registerVertex(center + p3); + mLastFarVertex = mVertices.size() - 1; + + // do not update it as these vertices are irrelevant + //mBoundDiagonal = mBound.minimum.distance(mBound.maximum); + + FullTetrahedron tetra; + tetra.set((int32_t)mFirstFarVertex, (int32_t)mFirstFarVertex + 1, (int32_t)mFirstFarVertex + 2, (int32_t)mFirstFarVertex + 3); + mTetras.pushBack(tetra); + + // build Delaunay triangulation iteratively + + for (uint32_t i = 0; i < mFirstFarVertex; i++) + { + + if (progress != NULL && (i & 0x7f) == 0) + { + const int32_t percent = (int32_t)(100 * i / mFirstFarVertex); + progress->setProgress(percent); + } + + PxVec3& v = mVertices[i].pos; + /// \todo vertex reordering could speed up this search via warm start + int tNr = findSurroundingTetra(mTetras.size() - 1, v); // fast walk + if (tNr == -1) + { + continue; + } + PX_ASSERT(tNr >= 0); + + const uint32_t newTetraNr = mTetras.size(); + const float volumeDiff = retriangulate((uint32_t)tNr, i); + +#if TETRALIZER_SANITY_CHECKS + if (PxAbs(volumeDiff) > 0.001f) + { + APEX_INTERNAL_ERROR("Volume added: %f", volumeDiff); + } +#else + PX_UNUSED(volumeDiff); +#endif + + edges.clear(); + for (uint32_t j = newTetraNr; j < mTetras.size(); j++) + { + FullTetrahedron& newTet = mTetras[j]; + edge.init(newTet.vertexNr[2], newTet.vertexNr[3], (int32_t)j, 1); + edges.pushBack(edge); + edge.init(newTet.vertexNr[3], newTet.vertexNr[1], (int32_t)j, 2); + edges.pushBack(edge); + edge.init(newTet.vertexNr[1], newTet.vertexNr[2], (int32_t)j, 3); + edges.pushBack(edge); + } + + shdfnd::sort(edges.begin(), edges.size(), TetraEdge()); + + for (uint32_t j = 0; j < edges.size(); j += 2) + { + TetraEdge& edge0 = edges[j]; + TetraEdge& edge1 = edges[j + 1]; + PX_ASSERT(edge0 == edge1); + mTetras[edge0.tetraNr].neighborNr[(uint32_t)edge0.neighborNr] = (int32_t)edge1.tetraNr; + mTetras[edge1.tetraNr].neighborNr[(uint32_t)edge1.neighborNr] = (int32_t)edge0.tetraNr; + } + } +} + + + + + +int32_t ApexTetrahedralizer::findSurroundingTetra(uint32_t startTetra, const PxVec3& p) const +{ + // find the tetrahedra which contains the vertex + // by walking through mesh O(n ^ (1/3)) + + if (mTetras.empty()) + { + return -1; + } + + PX_ASSERT(mTetras[startTetra].bDeleted == 0); + + + uint32_t iterationCounter = 0; + const uint32_t iterationCounterMax = PxMin(1000u, mTetras.size()); + + int32_t tetNr = (int32_t)startTetra; + while (tetNr >= 0 && iterationCounter++ < iterationCounterMax) + { + const FullTetrahedron& tetra = mTetras[(uint32_t)tetNr]; + PX_ASSERT(tetra.bDeleted == 0); + + const PxVec3& p0 = mVertices[(uint32_t)tetra.vertexNr[0]].pos; + PxVec3 q = p - p0; + PxVec3 q0 = mVertices[(uint32_t)tetra.vertexNr[1]].pos - p0; + PxVec3 q1 = mVertices[(uint32_t)tetra.vertexNr[2]].pos - p0; + PxVec3 q2 = mVertices[(uint32_t)tetra.vertexNr[3]].pos - p0; + PxMat33 m(q0,q1,q2); + const float det = m.getDeterminant(); + m.column0 = q; + const float x = m.getDeterminant(); + if (x < 0.0f && tetra.neighborNr[1] >= 0) + { + tetNr = tetra.neighborNr[1]; + continue; + } + + m.column0 = q0; + m.column1 = q; + const float y = m.getDeterminant(); + if (y < 0.0f && tetra.neighborNr[2] >= 0) + { + tetNr = tetra.neighborNr[2]; + continue; + } + + m.column1 = q1; + m.column2 = q; + const float z = m.getDeterminant(); + + if (z < 0.0f && tetra.neighborNr[3] >= 0) + { + tetNr = tetra.neighborNr[3]; + continue; + } + + if (x + y + z > det && tetra.neighborNr[0] >= 0) + { + tetNr = tetra.neighborNr[0]; + continue; + } + +#if TETRALIZER_SANITY_CHECKS + static uint32_t maxIterationCounter = 0; + maxIterationCounter = PxMax(maxIterationCounter, iterationCounter); +#endif + return tetNr; + } + if (iterationCounter == iterationCounterMax + 1) + { + // search all vertices + for (uint32_t i = 0; i < mVertices.size(); i++) + { + if (mVertices[i].pos == p) + { + PX_ASSERT(!mVertices[i].isDeleted()); + return -1; + } + } + + uint32_t nbDeleted = 0; + for (uint32_t i = 0; i < mTetras.size(); i++) + { + const FullTetrahedron& tetra = mTetras[i]; + if (tetra.bDeleted == 0) + { + if (pointInTetra(tetra, p)) + { + if (tetra.bDeleted == 0) + { + return (int32_t)i; + } + nbDeleted++; + } + } + } +#if defined(_DEBUG) || TETRALIZER_SANITY_CHECKS + // PH: Do not give out this warning/error when running in release mode + APEX_INTERNAL_ERROR("Failed to find tetra for hull vertex"); + //debugPoints.pushBack(p); +#endif + } + return -1; +} + + + +float ApexTetrahedralizer::retriangulate(const uint32_t tetraNr, uint32_t vertexNr) +{ + PX_ASSERT(tetraNr < mTetras.size()); + PX_ASSERT(vertexNr < mVertices.size()); + + if (mTetras[tetraNr].bDeleted == 1) + { + return 0; + } + +#if TETRALIZER_SANITY_CHECKS + const float volumeDeleted = getTetraVolume(mTetras[tetraNr].vertexNr[0], mTetras[tetraNr].vertexNr[1], mTetras[tetraNr].vertexNr[2], mTetras[tetraNr].vertexNr[3]);; +#endif + float volumeCreated = 0; + + mTetras[tetraNr].bDeleted = 1; + + PxVec3& v = mVertices[vertexNr].pos; + for (uint32_t i = 0; i < 4; i++) + { + int n = mTetras[tetraNr].neighborNr[i]; + if (n >= 0 && mTetras[(uint32_t)n].bDeleted == 1) + { + continue; + } + + if (n >= 0 && pointInCircumSphere(mTetras[(uint32_t)n], v)) + { + volumeCreated += retriangulate((uint32_t)n, vertexNr); + } + else + { + FullTetrahedron& tetra = mTetras[tetraNr]; + FullTetrahedron tNew; + tNew.set((int32_t)vertexNr, + tetra.vertexNr[FullTetrahedron::sideIndices[i][0]], + tetra.vertexNr[FullTetrahedron::sideIndices[i][1]], + tetra.vertexNr[FullTetrahedron::sideIndices[i][2]] + ); + +#if TETRALIZER_SANITY_CHECKS + volumeCreated += getTetraVolume(tNew.vertexNr[0], tNew.vertexNr[1], tNew.vertexNr[2], tNew.vertexNr[3]); +#endif + + tNew.neighborNr[0] = n; + + if (n >= 0) + { + PX_ASSERT(mTetras[(uint32_t)n].neighborOf(tNew.vertexNr[1], tNew.vertexNr[2], tNew.vertexNr[3]) == (int32_t)tetraNr); + mTetras[(uint32_t)n].neighborOf(tNew.vertexNr[1], tNew.vertexNr[2], tNew.vertexNr[3]) = (int32_t)mTetras.size(); + } + mTetras.pushBack(tNew); + } + } +#if TETRALIZER_SANITY_CHECKS + return volumeCreated - volumeDeleted; +#else + return 0; +#endif +} + + + +uint32_t ApexTetrahedralizer::swapTetrahedra(uint32_t startTet, IProgressListener* progress) +{ + PX_ASSERT(mMeshHash != NULL); + + const float threshold = 0.05f * mBoundDiagonal / mSubdivision; + + TetraEdgeList edges; + edges.mEdges.reserve(mTetras.size() * 6); + for (uint32_t i = startTet; i < mTetras.size(); i++) + { + FullTetrahedron& t = mTetras[i]; + if (t.bDeleted == 1) + { + continue; + } + + TetraEdge edge; + edge.init(t.vertexNr[0], t.vertexNr[1], (int32_t)i); + edges.add(edge); + edge.init(t.vertexNr[1], t.vertexNr[2], (int32_t)i); + edges.add(edge); + edge.init(t.vertexNr[2], t.vertexNr[0], (int32_t)i); + edges.add(edge); + edge.init(t.vertexNr[0], t.vertexNr[3], (int32_t)i); + edges.add(edge); + edge.init(t.vertexNr[1], t.vertexNr[3], (int32_t)i); + edges.add(edge); + edge.init(t.vertexNr[2], t.vertexNr[3], (int32_t)i); + edges.add(edge); + } + edges.sort(); + + + uint32_t index = 0; + uint32_t progressCounter = 0; + uint32_t numEdgesSwapped = 0; + while (index < edges.numEdges()) + { + if (progress != NULL && ((progressCounter++ & 0xf) == 0)) + { + const int32_t percent = (int32_t)(100 * index / edges.numEdges()); + progress->setProgress(percent); + } + TetraEdge edge = edges[index]; + uint32_t vNr[2] = { edge.vNr0, edge.vNr1 }; + + index++; + while (index < edges.numEdges() && edges[index] == edge) + { + index++; + } + + if (isFarVertex(vNr[0]) || isFarVertex(vNr[1])) + { + continue; + } + + const PxVec3 v0 = mVertices[vNr[0]].pos; + const PxVec3 v1 = mVertices[vNr[1]].pos; + PxBounds3 edgeBounds; + edgeBounds.setEmpty(); + edgeBounds.include(v0); + edgeBounds.include(v1); + mMeshHash->queryUnique(edgeBounds, mTempItemIndices); + if (mTempItemIndices.size() == 0) + { + continue; + } + + bool cut = false; + for (uint32_t k = 0; k < mTempItemIndices.size(); k++) + { + uint32_t triNr = mTempItemIndices[k]; + PX_ASSERT(triNr % 3 == 0); + uint32_t* triangle = &mIndices[triNr]; + + // if one vertex is part of the iso surface + if (triangleContainsVertexNr(triangle, vNr, 2)) + { + continue; + } + + const PxVec3 p0 = mVertices[triangle[0]].pos; + const PxVec3 p1 = mVertices[triangle[1]].pos; + const PxVec3 p2 = mVertices[triangle[2]].pos; + + PxBounds3 triBounds; + triBounds.minimum = triBounds.maximum = p0; + triBounds.include(p1); + triBounds.include(p2); + + if (!triBounds.intersects(edgeBounds)) + { + continue; + } + + PxVec3 normal = (p1 - p0).cross(p2 - p0); + normal.normalize(); + + if (PxAbs(normal.dot(v0 - p0)) < threshold) + { + continue; + } + if (PxAbs(normal.dot(v1 - p0)) < threshold) + { + continue; + } + + float t, u, v; + if (!APEX_RayTriangleIntersect(v0, v1 - v0, p0, p1, p2, t, u, v)) + { + continue; + } + + // PH: I guess we don't need to cut when edge does not intersect triangle + if (t < 0 || t > 1) + { + continue; + } + + cut = true; + break; + } + if (cut) + { +#if TETRAHEDRALIZER_DEBUG_RENDERING + debugLines.pushBack(mVertices[vNr[0]].pos); + debugLines.pushBack(mVertices[vNr[1]].pos); +#endif + numEdgesSwapped += swapEdge(vNr[0], vNr[1]) ? 1 : 0; + } + } + + return numEdgesSwapped; +} + + + +bool ApexTetrahedralizer::swapEdge(uint32_t v0, uint32_t v1) +{ + physx::Array<int32_t> borderEdges; + physx::Array<FullTetrahedron> newTetras; + + uint32_t nbDeleted = 0; + + mTempItemIndices.clear(); + + for (uint32_t i = 0; i < mTetras.size(); i++) + { + FullTetrahedron& t = mTetras[i]; + + if (t.bDeleted == 1) + { + continue; + } + + if (!t.containsVertex((int32_t)v0) || !t.containsVertex((int32_t)v1)) + { + continue; + } + + //t.bDeleted = 1; + mTempItemIndices.pushBack(i); + nbDeleted++; + int32_t v2, v3; + t.get2OppositeVertices((int32_t)v0, (int32_t)v1, v2, v3); + if (getTetraVolume((int32_t)v0, (int32_t)v1, v2, v3) >= 0.0f) + { + borderEdges.pushBack(v2); + borderEdges.pushBack(v3); + } + else + { + borderEdges.pushBack(v3); + borderEdges.pushBack(v2); + } + } + + if (borderEdges.size() < 6) + { + return false; + } + + // start with first edge + physx::Array<int32_t> borderVertices; + physx::Array<float> borderQuality; + borderVertices.pushBack(borderEdges[borderEdges.size() - 2]); + borderVertices.pushBack(borderEdges[borderEdges.size() - 1]); + borderEdges.popBack(); + borderEdges.popBack(); + + // construct border + int vEnd = borderVertices[1]; + while (borderEdges.size() > 0) + { + uint32_t i = 0; + uint32_t num = borderEdges.size(); + for (; i < num - 1; i += 2) + { + if (borderEdges[i] == vEnd) + { + vEnd = borderEdges[i + 1]; + break; + } + } + // not connected + if (i >= num) + { + return false; + } + + borderVertices.pushBack(vEnd); + borderEdges[i] = borderEdges[num - 2]; + borderEdges[i + 1] = borderEdges[num - 1]; + borderEdges.popBack(); + borderEdges.popBack(); + } + + // not circular + if (borderVertices[0] != borderVertices[borderVertices.size() - 1]) + { + return false; + } + + borderVertices.popBack(); + + if (borderVertices.size() < 3) + { + return false; + } + + // generate tetrahedra + FullTetrahedron tetra0, tetra1; + borderQuality.resize(borderVertices.size()); + while (borderVertices.size() > 3) + { + uint32_t num = borderVertices.size(); + uint32_t i0, i1, i2; + for (i0 = 0; i0 < num; i0++) + { + i1 = (i0 + 1) % num; + i2 = (i1 + 1) % num; + tetra0.set(borderVertices[i0], borderVertices[i1], borderVertices[i2], (int32_t)v1); + borderQuality[i1] = getTetraQuality(tetra0); + } + + float maxQ = 0.0f; + int32_t maxI0 = -1; + for (i0 = 0; i0 < num; i0++) + { + i1 = (i0 + 1) % num; + i2 = (i1 + 1) % num; + tetra0.set(borderVertices[i0], borderVertices[i1], borderVertices[i2], (int32_t)v1); + tetra1.set(borderVertices[i2], borderVertices[i1], borderVertices[i0], (int32_t)v0); + if (getTetraVolume(tetra0) < 0.0f || getTetraVolume(tetra1) < 0.0f) + { + continue; + } + + bool ear = true; + for (uint32_t i = 0; i < num; i++) + { + if (i == i0 || i == i1 || i == i2) + { + continue; + } + + PxVec3& pos = mVertices[(uint32_t)borderVertices[i]].pos; + if (pointInTetra(tetra0, pos) || pointInTetra(tetra1, pos)) + { + ear = false; + } + } + + if (!ear) + { + continue; + } + + float q = (1.0f - borderQuality[i0]) + borderQuality[i1] + (1.0f - borderQuality[i2]); + + if (maxI0 < 0 || q > maxQ) + { + maxQ = q; + maxI0 = (int32_t)i0; + } + } + if (maxI0 < 0) + { + return false; + } + + i0 = (uint32_t)maxI0; + i1 = (i0 + 1) % num; + i2 = (i1 + 1) % num; + tetra0.set(borderVertices[i0], borderVertices[i1], borderVertices[i2], (int32_t)v1); + tetra1.set(borderVertices[i2], borderVertices[i1], borderVertices[i0], (int32_t)v0); + + // add tetras, remove vertex; + newTetras.pushBack(tetra0); + newTetras.pushBack(tetra1); + for (uint32_t i = i1; i < num - 1; i++) + { + borderVertices[i] = borderVertices[i + 1]; + } + borderVertices.popBack(); + } + tetra0.set(borderVertices[0], borderVertices[1], borderVertices[2], (int32_t)v1); + tetra1.set(borderVertices[2], borderVertices[1], borderVertices[0], (int32_t)v0); + + if (getTetraVolume(tetra0) < 0.0f || getTetraVolume(tetra1) < 0.0f) + { + return false; + } + + newTetras.pushBack(tetra0); + newTetras.pushBack(tetra1); + + PX_ASSERT(nbDeleted <= newTetras.size() + 1); + + for (uint32_t i = 0; i < mTempItemIndices.size(); i++) + { + mTetras[mTempItemIndices[i]].bDeleted = 1; + } + + // add new tetras; + for (uint32_t i = 0; i < newTetras.size(); i++) + { + mTetras.pushBack(newTetras[i]); + } + + return true; +} + + +class F32Less +{ +public: + PX_INLINE bool operator()(float v1, float v2) const + { + return v1 < v2; + } +}; + + + +bool ApexTetrahedralizer::removeOuterTetrahedra(IProgressListener* progress) +{ + const float EPSILON = 1e-5f; + PX_ASSERT((float)(mBoundDiagonal + EPSILON) > mBoundDiagonal); + +#if TETRAHEDRALIZER_DEBUG_RENDERING && TETRALIZER_SANITY_CHECKS + static uint32_t interesting = 0xffffffff; + debugBounds.clear(); +#endif + + physx::Array<float> raycastHitTimes; + + for (uint32_t i = 0; i < mTetras.size(); i++) + { + if (progress != NULL && (i & 0x7) == 0) + { + const int32_t percent = (int32_t)(100 * i / mTetras.size()); + progress->setProgress(percent); + } + FullTetrahedron& tetra = mTetras[i]; + + if (isFarVertex((uint32_t)tetra.vertexNr[0]) || isFarVertex((uint32_t)tetra.vertexNr[1]) + || isFarVertex((uint32_t)tetra.vertexNr[2]) || isFarVertex((uint32_t)tetra.vertexNr[3])) + { + tetra.bDeleted = 1; + } + else if (tetra.bDeleted == 0) + { + PxVec3 orig, dir; + orig = mVertices[(uint32_t)tetra.vertexNr[0]].pos; + orig += mVertices[(uint32_t)tetra.vertexNr[1]].pos; + orig += mVertices[(uint32_t)tetra.vertexNr[2]].pos; + orig += mVertices[(uint32_t)tetra.vertexNr[3]].pos; + orig *= 0.25f; + + uint32_t numInside = 0; // test 6 standard rays, majority vote + for (uint32_t j = 0; j < 3; j++) + { + PxBounds3 rayBounds(orig, orig); + switch (j) + { + case 0: + { + rayBounds.maximum.x = mBound.maximum.x + EPSILON; + rayBounds.minimum.x = mBound.minimum.x - EPSILON; + dir = PxVec3(1.0f, 0.0f, 0.0f); + } + break; + case 1: + { + rayBounds.maximum.y = mBound.maximum.y + EPSILON; + rayBounds.minimum.y = mBound.minimum.y - EPSILON; + dir = PxVec3(0.0f, 1.0f, 0.0f); + } + break; + case 2: + { + rayBounds.maximum.z = mBound.maximum.z + EPSILON; + rayBounds.minimum.z = mBound.minimum.z - EPSILON; + dir = PxVec3(0.0f, 0.0f, 1.0f); + } + break; + } + mMeshHash->queryUnique(rayBounds, mTempItemIndices); + + raycastHitTimes.clear(); + +#if TETRAHEDRALIZER_DEBUG_RENDERING && TETRALIZER_SANITY_CHECKS + if (i == interesting) + { + debugBounds.pushBack(rayBounds.minimum); + debugBounds.pushBack(rayBounds.maximum); + } +#endif + + for (uint32_t k = 0; k < mTempItemIndices.size(); k++) + { + uint32_t indexNr = mTempItemIndices[k]; + PX_ASSERT(indexNr % 3 == 0); + + const PxVec3 p0 = mVertices[mIndices[indexNr + 0]].pos; + const PxVec3 p1 = mVertices[mIndices[indexNr + 1]].pos; + const PxVec3 p2 = mVertices[mIndices[indexNr + 2]].pos; + PxBounds3 triBounds; + triBounds.minimum = triBounds.maximum = p0; + triBounds.include(p1); + triBounds.include(p2); + + if (!rayBounds.intersects(triBounds)) + { + continue; + } + +#if TETRAHEDRALIZER_DEBUG_RENDERING && TETRALIZER_SANITY_CHECKS + if (i == interesting) + { + //debugTriangles.pushBack(p0); + //debugTriangles.pushBack(p1); + //debugTriangles.pushBack(p2); + debugBounds.pushBack(triBounds.minimum); + debugBounds.pushBack(triBounds.maximum); + } +#endif + + + float t, u, v; + if (!APEX_RayTriangleIntersect(orig, dir, p0, p1, p2, t, u, v)) + { + continue; + } + + raycastHitTimes.pushBack(t); + } + + if (!raycastHitTimes.empty()) + { + shdfnd::sort(raycastHitTimes.begin(), raycastHitTimes.size(), F32Less()); + + uint32_t negative = 0, positive = 0; +#if TETRALIZER_SANITY_CHECKS + bool report = false;//0 <= i && i < 1033; +#endif + for (uint32_t k = 0; k < raycastHitTimes.size(); k++) + { + if (k > 0 && PxAbs(raycastHitTimes[k] - raycastHitTimes[k - 1]) < EPSILON) + { +#if TETRALIZER_SANITY_CHECKS + report = true; +#endif + // PH: This proved to not be working right, or not making any difference + //continue; + } + if (raycastHitTimes[k] < 0) + { + negative++; + } + else + { + positive++; + } + } + numInside += (positive & 0x1) + (negative & 0x1); +#if TETRAHEDRALIZER_DEBUG_RENDERING && TETRALIZER_SANITY_CHECKS + if (report) + { + float scale = 1.01f; + debugLines.pushBack(worldRay.orig - worldRay.dir * scale); + debugLines.pushBack(worldRay.orig + worldRay.dir * scale); + } +#endif + } + } + if (numInside < 3) + { + tetra.bDeleted = 1; + } +#if TETRAHEDRALIZER_DEBUG_RENDERING && TETRALIZER_SANITY_CHECKS + if (numInside > 0 && numInside < 6) + { + debugTetras.pushBack(mVertices[mTetras[i].vertexNr[0]].pos); + debugTetras.pushBack(mVertices[mTetras[i].vertexNr[1]].pos); + debugTetras.pushBack(mVertices[mTetras[i].vertexNr[2]].pos); + debugTetras.pushBack(mVertices[mTetras[i].vertexNr[3]].pos); + } +#endif + + // remove degenerated tetrahedra (slivers) + float quality = getTetraQuality(tetra); + PX_ASSERT(quality >= 0); + PX_ASSERT(quality <= 1); + tetra.quality = (uint32_t)(quality * 1023.0f); + if (quality < 0.001f) + { + tetra.bDeleted = 1; + } + } + } + + return true; +} + + + +void ApexTetrahedralizer::updateCircumSphere(FullTetrahedron& tetra) const +{ + if (tetra.bCircumSphereDirty == 0) + { + return; + } + + const PxVec3 p0 = mVertices[(uint32_t)tetra.vertexNr[0]].pos; + const PxVec3 b = mVertices[(uint32_t)tetra.vertexNr[1]].pos - p0; + const PxVec3 c = mVertices[(uint32_t)tetra.vertexNr[2]].pos - p0; + const PxVec3 d = mVertices[(uint32_t)tetra.vertexNr[3]].pos - p0; + + float det = b.x * (c.y * d.z - c.z * d.y) - b.y * (c.x * d.z - c.z * d.x) + b.z * (c.x * d.y - c.y * d.x); + + if (det == 0.0f) + { + tetra.center = p0; + tetra.radiusSquared = 0.0f; + return; // singular case + } + + det *= 2.0f; + PxVec3 v = c.cross(d) * b.dot(b) + d.cross(b) * c.dot(c) + b.cross(c) * d.dot(d); + v /= det; + + tetra.radiusSquared = v.magnitudeSquared(); + tetra.center = p0 + v; + tetra.bCircumSphereDirty = 0; +} + + + +bool ApexTetrahedralizer::pointInCircumSphere(FullTetrahedron& tetra, const PxVec3& p) const +{ + updateCircumSphere(tetra); + return (tetra.center - p).magnitudeSquared() < tetra.radiusSquared; +} + + + +bool ApexTetrahedralizer::pointInTetra(const FullTetrahedron& tetra, const PxVec3& p) const +{ + const PxVec3 q = p - mVertices[(uint32_t)tetra.vertexNr[0]].pos; + const PxVec3 q0 = mVertices[(uint32_t)tetra.vertexNr[1]].pos - mVertices[(uint32_t)tetra.vertexNr[0]].pos; + const PxVec3 q1 = mVertices[(uint32_t)tetra.vertexNr[2]].pos - mVertices[(uint32_t)tetra.vertexNr[0]].pos; + const PxVec3 q2 = mVertices[(uint32_t)tetra.vertexNr[3]].pos - mVertices[(uint32_t)tetra.vertexNr[0]].pos; + + PxMat33 m(q0,q1,q2); + float det = m.getDeterminant(); + m.column0 = q; + float x = m.getDeterminant(); + m.column0 = q0; + m.column1 = q; + float y = m.getDeterminant(); + m.column1 = q1; + m.column2 = q; + float z = m.getDeterminant(); + if (det < 0.0f) + { + x = -x; + y = -y; + z = -z; + det = -det; + } + + if (x < 0.0f || y < 0.0f || z < 0.0f) + { + return false; + } + + return (x + y + z < det); +} + + + +float ApexTetrahedralizer::getTetraVolume(const FullTetrahedron& tetra) const +{ + const PxVec3 v0 = mVertices[(uint32_t)tetra.vertexNr[0]].pos; + const PxVec3 v1 = mVertices[(uint32_t)tetra.vertexNr[1]].pos - v0; + const PxVec3 v2 = mVertices[(uint32_t)tetra.vertexNr[2]].pos - v0; + const PxVec3 v3 = mVertices[(uint32_t)tetra.vertexNr[3]].pos - v0; + return v3.dot(v1.cross(v2)) / 6.0f; +} + + + +float ApexTetrahedralizer::getTetraVolume(int32_t v0, int32_t v1, int32_t v2, int32_t v3) const +{ + FullTetrahedron t; + t.set(v0, v1, v2, v3); + return getTetraVolume(t); +} + + + +float ApexTetrahedralizer::getTetraQuality(const FullTetrahedron& tetra) const +{ + const float sqrt2 = 1.4142135623f; + + const float e = getTetraLongestEdge(tetra); + if (e == 0.0f) + { + return 0.0f; + } + + // for regular tetrahedron vol * 6 * sqrt(2) = s^3 -> quality = 1.0 + return PxAbs(getTetraVolume(tetra)) * 6.0f * sqrt2 / (e * e * e); +} + + + +float ApexTetrahedralizer::getTetraLongestEdge(const FullTetrahedron& tetra) const +{ + const PxVec3& v0 = mVertices[(uint32_t)tetra.vertexNr[0]].pos; + const PxVec3& v1 = mVertices[(uint32_t)tetra.vertexNr[1]].pos; + const PxVec3& v2 = mVertices[(uint32_t)tetra.vertexNr[2]].pos; + const PxVec3& v3 = mVertices[(uint32_t)tetra.vertexNr[3]].pos; + float max = (v0 - v1).magnitudeSquared();; + max = PxMax(max, (v0 - v2).magnitudeSquared()); + max = PxMax(max, (v0 - v3).magnitudeSquared()); + max = PxMax(max, (v1 - v2).magnitudeSquared()); + max = PxMax(max, (v1 - v3).magnitudeSquared()); + max = PxMax(max, (v2 - v3).magnitudeSquared()); + return PxSqrt(max); +} + + + +bool ApexTetrahedralizer::triangleContainsVertexNr(uint32_t* triangle, uint32_t* vertexNumber, uint32_t nbVertices) +{ + if (triangle != NULL && vertexNumber != NULL && nbVertices > 0) + { + for (uint32_t i = 0; i < nbVertices; i++) + { + if (triangle[0] == vertexNumber[i] || triangle[1] == vertexNumber[i] || triangle[2] == vertexNumber[i]) + { + return true; + } + } + } + return false; +} + + + +void ApexTetrahedralizer::compressTetrahedra(bool trashNeighbours) +{ + if (trashNeighbours) + { + uint32_t i = 0; + while (i < mTetras.size()) + { + if (mTetras[i].bDeleted == 1) + { + mTetras.replaceWithLast(i); + } + else + { + mTetras[i].neighborNr[0] = mTetras[i].neighborNr[1] = mTetras[i].neighborNr[2] = mTetras[i].neighborNr[3] = -1; + i++; + } + } + } + else + { + physx::Array<int32_t> oldToNew(mTetras.size()); + + uint32_t i = 0; + while (i < mTetras.size()) + { + if (mTetras[i].bDeleted == 1) + { + oldToNew[i] = -1; + if (mTetras[mTetras.size() - 1].bDeleted == 1) + { + oldToNew[mTetras.size() - 1] = -1; + } + else + { + oldToNew[mTetras.size() - 1] = (int32_t)i; + mTetras[i] = mTetras[mTetras.size() - 1]; + i++; + } + mTetras.popBack(); + } + else + { + oldToNew[i] = (int32_t)i; + i++; + } + } + + for (i = 0; i < mTetras.size(); i++) + { + FullTetrahedron& t = mTetras[i]; + for (uint32_t j = 0; j < 4; j++) + { + if (t.neighborNr[j] >= 0) + { + PX_ASSERT((uint32_t)t.neighborNr[j] < oldToNew.size()); + t.neighborNr[j] = oldToNew[(uint32_t)t.neighborNr[j]]; + } + } + } + } +} + + + +void ApexTetrahedralizer::compressVertices() +{ + // remove vertices that are not referenced by any tetrahedra + physx::Array<int32_t> oldToNew; + physx::Array<TetraVertex> newVertices; + + mBound.setEmpty(); + + oldToNew.resize(mVertices.size(), -1); + + for (uint32_t i = 0; i < mTetras.size(); i++) + { + FullTetrahedron& t = mTetras[i]; + for (uint32_t j = 0; j < 4; j++) + { + uint32_t vNr = (uint32_t)t.vertexNr[j]; + if (oldToNew[vNr] < 0) + { + oldToNew[vNr] = (int32_t)newVertices.size(); + newVertices.pushBack(mVertices[vNr]); + mBound.include(mVertices[vNr].pos); + } + t.vertexNr[j] = oldToNew[vNr]; + } + } + + mVertices.clear(); + mVertices.resize(newVertices.size()); + for (uint32_t i = 0; i < newVertices.size(); i++) + { + mVertices[i] = newVertices[i]; + } +} + +} +} // end namespace nvidia::apex + diff --git a/APEX_1.4/common/src/CurveImpl.cpp b/APEX_1.4/common/src/CurveImpl.cpp new file mode 100644 index 00000000..c2daba5c --- /dev/null +++ b/APEX_1.4/common/src/CurveImpl.cpp @@ -0,0 +1,182 @@ +/* + * 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. + */ + + +#include "Apex.h" +#include "PxAssert.h" +#include "nvparameterized/NvParameterized.h" +#include "Curve.h" +#include "CurveImpl.h" + +namespace nvidia +{ +namespace apex +{ + +/** + Linear interpolation. "in" and "out" stands here for X and Y coordinates of the control points +*/ +inline float lerp(float inCurrent, float inMin, float inMax, float outMin, float outMax) +{ + if (inMin == inMax) + { + return outMin; + } + + return ((inCurrent - inMin) / (inMax - inMin)) * (outMax - outMin) + outMin; +} + +/** + The CurveImpl is a class for storing control points on a curve and evaluating the results later. +*/ + +/** + Retrieve the output Y for the specified input x, based on the properties of the stored curve described + by mControlPoints. +*/ +float CurveImpl::evaluate(float x) const +{ + Vec2R xPoints, yPoints; + if (calculateControlPoints(x, xPoints, yPoints)) + { + return lerp(x, xPoints[0], xPoints[1], yPoints[0], yPoints[1]); + } + else if (mControlPoints.size() == 1) + { + return mControlPoints[0].y; + } + else + { + // This is too noisy for editors... + //PX_ASSERT(!"Unable to find control points that contained the specified curve parameter"); + return 0; + } +} + +/** + Add a control point to the list of control points, returning the index of the new point. +*/ +uint32_t CurveImpl::addControlPoint(const Vec2R& controlPoint) +{ + uint32_t index = calculateFollowingControlPoint(controlPoint.x); + + if (index == mControlPoints.size()) + { + // add element to the end + Vec2R& v2 = mControlPoints.insert(); + v2 = controlPoint; + } + else + { + // memmove all elements from index - end to index+1 - new_end + uint32_t oldSize = mControlPoints.size(); + mControlPoints.insert(); + memmove(&mControlPoints[index + 1], &mControlPoints[index], sizeof(Vec2R) * (oldSize - index)); + mControlPoints[index] = controlPoint; + } + return index; +} + +/** + Add a control points to the list of control points. Assuming the + hPoints points to a list of vec2s +*/ +void CurveImpl::addControlPoints(::NvParameterized::Interface* param, ::NvParameterized::Handle& hPoints) +{ + ::NvParameterized::Handle ih(*param), hMember(*param); + int arraySize = 0; + PX_ASSERT(hPoints.getConstInterface() == param); + hPoints.getArraySize(arraySize); + for (int i = 0; i < arraySize; i++) + { + hPoints.getChildHandle(i, ih); + Vec2R tmpVec2; + ih.getChildHandle(0, hMember); + hMember.getParamF32(tmpVec2.x); + ih.getChildHandle(1, hMember); + hMember.getParamF32(tmpVec2.y); + + addControlPoint(tmpVec2); + } +} + +/** + Locates the control points that contain x, placing the resulting control points in the two + out parameters. Returns true if the points were found, false otherwise. If the points were not + found, the output variables are untouched +*/ +bool CurveImpl::calculateControlPoints(float x, Vec2R& outXPoints, Vec2R& outYPoints) const +{ + uint32_t controlPointSize = mControlPoints.size(); + if (controlPointSize < 2) + { + return false; + } + + uint32_t followControlPoint = calculateFollowingControlPoint(x); + if (followControlPoint == 0) + { + outXPoints[0] = outXPoints[1] = mControlPoints[0].x; + outYPoints[0] = outYPoints[1] = mControlPoints[0].y; + return true; + } + else if (followControlPoint == controlPointSize) + { + outXPoints[0] = outXPoints[1] = mControlPoints[followControlPoint - 1].x; + outYPoints[0] = outYPoints[1] = mControlPoints[followControlPoint - 1].y; + return true; + } + + outXPoints[0] = mControlPoints[followControlPoint - 1].x; + outXPoints[1] = mControlPoints[followControlPoint].x; + + outYPoints[0] = mControlPoints[followControlPoint - 1].y; + outYPoints[1] = mControlPoints[followControlPoint].y; + + return true; +} + +/** + Locates the first control point with x larger than xValue or the nimber of control points if such point doesn't exist +*/ +uint32_t CurveImpl::calculateFollowingControlPoint(float xValue) const +{ + // TODO: This could be made O(log(N)), but I think there should + // be so few entries that it's not worth the code complexity. + uint32_t cpSize = mControlPoints.size(); + + for (uint32_t u = 0; u < cpSize; ++u) + { + if (xValue <= mControlPoints[u].x) + { + return u; + } + } + + return cpSize; +} + +///get the array of control points +const Vec2R* CurveImpl::getControlPoints(uint32_t& outCount) const +{ + outCount = mControlPoints.size(); + if (outCount) + { + return &mControlPoints.front(); + } + else + { + // LRR: there's more to this, chase this down later + return NULL; + } +} + +} +} // namespace nvidia::apex diff --git a/APEX_1.4/common/src/ModuleBase.cpp b/APEX_1.4/common/src/ModuleBase.cpp new file mode 100644 index 00000000..51253706 --- /dev/null +++ b/APEX_1.4/common/src/ModuleBase.cpp @@ -0,0 +1,52 @@ +/* + * 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. + */ + + +#include "Apex.h" +#include "ApexSharedUtils.h" +#include "PsUserAllocated.h" +#include "ProfilerCallback.h" +#include "ModuleBase.h" + +#ifdef PHYSX_PROFILE_SDK + +#if PX_WINDOWS_FAMILY +nvidia::profile::PxProfileZone *gProfileZone=NULL; +#endif + +#endif + +namespace nvidia +{ +namespace apex +{ + +ModuleBase::ModuleBase() : + mSdk(0), + mApiProxy(0) +{ +} + +const char* ModuleBase::getName() const +{ + return mName.c_str(); +} + +void ModuleBase::release() +{ + GetApexSDK()->releaseModule(mApiProxy); +} + +void ModuleBase::destroy() +{ +} + +} +} // end namespace nvidia::apex diff --git a/APEX_1.4/common/src/ModuleUpdateLoader.cpp b/APEX_1.4/common/src/ModuleUpdateLoader.cpp new file mode 100644 index 00000000..a80803e4 --- /dev/null +++ b/APEX_1.4/common/src/ModuleUpdateLoader.cpp @@ -0,0 +1,57 @@ +/* + * 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. + */ + + +#ifdef WIN32 +#include "ModuleUpdateLoader.h" + +typedef HMODULE (GetUpdatedModule_FUNC)(const char*, const char*); + +ModuleUpdateLoader::ModuleUpdateLoader(const char* updateLoaderDllName) + : mGetUpdatedModuleFunc(NULL) +{ + mUpdateLoaderDllHandle = LoadLibrary(updateLoaderDllName); + + if (mUpdateLoaderDllHandle != NULL) + { + mGetUpdatedModuleFunc = GetProcAddress(mUpdateLoaderDllHandle, "GetUpdatedModule"); + } +} + +ModuleUpdateLoader::~ModuleUpdateLoader() +{ + if (mUpdateLoaderDllHandle != NULL) + { + FreeLibrary(mUpdateLoaderDllHandle); + mUpdateLoaderDllHandle = NULL; + } +} + +HMODULE ModuleUpdateLoader::loadModule(const char* moduleName, const char* appGuid) +{ + HMODULE result = NULL; + + if (mGetUpdatedModuleFunc != NULL) + { + // Try to get the module through PhysXUpdateLoader + GetUpdatedModule_FUNC* getUpdatedModuleFunc = (GetUpdatedModule_FUNC*)mGetUpdatedModuleFunc; + result = getUpdatedModuleFunc(moduleName, appGuid); + } + else + { + // If no PhysXUpdateLoader, just load the DLL directly + result = LoadLibrary(moduleName); + } + + return result; +} + + +#endif // WIN32 diff --git a/APEX_1.4/common/src/PVDParameterizedHandler.cpp b/APEX_1.4/common/src/PVDParameterizedHandler.cpp new file mode 100644 index 00000000..61f2092b --- /dev/null +++ b/APEX_1.4/common/src/PVDParameterizedHandler.cpp @@ -0,0 +1,658 @@ +/* + * 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. + */ + + +#include "PVDParameterizedHandler.h" +#include "ApexPvdClient.h" + +#ifndef WITHOUT_PVD + +#include "PxPvdDataStream.h" +#include "nvparameterized/NvParameterized.h" +#include "NvParameters.h" + +using namespace nvidia; +using namespace nvidia::shdfnd; + +#define SET_PROPERTY_VALUE \ + if (pvdAction != PvdAction::DESTROY) \ + {\ + ok = propertyHandle.getParam(val) == NvParameterized::ERROR_NONE; \ + if (ok)\ + {\ + if (isArrayElement)\ + mPvdStream->appendPropertyValueData(DataRef<const uint8_t>((const uint8_t*)&val, sizeof(val)));\ + else\ + mPvdStream->setPropertyValue(pvdInstance, propertyName, val);\ + }\ + }\ + +namespace physx +{ +namespace pvdsdk +{ + + + +bool PvdParameterizedHandler::createClass(const NamespacedName& className) +{ + bool create = !mCreatedClasses.contains(className.mName); + if (create) + { + mPvdStream->createClass(className); + mCreatedClasses.insert(className.mName); + } + + return create; +} + + + +bool PvdParameterizedHandler::getPvdType(const NvParameterized::Definition& def, NamespacedName& pvdTypeName) +{ + NvParameterized::DataType paramType = def.type(); + + bool ok = true; + switch(paramType) + { + case NvParameterized::TYPE_BOOL : + pvdTypeName = getPvdNamespacedNameForType<bool>(); + break; + + case NvParameterized::TYPE_STRING : + pvdTypeName = getPvdNamespacedNameForType<const char*>(); + break; + + case NvParameterized::TYPE_I8 : + pvdTypeName = getPvdNamespacedNameForType<int8_t>(); + break; + + case NvParameterized::TYPE_I16 : + pvdTypeName = getPvdNamespacedNameForType<int16_t>(); + break; + + case NvParameterized::TYPE_I32 : + pvdTypeName = getPvdNamespacedNameForType<int32_t>(); + break; + + case NvParameterized::TYPE_I64 : + pvdTypeName = getPvdNamespacedNameForType<int64_t>(); + break; + + case NvParameterized::TYPE_U8 : + pvdTypeName = getPvdNamespacedNameForType<uint8_t>(); + break; + + case NvParameterized::TYPE_U16 : + pvdTypeName = getPvdNamespacedNameForType<uint16_t>(); + break; + + case NvParameterized::TYPE_U32 : + pvdTypeName = getPvdNamespacedNameForType<uint32_t>(); + break; + + case NvParameterized::TYPE_U64 : + pvdTypeName = getPvdNamespacedNameForType<uint64_t>(); + break; + + case NvParameterized::TYPE_F32 : + pvdTypeName = getPvdNamespacedNameForType<float>(); + break; + + case NvParameterized::TYPE_F64 : + pvdTypeName = getPvdNamespacedNameForType<double>(); + break; + + case NvParameterized::TYPE_VEC2 : + pvdTypeName = getPvdNamespacedNameForType<PxVec2>(); + break; + + case NvParameterized::TYPE_VEC3 : + pvdTypeName = getPvdNamespacedNameForType<PxVec3>(); + break; + + case NvParameterized::TYPE_VEC4 : + pvdTypeName = getPvdNamespacedNameForType<uint16_t>(); + break; + + case NvParameterized::TYPE_QUAT : + pvdTypeName = getPvdNamespacedNameForType<PxQuat>(); + break; + + case NvParameterized::TYPE_MAT33 : + pvdTypeName = getPvdNamespacedNameForType<PxMat33>(); + break; + + case NvParameterized::TYPE_BOUNDS3 : + pvdTypeName = getPvdNamespacedNameForType<PxBounds3>(); + break; + + case NvParameterized::TYPE_MAT44 : + pvdTypeName = getPvdNamespacedNameForType<PxMat44>(); + break; + + case NvParameterized::TYPE_POINTER : + pvdTypeName = getPvdNamespacedNameForType<VoidPtr>(); + break; + + case NvParameterized::TYPE_TRANSFORM : + pvdTypeName = getPvdNamespacedNameForType<PxTransform>(); + break; + + case NvParameterized::TYPE_REF : + case NvParameterized::TYPE_STRUCT : + pvdTypeName = getPvdNamespacedNameForType<ObjectRef>(); + break; + + case NvParameterized::TYPE_ARRAY: + { + PX_ASSERT(def.numChildren() > 0); + const NvParameterized::Definition* arrayMemberDef = def.child(0); + + ok = getPvdType(*arrayMemberDef, pvdTypeName); + + // array of strings is not supported by pvd + if (arrayMemberDef->type() == NvParameterized::TYPE_STRING) + { + ok = false; + } + + break; + } + + default: + ok = false; + break; + }; + + return ok; +} + + +size_t PvdParameterizedHandler::getStructId(void* structAddress, const char* structName, bool deleteId) +{ + StructId structId(structAddress, structName); + + size_t pvdStructId = 0; + if (mStructIdMap.find(structId) != NULL) + { + pvdStructId = mStructIdMap[structId]; + } + else + { + PX_ASSERT(!deleteId); + + // addresses are 4 byte aligned, so this id is probably not used by another object + pvdStructId = mNextStructId++; + pvdStructId = (pvdStructId << 1) | 1; + + mStructIdMap[structId] = pvdStructId; + } + + if (deleteId) + { + mStructIdMap.erase(structId); + } + + return pvdStructId; +} + + +const void* PvdParameterizedHandler::getPvdId(const NvParameterized::Handle& handle, bool deleteId) +{ + void* retVal = 0; + NvParameterized::DataType type = handle.parameterDefinition()->type(); + + switch(type) + { + case NvParameterized::TYPE_REF: + { + // references use the referenced interface pointer as ID + NvParameterized::Interface* paramRef = NULL; + handle.getParamRef(paramRef); + retVal = (void*)paramRef; + break; + } + + case NvParameterized::TYPE_STRUCT: + { + // structs use custom ID's, because two structs can have the same location if + // a struct contains another struct as its first member + NvParameterized::NvParameters* param = (NvParameterized::NvParameters*)(handle.getInterface()); + if (param != NULL) + { + // get struct address + size_t offset = 0; + void* structAddress = 0; + param->getVarPtr(handle, structAddress, offset); + + // create an id from address and name + retVal = (void*)getStructId(structAddress, handle.parameterDefinition()->longName(), deleteId); + } + break; + } + + default: + break; + } + + return retVal; +} + + +bool PvdParameterizedHandler::setProperty(const void* pvdInstance, NvParameterized::Handle& propertyHandle, bool isArrayElement, PvdAction::Enum pvdAction) +{ + const char* propertyName = propertyHandle.parameterDefinition()->name(); + NvParameterized::DataType propertyType = propertyHandle.parameterDefinition()->type(); + + bool ok = true; + switch(propertyType) + { + case NvParameterized::TYPE_BOOL : + { + //bool val; + //SET_PROPERTY_VALUE; + break; + } + + case NvParameterized::TYPE_STRING : + { + if (isArrayElement) + { + // pvd doesn't support arrays of strings + ok = false; + } + else + { + const char* val; + ok = propertyHandle.getParamString(val) == NvParameterized::ERROR_NONE; + if (ok) + { + mPvdStream->setPropertyValue(pvdInstance, propertyName, val); + } + } + + break; + } + + case NvParameterized::TYPE_I8 : + { + int8_t val; + SET_PROPERTY_VALUE; + break; + } + + case NvParameterized::TYPE_I16 : + { + int16_t val; + SET_PROPERTY_VALUE; + break; + } + + case NvParameterized::TYPE_I32 : + { + int32_t val; + SET_PROPERTY_VALUE; + break; + } + + case NvParameterized::TYPE_I64 : + { + int64_t val; + SET_PROPERTY_VALUE; + break; + } + + case NvParameterized::TYPE_U8 : + { + uint8_t val; + SET_PROPERTY_VALUE; + break; + } + + case NvParameterized::TYPE_U16 : + { + uint16_t val; + SET_PROPERTY_VALUE; + break; + } + + case NvParameterized::TYPE_U32 : + { + uint32_t val; + SET_PROPERTY_VALUE; + break; + } + + case NvParameterized::TYPE_U64 : + { + uint64_t val; + SET_PROPERTY_VALUE; + break; + } + + case NvParameterized::TYPE_F32 : + { + float val; + SET_PROPERTY_VALUE; + break; + } + + case NvParameterized::TYPE_F64 : + { + double val; + SET_PROPERTY_VALUE; + break; + } + + case NvParameterized::TYPE_VEC2 : + { + PxVec2 val; + SET_PROPERTY_VALUE; + break; + } + + case NvParameterized::TYPE_VEC3 : + { + PxVec3 val; + SET_PROPERTY_VALUE; + break; + } + + case NvParameterized::TYPE_VEC4 : + { + PxVec4 val; + SET_PROPERTY_VALUE; + break; + } + + case NvParameterized::TYPE_QUAT : + { + PxQuat val; + SET_PROPERTY_VALUE; + break; + } + + case NvParameterized::TYPE_MAT33 : + { + PxMat33 val; + SET_PROPERTY_VALUE; + break; + } + + case NvParameterized::TYPE_BOUNDS3 : + { + PxBounds3 val; + SET_PROPERTY_VALUE; + break; + } + + case NvParameterized::TYPE_MAT44 : + { + PxMat44 val; + SET_PROPERTY_VALUE; + break; + } + + /* + case NvParameterized::TYPE_POINTER : + { + void* val; + SET_PROPERTY_VALUE; + break; + } + */ + + case NvParameterized::TYPE_TRANSFORM : + { + PxTransform val; + SET_PROPERTY_VALUE; + break; + } + + case NvParameterized::TYPE_STRUCT: + { + const void* pvdId = getPvdId(propertyHandle, pvdAction == PvdAction::DESTROY); + + if (pvdId != 0) + { + if (!mInstanceIds.contains(pvdId)) + { + // create pvd instance for struct + mInstanceIds.insert(pvdId); + NamespacedName structName(APEX_PVD_NAMESPACE, propertyHandle.parameterDefinition()->structName()); + mPvdStream->createInstance(structName, pvdId); + mPvdStream->setPropertyValue(pvdInstance, propertyName, DataRef<const uint8_t>((const uint8_t*)&pvdId, sizeof(NvParameterized::Interface*)), getPvdNamespacedNameForType<ObjectRef>()); + } + + // recursively update struct properties + updatePvd(pvdId, propertyHandle, pvdAction); + + if (pvdAction == PvdAction::DESTROY) + { + // destroy pvd instance of struct + mPvdStream->destroyInstance(pvdId); + mInstanceIds.erase(pvdId); + } + } + break; + } + + case NvParameterized::TYPE_REF: + { + const void* pvdId = getPvdId(propertyHandle, pvdAction == PvdAction::DESTROY); + + if (pvdId != 0) + { + // get a handle in the referenced parameterized object + NvParameterized::Handle refHandle = propertyHandle; + propertyHandle.getChildHandle(0, refHandle); + NvParameterized::Interface* paramRef = NULL; + ok = refHandle.getParamRef(paramRef) == NvParameterized::ERROR_NONE; + + if (ok) + { + if (!mInstanceIds.contains(pvdId)) + { + // create a pvd instance for the reference + mInstanceIds.insert(pvdId); + NamespacedName refClassName(APEX_PVD_NAMESPACE, paramRef->className()); + mPvdStream->createInstance(refClassName, pvdId); + mPvdStream->setPropertyValue(pvdInstance, propertyName, DataRef<const uint8_t>((const uint8_t*)&pvdId, sizeof(NvParameterized::Interface*)), getPvdNamespacedNameForType<ObjectRef>()); + } + + // recursivly update pvd instance of the referenced object + refHandle = NvParameterized::Handle(paramRef); + updatePvd(pvdId, refHandle, pvdAction); + + if (pvdAction == PvdAction::DESTROY) + { + // destroy pvd instance of reference + mPvdStream->destroyInstance(pvdId); + mInstanceIds.erase(pvdId); + } + } + } + break; + } + + case NvParameterized::TYPE_ARRAY: + { + const NvParameterized::Definition* def = propertyHandle.parameterDefinition(); + PX_ASSERT(def->numChildren() > 0); + + const NvParameterized::Definition* arrayMemberDef = def->child(0); + NvParameterized::DataType arrayMemberType = arrayMemberDef->type(); + + PX_ASSERT(def->arrayDimension() == 1); + int32_t arraySize = 0; + propertyHandle.getArraySize(arraySize); + + if (arraySize > 0) + { + if (arrayMemberType == NvParameterized::TYPE_STRUCT || arrayMemberType == NvParameterized::TYPE_REF) + { + for (int32_t i = 0; i < arraySize; ++i) + { + NvParameterized::Handle childHandle(propertyHandle); + propertyHandle.getChildHandle(i, childHandle); + + const void* pvdId = getPvdId(childHandle, pvdAction == PvdAction::DESTROY); + + // get the class name of the member + NamespacedName childClassName(APEX_PVD_NAMESPACE, ""); + if (arrayMemberType == NvParameterized::TYPE_STRUCT) + { + childClassName.mName = childHandle.parameterDefinition()->structName(); + } + else if (arrayMemberType == NvParameterized::TYPE_REF) + { + // continue on a handle in the referenced object + NvParameterized::Interface* paramRef = NULL; + ok = childHandle.getParamRef(paramRef) == NvParameterized::ERROR_NONE; + PX_ASSERT(ok); + if (!ok) + { + break; + } + childHandle = NvParameterized::Handle(paramRef); + childClassName.mName = paramRef->className(); + } + + if (!mInstanceIds.contains(pvdId)) + { + // create pvd instance for struct or ref and add it to the array + mInstanceIds.insert(pvdId); + mPvdStream->createInstance(childClassName, pvdId); + mPvdStream->pushBackObjectRef(pvdInstance, propertyName, pvdId); + } + + // recursively update the array member + updatePvd(pvdId, childHandle, pvdAction); + + if (pvdAction == PvdAction::DESTROY) + { + // destroy pvd instance for struct or ref + mPvdStream->removeObjectRef(pvdInstance, propertyName, pvdId); // might not be necessary + mPvdStream->destroyInstance(pvdId); + mInstanceIds.erase(pvdId); + } + } + } + else if (pvdAction != PvdAction::DESTROY) + { + // for arrays of simple types just update the property values + NamespacedName pvdTypeName; + if (getPvdType(*def, pvdTypeName)) + { + mPvdStream->beginSetPropertyValue(pvdInstance, propertyName, pvdTypeName); + for (int32_t i = 0; i < arraySize; ++i) + { + NvParameterized::Handle childHandle(propertyHandle); + propertyHandle.getChildHandle(i, childHandle); + + setProperty(pvdInstance, childHandle, true, pvdAction); + } + mPvdStream->endSetPropertyValue(); + } + } + } + break; + } + + default: + ok = false; + break; + }; + + return ok; +} + + + +void PvdParameterizedHandler::initPvdClasses(const NvParameterized::Definition& paramDefinition, const char* className) +{ + NamespacedName pvdClassName(APEX_PVD_NAMESPACE, className); + + // iterate all properties + const int numChildren = paramDefinition.numChildren(); + for (int i = 0; i < numChildren; i++) + { + const NvParameterized::Definition* childDef = paramDefinition.child(i); + + const char* propertyName = childDef->name(); + NvParameterized::DataType propertyDataType = childDef->type(); + + + // First, recursively create pvd classes for encountered structs + // + // if it's an array, continue with its member type, and remember that it's an array + bool isArray = false; + if (propertyDataType == NvParameterized::TYPE_ARRAY) + { + PX_ASSERT(childDef->numChildren() > 0); + + const NvParameterized::Definition* arrayMemberDef = childDef->child(0); + if (arrayMemberDef->type() == NvParameterized::TYPE_STRUCT) + { + NamespacedName memberClassName(APEX_PVD_NAMESPACE, arrayMemberDef->structName()); + if (createClass(memberClassName)) + { + // only recurse if this we encounter the struct the first time and a class has been created + initPvdClasses(*arrayMemberDef, memberClassName.mName); + } + } + + isArray = true; + } + else if (propertyDataType == NvParameterized::TYPE_STRUCT) + { + // create classes for structs + // (doesn't work for refs, looks like Definitions don't contain the Definitions of references) + + NamespacedName childClassName(APEX_PVD_NAMESPACE, childDef->structName()); + if (createClass(childClassName)) + { + // only recurse if this we encounter the struct the first time and a class has been created + initPvdClasses(*childDef, childClassName.mName); + } + } + + + // Then, create the property + NamespacedName typeName; + if (!childDef->hint("NOPVD") && getPvdType(*childDef, typeName)) + { + mPvdStream->createProperty(pvdClassName, propertyName, "", typeName, isArray ? PropertyType::Array : PropertyType::Scalar); + } + } +} + + +void PvdParameterizedHandler::updatePvd(const void* pvdInstance, NvParameterized::Handle& paramsHandle, PvdAction::Enum pvdAction) +{ + // iterate all properties + const int numChildren = paramsHandle.parameterDefinition()->numChildren(); + for (int i = 0; i < numChildren; i++) + { + paramsHandle.set(i); + + if (!paramsHandle.parameterDefinition()->hint("NOPVD")) + { + setProperty(pvdInstance, paramsHandle, false, pvdAction); + } + paramsHandle.popIndex(); + } +} + +} +} + +#endif
\ No newline at end of file diff --git a/APEX_1.4/common/src/ReadCheck.cpp b/APEX_1.4/common/src/ReadCheck.cpp new file mode 100644 index 00000000..fe6994d9 --- /dev/null +++ b/APEX_1.4/common/src/ReadCheck.cpp @@ -0,0 +1,63 @@ +/* + * 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. + */ + +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "ReadCheck.h" +#include "ApexRWLockable.h" +#include "PsThread.h" +#include "ApexSDKIntl.h" + +namespace nvidia +{ +namespace apex +{ + +ReadCheck::ReadCheck(const ApexRWLockable* scene, const char* functionName) + : mLockable(scene), mName(functionName), mErrorCount(0) +{ + if (GetApexSDK()->isConcurrencyCheckEnabled() && mLockable && !mLockable->isEnabled()) + { + if (!mLockable->startRead() && !mLockable->isEnabled()) + { + APEX_INTERNAL_ERROR("An API read call (%s) was made from thread %d but acquireReadLock() was not called first, note that " + "when ApexSDKDesc::enableConcurrencyCheck is enabled all API reads and writes must be " + "wrapped in the appropriate locks.", mName, uint32_t(nvidia::Thread::getId())); + } + + // Record the NpScene read/write error counter which is + // incremented any time a NpScene::startWrite/startRead fails + // (see destructor for additional error checking based on this count) + mErrorCount = mLockable->getReadWriteErrorCount(); + } +} + + +ReadCheck::~ReadCheck() +{ + if (GetApexSDK()->isConcurrencyCheckEnabled() && mLockable) + { + // By checking if the NpScene::mConcurrentErrorCount has been incremented + // we can detect if an erroneous read/write was performed during + // this objects lifetime. In this case we also print this function's + // details so that the user can see which two API calls overlapped + if (mLockable->getReadWriteErrorCount() != mErrorCount && !mLockable->isEnabled()) + { + APEX_INTERNAL_ERROR("Leaving %s on thread %d, an API overlapping write on another thread was detected.", mName, uint32_t(nvidia::Thread::getId())); + } + + mLockable->stopRead(); + } +} + +} +}
\ No newline at end of file diff --git a/APEX_1.4/common/src/Spline.cpp b/APEX_1.4/common/src/Spline.cpp new file mode 100644 index 00000000..098883ca --- /dev/null +++ b/APEX_1.4/common/src/Spline.cpp @@ -0,0 +1,176 @@ +/* + * 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. + */ + + +#include "Spline.h" +#include <assert.h> + +using namespace nvidia; + +static float f(float x) +{ + return( x*x*x - x); +} + + +// build spline. +// copied from Sedgwick's Algorithm's in C, page 548. +void Spline::ComputeSpline(void) +{ + uint32_t i; + uint32_t n = mNodes.size(); + + float *d = new float[n]; + float *w = new float[n]; + + for (i=1; i<(n-1); i++) + { + d[i] = 2.0f * (mNodes[i+1].x - mNodes[i-1].x); + } + + for (i=0; i<(n-1); i++) + { + mNodes[i].u = mNodes[i+1].x-mNodes[i].x;} + + for (i=1; i<(n-1); i++) + { + w[i] = 6.0f*((mNodes[i+1].y - mNodes[i].y) / mNodes[i].u - (mNodes[i].y - mNodes[i-1].y) / mNodes[i-1].u); + } + + mNodes[0].p = 0.0f; + mNodes[n-1].p = 0.0f; + + for (i=1; i<(n-2); i++) + { + w[i+1] = w[i+1] - w[i]*mNodes[i].u /d[i]; + d[i+1] = d[i+1] - mNodes[i].u * mNodes[i].u / d[i]; + } + + for (i=n-2; i>0; i--) + { + mNodes[i].p = (w[i] - mNodes[i].u * mNodes[i+1].p ) / d[i]; + } + + delete[]d; + delete[]w; +} + +float Spline::Evaluate(float v,uint32_t &index,float &fraction) const +{ + float t; + uint32_t i=0; + uint32_t n = mNodes.size(); + + while ( i < (n-1) && v > mNodes[i+1].x ) + { + i++; + } + if ( i == (n-1) ) + { + i = n-2; + } + + index = (uint32_t)i; + + t = (v - mNodes[i].x ) / mNodes[i].u; + + fraction = t; + + return( t*mNodes[i+1].y + (1-t)*mNodes[i].y + mNodes[i].u * mNodes[i].u * (f(t)*mNodes[i+1].p + f(1-t)*mNodes[i].p )/6.0f ); +} + + +void Spline::AddNode(float x,float y) +{ + SplineNode s; + s.x = x; + s.y = y; + mNodes.pushBack(s); +} + +float SplineCurve::AddControlPoint(const PxVec3& p) // add control point. +{ + int32_t size = mXaxis.GetSize(); + + if ( size ) + { + size--; + PxVec3 last; + last.x = mXaxis.GetEntry(size); + last.y = mYaxis.GetEntry(size); + last.z = mZaxis.GetEntry(size); + PxVec3 diff = last-p; + float dist = diff.magnitude(); + mTime+=dist; + } + else + { + mTime = 0; + } + + mXaxis.AddNode(mTime,p.x); + mYaxis.AddNode(mTime,p.y); + mZaxis.AddNode(mTime,p.z); + return mTime; +} + +void SplineCurve::AddControlPoint(const PxVec3& p,float t) // add control point. +{ + int32_t size = mXaxis.GetSize(); + if ( size ) + { + size--; + PxVec3 last; + last.x = mXaxis.GetEntry(size); + last.y = mYaxis.GetEntry(size); + last.z = mZaxis.GetEntry(size); + } + + mTime = t; + + mXaxis.AddNode(mTime,p.x); + mYaxis.AddNode(mTime,p.y); + mZaxis.AddNode(mTime,p.z); +} + +void SplineCurve::ComputeSpline(void) // compute spline. +{ + mXaxis.ComputeSpline(); + mYaxis.ComputeSpline(); + mZaxis.ComputeSpline(); +} + +PxVec3 SplineCurve::Evaluate(float dist,uint32_t &index,float &fraction) +{ + PxVec3 p; + + uint32_t index1,index2,index3; + float t1,t2,t3; + p.x = mXaxis.Evaluate(dist,index1,t1); + p.y = mYaxis.Evaluate(dist,index2,t2); + p.z = mZaxis.Evaluate(dist,index3,t3); + assert( index1 == index2 && index1 == index3 && index2 == index3 ); + index = index1; + fraction = t1; + + return p; +} + +PxVec3 SplineCurve::GetEntry(int32_t i) +{ + PxVec3 p; + if ( i >= 0 && i < GetSize() ) + { + p.x = mXaxis.GetEntry(i); + p.y = mYaxis.GetEntry(i); + p.z = mZaxis.GetEntry(i); + } + return p; +} diff --git a/APEX_1.4/common/src/WriteCheck.cpp b/APEX_1.4/common/src/WriteCheck.cpp new file mode 100644 index 00000000..af177eb1 --- /dev/null +++ b/APEX_1.4/common/src/WriteCheck.cpp @@ -0,0 +1,62 @@ +/* + * 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. + */ + +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "WriteCheck.h" +#include "ApexRWLockable.h" +#include "PsThread.h" +#include "ApexSDKIntl.h" + +namespace nvidia +{ +namespace apex +{ + +WriteCheck::WriteCheck(ApexRWLockable* scene, const char* functionName, bool allowReentry) +: mLockable(scene), mName(functionName), mAllowReentry(allowReentry), mErrorCount(0) +{ + if (GetApexSDK()->isConcurrencyCheckEnabled() && mLockable) + { + if (!mLockable->startWrite(mAllowReentry) && !mLockable->isEnabled()) + { + APEX_INTERNAL_ERROR("An API write call (%s) was made from thread %d but acquireWriteLock() was not called first, note that " + "when ApexSDKDesc::enableConcurrencyCheck is enabled all API reads and writes must be " + "wrapped in the appropriate locks.", mName, uint32_t(nvidia::Thread::getId())); + } + + // Record the Scene read/write error counter which is + // incremented any time a Scene::apexStartWrite/apexStartRead fails + // (see destructor for additional error checking based on this count) + mErrorCount = mLockable->getReadWriteErrorCount(); + } +} + + +WriteCheck::~WriteCheck() +{ + if (GetApexSDK()->isConcurrencyCheckEnabled() && mLockable) + { + // By checking if the NpScene::mConcurrentErrorCount has been incremented + // we can detect if an erroneous read/write was performed during + // this objects lifetime. In this case we also print this function's + // details so that the user can see which two API calls overlapped + if (mLockable->getReadWriteErrorCount() != mErrorCount && !mLockable->isEnabled()) + { + APEX_INTERNAL_ERROR("Leaving %s on thread %d, an overlapping API read or write by another thread was detected.", mName, uint32_t(nvidia::Thread::getId())); + } + + mLockable->stopWrite(mAllowReentry); + } +} + +} +}
\ No newline at end of file diff --git a/APEX_1.4/common/src/autogen/ConvexHullParameters.cpp b/APEX_1.4/common/src/autogen/ConvexHullParameters.cpp new file mode 100644 index 00000000..88ab1636 --- /dev/null +++ b/APEX_1.4/common/src/autogen/ConvexHullParameters.cpp @@ -0,0 +1,768 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and 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. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2015 NVIDIA Corporation. All rights reserved. + +// This file was generated by NvParameterized/scripts/GenParameterized.pl + + +#include "ConvexHullParameters.h" +#include <string.h> +#include <stdlib.h> + +using namespace NvParameterized; + +namespace nvidia +{ +namespace apex +{ + +using namespace ConvexHullParametersNS; + +const char* const ConvexHullParametersFactory::vptr = + NvParameterized::getVptr<ConvexHullParameters, ConvexHullParameters::ClassAlignment>(); + +const uint32_t NumParamDefs = 17; +static NvParameterized::DefinitionImpl* ParamDefTable; // now allocated in buildTree [NumParamDefs]; + + +static const size_t ParamLookupChildrenTable[] = +{ + 1, 3, 7, 9, 11, 13, 14, 15, 16, 2, 4, 5, 6, 8, 10, 12, +}; + +#define TENUM(type) nvidia::##type +#define CHILDREN(index) &ParamLookupChildrenTable[index] +static const NvParameterized::ParamLookupNode ParamLookupTable[NumParamDefs] = +{ + { TYPE_STRUCT, false, 0, CHILDREN(0), 9 }, + { TYPE_ARRAY, true, (size_t)(&((ParametersStruct*)0)->vertices), CHILDREN(9), 1 }, // vertices + { TYPE_VEC3, false, 1 * sizeof(physx::PxVec3), NULL, 0 }, // vertices[] + { TYPE_ARRAY, true, (size_t)(&((ParametersStruct*)0)->uniquePlanes), CHILDREN(10), 1 }, // uniquePlanes + { TYPE_STRUCT, false, 1 * sizeof(Plane_Type), CHILDREN(11), 2 }, // uniquePlanes[] + { TYPE_VEC3, false, (size_t)(&((Plane_Type*)0)->normal), NULL, 0 }, // uniquePlanes[].normal + { TYPE_F32, false, (size_t)(&((Plane_Type*)0)->d), NULL, 0 }, // uniquePlanes[].d + { TYPE_ARRAY, true, (size_t)(&((ParametersStruct*)0)->widths), CHILDREN(13), 1 }, // widths + { TYPE_F32, false, 1 * sizeof(float), NULL, 0 }, // widths[] + { TYPE_ARRAY, true, (size_t)(&((ParametersStruct*)0)->edges), CHILDREN(14), 1 }, // edges + { TYPE_U32, false, 1 * sizeof(uint32_t), NULL, 0 }, // edges[] + { TYPE_ARRAY, true, (size_t)(&((ParametersStruct*)0)->adjacentFaces), CHILDREN(15), 1 }, // adjacentFaces + { TYPE_U32, false, 1 * sizeof(uint32_t), NULL, 0 }, // adjacentFaces[] + { TYPE_BOUNDS3, false, (size_t)(&((ParametersStruct*)0)->bounds), NULL, 0 }, // bounds + { TYPE_F32, false, (size_t)(&((ParametersStruct*)0)->volume), NULL, 0 }, // volume + { TYPE_U32, false, (size_t)(&((ParametersStruct*)0)->uniqueEdgeDirectionCount), NULL, 0 }, // uniqueEdgeDirectionCount + { TYPE_U32, false, (size_t)(&((ParametersStruct*)0)->planeCount), NULL, 0 }, // planeCount +}; + + +bool ConvexHullParameters::mBuiltFlag = false; +NvParameterized::MutexType ConvexHullParameters::mBuiltFlagMutex; + +ConvexHullParameters::ConvexHullParameters(NvParameterized::Traits* traits, void* buf, int32_t* refCount) : + NvParameters(traits, buf, refCount) +{ + //mParameterizedTraits->registerFactory(className(), &ConvexHullParametersFactoryInst); + + if (!buf) //Do not init data if it is inplace-deserialized + { + initDynamicArrays(); + initStrings(); + initReferences(); + initDefaults(); + } +} + +ConvexHullParameters::~ConvexHullParameters() +{ + freeStrings(); + freeReferences(); + freeDynamicArrays(); +} + +void ConvexHullParameters::destroy() +{ + // We cache these fields here to avoid overwrite in destructor + bool doDeallocateSelf = mDoDeallocateSelf; + NvParameterized::Traits* traits = mParameterizedTraits; + int32_t* refCount = mRefCount; + void* buf = mBuffer; + + this->~ConvexHullParameters(); + + NvParameters::destroy(this, traits, doDeallocateSelf, refCount, buf); +} + +const NvParameterized::DefinitionImpl* ConvexHullParameters::getParameterDefinitionTree(void) +{ + if (!mBuiltFlag) // Double-checked lock + { + NvParameterized::MutexType::ScopedLock lock(mBuiltFlagMutex); + if (!mBuiltFlag) + { + buildTree(); + } + } + + return(&ParamDefTable[0]); +} + +const NvParameterized::DefinitionImpl* ConvexHullParameters::getParameterDefinitionTree(void) const +{ + ConvexHullParameters* tmpParam = const_cast<ConvexHullParameters*>(this); + + if (!mBuiltFlag) // Double-checked lock + { + NvParameterized::MutexType::ScopedLock lock(mBuiltFlagMutex); + if (!mBuiltFlag) + { + tmpParam->buildTree(); + } + } + + return(&ParamDefTable[0]); +} + +NvParameterized::ErrorType ConvexHullParameters::getParameterHandle(const char* long_name, Handle& handle) const +{ + ErrorType Ret = NvParameters::getParameterHandle(long_name, handle); + if (Ret != ERROR_NONE) + { + return(Ret); + } + + size_t offset; + void* ptr; + + getVarPtr(handle, ptr, offset); + + if (ptr == NULL) + { + return(ERROR_INDEX_OUT_OF_RANGE); + } + + return(ERROR_NONE); +} + +NvParameterized::ErrorType ConvexHullParameters::getParameterHandle(const char* long_name, Handle& handle) +{ + ErrorType Ret = NvParameters::getParameterHandle(long_name, handle); + if (Ret != ERROR_NONE) + { + return(Ret); + } + + size_t offset; + void* ptr; + + getVarPtr(handle, ptr, offset); + + if (ptr == NULL) + { + return(ERROR_INDEX_OUT_OF_RANGE); + } + + return(ERROR_NONE); +} + +void ConvexHullParameters::getVarPtr(const Handle& handle, void*& ptr, size_t& offset) const +{ + ptr = getVarPtrHelper(&ParamLookupTable[0], const_cast<ConvexHullParameters::ParametersStruct*>(¶meters()), handle, offset); +} + + +/* Dynamic Handle Indices */ + +void ConvexHullParameters::freeParameterDefinitionTable(NvParameterized::Traits* traits) +{ + if (!traits) + { + return; + } + + if (!mBuiltFlag) // Double-checked lock + { + return; + } + + NvParameterized::MutexType::ScopedLock lock(mBuiltFlagMutex); + + if (!mBuiltFlag) + { + return; + } + + for (uint32_t i = 0; i < NumParamDefs; ++i) + { + ParamDefTable[i].~DefinitionImpl(); + } + + traits->free(ParamDefTable); + + mBuiltFlag = false; +} + +#define PDEF_PTR(index) (&ParamDefTable[index]) + +void ConvexHullParameters::buildTree(void) +{ + + uint32_t allocSize = sizeof(NvParameterized::DefinitionImpl) * NumParamDefs; + ParamDefTable = (NvParameterized::DefinitionImpl*)(mParameterizedTraits->alloc(allocSize)); + memset(ParamDefTable, 0, allocSize); + + for (uint32_t i = 0; i < NumParamDefs; ++i) + { + NV_PARAM_PLACEMENT_NEW(ParamDefTable + i, NvParameterized::DefinitionImpl)(*mParameterizedTraits); + } + + // Initialize DefinitionImpl node: nodeIndex=0, longName="" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[0]; + ParamDef->init("", TYPE_STRUCT, "STRUCT", true); + + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=1, longName="vertices" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[1]; + ParamDef->init("vertices", TYPE_ARRAY, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[2]; + static Hint* HintPtrTable[2] = { &HintTable[0], &HintTable[1], }; + HintTable[0].init("longDescription", "The vertices of a convex polytope.", true); + HintTable[1].init("shortDescription", "Convex hull vertices", true); + ParamDefTable[1].setHints((const NvParameterized::Hint**)HintPtrTable, 2); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + ParamDef->setArraySize(-1); + } + + // Initialize DefinitionImpl node: nodeIndex=2, longName="vertices[]" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[2]; + ParamDef->init("vertices", TYPE_VEC3, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[2]; + static Hint* HintPtrTable[2] = { &HintTable[0], &HintTable[1], }; + HintTable[0].init("longDescription", "The vertices of a convex polytope.", true); + HintTable[1].init("shortDescription", "Convex hull vertices", true); + ParamDefTable[2].setHints((const NvParameterized::Hint**)HintPtrTable, 2); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=3, longName="uniquePlanes" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[3]; + ParamDef->init("uniquePlanes", TYPE_ARRAY, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[2]; + static Hint* HintPtrTable[2] = { &HintTable[0], &HintTable[1], }; + HintTable[0].init("longDescription", "Unique planes (neglecting parallel opposites). That is, if two faces exist\n on the convex hull which have opposite normals, the plane for only one of those faces is recorded.\n The other face is implicitly recorded by a corresponding width (in the widths array). Edges and\n vertices are also recorded, explicitly defining all faces.", true); + HintTable[1].init("shortDescription", "Unique planes (neglecting parallel opposites)", true); + ParamDefTable[3].setHints((const NvParameterized::Hint**)HintPtrTable, 2); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + ParamDef->setArraySize(-1); + } + + // Initialize DefinitionImpl node: nodeIndex=4, longName="uniquePlanes[]" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[4]; + ParamDef->init("uniquePlanes", TYPE_STRUCT, "Plane", true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[2]; + static Hint* HintPtrTable[2] = { &HintTable[0], &HintTable[1], }; + HintTable[0].init("longDescription", "Unique planes (neglecting parallel opposites). That is, if two faces exist\n on the convex hull which have opposite normals, the plane for only one of those faces is recorded.\n The other face is implicitly recorded by a corresponding width (in the widths array). Edges and\n vertices are also recorded, explicitly defining all faces.", true); + HintTable[1].init("shortDescription", "Unique planes (neglecting parallel opposites)", true); + ParamDefTable[4].setHints((const NvParameterized::Hint**)HintPtrTable, 2); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=5, longName="uniquePlanes[].normal" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[5]; + ParamDef->init("normal", TYPE_VEC3, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[2]; + static Hint* HintPtrTable[2] = { &HintTable[0], &HintTable[1], }; + HintTable[0].init("longDescription", "A plane normal. This plane is used to define convex volumes.", true); + HintTable[1].init("shortDescription", "A plane normal", true); + ParamDefTable[5].setHints((const NvParameterized::Hint**)HintPtrTable, 2); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=6, longName="uniquePlanes[].d" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[6]; + ParamDef->init("d", TYPE_F32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[2]; + static Hint* HintPtrTable[2] = { &HintTable[0], &HintTable[1], }; + HintTable[0].init("longDescription", "A plane displacement, defined by the negative of the plane normal\n dotted with a point in the plane. This plane is used to define convex volumes.", true); + HintTable[1].init("shortDescription", "A plane displacement", true); + ParamDefTable[6].setHints((const NvParameterized::Hint**)HintPtrTable, 2); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=7, longName="widths" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[7]; + ParamDef->init("widths", TYPE_ARRAY, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[2]; + static Hint* HintPtrTable[2] = { &HintTable[0], &HintTable[1], }; + HintTable[0].init("longDescription", "For each unique plane (see uniquePlanes), this is the width of the convex polytope.\n That is, if an opposing face exists, it is the distance between the faces. If no opposing face exists,\n it is the maximum distance below the unique plane over all vertices.", true); + HintTable[1].init("shortDescription", "For each unique plane (see uniquePlanes), this is the width of the convex polytope", true); + ParamDefTable[7].setHints((const NvParameterized::Hint**)HintPtrTable, 2); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + ParamDef->setArraySize(-1); + } + + // Initialize DefinitionImpl node: nodeIndex=8, longName="widths[]" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[8]; + ParamDef->init("widths", TYPE_F32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[2]; + static Hint* HintPtrTable[2] = { &HintTable[0], &HintTable[1], }; + HintTable[0].init("longDescription", "For each unique plane (see uniquePlanes), this is the width of the convex polytope.\n That is, if an opposing face exists, it is the distance between the faces. If no opposing face exists,\n it is the maximum distance below the unique plane over all vertices.", true); + HintTable[1].init("shortDescription", "For each unique plane (see uniquePlanes), this is the width of the convex polytope", true); + ParamDefTable[8].setHints((const NvParameterized::Hint**)HintPtrTable, 2); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=9, longName="edges" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[9]; + ParamDef->init("edges", TYPE_ARRAY, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[2]; + static Hint* HintPtrTable[2] = { &HintTable[0], &HintTable[1], }; + HintTable[0].init("longDescription", "All edges of the convex polytope, stored in a compressed index format.\n Each 32-bit integer stores the indices of two endpoints, in the high and low words. The indices\n refer to the vertices array.\n The edges are stored such that all unique edge directions are represented by the first\n uniqueEdgeDirectionCount entries.", true); + HintTable[1].init("shortDescription", "All edges of the convex polytope, stored in a compressed index format", true); + ParamDefTable[9].setHints((const NvParameterized::Hint**)HintPtrTable, 2); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + ParamDef->setArraySize(-1); + } + + // Initialize DefinitionImpl node: nodeIndex=10, longName="edges[]" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[10]; + ParamDef->init("edges", TYPE_U32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[2]; + static Hint* HintPtrTable[2] = { &HintTable[0], &HintTable[1], }; + HintTable[0].init("longDescription", "All edges of the convex polytope, stored in a compressed index format.\n Each 32-bit integer stores the indices of two endpoints, in the high and low words. The indices\n refer to the vertices array.\n The edges are stored such that all unique edge directions are represented by the first\n uniqueEdgeDirectionCount entries.", true); + HintTable[1].init("shortDescription", "All edges of the convex polytope, stored in a compressed index format", true); + ParamDefTable[10].setHints((const NvParameterized::Hint**)HintPtrTable, 2); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=11, longName="adjacentFaces" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[11]; + ParamDef->init("adjacentFaces", TYPE_ARRAY, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[2]; + static Hint* HintPtrTable[2] = { &HintTable[0], &HintTable[1], }; + HintTable[0].init("longDescription", "Face (plane) indices which are adjacent to each edge in the edges array.\n Each 32-bit integer stores the indices of two faces, in the high and low words. The indices\n refer to the face planes, and will be in the range [0, planeCount). To interpret the indices\n correctly, see the description of planeCount.\n If a \"dangling edge\" is generated, the face index stored in the high word will be 0xFFFF. (Invalid value.)", true); + HintTable[1].init("shortDescription", "Face (plane) indices which are adjacent to each edge in the edges array.", true); + ParamDefTable[11].setHints((const NvParameterized::Hint**)HintPtrTable, 2); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + ParamDef->setArraySize(-1); + } + + // Initialize DefinitionImpl node: nodeIndex=12, longName="adjacentFaces[]" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[12]; + ParamDef->init("adjacentFaces", TYPE_U32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[2]; + static Hint* HintPtrTable[2] = { &HintTable[0], &HintTable[1], }; + HintTable[0].init("longDescription", "Face (plane) indices which are adjacent to each edge in the edges array.\n Each 32-bit integer stores the indices of two faces, in the high and low words. The indices\n refer to the face planes, and will be in the range [0, planeCount). To interpret the indices\n correctly, see the description of planeCount.\n If a \"dangling edge\" is generated, the face index stored in the high word will be 0xFFFF. (Invalid value.)", true); + HintTable[1].init("shortDescription", "Face (plane) indices which are adjacent to each edge in the edges array.", true); + ParamDefTable[12].setHints((const NvParameterized::Hint**)HintPtrTable, 2); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=13, longName="bounds" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[13]; + ParamDef->init("bounds", TYPE_BOUNDS3, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[2]; + static Hint* HintPtrTable[2] = { &HintTable[0], &HintTable[1], }; + HintTable[0].init("longDescription", "The AABB of the convex hull.", true); + HintTable[1].init("shortDescription", "The AABB of the convex hull", true); + ParamDefTable[13].setHints((const NvParameterized::Hint**)HintPtrTable, 2); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=14, longName="volume" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[14]; + ParamDef->init("volume", TYPE_F32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[2]; + static Hint* HintPtrTable[2] = { &HintTable[0], &HintTable[1], }; + HintTable[0].init("longDescription", "The volume of the convex hull.", true); + HintTable[1].init("shortDescription", "The volume of the convex hull", true); + ParamDefTable[14].setHints((const NvParameterized::Hint**)HintPtrTable, 2); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=15, longName="uniqueEdgeDirectionCount" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[15]; + ParamDef->init("uniqueEdgeDirectionCount", TYPE_U32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[2]; + static Hint* HintPtrTable[2] = { &HintTable[0], &HintTable[1], }; + HintTable[0].init("longDescription", "The number of unique edge directions. The first uniqueEdgeDirectionCount\n elements of the edges array represent these directions.", true); + HintTable[1].init("shortDescription", "The number of unique edge directions", true); + ParamDefTable[15].setHints((const NvParameterized::Hint**)HintPtrTable, 2); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=16, longName="planeCount" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[16]; + ParamDef->init("planeCount", TYPE_U32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[2]; + static Hint* HintPtrTable[2] = { &HintTable[0], &HintTable[1], }; + HintTable[0].init("longDescription", "The total number of faces. This includes parallel opposite faces,\n so may be larger than the array size of uniquePlanes. For plane indices i less than uniquePlanes.size(),\n simply use uniquePlanes[i] to find the corresponding plane. For plane indicies i in the range\n [uniquePlanes.size(), planeCount), the uniquePlanes array is arranged such that you obtain the correct plane\n by starting with the plane p = uniquePlanes[index-uniquePlanes.size()]. Then, add widths[index-uniquePlanes.size()]\n to the plane displacement p.d, and finally negate both the plane normal p.n and the displacement p.d.", true); + HintTable[1].init("shortDescription", "The total number of faces", true); + ParamDefTable[16].setHints((const NvParameterized::Hint**)HintPtrTable, 2); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // SetChildren for: nodeIndex=0, longName="" + { + static Definition* Children[9]; + Children[0] = PDEF_PTR(1); + Children[1] = PDEF_PTR(3); + Children[2] = PDEF_PTR(7); + Children[3] = PDEF_PTR(9); + Children[4] = PDEF_PTR(11); + Children[5] = PDEF_PTR(13); + Children[6] = PDEF_PTR(14); + Children[7] = PDEF_PTR(15); + Children[8] = PDEF_PTR(16); + + ParamDefTable[0].setChildren(Children, 9); + } + + // SetChildren for: nodeIndex=1, longName="vertices" + { + static Definition* Children[1]; + Children[0] = PDEF_PTR(2); + + ParamDefTable[1].setChildren(Children, 1); + } + + // SetChildren for: nodeIndex=3, longName="uniquePlanes" + { + static Definition* Children[1]; + Children[0] = PDEF_PTR(4); + + ParamDefTable[3].setChildren(Children, 1); + } + + // SetChildren for: nodeIndex=4, longName="uniquePlanes[]" + { + static Definition* Children[2]; + Children[0] = PDEF_PTR(5); + Children[1] = PDEF_PTR(6); + + ParamDefTable[4].setChildren(Children, 2); + } + + // SetChildren for: nodeIndex=7, longName="widths" + { + static Definition* Children[1]; + Children[0] = PDEF_PTR(8); + + ParamDefTable[7].setChildren(Children, 1); + } + + // SetChildren for: nodeIndex=9, longName="edges" + { + static Definition* Children[1]; + Children[0] = PDEF_PTR(10); + + ParamDefTable[9].setChildren(Children, 1); + } + + // SetChildren for: nodeIndex=11, longName="adjacentFaces" + { + static Definition* Children[1]; + Children[0] = PDEF_PTR(12); + + ParamDefTable[11].setChildren(Children, 1); + } + + mBuiltFlag = true; + +} +void ConvexHullParameters::initStrings(void) +{ +} + +void ConvexHullParameters::initDynamicArrays(void) +{ + vertices.buf = NULL; + vertices.isAllocated = true; + vertices.elementSize = sizeof(physx::PxVec3); + vertices.arraySizes[0] = 0; + uniquePlanes.buf = NULL; + uniquePlanes.isAllocated = true; + uniquePlanes.elementSize = sizeof(Plane_Type); + uniquePlanes.arraySizes[0] = 0; + widths.buf = NULL; + widths.isAllocated = true; + widths.elementSize = sizeof(float); + widths.arraySizes[0] = 0; + edges.buf = NULL; + edges.isAllocated = true; + edges.elementSize = sizeof(uint32_t); + edges.arraySizes[0] = 0; + adjacentFaces.buf = NULL; + adjacentFaces.isAllocated = true; + adjacentFaces.elementSize = sizeof(uint32_t); + adjacentFaces.arraySizes[0] = 0; +} + +void ConvexHullParameters::initDefaults(void) +{ + + freeStrings(); + freeReferences(); + freeDynamicArrays(); + volume = float(0); + uniqueEdgeDirectionCount = uint32_t(0); + planeCount = uint32_t(0); + + initDynamicArrays(); + initStrings(); + initReferences(); +} + +void ConvexHullParameters::initReferences(void) +{ +} + +void ConvexHullParameters::freeDynamicArrays(void) +{ + if (vertices.isAllocated && vertices.buf) + { + mParameterizedTraits->free(vertices.buf); + } + if (uniquePlanes.isAllocated && uniquePlanes.buf) + { + mParameterizedTraits->free(uniquePlanes.buf); + } + if (widths.isAllocated && widths.buf) + { + mParameterizedTraits->free(widths.buf); + } + if (edges.isAllocated && edges.buf) + { + mParameterizedTraits->free(edges.buf); + } + if (adjacentFaces.isAllocated && adjacentFaces.buf) + { + mParameterizedTraits->free(adjacentFaces.buf); + } +} + +void ConvexHullParameters::freeStrings(void) +{ +} + +void ConvexHullParameters::freeReferences(void) +{ +} + +} // namespace apex +} // namespace nvidia diff --git a/APEX_1.4/common/src/autogen/DebugColorParams.cpp b/APEX_1.4/common/src/autogen/DebugColorParams.cpp new file mode 100644 index 00000000..72551910 --- /dev/null +++ b/APEX_1.4/common/src/autogen/DebugColorParams.cpp @@ -0,0 +1,1184 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and 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. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2015 NVIDIA Corporation. All rights reserved. + +// This file was generated by NvParameterized/scripts/GenParameterized.pl + + +#include "DebugColorParams.h" +#include <string.h> +#include <stdlib.h> + +using namespace NvParameterized; + +namespace nvidia +{ +namespace apex +{ + +using namespace DebugColorParamsNS; + +const char* const DebugColorParamsFactory::vptr = + NvParameterized::getVptr<DebugColorParams, DebugColorParams::ClassAlignment>(); + +const uint32_t NumParamDefs = 37; +static NvParameterized::DefinitionImpl* ParamDefTable; // now allocated in buildTree [NumParamDefs]; + + +static const size_t ParamLookupChildrenTable[] = +{ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, +}; + +#define TENUM(type) nvidia::##type +#define CHILDREN(index) &ParamLookupChildrenTable[index] +static const NvParameterized::ParamLookupNode ParamLookupTable[NumParamDefs] = +{ + { TYPE_STRUCT, false, 0, CHILDREN(0), 36 }, + { TYPE_U32, false, (size_t)(&((ParametersStruct*)0)->Default), NULL, 0 }, // Default + { TYPE_U32, false, (size_t)(&((ParametersStruct*)0)->PoseArrows), NULL, 0 }, // PoseArrows + { TYPE_U32, false, (size_t)(&((ParametersStruct*)0)->MeshStatic), NULL, 0 }, // MeshStatic + { TYPE_U32, false, (size_t)(&((ParametersStruct*)0)->MeshDynamic), NULL, 0 }, // MeshDynamic + { TYPE_U32, false, (size_t)(&((ParametersStruct*)0)->Shape), NULL, 0 }, // Shape + { TYPE_U32, false, (size_t)(&((ParametersStruct*)0)->Text0), NULL, 0 }, // Text0 + { TYPE_U32, false, (size_t)(&((ParametersStruct*)0)->Text1), NULL, 0 }, // Text1 + { TYPE_U32, false, (size_t)(&((ParametersStruct*)0)->ForceArrowsLow), NULL, 0 }, // ForceArrowsLow + { TYPE_U32, false, (size_t)(&((ParametersStruct*)0)->ForceArrowsNorm), NULL, 0 }, // ForceArrowsNorm + { TYPE_U32, false, (size_t)(&((ParametersStruct*)0)->ForceArrowsHigh), NULL, 0 }, // ForceArrowsHigh + { TYPE_U32, false, (size_t)(&((ParametersStruct*)0)->Color0), NULL, 0 }, // Color0 + { TYPE_U32, false, (size_t)(&((ParametersStruct*)0)->Color1), NULL, 0 }, // Color1 + { TYPE_U32, false, (size_t)(&((ParametersStruct*)0)->Color2), NULL, 0 }, // Color2 + { TYPE_U32, false, (size_t)(&((ParametersStruct*)0)->Color3), NULL, 0 }, // Color3 + { TYPE_U32, false, (size_t)(&((ParametersStruct*)0)->Color4), NULL, 0 }, // Color4 + { TYPE_U32, false, (size_t)(&((ParametersStruct*)0)->Color5), NULL, 0 }, // Color5 + { TYPE_U32, false, (size_t)(&((ParametersStruct*)0)->Red), NULL, 0 }, // Red + { TYPE_U32, false, (size_t)(&((ParametersStruct*)0)->Green), NULL, 0 }, // Green + { TYPE_U32, false, (size_t)(&((ParametersStruct*)0)->Blue), NULL, 0 }, // Blue + { TYPE_U32, false, (size_t)(&((ParametersStruct*)0)->DarkRed), NULL, 0 }, // DarkRed + { TYPE_U32, false, (size_t)(&((ParametersStruct*)0)->DarkGreen), NULL, 0 }, // DarkGreen + { TYPE_U32, false, (size_t)(&((ParametersStruct*)0)->DarkBlue), NULL, 0 }, // DarkBlue + { TYPE_U32, false, (size_t)(&((ParametersStruct*)0)->LightRed), NULL, 0 }, // LightRed + { TYPE_U32, false, (size_t)(&((ParametersStruct*)0)->LightGreen), NULL, 0 }, // LightGreen + { TYPE_U32, false, (size_t)(&((ParametersStruct*)0)->LightBlue), NULL, 0 }, // LightBlue + { TYPE_U32, false, (size_t)(&((ParametersStruct*)0)->Purple), NULL, 0 }, // Purple + { TYPE_U32, false, (size_t)(&((ParametersStruct*)0)->DarkPurple), NULL, 0 }, // DarkPurple + { TYPE_U32, false, (size_t)(&((ParametersStruct*)0)->Yellow), NULL, 0 }, // Yellow + { TYPE_U32, false, (size_t)(&((ParametersStruct*)0)->Orange), NULL, 0 }, // Orange + { TYPE_U32, false, (size_t)(&((ParametersStruct*)0)->Gold), NULL, 0 }, // Gold + { TYPE_U32, false, (size_t)(&((ParametersStruct*)0)->Emerald), NULL, 0 }, // Emerald + { TYPE_U32, false, (size_t)(&((ParametersStruct*)0)->White), NULL, 0 }, // White + { TYPE_U32, false, (size_t)(&((ParametersStruct*)0)->Black), NULL, 0 }, // Black + { TYPE_U32, false, (size_t)(&((ParametersStruct*)0)->Gray), NULL, 0 }, // Gray + { TYPE_U32, false, (size_t)(&((ParametersStruct*)0)->LightGray), NULL, 0 }, // LightGray + { TYPE_U32, false, (size_t)(&((ParametersStruct*)0)->DarkGray), NULL, 0 }, // DarkGray +}; + + +bool DebugColorParams::mBuiltFlag = false; +NvParameterized::MutexType DebugColorParams::mBuiltFlagMutex; + +DebugColorParams::DebugColorParams(NvParameterized::Traits* traits, void* buf, int32_t* refCount) : + NvParameters(traits, buf, refCount) +{ + //mParameterizedTraits->registerFactory(className(), &DebugColorParamsFactoryInst); + + if (!buf) //Do not init data if it is inplace-deserialized + { + initDynamicArrays(); + initStrings(); + initReferences(); + initDefaults(); + } +} + +DebugColorParams::~DebugColorParams() +{ + freeStrings(); + freeReferences(); + freeDynamicArrays(); +} + +void DebugColorParams::destroy() +{ + // We cache these fields here to avoid overwrite in destructor + bool doDeallocateSelf = mDoDeallocateSelf; + NvParameterized::Traits* traits = mParameterizedTraits; + int32_t* refCount = mRefCount; + void* buf = mBuffer; + + this->~DebugColorParams(); + + NvParameters::destroy(this, traits, doDeallocateSelf, refCount, buf); +} + +const NvParameterized::DefinitionImpl* DebugColorParams::getParameterDefinitionTree(void) +{ + if (!mBuiltFlag) // Double-checked lock + { + NvParameterized::MutexType::ScopedLock lock(mBuiltFlagMutex); + if (!mBuiltFlag) + { + buildTree(); + } + } + + return(&ParamDefTable[0]); +} + +const NvParameterized::DefinitionImpl* DebugColorParams::getParameterDefinitionTree(void) const +{ + DebugColorParams* tmpParam = const_cast<DebugColorParams*>(this); + + if (!mBuiltFlag) // Double-checked lock + { + NvParameterized::MutexType::ScopedLock lock(mBuiltFlagMutex); + if (!mBuiltFlag) + { + tmpParam->buildTree(); + } + } + + return(&ParamDefTable[0]); +} + +NvParameterized::ErrorType DebugColorParams::getParameterHandle(const char* long_name, Handle& handle) const +{ + ErrorType Ret = NvParameters::getParameterHandle(long_name, handle); + if (Ret != ERROR_NONE) + { + return(Ret); + } + + size_t offset; + void* ptr; + + getVarPtr(handle, ptr, offset); + + if (ptr == NULL) + { + return(ERROR_INDEX_OUT_OF_RANGE); + } + + return(ERROR_NONE); +} + +NvParameterized::ErrorType DebugColorParams::getParameterHandle(const char* long_name, Handle& handle) +{ + ErrorType Ret = NvParameters::getParameterHandle(long_name, handle); + if (Ret != ERROR_NONE) + { + return(Ret); + } + + size_t offset; + void* ptr; + + getVarPtr(handle, ptr, offset); + + if (ptr == NULL) + { + return(ERROR_INDEX_OUT_OF_RANGE); + } + + return(ERROR_NONE); +} + +void DebugColorParams::getVarPtr(const Handle& handle, void*& ptr, size_t& offset) const +{ + ptr = getVarPtrHelper(&ParamLookupTable[0], const_cast<DebugColorParams::ParametersStruct*>(¶meters()), handle, offset); +} + + +/* Dynamic Handle Indices */ + +void DebugColorParams::freeParameterDefinitionTable(NvParameterized::Traits* traits) +{ + if (!traits) + { + return; + } + + if (!mBuiltFlag) // Double-checked lock + { + return; + } + + NvParameterized::MutexType::ScopedLock lock(mBuiltFlagMutex); + + if (!mBuiltFlag) + { + return; + } + + for (uint32_t i = 0; i < NumParamDefs; ++i) + { + ParamDefTable[i].~DefinitionImpl(); + } + + traits->free(ParamDefTable); + + mBuiltFlag = false; +} + +#define PDEF_PTR(index) (&ParamDefTable[index]) + +void DebugColorParams::buildTree(void) +{ + + uint32_t allocSize = sizeof(NvParameterized::DefinitionImpl) * NumParamDefs; + ParamDefTable = (NvParameterized::DefinitionImpl*)(mParameterizedTraits->alloc(allocSize)); + memset(ParamDefTable, 0, allocSize); + + for (uint32_t i = 0; i < NumParamDefs; ++i) + { + NV_PARAM_PLACEMENT_NEW(ParamDefTable + i, NvParameterized::DefinitionImpl)(*mParameterizedTraits); + } + + // Initialize DefinitionImpl node: nodeIndex=0, longName="" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[0]; + ParamDef->init("", TYPE_STRUCT, "STRUCT", true); + + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=1, longName="Default" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[1]; + ParamDef->init("Default", TYPE_U32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "Default color value", true); + ParamDefTable[1].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=2, longName="PoseArrows" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[2]; + ParamDef->init("PoseArrows", TYPE_U32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "Pose arrows color value", true); + ParamDefTable[2].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=3, longName="MeshStatic" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[3]; + ParamDef->init("MeshStatic", TYPE_U32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "Static mesh color value", true); + ParamDefTable[3].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=4, longName="MeshDynamic" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[4]; + ParamDef->init("MeshDynamic", TYPE_U32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "Dynamic mesh color value", true); + ParamDefTable[4].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=5, longName="Shape" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[5]; + ParamDef->init("Shape", TYPE_U32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "Shape color value", true); + ParamDefTable[5].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=6, longName="Text0" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[6]; + ParamDef->init("Text0", TYPE_U32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "Text0 color value", true); + ParamDefTable[6].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=7, longName="Text1" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[7]; + ParamDef->init("Text1", TYPE_U32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "Text1 color value", true); + ParamDefTable[7].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=8, longName="ForceArrowsLow" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[8]; + ParamDef->init("ForceArrowsLow", TYPE_U32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "Force arrows color value (low value)", true); + ParamDefTable[8].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=9, longName="ForceArrowsNorm" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[9]; + ParamDef->init("ForceArrowsNorm", TYPE_U32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "Force arrows color value (normal value)", true); + ParamDefTable[9].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=10, longName="ForceArrowsHigh" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[10]; + ParamDef->init("ForceArrowsHigh", TYPE_U32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "Force arrows color value (high value)", true); + ParamDefTable[10].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=11, longName="Color0" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[11]; + ParamDef->init("Color0", TYPE_U32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "Color0 value", true); + ParamDefTable[11].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=12, longName="Color1" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[12]; + ParamDef->init("Color1", TYPE_U32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "Color1 value", true); + ParamDefTable[12].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=13, longName="Color2" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[13]; + ParamDef->init("Color2", TYPE_U32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "Color2 value", true); + ParamDefTable[13].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=14, longName="Color3" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[14]; + ParamDef->init("Color3", TYPE_U32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "Color3 value", true); + ParamDefTable[14].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=15, longName="Color4" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[15]; + ParamDef->init("Color4", TYPE_U32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "Color4 value", true); + ParamDefTable[15].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=16, longName="Color5" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[16]; + ParamDef->init("Color5", TYPE_U32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "Color5 value", true); + ParamDefTable[16].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=17, longName="Red" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[17]; + ParamDef->init("Red", TYPE_U32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "Red color value", true); + ParamDefTable[17].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=18, longName="Green" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[18]; + ParamDef->init("Green", TYPE_U32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "Green color value", true); + ParamDefTable[18].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=19, longName="Blue" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[19]; + ParamDef->init("Blue", TYPE_U32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "Blue color value", true); + ParamDefTable[19].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=20, longName="DarkRed" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[20]; + ParamDef->init("DarkRed", TYPE_U32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "Dark red value", true); + ParamDefTable[20].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=21, longName="DarkGreen" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[21]; + ParamDef->init("DarkGreen", TYPE_U32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "Dark green value", true); + ParamDefTable[21].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=22, longName="DarkBlue" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[22]; + ParamDef->init("DarkBlue", TYPE_U32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "Dark blue value", true); + ParamDefTable[22].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=23, longName="LightRed" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[23]; + ParamDef->init("LightRed", TYPE_U32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "Light red value", true); + ParamDefTable[23].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=24, longName="LightGreen" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[24]; + ParamDef->init("LightGreen", TYPE_U32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "Light green value", true); + ParamDefTable[24].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=25, longName="LightBlue" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[25]; + ParamDef->init("LightBlue", TYPE_U32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "Light blue value", true); + ParamDefTable[25].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=26, longName="Purple" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[26]; + ParamDef->init("Purple", TYPE_U32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "Purple value", true); + ParamDefTable[26].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=27, longName="DarkPurple" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[27]; + ParamDef->init("DarkPurple", TYPE_U32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "Dark purple value", true); + ParamDefTable[27].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=28, longName="Yellow" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[28]; + ParamDef->init("Yellow", TYPE_U32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "Yellow value", true); + ParamDefTable[28].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=29, longName="Orange" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[29]; + ParamDef->init("Orange", TYPE_U32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "Orange value", true); + ParamDefTable[29].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=30, longName="Gold" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[30]; + ParamDef->init("Gold", TYPE_U32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "Gold value", true); + ParamDefTable[30].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=31, longName="Emerald" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[31]; + ParamDef->init("Emerald", TYPE_U32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "Emerald value", true); + ParamDefTable[31].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=32, longName="White" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[32]; + ParamDef->init("White", TYPE_U32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "White value", true); + ParamDefTable[32].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=33, longName="Black" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[33]; + ParamDef->init("Black", TYPE_U32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "Black value", true); + ParamDefTable[33].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=34, longName="Gray" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[34]; + ParamDef->init("Gray", TYPE_U32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "Gray value", true); + ParamDefTable[34].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=35, longName="LightGray" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[35]; + ParamDef->init("LightGray", TYPE_U32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "Light gray value", true); + ParamDefTable[35].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=36, longName="DarkGray" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[36]; + ParamDef->init("DarkGray", TYPE_U32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "Dark gray value", true); + ParamDefTable[36].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // SetChildren for: nodeIndex=0, longName="" + { + static Definition* Children[36]; + Children[0] = PDEF_PTR(1); + Children[1] = PDEF_PTR(2); + Children[2] = PDEF_PTR(3); + Children[3] = PDEF_PTR(4); + Children[4] = PDEF_PTR(5); + Children[5] = PDEF_PTR(6); + Children[6] = PDEF_PTR(7); + Children[7] = PDEF_PTR(8); + Children[8] = PDEF_PTR(9); + Children[9] = PDEF_PTR(10); + Children[10] = PDEF_PTR(11); + Children[11] = PDEF_PTR(12); + Children[12] = PDEF_PTR(13); + Children[13] = PDEF_PTR(14); + Children[14] = PDEF_PTR(15); + Children[15] = PDEF_PTR(16); + Children[16] = PDEF_PTR(17); + Children[17] = PDEF_PTR(18); + Children[18] = PDEF_PTR(19); + Children[19] = PDEF_PTR(20); + Children[20] = PDEF_PTR(21); + Children[21] = PDEF_PTR(22); + Children[22] = PDEF_PTR(23); + Children[23] = PDEF_PTR(24); + Children[24] = PDEF_PTR(25); + Children[25] = PDEF_PTR(26); + Children[26] = PDEF_PTR(27); + Children[27] = PDEF_PTR(28); + Children[28] = PDEF_PTR(29); + Children[29] = PDEF_PTR(30); + Children[30] = PDEF_PTR(31); + Children[31] = PDEF_PTR(32); + Children[32] = PDEF_PTR(33); + Children[33] = PDEF_PTR(34); + Children[34] = PDEF_PTR(35); + Children[35] = PDEF_PTR(36); + + ParamDefTable[0].setChildren(Children, 36); + } + + mBuiltFlag = true; + +} +void DebugColorParams::initStrings(void) +{ +} + +void DebugColorParams::initDynamicArrays(void) +{ +} + +void DebugColorParams::initDefaults(void) +{ + + freeStrings(); + freeReferences(); + freeDynamicArrays(); + Default = uint32_t(0x00000000); + PoseArrows = uint32_t(0x00000000); + MeshStatic = uint32_t(0x00000000); + MeshDynamic = uint32_t(0x00000000); + Shape = uint32_t(0x00000000); + Text0 = uint32_t(0x00000000); + Text1 = uint32_t(0x00000000); + ForceArrowsLow = uint32_t(0xFFFFFF00); + ForceArrowsNorm = uint32_t(0xFF00FF00); + ForceArrowsHigh = uint32_t(0xFFFF0000); + Color0 = uint32_t(0x00000000); + Color1 = uint32_t(0x00000000); + Color2 = uint32_t(0x00000000); + Color3 = uint32_t(0x00000000); + Color4 = uint32_t(0x00000000); + Color5 = uint32_t(0x00000000); + Red = uint32_t(0xFFFF0000); + Green = uint32_t(0xFF00FF00); + Blue = uint32_t(0xFF0000FF); + DarkRed = uint32_t(0xFF800000); + DarkGreen = uint32_t(0xFF008000); + DarkBlue = uint32_t(0xFF000080); + LightRed = uint32_t(0xFFFF8080); + LightGreen = uint32_t(0xFF80FF00); + LightBlue = uint32_t(0xFF00FFFF); + Purple = uint32_t(0xFFFF00FF); + DarkPurple = uint32_t(0xFF800080); + Yellow = uint32_t(0xFFFFFF00); + Orange = uint32_t(0xFFFF8000); + Gold = uint32_t(0xFF808000); + Emerald = uint32_t(0xFF008080); + White = uint32_t(0xFFFFFFFF); + Black = uint32_t(0x00000000); + Gray = uint32_t(0xFF808080); + LightGray = uint32_t(0xFFC0C0C0); + DarkGray = uint32_t(0xFF404040); + + initDynamicArrays(); + initStrings(); + initReferences(); +} + +void DebugColorParams::initReferences(void) +{ +} + +void DebugColorParams::freeDynamicArrays(void) +{ +} + +void DebugColorParams::freeStrings(void) +{ +} + +void DebugColorParams::freeReferences(void) +{ +} + +} // namespace apex +} // namespace nvidia diff --git a/APEX_1.4/common/src/autogen/DebugRenderParams.cpp b/APEX_1.4/common/src/autogen/DebugRenderParams.cpp new file mode 100644 index 00000000..38f95033 --- /dev/null +++ b/APEX_1.4/common/src/autogen/DebugRenderParams.cpp @@ -0,0 +1,632 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and 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. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2015 NVIDIA Corporation. All rights reserved. + +// This file was generated by NvParameterized/scripts/GenParameterized.pl + + +#include "DebugRenderParams.h" +#include <string.h> +#include <stdlib.h> + +using namespace NvParameterized; + +namespace nvidia +{ +namespace apex +{ + +using namespace DebugRenderParamsNS; + +const char* const DebugRenderParamsFactory::vptr = + NvParameterized::getVptr<DebugRenderParams, DebugRenderParams::ClassAlignment>(); + +const uint32_t NumParamDefs = 13; +static NvParameterized::DefinitionImpl* ParamDefTable; // now allocated in buildTree [NumParamDefs]; + + +static const size_t ParamLookupChildrenTable[] = +{ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, +}; + +#define TENUM(type) nvidia::##type +#define CHILDREN(index) &ParamLookupChildrenTable[index] +static const NvParameterized::ParamLookupNode ParamLookupTable[NumParamDefs] = +{ + { TYPE_STRUCT, false, 0, CHILDREN(0), 11 }, + { TYPE_BOOL, false, (size_t)(&((ParametersStruct*)0)->Enable), NULL, 0 }, // Enable + { TYPE_F32, false, (size_t)(&((ParametersStruct*)0)->Scale), NULL, 0 }, // Scale + { TYPE_F32, false, (size_t)(&((ParametersStruct*)0)->LodBenefits), NULL, 0 }, // LodBenefits + { TYPE_F32, false, (size_t)(&((ParametersStruct*)0)->RelativeLodBenefitsScreenPos), NULL, 0 }, // RelativeLodBenefitsScreenPos + { TYPE_F32, false, (size_t)(&((ParametersStruct*)0)->RelativeLodBenefitsThickness), NULL, 0 }, // RelativeLodBenefitsThickness + { TYPE_F32, false, (size_t)(&((ParametersStruct*)0)->LodDistanceScale), NULL, 0 }, // LodDistanceScale + { TYPE_F32, false, (size_t)(&((ParametersStruct*)0)->RenderNormals), NULL, 0 }, // RenderNormals + { TYPE_F32, false, (size_t)(&((ParametersStruct*)0)->RenderTangents), NULL, 0 }, // RenderTangents + { TYPE_F32, false, (size_t)(&((ParametersStruct*)0)->RenderBitangents), NULL, 0 }, // RenderBitangents + { TYPE_BOOL, false, (size_t)(&((ParametersStruct*)0)->Bounds), NULL, 0 }, // Bounds + { TYPE_ARRAY, true, (size_t)(&((ParametersStruct*)0)->moduleName), CHILDREN(11), 1 }, // moduleName + { TYPE_REF, false, 1 * sizeof(NvParameterized::Interface*), NULL, 0 }, // moduleName[] +}; + + +bool DebugRenderParams::mBuiltFlag = false; +NvParameterized::MutexType DebugRenderParams::mBuiltFlagMutex; + +DebugRenderParams::DebugRenderParams(NvParameterized::Traits* traits, void* buf, int32_t* refCount) : + NvParameters(traits, buf, refCount) +{ + //mParameterizedTraits->registerFactory(className(), &DebugRenderParamsFactoryInst); + + if (!buf) //Do not init data if it is inplace-deserialized + { + initDynamicArrays(); + initStrings(); + initReferences(); + initDefaults(); + } +} + +DebugRenderParams::~DebugRenderParams() +{ + freeStrings(); + freeReferences(); + freeDynamicArrays(); +} + +void DebugRenderParams::destroy() +{ + // We cache these fields here to avoid overwrite in destructor + bool doDeallocateSelf = mDoDeallocateSelf; + NvParameterized::Traits* traits = mParameterizedTraits; + int32_t* refCount = mRefCount; + void* buf = mBuffer; + + this->~DebugRenderParams(); + + NvParameters::destroy(this, traits, doDeallocateSelf, refCount, buf); +} + +const NvParameterized::DefinitionImpl* DebugRenderParams::getParameterDefinitionTree(void) +{ + if (!mBuiltFlag) // Double-checked lock + { + NvParameterized::MutexType::ScopedLock lock(mBuiltFlagMutex); + if (!mBuiltFlag) + { + buildTree(); + } + } + + return(&ParamDefTable[0]); +} + +const NvParameterized::DefinitionImpl* DebugRenderParams::getParameterDefinitionTree(void) const +{ + DebugRenderParams* tmpParam = const_cast<DebugRenderParams*>(this); + + if (!mBuiltFlag) // Double-checked lock + { + NvParameterized::MutexType::ScopedLock lock(mBuiltFlagMutex); + if (!mBuiltFlag) + { + tmpParam->buildTree(); + } + } + + return(&ParamDefTable[0]); +} + +NvParameterized::ErrorType DebugRenderParams::getParameterHandle(const char* long_name, Handle& handle) const +{ + ErrorType Ret = NvParameters::getParameterHandle(long_name, handle); + if (Ret != ERROR_NONE) + { + return(Ret); + } + + size_t offset; + void* ptr; + + getVarPtr(handle, ptr, offset); + + if (ptr == NULL) + { + return(ERROR_INDEX_OUT_OF_RANGE); + } + + return(ERROR_NONE); +} + +NvParameterized::ErrorType DebugRenderParams::getParameterHandle(const char* long_name, Handle& handle) +{ + ErrorType Ret = NvParameters::getParameterHandle(long_name, handle); + if (Ret != ERROR_NONE) + { + return(Ret); + } + + size_t offset; + void* ptr; + + getVarPtr(handle, ptr, offset); + + if (ptr == NULL) + { + return(ERROR_INDEX_OUT_OF_RANGE); + } + + return(ERROR_NONE); +} + +void DebugRenderParams::getVarPtr(const Handle& handle, void*& ptr, size_t& offset) const +{ + ptr = getVarPtrHelper(&ParamLookupTable[0], const_cast<DebugRenderParams::ParametersStruct*>(¶meters()), handle, offset); +} + + +/* Dynamic Handle Indices */ +/* [0] - moduleName (not an array of structs) */ + +void DebugRenderParams::freeParameterDefinitionTable(NvParameterized::Traits* traits) +{ + if (!traits) + { + return; + } + + if (!mBuiltFlag) // Double-checked lock + { + return; + } + + NvParameterized::MutexType::ScopedLock lock(mBuiltFlagMutex); + + if (!mBuiltFlag) + { + return; + } + + for (uint32_t i = 0; i < NumParamDefs; ++i) + { + ParamDefTable[i].~DefinitionImpl(); + } + + traits->free(ParamDefTable); + + mBuiltFlag = false; +} + +#define PDEF_PTR(index) (&ParamDefTable[index]) + +void DebugRenderParams::buildTree(void) +{ + + uint32_t allocSize = sizeof(NvParameterized::DefinitionImpl) * NumParamDefs; + ParamDefTable = (NvParameterized::DefinitionImpl*)(mParameterizedTraits->alloc(allocSize)); + memset(ParamDefTable, 0, allocSize); + + for (uint32_t i = 0; i < NumParamDefs; ++i) + { + NV_PARAM_PLACEMENT_NEW(ParamDefTable + i, NvParameterized::DefinitionImpl)(*mParameterizedTraits); + } + + // Initialize DefinitionImpl node: nodeIndex=0, longName="" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[0]; + ParamDef->init("", TYPE_STRUCT, "STRUCT", true); + + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=1, longName="Enable" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[1]; + ParamDef->init("Enable", TYPE_BOOL, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "Enable/disable debug rendering", true); + ParamDefTable[1].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=2, longName="Scale" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[2]; + ParamDef->init("Scale", TYPE_F32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "Debug rendering scale", true); + ParamDefTable[2].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=3, longName="LodBenefits" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[3]; + ParamDef->init("LodBenefits", TYPE_F32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "LOD benefit debug visualization", true); + ParamDefTable[3].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=4, longName="RelativeLodBenefitsScreenPos" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[4]; + ParamDef->init("RelativeLodBenefitsScreenPos", TYPE_F32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + + static HintImpl HintTable[2]; + static Hint* HintPtrTable[2] = { &HintTable[0], &HintTable[1], }; + HintTable[0].init("max", double(1.0), true); + HintTable[1].init("min", double(-1.0), true); + ParamDefTable[4].setHints((const NvParameterized::Hint**)HintPtrTable, 2); + +#else + + static HintImpl HintTable[3]; + static Hint* HintPtrTable[3] = { &HintTable[0], &HintTable[1], &HintTable[2], }; + HintTable[0].init("max", double(1.0), true); + HintTable[1].init("min", double(-1.0), true); + HintTable[2].init("shortDescription", "The y-axis value of the relative benefits bar (-1.0 - 1.0)", true); + ParamDefTable[4].setHints((const NvParameterized::Hint**)HintPtrTable, 3); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=5, longName="RelativeLodBenefitsThickness" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[5]; + ParamDef->init("RelativeLodBenefitsThickness", TYPE_F32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "The thickness scale of the relative benefits bar", true); + ParamDefTable[5].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=6, longName="LodDistanceScale" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[6]; + ParamDef->init("LodDistanceScale", TYPE_F32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "LOD distance debug visualization", true); + ParamDefTable[6].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=7, longName="RenderNormals" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[7]; + ParamDef->init("RenderNormals", TYPE_F32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "Draws the normals (blue) of the rendered mesh. (scalable)", true); + ParamDefTable[7].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=8, longName="RenderTangents" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[8]; + ParamDef->init("RenderTangents", TYPE_F32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "Draws the tangents (red) of the rendered mesh. (scalable)", true); + ParamDefTable[8].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=9, longName="RenderBitangents" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[9]; + ParamDef->init("RenderBitangents", TYPE_F32, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "Draws the bitangents (green) of the rendered mesh. (scalable)", true); + ParamDefTable[9].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=10, longName="Bounds" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[10]; + ParamDef->init("Bounds", TYPE_BOOL, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + +#else + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("shortDescription", "Draws the bounds of every apex actor", true); + ParamDefTable[10].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + + + + } + + // Initialize DefinitionImpl node: nodeIndex=11, longName="moduleName" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[11]; + ParamDef->init("moduleName", TYPE_ARRAY, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("INCLUDED", uint64_t(1), true); + ParamDefTable[11].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#else + + static HintImpl HintTable[2]; + static Hint* HintPtrTable[2] = { &HintTable[0], &HintTable[1], }; + HintTable[0].init("INCLUDED", uint64_t(1), true); + HintTable[1].init("shortDescription", "Module name", true); + ParamDefTable[11].setHints((const NvParameterized::Hint**)HintPtrTable, 2); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + static const char* const RefVariantVals[] = { "BasicFSDebugRenderParams", "BasicIosDebugRenderParams", "ParticleIosDebugRenderParams", "ClothingDebugRenderParams", "DestructibleDebugRenderParams", "EmitterDebugRenderParams", "ForceFieldDebugRenderParams", "IofxDebugRenderParams", "TurbulenceFSDebugRenderParams", "ParticlesDebugRenderParams" }; + ParamDefTable[11].setRefVariantVals((const char**)RefVariantVals, 10); + + + ParamDef->setArraySize(-1); + static const uint8_t dynHandleIndices[1] = { 0, }; + ParamDef->setDynamicHandleIndicesMap(dynHandleIndices, 1); + + } + + // Initialize DefinitionImpl node: nodeIndex=12, longName="moduleName[]" + { + NvParameterized::DefinitionImpl* ParamDef = &ParamDefTable[12]; + ParamDef->init("moduleName", TYPE_REF, NULL, true); + +#ifdef NV_PARAMETERIZED_HIDE_DESCRIPTIONS + + static HintImpl HintTable[1]; + static Hint* HintPtrTable[1] = { &HintTable[0], }; + HintTable[0].init("INCLUDED", uint64_t(1), true); + ParamDefTable[12].setHints((const NvParameterized::Hint**)HintPtrTable, 1); + +#else + + static HintImpl HintTable[2]; + static Hint* HintPtrTable[2] = { &HintTable[0], &HintTable[1], }; + HintTable[0].init("INCLUDED", uint64_t(1), true); + HintTable[1].init("shortDescription", "Module name", true); + ParamDefTable[12].setHints((const NvParameterized::Hint**)HintPtrTable, 2); + +#endif /* NV_PARAMETERIZED_HIDE_DESCRIPTIONS */ + + + static const char* const RefVariantVals[] = { "BasicFSDebugRenderParams", "BasicIosDebugRenderParams", "ParticleIosDebugRenderParams", "ClothingDebugRenderParams", "DestructibleDebugRenderParams", "EmitterDebugRenderParams", "ForceFieldDebugRenderParams", "IofxDebugRenderParams", "TurbulenceFSDebugRenderParams", "ParticlesDebugRenderParams" }; + ParamDefTable[12].setRefVariantVals((const char**)RefVariantVals, 10); + + + + } + + // SetChildren for: nodeIndex=0, longName="" + { + static Definition* Children[11]; + Children[0] = PDEF_PTR(1); + Children[1] = PDEF_PTR(2); + Children[2] = PDEF_PTR(3); + Children[3] = PDEF_PTR(4); + Children[4] = PDEF_PTR(5); + Children[5] = PDEF_PTR(6); + Children[6] = PDEF_PTR(7); + Children[7] = PDEF_PTR(8); + Children[8] = PDEF_PTR(9); + Children[9] = PDEF_PTR(10); + Children[10] = PDEF_PTR(11); + + ParamDefTable[0].setChildren(Children, 11); + } + + // SetChildren for: nodeIndex=11, longName="moduleName" + { + static Definition* Children[1]; + Children[0] = PDEF_PTR(12); + + ParamDefTable[11].setChildren(Children, 1); + } + + mBuiltFlag = true; + +} +void DebugRenderParams::initStrings(void) +{ +} + +void DebugRenderParams::initDynamicArrays(void) +{ + moduleName.buf = NULL; + moduleName.isAllocated = true; + moduleName.elementSize = sizeof(NvParameterized::Interface*); + moduleName.arraySizes[0] = 0; +} + +void DebugRenderParams::initDefaults(void) +{ + + freeStrings(); + freeReferences(); + freeDynamicArrays(); + Enable = bool(false); + Scale = float(0); + LodBenefits = float(0); + RelativeLodBenefitsScreenPos = float(-0.8); + RelativeLodBenefitsThickness = float(0.6); + LodDistanceScale = float(0); + RenderNormals = float(0); + RenderTangents = float(0); + RenderBitangents = float(0); + Bounds = bool(false); + + initDynamicArrays(); + initStrings(); + initReferences(); +} + +void DebugRenderParams::initReferences(void) +{ +} + +void DebugRenderParams::freeDynamicArrays(void) +{ + if (moduleName.isAllocated && moduleName.buf) + { + mParameterizedTraits->free(moduleName.buf); + } +} + +void DebugRenderParams::freeStrings(void) +{ +} + +void DebugRenderParams::freeReferences(void) +{ + + for (int i = 0; i < moduleName.arraySizes[0]; ++i) + { + if (moduleName.buf[i]) + { + moduleName.buf[i]->destroy(); + } + } +} + +} // namespace apex +} // namespace nvidia diff --git a/APEX_1.4/common/src/variable_oscillator.cpp b/APEX_1.4/common/src/variable_oscillator.cpp new file mode 100644 index 00000000..50dcbb2f --- /dev/null +++ b/APEX_1.4/common/src/variable_oscillator.cpp @@ -0,0 +1,95 @@ +/* + * 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. + */ + + +#include "Apex.h" +#include "variable_oscillator.h" +#include "PxMath.h" + +namespace nvidia +{ +namespace apex +{ + +variableOscillator::variableOscillator(float min, float max, float initial, float period) : + mMin(min), + mMax(max), + mPeriod(period), + mStartVal(initial), + mLastVal(initial) +{ + mCumTime = 0.0f; + mGoingUp = true; + mEndVal = computeEndVal(mMin, mMax); +} + + +variableOscillator::~variableOscillator() +{ +} + +float variableOscillator::computeEndVal(float current, float maxOrMin) +{ + float target; + float maxDelta; + float quarterVal; + + // compute the max range of the oscillator + maxDelta = maxOrMin - current; + // find the 'lower bound' of the oscillator peak + quarterVal = current + (maxDelta / 4.0f); + // get a rand between 0 and 1 + target = (float) ::rand() / (float) RAND_MAX; + // scale the rand to the range we want + target = target * PxAbs(quarterVal - maxOrMin); + // add the offset to the scaled random number. + if (current < maxOrMin) + { + target = target + quarterVal; + } + else + { + target = quarterVal - target; + } + return(target); +} + +float variableOscillator::updateVariableOscillator(float deltaTime) +{ + float returnVal; + float halfRange; + + mCumTime += deltaTime; + + // has the function crossed a max or a min? + if ((mGoingUp && (mCumTime > (mPeriod / 2.0f))) || + (!mGoingUp && (mCumTime > mPeriod))) + { + mStartVal = mLastVal; + if (mGoingUp) + { + mEndVal = computeEndVal(mStartVal, mMin); + } + else + { + mEndVal = computeEndVal(mStartVal, mMax); + mCumTime = mCumTime - mPeriod; + } + mGoingUp = !mGoingUp; + } + halfRange = 0.5f * PxAbs(mEndVal - mStartVal); + returnVal = -halfRange * PxCos(mCumTime * PxTwoPi / mPeriod) + halfRange + PxMin(mStartVal, mEndVal); + mLastVal = returnVal; + + return(returnVal); +} + +} +} // namespace nvidia::apex |