/************************************************************************* * * * Open Dynamics Engine, Copyright (C) 2001-2003 Russell L. Smith. * * All rights reserved. Email: russ@q12.org Web: www.q12.org * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of EITHER: * * (1) The GNU Lesser General Public License as published by the Free * * Software Foundation; either version 2.1 of the License, or (at * * your option) any later version. The text of the GNU Lesser * * General Public License is included with this library in the * * file LICENSE.TXT. * * (2) The BSD-style license that is included with this library in * * the file LICENSE-BSD.TXT. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files * * LICENSE.TXT and LICENSE-BSD.TXT for more details. * * * *************************************************************************/ // TriMesh storage classes refactoring and face angle computation code by Oleh Derevenko (C) 2016-2019 #include #include #include "config.h" #include "matrix.h" #include "odemath.h" #if dTRIMESH_ENABLED #include "collision_trimesh_internal.h" #include "odeou.h" #include ////////////////////////////////////////////////////////////////////////// enum EdgeStorageSignInclusion { SSI__MIN, SSI_SIGNED_STORED = SSI__MIN, SSI_POSITIVE_STORED, SSI__MAX, }; template class FaceAngleStorageCodec; template class FaceAngleStorageCodec { public: typedef typename _make_signed::type storage_type; enum { STORAGE_TYPE_MAX = (typename _make_unsigned::type)(~(typename _make_unsigned::type)0) >> 1, }; static bool areNegativeAnglesCoded() { return true; } static storage_type encodeForStorage(dReal angleValue) { unsigned angleAsInt = (unsigned)dFloor(dFabs(angleValue) * (dReal)(STORAGE_TYPE_MAX / M_PI)); unsigned limitedAngleAsInt = dMACRO_MIN(angleAsInt, STORAGE_TYPE_MAX); storage_type result = angleValue < REAL(0.0) ? -(storage_type)limitedAngleAsInt : (storage_type)limitedAngleAsInt; return result; } static FaceAngleDomain classifyStorageValue(storage_type storedValue) { dSASSERT(EAD__MAX == 3); return storedValue < 0 ? FAD_CONCAVE : (storedValue == 0 ? FAD_FLAT : FAD_CONVEX); } static bool isAngleDomainStored(FaceAngleDomain domainValue) { return !dTMPL_IN_RANGE(domainValue, FAD__SIGNSTORED_IMPLICITVALUE_MIN, FAD__SIGNSTORED_IMPLICITVALUE_MAX); } static dReal decodeStorageValue(storage_type storedValue) { return storedValue * (dReal)(M_PI / STORAGE_TYPE_MAX); } }; template class FaceAngleStorageCodec { public: typedef typename _make_unsigned::type storage_type; enum { STORAGE_TYPE_MIN = 0, STORAGE_TYPE_MAX = (storage_type)(~(storage_type)0), }; static bool areNegativeAnglesCoded() { return false; } static storage_type encodeForStorage(dReal angleValue) { storage_type result = STORAGE_TYPE_MIN; if (angleValue >= REAL(0.0)) { unsigned angleAsInt = (unsigned)dFloor(angleValue * (dReal)(((STORAGE_TYPE_MAX - STORAGE_TYPE_MIN - 1) / M_PI))); result = (STORAGE_TYPE_MIN + 1) + dMACRO_MIN(angleAsInt, STORAGE_TYPE_MAX - STORAGE_TYPE_MIN - 1); } return result; } static FaceAngleDomain classifyStorageValue(storage_type storedValue) { dSASSERT(EAD__MAX == 3); return storedValue < STORAGE_TYPE_MIN + 1 ? FAD_CONCAVE : (storedValue == STORAGE_TYPE_MIN + 1 ? FAD_FLAT : FAD_CONVEX); } static bool isAngleDomainStored(FaceAngleDomain domainValue) { return dTMPL_IN_RANGE(domainValue, FAD__BYTEPOS_STORED_MIN, FAD__BYTEPOS_STORED_MAX); } static dReal decodeStorageValue(storage_type storedValue) { dIASSERT(storedValue >= (STORAGE_TYPE_MIN + 1)); return (storedValue - (STORAGE_TYPE_MIN + 1)) * (dReal)(M_PI / (STORAGE_TYPE_MAX - STORAGE_TYPE_MIN - 1)); } }; template class FaceAnglesWrapper: public IFaceAngleStorageControl, public IFaceAngleStorageView { protected: FaceAnglesWrapper(unsigned triangleCount) { setAllocatedTriangleCount(triangleCount); } public: virtual ~FaceAnglesWrapper(); static IFaceAngleStorageControl *allocateInstance(unsigned triangleCount, IFaceAngleStorageView *&out_storageView); static bool calculateInstanceSizeRequired(sizeint &out_sizeRequired, unsigned triangleCount); private: void freeInstance(); private: typedef typename TStorageCodec::storage_type storage_type; typedef storage_type TriangleFaceAngles[dMTV__MAX]; struct StorageRecord { StorageRecord(): m_triangleCount(0) {} unsigned m_triangleCount; TriangleFaceAngles m_triangleFaceAngles[1]; }; static sizeint calculateStorageSizeForTriangleCount(unsigned triangleCount) { const unsigned baseIncludedTriangleCount = dSTATIC_ARRAY_SIZE(FaceAnglesWrapper::StorageRecord, m_triangleFaceAngles); const sizeint singleTriangleSize = membersize(FaceAnglesWrapper::StorageRecord, m_triangleFaceAngles[0]); return sizeof(FaceAnglesWrapper) + (triangleCount > baseIncludedTriangleCount ? (triangleCount - baseIncludedTriangleCount) * singleTriangleSize : 0U); } static sizeint calculateTriangleCountForStorageSize(sizeint storageSize) { dIASSERT(storageSize >= sizeof(FaceAnglesWrapper)); const unsigned baseIncludedTriangleCount = dSTATIC_ARRAY_SIZE(FaceAnglesWrapper::StorageRecord, m_triangleFaceAngles); const sizeint singleTriangleSize = membersize(FaceAnglesWrapper::StorageRecord, m_triangleFaceAngles[0]); return (storageSize - sizeof(FaceAnglesWrapper)) / singleTriangleSize + baseIncludedTriangleCount; } private: // IFaceAngleStorageControl virtual void disposeStorage(); virtual bool areNegativeAnglesStored() const; virtual void assignFacesAngleIntoStorage(unsigned triangleIndex, dMeshTriangleVertex vertexIndex, dReal dAngleValue); private: // IFaceAngleStorageView virtual FaceAngleDomain retrieveFacesAngleFromStorage(dReal &out_angleValue, unsigned triangleIndex, dMeshTriangleVertex vertexIndex); public: void setFaceAngle(unsigned triangleIndex, dMeshTriangleVertex vertexIndex, dReal dAngleValue) { dIASSERT(dTMPL_IN_RANGE(triangleIndex, 0, getAllocatedTriangleCount())); dIASSERT(dTMPL_IN_RANGE(vertexIndex, dMTV__MIN, dMTV__MAX)); m_record.m_triangleFaceAngles[triangleIndex][vertexIndex] = TStorageCodec::encodeForStorage(dAngleValue); } FaceAngleDomain getFaceAngle(dReal &out_angleValue, unsigned triangleIndex, dMeshTriangleVertex vertexIndex) const { dIASSERT(dTMPL_IN_RANGE(triangleIndex, 0, getAllocatedTriangleCount())); dIASSERT(dTMPL_IN_RANGE(vertexIndex, dMTV__MIN, dMTV__MAX)); storage_type storedValue = m_record.m_triangleFaceAngles[triangleIndex][vertexIndex]; FaceAngleDomain resultDomain = TStorageCodec::classifyStorageValue(storedValue); out_angleValue = TStorageCodec::isAngleDomainStored(resultDomain) ? TStorageCodec::decodeStorageValue(storedValue) : REAL(0.0); return resultDomain; } private: unsigned getAllocatedTriangleCount() const { return m_record.m_triangleCount; } void setAllocatedTriangleCount(unsigned triangleCount) { m_record.m_triangleCount = triangleCount; } private: StorageRecord m_record; }; template FaceAnglesWrapper::~FaceAnglesWrapper() { } template /*static */ IFaceAngleStorageControl *FaceAnglesWrapper::allocateInstance(unsigned triangleCount, IFaceAngleStorageView *&out_storageView) { FaceAnglesWrapper *result = NULL; do { sizeint sizeRequired; if (!FaceAnglesWrapper::calculateInstanceSizeRequired(sizeRequired, triangleCount)) { break; } void *bufferPointer = dAlloc(sizeRequired); if (bufferPointer == NULL) { break; } result = (FaceAnglesWrapper *)bufferPointer; new(result) FaceAnglesWrapper(triangleCount); out_storageView = result; } while (false); return result; } template /*static */ bool FaceAnglesWrapper::calculateInstanceSizeRequired(sizeint &out_sizeRequired, unsigned triangleCount) { bool result = false; do { sizeint triangleMaximumCount = calculateTriangleCountForStorageSize(SIZE_MAX); dIASSERT(triangleCount <= triangleMaximumCount); if (triangleCount > triangleMaximumCount) // Check for overflow { break; } out_sizeRequired = calculateStorageSizeForTriangleCount(triangleCount); // Trailing alignment is going to be added by memory manager automatically result = true; } while (false); return result; } template void FaceAnglesWrapper::freeInstance() { unsigned triangleCount = getAllocatedTriangleCount(); this->FaceAnglesWrapper::~FaceAnglesWrapper(); sizeint memoryBlockSize = calculateStorageSizeForTriangleCount(triangleCount); dFree(this, memoryBlockSize); } template /*virtual */ void FaceAnglesWrapper::disposeStorage() { freeInstance(); } template /*virtual */ bool FaceAnglesWrapper::areNegativeAnglesStored() const { return TStorageCodec::areNegativeAnglesCoded(); } template /*virtual */ void FaceAnglesWrapper::assignFacesAngleIntoStorage(unsigned triangleIndex, dMeshTriangleVertex vertexIndex, dReal dAngleValue) { setFaceAngle(triangleIndex, vertexIndex, dAngleValue); } template /*virtual */ FaceAngleDomain FaceAnglesWrapper::retrieveFacesAngleFromStorage(dReal &out_angleValue, unsigned triangleIndex, dMeshTriangleVertex vertexIndex) { return getFaceAngle(out_angleValue, triangleIndex, vertexIndex); } typedef IFaceAngleStorageControl *(FAngleStorageAllocProc)(unsigned triangleCount, IFaceAngleStorageView *&out_storageView); BEGIN_NAMESPACE_OU(); template<> FAngleStorageAllocProc *const CEnumUnsortedElementArray::m_aetElementArray[] = { &FaceAnglesWrapper >::allocateInstance, // ASM_BYTE_SIGNED, &FaceAnglesWrapper >::allocateInstance, // ASM_BYTE_POSITIVE, &FaceAnglesWrapper >::allocateInstance, // ASM_WORD_SIGNED, }; END_NAMESPACE_OU(); static const CEnumUnsortedElementArray g_AngleStorageAllocProcs; ////////////////////////////////////////////////////////////////////////// dxTriDataBase::~dxTriDataBase() { freeFaceAngles(); } void dxTriDataBase::buildData(const void *vertices, int vertexStride, unsigned vertexCount, const void *indices, unsigned indexCount, int triStride, const void *normals, bool single) { dIASSERT(vertices); dIASSERT(indices); dIASSERT(vertexStride); dIASSERT(triStride); dIASSERT(indexCount); dIASSERT(indexCount % dMTV__MAX == 0); m_vertices = vertices; m_vertexStride = vertexStride; m_vertexCount = vertexCount; m_indices = indices; m_triangleCount = indexCount / dMTV__MAX; m_triStride = triStride; m_single = single; m_normals = normals; } bool dxTriDataBase::allocateFaceAngles(FaceAngleStorageMethod storageMethod) { bool result = false; dIASSERT(m_faceAngles == NULL); IFaceAngleStorageView *storageView; unsigned triangleCount = m_triangleCount; FAngleStorageAllocProc *allocProc = g_AngleStorageAllocProcs.Encode(storageMethod); IFaceAngleStorageControl *storageInstance = allocProc(triangleCount, storageView); if (storageInstance != NULL) { m_faceAngles = storageInstance; m_faceAngleView = storageView; result = true; } return result; } void dxTriDataBase::freeFaceAngles() { if (m_faceAngles != NULL) { m_faceAngles->disposeStorage(); m_faceAngles = NULL; m_faceAngleView = NULL; } } void dxTriDataBase::EdgeRecord::setupEdge(dMeshTriangleVertex edgeIdx, int triIdx, const unsigned vertexIndices[dMTV__MAX]) { if (edgeIdx < dMTV_SECOND) { dIASSERT(edgeIdx == dMTV_FIRST); m_edgeFlags = dxTriMeshData::CUF_USE_FIRST_EDGE; m_vert1Flags = dxTriMeshData::CUF_USE_FIRST_VERTEX; m_vert2Flags = dxTriMeshData::CUF_USE_SECOND_VERTEX; m_vertIdx1 = vertexIndices[dMTV_FIRST]; m_vertIdx2 = vertexIndices[dMTV_SECOND]; } else if (edgeIdx == dMTV_SECOND) { m_edgeFlags = dxTriMeshData::CUF_USE_SECOND_EDGE; m_vert1Flags = dxTriMeshData::CUF_USE_SECOND_VERTEX; m_vert2Flags = dxTriMeshData::CUF_USE_THIRD_VERTEX; m_vertIdx1 = vertexIndices[dMTV_SECOND]; m_vertIdx2 = vertexIndices[dMTV_THIRD]; } else { dIASSERT(edgeIdx == dMTV_THIRD); m_edgeFlags = dxTriMeshData::CUF_USE_THIRD_EDGE; m_vert1Flags = dxTriMeshData::CUF_USE_THIRD_VERTEX; m_vert2Flags = dxTriMeshData::CUF_USE_FIRST_VERTEX; m_vertIdx1 = vertexIndices[dMTV_THIRD]; m_vertIdx2 = vertexIndices[dMTV_FIRST]; } // Make sure vertex index 1 is less than index 2 (for easier sorting) if (m_vertIdx1 > m_vertIdx2) { dxSwap(m_vertIdx1, m_vertIdx2); dxSwap(m_vert1Flags, m_vert2Flags); } m_triIdx = triIdx; m_absVertexFlags = 0; } BEGIN_NAMESPACE_OU(); template<> const dMeshTriangleVertex CEnumUnsortedElementArray::m_aetElementArray[] = { dMTV_FIRST, // kVert0 / kVert_Base dMTV_SECOND, // kVert1 / kVert_Base dMTV__MAX, dMTV_THIRD, // kVert2 / kVert_Base }; END_NAMESPACE_OU(); /*extern */const CEnumUnsortedElementArray g_VertFlagOppositeIndices; BEGIN_NAMESPACE_OU(); template<> const dMeshTriangleVertex CEnumUnsortedElementArray::m_aetElementArray[] = { dMTV_SECOND, // kVert0 / kVert_Base dMTV_THIRD, // kVert1 / kVert_Base dMTV__MAX, dMTV_FIRST, // kVert2 / kVert_Base }; END_NAMESPACE_OU(); /*extern */const CEnumUnsortedElementArray g_VertFlagEdgeStartIndices; ////////////////////////////////////////////////////////////////////////// /*extern ODE_API */ void dGeomTriMeshDataBuildSimple1(dTriMeshDataID g, const dReal* Vertices, int VertexCount, const dTriIndex* Indices, int IndexCount, const int *Normals) { #ifdef dSINGLE dGeomTriMeshDataBuildSingle1(g, Vertices, 4 * sizeof(dReal), VertexCount, Indices, IndexCount, 3 * sizeof(dTriIndex), Normals); #else dGeomTriMeshDataBuildDouble1(g, Vertices, 4 * sizeof(dReal), VertexCount, Indices, IndexCount, 3 * sizeof(dTriIndex), Normals); #endif } /*extern ODE_API */ void dGeomTriMeshDataBuildSingle(dTriMeshDataID g, const void* Vertices, int VertexStride, int VertexCount, const void* Indices, int IndexCount, int TriStride) { dGeomTriMeshDataBuildSingle1(g, Vertices, VertexStride, VertexCount, Indices, IndexCount, TriStride, (const void *)NULL); } /*extern ODE_API */ void dGeomTriMeshDataBuildDouble(dTriMeshDataID g, const void* Vertices, int VertexStride, int VertexCount, const void* Indices, int IndexCount, int TriStride) { dGeomTriMeshDataBuildDouble1(g, Vertices, VertexStride, VertexCount, Indices, IndexCount, TriStride, NULL); } /*extern ODE_API */ void dGeomTriMeshDataBuildSimple(dTriMeshDataID g, const dReal* Vertices, int VertexCount, const dTriIndex* Indices, int IndexCount) { dGeomTriMeshDataBuildSimple1(g, Vertices, VertexCount, Indices, IndexCount, (int *)NULL); } /*extern ODE_API */ int dGeomTriMeshDataPreprocess(dTriMeshDataID g) { unsigned buildRequestFlags = (1U << dTRIDATAPREPROCESS_BUILD_CONCAVE_EDGES); return dGeomTriMeshDataPreprocess2(g, buildRequestFlags, NULL); } BEGIN_NAMESPACE_OU(); template<> const FaceAngleStorageMethod CEnumUnsortedElementArray::m_aetElementArray[] = { ASM_BYTE_POSITIVE, // dTRIDATAPREPROCESS_FACE_ANGLES_EXTRA_BYTE_POSITIVE, ASM_BYTE_SIGNED, // dTRIDATAPREPROCESS_FACE_ANGLES_EXTRA_BYTE_ALL, ASM_WORD_SIGNED, // dTRIDATAPREPROCESS_FACE_ANGLES_EXTRA_WORD_ALL, }; END_NAMESPACE_OU(); static const CEnumUnsortedElementArray g_TriMeshDataPreprocess_FaceAndlesExtraDataAngleStorageMethods; /*extern ODE_API */ int dGeomTriMeshDataPreprocess2(dTriMeshDataID g, unsigned int buildRequestFlags, const intptr *requestExtraData/*=NULL | const intptr (*)[dTRIDATAPREPROCESS_BUILD__MAX]*/) { dUASSERT(g, "The argument is not a trimesh data"); dAASSERT((buildRequestFlags & (1U << dTRIDATAPREPROCESS_BUILD_FACE_ANGLES)) == 0 || requestExtraData == NULL || dIN_RANGE(requestExtraData[dTRIDATAPREPROCESS_BUILD_FACE_ANGLES], dTRIDATAPREPROCESS_FACE_ANGLES_EXTRA__MIN, dTRIDATAPREPROCESS_FACE_ANGLES_EXTRA__MAX)); dxTriMeshData *data = g; bool buildUseFlags = (buildRequestFlags & (1U << dTRIDATAPREPROCESS_BUILD_CONCAVE_EDGES)) != 0; FaceAngleStorageMethod faceAnglesRequirement = (buildRequestFlags & (1U << dTRIDATAPREPROCESS_BUILD_FACE_ANGLES)) != 0 ? g_TriMeshDataPreprocess_FaceAndlesExtraDataAngleStorageMethods.Encode(requestExtraData != NULL && dIN_RANGE(requestExtraData[dTRIDATAPREPROCESS_BUILD_FACE_ANGLES], dTRIDATAPREPROCESS_FACE_ANGLES_EXTRA__MIN, dTRIDATAPREPROCESS_FACE_ANGLES_EXTRA__MAX) ? (unsigned)requestExtraData[dTRIDATAPREPROCESS_BUILD_FACE_ANGLES] : dTRIDATAPREPROCESS_FACE_ANGLES_EXTRA__DEFAULT) : ASM__INVALID; return data->preprocessData(buildUseFlags, faceAnglesRequirement); } /*extern ODE_API */ void dGeomTriMeshDataUpdate(dTriMeshDataID g) { dUASSERT(g, "The argument is not a trimesh data"); dxTriMeshData *data = g; data->updateData(); } ////////////////////////////////////////////////////////////////////////// /*extern ODE_API */ void dGeomTriMeshSetCallback(dGeomID g, dTriCallback* Callback) { dUASSERT(g && g->type == dTriMeshClass, "The argument is not a trimesh"); dxTriMesh *mesh = static_cast(g); mesh->assignCallback(Callback); } /*extern ODE_API */ dTriCallback* dGeomTriMeshGetCallback(dGeomID g) { dUASSERT(g && g->type == dTriMeshClass, "The argument is not a trimesh"); const dxTriMesh *mesh = static_cast(g); return mesh->retrieveCallback(); } /*extern ODE_API */ void dGeomTriMeshSetArrayCallback(dGeomID g, dTriArrayCallback* ArrayCallback) { dUASSERT(g && g->type == dTriMeshClass, "The argument is not a trimesh"); dxTriMesh *mesh = static_cast(g); mesh->assignArrayCallback(ArrayCallback); } /*extern ODE_API */ dTriArrayCallback *dGeomTriMeshGetArrayCallback(dGeomID g) { dUASSERT(g && g->type == dTriMeshClass, "The argument is not a trimesh"); const dxTriMesh *mesh = static_cast(g); return mesh->retrieveArrayCallback(); } /*extern ODE_API */ void dGeomTriMeshSetRayCallback(dGeomID g, dTriRayCallback* Callback) { dUASSERT(g && g->type == dTriMeshClass, "The argument is not a trimesh"); dxTriMesh *mesh = static_cast(g); mesh->assignRayCallback(Callback); } /*extern ODE_API */ dTriRayCallback* dGeomTriMeshGetRayCallback(dGeomID g) { dUASSERT(g && g->type == dTriMeshClass, "The argument is not a trimesh"); const dxTriMesh *mesh = static_cast(g); return mesh->retrieveRayCallback(); } /*extern ODE_API */ void dGeomTriMeshSetTriMergeCallback(dGeomID g, dTriTriMergeCallback* Callback) { dUASSERT(g && g->type == dTriMeshClass, "The argument is not a trimesh"); dxTriMesh *mesh = static_cast(g); mesh->assignTriMergeCallback(Callback); } /*extern ODE_API */ dTriTriMergeCallback *dGeomTriMeshGetTriMergeCallback(dGeomID g) { dUASSERT(g && g->type == dTriMeshClass, "The argument is not a trimesh"); const dxTriMesh *mesh = static_cast(g); return mesh->retrieveTriMergeCallback(); } /*extern ODE_API */ void dGeomTriMeshSetData(dGeomID g, dTriMeshDataID Data) { dUASSERT(g && g->type == dTriMeshClass, "The argument is not a trimesh"); dxTriMesh *mesh = static_cast(g); mesh->assignMeshData(Data); } /*extern ODE_API */ dTriMeshDataID dGeomTriMeshGetData(dGeomID g) { dUASSERT(g && g->type == dTriMeshClass, "The argument is not a trimesh"); const dxTriMesh *mesh = static_cast(g); return mesh->retrieveMeshData(); } BEGIN_NAMESPACE_OU(); template<> const int CEnumSortedElementArray::m_aetElementArray[] = { dSphereClass, // TTC_SPHERE, dBoxClass, // TTC_BOX, dCapsuleClass, // TTC_CAPSULE, }; END_NAMESPACE_OU(); static const CEnumSortedElementArray g_asiMeshTCGeomClasses; /*extern ODE_API */ void dGeomTriMeshEnableTC(dGeomID g, int geomClass, int enable) { dUASSERT(g && g->type == dTriMeshClass, "The argument is not a trimesh"); dxTriMesh *mesh = static_cast(g); dxTriMesh::TRIMESHTC tc = g_asiMeshTCGeomClasses.Decode(geomClass); if (g_asiMeshTCGeomClasses.IsValidDecode(tc)) { mesh->assignDoTC(tc, enable != 0); } } /*extern ODE_API */ int dGeomTriMeshIsTCEnabled(dGeomID g, int geomClass) { dUASSERT(g && g->type == dTriMeshClass, "The argument is not a trimesh"); const dxTriMesh *mesh = static_cast(g); dxTriMesh::TRIMESHTC tc = g_asiMeshTCGeomClasses.Decode(geomClass); bool result = g_asiMeshTCGeomClasses.IsValidDecode(tc) && mesh->retrieveDoTC(tc); return result; } /*extern ODE_API */ dTriMeshDataID dGeomTriMeshGetTriMeshDataID(dGeomID g) { dUASSERT(g && g->type == dTriMeshClass, "The argument is not a trimesh"); const dxTriMesh *mesh = static_cast(g); return mesh->retrieveMeshData(); } /*extern ODE_API */ void dGeomTriMeshClearTCCache(dGeomID g) { dUASSERT(g && g->type == dTriMeshClass, "The argument is not a trimesh"); dxTriMesh *mesh = static_cast(g); mesh->clearTCCache(); } /*extern ODE_API */ int dGeomTriMeshGetTriangleCount(dGeomID g) { dUASSERT(g && g->type == dTriMeshClass, "The argument is not a trimesh"); const dxTriMesh *mesh = static_cast(g); unsigned result = mesh->getMeshTriangleCount(); return result; } /*extern ODE_API */ void dGeomTriMeshGetTriangle(dGeomID g, int index, dVector3 *v0/*=NULL*/, dVector3 *v1/*=NULL*/, dVector3 *v2/*=NULL*/) { dUASSERT(g && g->type == dTriMeshClass, "The argument is not a trimesh"); dUASSERT(v0 != NULL || v1 != NULL || v2 != NULL, "A meaningless call"); dxTriMesh *mesh = static_cast(g); dVector3 *pv[3] = { v0, v1, v2 }; mesh->fetchMeshTransformedTriangle(pv, index); } /*extern ODE_API */ void dGeomTriMeshGetPoint(dGeomID g, int index, dReal u, dReal v, dVector3 Out) { dUASSERT(g && g->type == dTriMeshClass, "The argument is not a trimesh"); dxTriMesh *mesh = static_cast(g); dVector3 dv[3]; mesh->fetchMeshTransformedTriangle(dv, index); GetPointFromBarycentric(dv, u, v, Out); } /*extern */ IFaceAngleStorageView *dxGeomTriMeshGetFaceAngleView(dxGeom *triMeshGeom) { dUASSERT(triMeshGeom && triMeshGeom->type == dTriMeshClass, "The argument is not a trimesh"); dxTriMesh *mesh = static_cast(triMeshGeom); return mesh->retrieveFaceAngleView(); } #endif // #if dTRIMESH_ENABLED ////////////////////////////////////////////////////////////////////////// // Deprecated functions /*extern */ void dGeomTriMeshDataGetBuffer(dTriMeshDataID g, unsigned char **buf, int *bufLen) { sizeint dataSizeStorage; void *dataPointer = dGeomTriMeshDataGet2(g, dTRIMESHDATA_USE_FLAGS, (bufLen != NULL ? &dataSizeStorage : NULL)); if (bufLen != NULL) { *bufLen = (int)dataSizeStorage; } if (buf != NULL) { *buf = (unsigned char *)dataPointer; } } /*extern */ void dGeomTriMeshDataSetBuffer(dTriMeshDataID g, unsigned char* buf) { dGeomTriMeshDataSet(g, dTRIMESHDATA_USE_FLAGS, (void *)buf); }