/* * 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 "ApexUsingNamespace.h" #include "ApexRenderDebug.h" #include "ApexRenderable.h" #pragma warning(disable:4996) #pragma warning(disable:4100) #pragma warning(disable:4189) #if PX_PHYSICS_VERSION_MAJOR == 3 #include #endif // PX_PHYSICS_VERSION_MAJOR #include "RenderDebugInterface.h" #include "UserRenderer.h" #include "ApexSDKImpl.h" #include "PxIntrinsics.h" #include "PsString.h" #include "RenderDebugTyped.h" namespace nvidia { namespace apex { #if defined(WITHOUT_DEBUG_VISUALIZE) RenderDebugInterface* createApexRenderDebug(ApexSDKImpl* /*a*/) { return NULL; } void releaseApexRenderDebug(RenderDebugInterface* /*n*/) { } #else typedef physx::Array< RenderContext > RenderContextVector; typedef physx::Array< UserRenderResource*> RenderResourceVector; class ApexRenderDebug : public RenderDebugInterface, public ApexRWLockable, public UserAllocated { public: APEX_RW_LOCKABLE_BOILERPLATE ApexRenderDebug(ApexSDKImpl* sdk, RENDER_DEBUG::RenderDebugInterface* iface, bool useRemote) : mRenderDebugIface(iface) { mApexSDK = sdk; if (mRenderDebugUntyped == NULL) { RENDER_DEBUG::RenderDebug::Desc desc; desc.runMode = useRemote ? RENDER_DEBUG::RenderDebug::RM_CLIENT : RENDER_DEBUG::RenderDebug::RM_LOCAL; desc.foundation = mApexSDK->getFoundation(); mRenderDebugUntyped = createRenderDebugExport(desc); } if (mRenderDebugUntyped) { mRenderDebug = mRenderDebugUntyped->getRenderDebugTyped(); } else { PX_ASSERT(0); } mWireFrameMaterial = INVALID_RESOURCE_ID; mSolidShadedMaterial = INVALID_RESOURCE_ID; mLastRenderSolidCount = 0; mLastRenderLineCount = 0; mRenderSolidCount = 0; mRenderLineCount = 0; mUseDebugRenderable = false; } virtual RENDER_DEBUG::RenderDebugTyped* getRenderDebugInterface() { PX_ASSERT(mRenderDebugUntyped != NULL && mRenderDebug != NULL); return mRenderDebug; } virtual ~ApexRenderDebug(void) { if (mRenderDebug) { mRenderDebugUntyped->release(); mRenderDebugUntyped = NULL; } // APEX specific stuff { RenderResourceVector::Iterator i; for (i = mRenderLineResources.begin(); i != mRenderLineResources.end(); ++i) { UserRenderResource* resource = (*i); PX_ASSERT(resource); UserRenderVertexBuffer* vbuffer = resource->getVertexBuffer(0); PX_ASSERT(vbuffer); mApexSDK->getUserRenderResourceManager()->releaseResource(*resource); mApexSDK->getUserRenderResourceManager()->releaseVertexBuffer(*vbuffer); } } { RenderResourceVector::Iterator i; for (i = mRenderSolidResources.begin(); i != mRenderSolidResources.end(); ++i) { UserRenderResource* resource = (*i); PX_ASSERT(resource); UserRenderVertexBuffer* vbuffer = resource->getVertexBuffer(0); PX_ASSERT(vbuffer); mApexSDK->getUserRenderResourceManager()->releaseResource(*resource); mApexSDK->getUserRenderResourceManager()->releaseVertexBuffer(*vbuffer); } } mApexSDK->getInternalResourceProvider()->releaseResource(mWireFrameMaterial); mApexSDK->getInternalResourceProvider()->releaseResource(mSolidShadedMaterial); }; /* \brief Method to support rendering to a legacy PhysX SDK DebugRenderable object instead of to the APEX Render Resources API (i.e.: Renderable). This method is used to enable or disable the use of a legacy DebugRenderable. When enabled, use the getDebugRenderable() method to get a legacy DebugRenerable object that will contain all the debug output. */ virtual void setUseDebugRenderable(bool state) { mUseDebugRenderable = state; if (state == false) { #if PX_PHYSICS_VERSION_MAJOR == 3 mPxDebugTriangles.clear(); mPxDebugLines.clear(); mPxDebugTrianglesScreenSpace.clear(); mPxDebugLinesScreenSpace.clear(); #endif } } #if PX_PHYSICS_VERSION_MAJOR == 3 /* \brief Method to support rendering to a legacy PhysX SDK PxRenderBuffer object instead of to the APEX Render Resources API (i.e.: Renderable). When enabled with a call to setUseDebugRenderable(true), this method will return a legacy DebugRenderable object that contains all of the output of the RenderDebug class. */ virtual void getRenderBuffer(PhysXRenderBuffer& renderable) { renderable.mNbPoints = 0; renderable.mPoints = NULL; renderable.mNbLines = mPxDebugLines.size(); renderable.mLines = renderable.mNbLines ? &mPxDebugLines[0] : NULL; renderable.mNbTriangles = mPxDebugTriangles.size(); renderable.mTriangles = renderable.mNbTriangles ? &mPxDebugTriangles[0] : NULL; renderable.mNbTexts = 0; renderable.mTexts = NULL; } /* \brief Method to support rendering to a legacy PhysX SDK PxRenderBuffer object instead of to the APEX Render Resources API (i.e.: Renderable). When enabled with a call to setUseDebugRenderable(true), this method will return a legacy DebugRenderable object that contains all of the output of the RenderDebug class. */ virtual void getRenderBufferScreenSpace(PhysXRenderBuffer& renderable) { renderable.mNbPoints = 0; renderable.mPoints = NULL; renderable.mNbLines = mPxDebugLinesScreenSpace.size(); renderable.mLines = renderable.mNbLines ? &mPxDebugLinesScreenSpace[0] : NULL; renderable.mNbTriangles = mPxDebugTrianglesScreenSpace.size(); renderable.mTriangles = renderable.mNbTriangles ? &mPxDebugTrianglesScreenSpace[0] : NULL; renderable.mNbTexts = 0; renderable.mTexts = NULL; } virtual void addDebugRenderable(const physx::PxRenderBuffer& renderBuffer) { // Points mRenderDebug->pushRenderState(); const uint32_t color = mRenderDebug->getCurrentColor();; const uint32_t arrowColor = mRenderDebug->getCurrentArrowColor(); const uint32_t numPoints = renderBuffer.getNbPoints(); const physx::PxDebugPoint* points = renderBuffer.getPoints(); for (uint32_t i = 0; i < numPoints; i++) { const physx::PxDebugPoint& point = points[i]; mRenderDebug->setCurrentColor(point.color, arrowColor); mRenderDebug->debugPoint(point.pos, 0.01f); } // Lines const uint32_t numLines = renderBuffer.getNbLines(); const physx::PxDebugLine* lines = renderBuffer.getLines(); for (uint32_t i = 0; i < numLines; i++) { const physx::PxDebugLine& line = lines[i]; mRenderDebug->debugGradientLine(line.pos0, line.pos1, line.color0, line.color1); } // Triangles const uint32_t numTriangles = renderBuffer.getNbTriangles(); const physx::PxDebugTriangle* triangles = renderBuffer.getTriangles(); for (uint32_t i = 0; i < numTriangles; i++) { const physx::PxDebugTriangle& triangle = triangles[i]; mRenderDebug->debugGradientTri(triangle.pos0, triangle.pos1, triangle.pos2, triangle.color0, triangle.color1, triangle.color2); } // Texts const uint32_t numTexts = renderBuffer.getNbTexts(); const physx::PxDebugText* texts = renderBuffer.getTexts(); for (uint32_t i = 0; i < numTexts; i++) { const physx::PxDebugText& text = texts[i]; mRenderDebug->debugText(text.position, text.string); } mRenderDebug->setCurrentColor(color, arrowColor); mRenderDebug->popRenderState(); } #endif // PX_PHYSICS_VERSION_MAJOR == 3 /** \brief Release an object instance. Calling this will unhook the class and delete it from memory. You should not keep any reference to this class instance after calling release */ virtual void release() { delete this; } virtual void lockRenderResources() { } /** \brief Unlocks the renderable data of this Renderable actor. See locking semantics for xRenderDataProvider::lockRenderResources(). */ virtual void unlockRenderResources() { } /** \brief Update the renderable data of this Renderable actor. When called, this method will use the UserRenderResourceManager interface to inform the user about its render resource needs. It will also call the writeBuffer() methods of various graphics buffers. It must be called by the user each frame before any calls to dispatchRenderResources(). If the actor is not being rendered, this function may also be skipped. */ virtual void updateRenderResources(bool /*rewriteBuffers*/ = false, void* /*userRenderData*/ = 0) { URR_SCOPE; mRenderSolidContexts.clear(); mRenderLineContexts.clear(); // Free up the line draw vertex buffer resources if the debug renderer is now using a lot less memory than the last frame. if (mRenderLineCount < (mLastRenderLineCount / 2)) { RenderResourceVector::Iterator i; for (i = mRenderLineResources.begin(); i != mRenderLineResources.end(); ++i) { UserRenderResource* resource = (*i); PX_ASSERT(resource); UserRenderVertexBuffer* vbuffer = resource->getVertexBuffer(0); PX_ASSERT(vbuffer); mApexSDK->getUserRenderResourceManager()->releaseResource(*resource); mApexSDK->getUserRenderResourceManager()->releaseVertexBuffer(*vbuffer); } mRenderLineResources.clear(); } // free up the solid shaded triangle vertex buffers if the debug renderer is now using a lot less memory than the last frame. if (mRenderSolidCount < mLastRenderSolidCount / 2) // if we have less than 1/2 the number of solid shaded triangles we did last frame, free up the resources. { RenderResourceVector::Iterator i; for (i = mRenderSolidResources.begin(); i != mRenderSolidResources.end(); ++i) { UserRenderResource* resource = (*i); PX_ASSERT(resource); UserRenderVertexBuffer* vbuffer = resource->getVertexBuffer(0); PX_ASSERT(vbuffer); mApexSDK->getUserRenderResourceManager()->releaseResource(*resource); mApexSDK->getUserRenderResourceManager()->releaseVertexBuffer(*vbuffer); } mRenderSolidResources.clear(); } mLastRenderSolidCount = mRenderSolidCount; mLastRenderLineCount = mRenderLineCount; mRenderSolidCount = 0; mRenderLineCount = 0; #if PX_PHYSICS_VERSION_MAJOR == 3 mPxDebugLines.clear(); mPxDebugTriangles.clear(); mPxDebugLinesScreenSpace.clear(); mPxDebugTrianglesScreenSpace.clear(); #endif } virtual void dispatchRenderResources(UserRenderer& renderer) { mRenderDebug->render(1.0f / 60.0f, mRenderDebugIface); } virtual void debugRenderLines(uint32_t lcount, const RENDER_DEBUG::RenderDebugVertex* vertices, bool /*useZ*/, bool isScreenSpace) { #if PX_PHYSICS_VERSION_MAJOR == 3 if (mUseDebugRenderable) { for (uint32_t i = 0; i < lcount; i++) { PxVec3 v1( vertices[0].mPos[0], vertices[0].mPos[1], vertices[0].mPos[2] ); PxVec3 v2( vertices[1].mPos[0], vertices[1].mPos[1], vertices[1].mPos[2] ); PxDebugLine l(v1,v2,vertices->mColor); l.color1 = vertices[1].mColor; if ( isScreenSpace ) { mPxDebugLinesScreenSpace.pushBack(l); } else { mPxDebugLines.pushBack(l); } vertices += 2; } } else #endif { mRenderLineCount += (lcount * 2); if (mWireFrameMaterial == INVALID_RESOURCE_ID) { const char* mname = mApexSDK->getWireframeMaterial(); ResID name_space = mApexSDK->getInternalResourceProvider()->createNameSpace(APEX_MATERIALS_NAME_SPACE); mWireFrameMaterial = mApexSDK->getInternalResourceProvider()->createResource(name_space, mname, true); } const uint32_t MAX_LINE_VERTEX = 2048; PX_ASSERT((lcount * 2) <= MAX_LINE_VERTEX); uint32_t rcount = (uint32_t)mRenderLineContexts.size(); RenderContext context; UserRenderResource* resource; if (rcount < mRenderLineResources.size()) { resource = mRenderLineResources[rcount]; } else { UserRenderResourceDesc resourceDesc; UserRenderVertexBufferDesc vbdesc; vbdesc.hint = RenderBufferHint::DYNAMIC; vbdesc.buffersRequest[RenderVertexSemantic::POSITION] = RenderDataFormat::FLOAT3; vbdesc.buffersRequest[RenderVertexSemantic::COLOR] = RenderDataFormat::B8G8R8A8; vbdesc.maxVerts = MAX_LINE_VERTEX; resourceDesc.cullMode = RenderCullMode::NONE; for (uint32_t i = 0; i < RenderVertexSemantic::NUM_SEMANTICS; i++) { PX_ASSERT(vbdesc.buffersRequest[i] == RenderDataFormat::UNSPECIFIED || vertexSemanticFormatValid((RenderVertexSemantic::Enum)i, vbdesc.buffersRequest[i])); } UserRenderVertexBuffer* vb = mApexSDK->getUserRenderResourceManager()->createVertexBuffer(vbdesc); UserRenderVertexBuffer* vertexBuffers[1] = { vb }; resourceDesc.vertexBuffers = vertexBuffers; resourceDesc.numVertexBuffers = 1; resourceDesc.primitives = RenderPrimitiveType::LINES; resource = mApexSDK->getUserRenderResourceManager()->createResource(resourceDesc); resource->setMaterial(mApexSDK->getInternalResourceProvider()->getResource(mWireFrameMaterial)); mRenderLineResources.pushBack(resource); } UserRenderVertexBuffer* vb = resource->getVertexBuffer(0); resource->setVertexBufferRange(0, lcount * 2); RenderVertexBufferData writeData; writeData.setSemanticData(RenderVertexSemantic::POSITION, vertices[0].mPos, sizeof(RENDER_DEBUG::RenderDebugVertex), RenderDataFormat::FLOAT3); writeData.setSemanticData(RenderVertexSemantic::COLOR, &vertices[0].mColor, sizeof(RENDER_DEBUG::RenderDebugVertex), RenderDataFormat::B8G8R8A8); vb->writeBuffer(writeData, 0, lcount * 2); context.isScreenSpace = isScreenSpace; context.local2world = PxMat44(PxIdentity); context.renderResource = 0; context.world2local = PxMat44(PxIdentity); mRenderLineContexts.pushBack(context); } } virtual void debugRenderTriangles(uint32_t tcount, const RENDER_DEBUG::RenderDebugSolidVertex* vertices, bool /*useZ*/, bool isScreenSpace) { #if PX_PHYSICS_VERSION_MAJOR == 3 if (mUseDebugRenderable) { for (uint32_t i = 0; i < tcount; i++) { PxVec3 v1( vertices[0].mPos[0], vertices[0].mPos[1], vertices[0].mPos[2] ); PxVec3 v2( vertices[1].mPos[0], vertices[1].mPos[1], vertices[1].mPos[2] ); PxVec3 v3( vertices[2].mPos[0], vertices[2].mPos[1], vertices[2].mPos[2] ); PxDebugTriangle t( v1,v2, v3, vertices->mColor ); t.color1 = vertices[1].mColor; t.color2 = vertices[2].mColor; if ( isScreenSpace ) { mPxDebugTrianglesScreenSpace.pushBack(t); } else { mPxDebugTriangles.pushBack(t); } vertices += 3; } } else #endif { mRenderSolidCount += (tcount * 3); if (mSolidShadedMaterial == INVALID_RESOURCE_ID) { const char* mname = mApexSDK->getSolidShadedMaterial(); ResID name_space = mApexSDK->getInternalResourceProvider()->createNameSpace(APEX_MATERIALS_NAME_SPACE); mSolidShadedMaterial = mApexSDK->getInternalResourceProvider()->createResource(name_space, mname, true); } const uint32_t MAX_SOLID_VERTEX = 2048; PX_ASSERT((tcount * 3) <= MAX_SOLID_VERTEX); uint32_t rcount = (uint32_t)mRenderSolidContexts.size(); RenderContext context; UserRenderResource* resource; if (rcount < mRenderSolidResources.size()) { resource = mRenderSolidResources[rcount]; } else { UserRenderResourceDesc renderResourceDesc; UserRenderVertexBufferDesc vbdesc; vbdesc.hint = RenderBufferHint::DYNAMIC; vbdesc.buffersRequest[RenderVertexSemantic::POSITION] = RenderDataFormat::FLOAT3; vbdesc.buffersRequest[RenderVertexSemantic::NORMAL] = RenderDataFormat::FLOAT3; vbdesc.buffersRequest[RenderVertexSemantic::COLOR] = RenderDataFormat::B8G8R8A8; vbdesc.maxVerts = MAX_SOLID_VERTEX; renderResourceDesc.cullMode = RenderCullMode::COUNTER_CLOCKWISE; for (uint32_t i = 0; i < RenderVertexSemantic::NUM_SEMANTICS; i++) { PX_ASSERT(vbdesc.buffersRequest[i] == RenderDataFormat::UNSPECIFIED || vertexSemanticFormatValid((RenderVertexSemantic::Enum)i, vbdesc.buffersRequest[i])); } UserRenderVertexBuffer* vb = mApexSDK->getUserRenderResourceManager()->createVertexBuffer(vbdesc); UserRenderVertexBuffer* vertexBuffers[1] = { vb }; renderResourceDesc.vertexBuffers = vertexBuffers; renderResourceDesc.numVertexBuffers = 1; renderResourceDesc.primitives = RenderPrimitiveType::TRIANGLES; resource = mApexSDK->getUserRenderResourceManager()->createResource(renderResourceDesc); resource->setMaterial(mApexSDK->getInternalResourceProvider()->getResource(mSolidShadedMaterial)); mRenderSolidResources.pushBack(resource); } UserRenderVertexBuffer* vb = resource->getVertexBuffer(0); resource->setVertexBufferRange(0, tcount * 3); RenderVertexBufferData writeData; writeData.setSemanticData(RenderVertexSemantic::POSITION, vertices[0].mPos, sizeof(RENDER_DEBUG::RenderDebugSolidVertex), RenderDataFormat::FLOAT3); writeData.setSemanticData(RenderVertexSemantic::NORMAL, vertices[0].mNormal, sizeof(RENDER_DEBUG::RenderDebugSolidVertex), RenderDataFormat::FLOAT3); writeData.setSemanticData(RenderVertexSemantic::COLOR, &vertices[0].mColor, sizeof(RENDER_DEBUG::RenderDebugSolidVertex), RenderDataFormat::B8G8R8A8); vb->writeBuffer(writeData, 0, tcount * 3); context.isScreenSpace = isScreenSpace; context.local2world = PxMat44(PxIdentity); context.renderResource = 0; context.world2local = PxMat44(PxIdentity); mRenderSolidContexts.pushBack(context); } } virtual PxBounds3 getBounds() const { return PxBounds3(); } virtual void releaseRenderDebug(void) { release(); } private: static RENDER_DEBUG::RenderDebug* mRenderDebugUntyped; RENDER_DEBUG::RenderDebugTyped* mRenderDebug; uint32_t mRenderSolidCount; uint32_t mRenderLineCount; uint32_t mLastRenderLineCount; uint32_t mLastRenderSolidCount; ApexSDKImpl* mApexSDK; ResID mWireFrameMaterial; ResID mSolidShadedMaterial; RenderResourceVector mRenderLineResources; RenderContextVector mRenderLineContexts; RenderResourceVector mRenderSolidResources; RenderContextVector mRenderSolidContexts; bool mUseDebugRenderable; #if PX_PHYSICS_VERSION_MAJOR == 3 physx::Array mPxDebugLines; physx::Array mPxDebugTriangles; physx::Array mPxDebugLinesScreenSpace; physx::Array mPxDebugTrianglesScreenSpace; #endif RENDER_DEBUG::RenderDebugInterface* mRenderDebugIface; }; RENDER_DEBUG::RenderDebug* ApexRenderDebug::mRenderDebugUntyped = NULL; RenderDebugInterface* createApexRenderDebug(ApexSDKImpl* a, RENDER_DEBUG::RenderDebugInterface* iface, bool useRemote) { return PX_NEW(ApexRenderDebug)(a, iface, useRemote); } void releaseApexRenderDebug(RenderDebugInterface* n) { delete static_cast< ApexRenderDebug*>(n); } #endif // WITHOUT_DEBUG_VISUALIZE } } // end namespace nvidia::apex