ExplicitHierarchicalMesh.h
00001 //
00002 // Redistribution and use in source and binary forms, with or without
00003 // modification, are permitted provided that the following conditions
00004 // are met:
00005 //  * Redistributions of source code must retain the above copyright
00006 //    notice, this list of conditions and the following disclaimer.
00007 //  * Redistributions in binary form must reproduce the above copyright
00008 //    notice, this list of conditions and the following disclaimer in the
00009 //    documentation and/or other materials provided with the distribution.
00010 //  * Neither the name of NVIDIA CORPORATION nor the names of its
00011 //    contributors may be used to endorse or promote products derived
00012 //    from this software without specific prior written permission.
00013 //
00014 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
00015 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00016 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
00017 // PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
00018 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00019 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
00020 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00021 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
00022 // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00023 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
00024 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00025 //
00026 // Copyright (c) 2018 NVIDIA Corporation. All rights reserved.
00027 
00028 
00029 
00030 #ifndef EXPLICIT_HIERARCHICAL_MESH_H
00031 #define EXPLICIT_HIERARCHICAL_MESH_H
00032 
00033 #include "foundation/Px.h"
00034 #include "IProgressListener.h"
00035 #include "RenderMeshAsset.h"
00036 #include "ConvexHullMethod.h"
00037 #include "foundation/PxPlane.h"
00038 
00039 namespace nvidia
00040 {
00041 namespace apex
00042 {
00043 
00044 PX_PUSH_PACK_DEFAULT
00045 
00051 struct ExplicitVertexFormat
00052 {
00054     uint32_t            mWinding;
00055 
00057     bool                mHasStaticPositions;
00058 
00060     bool                mHasStaticNormals;
00061 
00063     bool                mHasStaticTangents;
00064 
00066     bool                mHasStaticBinormals;
00067 
00069     bool                mHasStaticColors;
00070 
00072     bool                mHasStaticSeparateBoneBuffer;
00073 
00075     bool                mHasStaticDisplacements;
00076 
00078     bool                mHasDynamicPositions;
00079 
00081     bool                mHasDynamicNormals;
00082 
00084     bool                mHasDynamicTangents;
00085 
00087     bool                mHasDynamicBinormals;
00088 
00090     bool                mHasDynamicColors;
00091 
00093     bool                mHasDynamicSeparateBoneBuffer;
00094 
00096     bool                mHasDynamicDisplacements;
00097 
00099     uint32_t            mUVCount;
00100 
00102     uint32_t            mBonesPerVertex;
00103 
00105     ExplicitVertexFormat()
00106     {
00107         clear();
00108     }
00109 
00131     void    clear()
00132     {
00133         mWinding = RenderCullMode::CLOCKWISE;
00134         mHasStaticPositions = false;
00135         mHasStaticNormals = false;
00136         mHasStaticTangents = false;
00137         mHasStaticBinormals = false;
00138         mHasStaticColors = false;
00139         mHasStaticSeparateBoneBuffer = false;
00140         mHasStaticDisplacements = false;
00141         mHasDynamicPositions = false;
00142         mHasDynamicNormals = false;
00143         mHasDynamicTangents = false;
00144         mHasDynamicBinormals = false;
00145         mHasDynamicColors = false;
00146         mHasDynamicSeparateBoneBuffer = false;
00147         mHasDynamicDisplacements = false;
00148         mUVCount = 0;
00149         mBonesPerVertex = 0;
00150     }
00151 
00155     bool    operator == (const ExplicitVertexFormat& data) const
00156     {
00157         if (mWinding != data.mWinding)
00158         {
00159             return false;
00160         }
00161         if (mHasStaticPositions != data.mHasStaticPositions ||
00162                 mHasStaticNormals != data.mHasStaticNormals ||
00163                 mHasStaticTangents != data.mHasStaticTangents ||
00164                 mHasStaticBinormals != data.mHasStaticBinormals ||
00165                 mHasStaticColors != data.mHasStaticColors ||
00166                 mHasStaticSeparateBoneBuffer != data.mHasStaticSeparateBoneBuffer ||
00167                 mHasStaticDisplacements != data.mHasStaticDisplacements)
00168         {
00169             return false;
00170         }
00171         if (mHasDynamicPositions != data.mHasDynamicPositions ||
00172                 mHasDynamicNormals != data.mHasDynamicNormals ||
00173                 mHasDynamicTangents != data.mHasDynamicTangents ||
00174                 mHasDynamicBinormals != data.mHasDynamicBinormals ||
00175                 mHasDynamicColors != data.mHasDynamicColors ||
00176                 mHasDynamicSeparateBoneBuffer != data.mHasDynamicSeparateBoneBuffer ||
00177                 mHasDynamicDisplacements != data.mHasDynamicDisplacements)
00178         {
00179             return false;
00180         }
00181         if (mUVCount != data.mUVCount)
00182         {
00183             return false;
00184         }
00185         return true;
00186     }
00187 
00191     bool    operator != (const ExplicitVertexFormat& data) const
00192     {
00193         return !(*this == data);
00194     }
00195 
00199     void    copyToVertexFormat(VertexFormat* format) const
00200     {
00201         format->reset();
00202         uint32_t bi;
00203         if (mHasStaticPositions)
00204         {
00205             bi = (uint32_t)format->addBuffer(format->getSemanticName(RenderVertexSemantic::POSITION));
00206             format->setBufferFormat(bi, RenderDataFormat::FLOAT3);
00207             format->setBufferAccess(bi, mHasDynamicPositions ? RenderDataAccess::DYNAMIC :  RenderDataAccess::STATIC);
00208         }
00209         if (mHasStaticNormals)
00210         {
00211             bi = (uint32_t)format->addBuffer(format->getSemanticName(RenderVertexSemantic::NORMAL));
00212             format->setBufferFormat(bi, RenderDataFormat::FLOAT3);
00213             format->setBufferAccess(bi, mHasDynamicNormals ? RenderDataAccess::DYNAMIC :  RenderDataAccess::STATIC);
00214         }
00215         if (mHasStaticTangents)
00216         {
00217             bi = (uint32_t)format->addBuffer(format->getSemanticName(RenderVertexSemantic::TANGENT));
00218             format->setBufferFormat(bi, RenderDataFormat::FLOAT3);
00219             format->setBufferAccess(bi, mHasDynamicTangents ? RenderDataAccess::DYNAMIC :  RenderDataAccess::STATIC);
00220         }
00221         if (mHasStaticBinormals)
00222         {
00223             bi = (uint32_t)format->addBuffer(format->getSemanticName(RenderVertexSemantic::BINORMAL));
00224             format->setBufferFormat(bi, RenderDataFormat::FLOAT3);
00225             format->setBufferAccess(bi, mHasDynamicBinormals ? RenderDataAccess::DYNAMIC :  RenderDataAccess::STATIC);
00226         }
00227         if (mHasStaticDisplacements)
00228         {
00229             bi = (uint32_t)format->addBuffer(format->getSemanticName(RenderVertexSemantic::DISPLACEMENT_TEXCOORD));
00230             format->setBufferFormat(bi, RenderDataFormat::FLOAT3);
00231             format->setBufferAccess(bi, mHasDynamicDisplacements ? RenderDataAccess::DYNAMIC : RenderDataAccess::STATIC);
00232             bi = (uint32_t)format->addBuffer(format->getSemanticName(RenderVertexSemantic::DISPLACEMENT_FLAGS));
00233             format->setBufferFormat(bi, RenderDataFormat::UINT1);
00234             format->setBufferAccess(bi, mHasDynamicDisplacements ? RenderDataAccess::DYNAMIC : RenderDataAccess::STATIC);
00235         }
00236         if (mUVCount > 0)
00237         {
00238             bi = (uint32_t)format->addBuffer(format->getSemanticName(RenderVertexSemantic::TEXCOORD0));
00239             format->setBufferFormat(bi, RenderDataFormat::FLOAT2);
00240         }
00241         if (mUVCount > 1)
00242         {
00243             bi = (uint32_t)format->addBuffer(format->getSemanticName(RenderVertexSemantic::TEXCOORD1));
00244             format->setBufferFormat(bi, RenderDataFormat::FLOAT2);
00245         }
00246         if (mUVCount > 2)
00247         {
00248             bi = (uint32_t)format->addBuffer(format->getSemanticName(RenderVertexSemantic::TEXCOORD2));
00249             format->setBufferFormat(bi, RenderDataFormat::FLOAT2);
00250         }
00251         if (mUVCount > 3)
00252         {
00253             bi = (uint32_t)format->addBuffer(format->getSemanticName(RenderVertexSemantic::TEXCOORD3));
00254             format->setBufferFormat(bi, RenderDataFormat::FLOAT2);
00255         }
00256         switch (mBonesPerVertex)
00257         {
00258         case 1:
00259             bi = (uint32_t)format->addBuffer(format->getSemanticName(RenderVertexSemantic::BONE_INDEX));
00260             format->setBufferFormat(bi, RenderDataFormat::USHORT1);
00261             break;
00262         case 2:
00263             bi = (uint32_t)format->addBuffer(format->getSemanticName(RenderVertexSemantic::BONE_INDEX));
00264             format->setBufferFormat(bi, RenderDataFormat::USHORT2);
00265             bi = (uint32_t)format->addBuffer(format->getSemanticName(RenderVertexSemantic::BONE_WEIGHT));
00266             format->setBufferFormat(bi, RenderDataFormat::FLOAT2);
00267             break;
00268         case 3:
00269             bi = (uint32_t)format->addBuffer(format->getSemanticName(RenderVertexSemantic::BONE_INDEX));
00270             format->setBufferFormat(bi, RenderDataFormat::USHORT3);
00271             bi = (uint32_t)format->addBuffer(format->getSemanticName(RenderVertexSemantic::BONE_WEIGHT));
00272             format->setBufferFormat(bi, RenderDataFormat::FLOAT3);
00273             break;
00274         case 4:
00275             bi = (uint32_t)format->addBuffer(format->getSemanticName(RenderVertexSemantic::BONE_INDEX));
00276             format->setBufferFormat(bi, RenderDataFormat::USHORT4);
00277             bi = (uint32_t)format->addBuffer(format->getSemanticName(RenderVertexSemantic::BONE_WEIGHT));
00278             format->setBufferFormat(bi, RenderDataFormat::FLOAT4);
00279             break;
00280         }
00281 
00282         format->setHasSeparateBoneBuffer(mHasStaticSeparateBoneBuffer);
00283         format->setWinding((RenderCullMode::Enum)mWinding);
00284     }
00285 };
00286 
00287 
00295 struct ExplicitSubmeshData
00296 {
00300     enum
00301     {
00302         MaterialNameBufferSize = 1024
00303     };
00304 
00308     char                    mMaterialName[MaterialNameBufferSize];
00309     
00314     ExplicitVertexFormat    mVertexFormat; 
00315 
00319     bool    operator == (const ExplicitSubmeshData& data) const
00320     {
00321         return !::strcmp(mMaterialName, data.mMaterialName) && mVertexFormat == data.mVertexFormat;
00322     }
00323 
00327     bool    operator != (const ExplicitSubmeshData& data) const
00328     {
00329         return !(*this == data);
00330     }
00331 };
00332 
00333 
00337 struct CollisionVolumeDesc
00338 {
00339     CollisionVolumeDesc()
00340     {
00341         setToDefault();
00342     }
00343 
00347     void setToDefault()
00348     {
00349         mHullMethod = ConvexHullMethod::CONVEX_DECOMPOSITION;
00350         mConcavityPercent = 4.0f;
00351         mMergeThreshold = 4.0f;
00352         mRecursionDepth = 0;
00353         mMaxVertexCount = 0;
00354         mMaxEdgeCount = 0;
00355         mMaxFaceCount = 0;
00356     }
00357 
00362     ConvexHullMethod::Enum  mHullMethod;
00363 
00368     float                   mConcavityPercent;
00369 
00374     float                   mMergeThreshold;
00375     
00382     uint32_t                mRecursionDepth;
00383 
00388     uint32_t                mMaxVertexCount;
00389 
00394     uint32_t                mMaxEdgeCount;
00395 
00400     uint32_t                mMaxFaceCount;
00401 };
00402 
00403 
00407 struct CollisionDesc
00408 {
00418     unsigned                mDepthCount;
00419 
00423     CollisionVolumeDesc*    mVolumeDescs;
00424 
00430     float                   mMaximumTrimming;
00431 
00433     CollisionDesc()
00434     {
00435         setToDefault();
00436     }
00437 
00441     void setToDefault()
00442     {
00443         mDepthCount = 0;
00444         mVolumeDescs = NULL;
00445         mMaximumTrimming = 0.2f;
00446     }
00447 };
00448 
00449 
00454 struct FractureMethod
00455 {
00459     enum Enum
00460     {
00461         Unknown,
00462         Slice,
00463         Cutout,
00464         Voronoi,
00465 
00466         FractureMethodCount
00467     };
00468 };
00469 
00470 
00476 struct FractureMaterialDesc
00477 {
00481     PxVec2  uvScale;
00482 
00486     PxVec2  uvOffset;
00487 
00492     PxVec3  tangent;
00493 
00497     float   uAngle;
00498 
00503     uint32_t interiorSubmeshIndex;
00504 
00506     FractureMaterialDesc()
00507     {
00508         setToDefault();
00509     }
00510 
00519     void    setToDefault()
00520     {
00521         uvScale = PxVec2(1.0f);
00522         uvOffset = PxVec2(0.0f);
00523         tangent = PxVec3(0.0f);
00524         uAngle = 0.0f;
00525         interiorSubmeshIndex = 0;
00526     }
00527 };
00528 
00529 
00534 struct MaterialFrame
00535 {
00536     MaterialFrame() :
00537         mCoordinateSystem(PxVec4(1.0f)),
00538         mUVPlane(PxVec3(0.0f, 0.0f, 1.0f), 0.0f),
00539         mUVScale(1.0f),
00540         mUVOffset(0.0f),
00541         mFractureMethod(FractureMethod::Unknown),
00542         mFractureIndex(-1),
00543         mSliceDepth(0)
00544     {       
00545     }
00546 
00550     void    buildCoordinateSystemFromMaterialDesc(const nvidia::FractureMaterialDesc& materialDesc, const PxPlane& plane)
00551             {
00552                 PxVec3 zAxis = plane.n;
00553                 zAxis.normalize();
00554                 PxVec3 xAxis = materialDesc.tangent;
00555                 PxVec3 yAxis = zAxis.cross(xAxis);
00556                 const float l2 = yAxis.magnitudeSquared();
00557                 if (l2 > PX_EPS_F32*PX_EPS_F32)
00558                 {
00559                     yAxis *= PxRecipSqrt(l2);
00560                 }
00561                 else
00562                 {
00563                     uint32_t maxDir =  PxAbs(plane.n.x) > PxAbs(plane.n.y) ?
00564                         (PxAbs(plane.n.x) > PxAbs(plane.n.z) ? 0u : 2u) :
00565                         (PxAbs(plane.n.y) > PxAbs(plane.n.z) ? 1u : 2u);
00566                     xAxis = PxMat33(PxIdentity)[(maxDir + 1) % 3];
00567                     yAxis = zAxis.cross(xAxis);
00568                     yAxis.normalize();
00569                 }
00570                 xAxis = yAxis.cross(zAxis);
00571 
00572                 const float c = PxCos(materialDesc.uAngle);
00573                 const float s = PxSin(materialDesc.uAngle);
00574 
00575                 mCoordinateSystem.column0 = PxVec4(c*xAxis + s*yAxis, 0.0f);
00576                 mCoordinateSystem.column1 = PxVec4(c*yAxis - s*xAxis, 0.0f);
00577                 mCoordinateSystem.column2 = PxVec4(zAxis, 0.0f);
00578                 mCoordinateSystem.setPosition(plane.project(PxVec3(0.0f)));
00579 
00580                 mUVPlane = plane;
00581                 mUVScale = materialDesc.uvScale;
00582                 mUVOffset = materialDesc.uvOffset;
00583             }
00584 
00585     PxMat44 mCoordinateSystem; 
00586     PxPlane mUVPlane; 
00587     PxVec2  mUVScale; 
00588     PxVec2  mUVOffset; 
00589     uint32_t    mFractureMethod; 
00590     int32_t mFractureIndex; 
00591     uint32_t    mSliceDepth;    
00592 };
00593 
00594 
00605 class DisplacementMapVolume
00606 {
00607 public:
00614     virtual void getData(uint32_t& width, uint32_t& height, uint32_t& depth, uint32_t& size, unsigned char const** ppData) const = 0;
00615 
00616     virtual ~DisplacementMapVolume() { }
00617 };
00618 
00619 
00627 struct BSPOpenMode
00628 {
00632     enum Enum
00633     {
00634         Automatic,
00635         Closed,
00636         Open,
00637 
00638         BSPOpenModeCount
00639     };
00640 };
00641 
00642 
00651 class ExplicitHierarchicalMesh
00652 {
00653 public:
00655     enum Enum
00656     {
00661         VisualizeMeshBSPOutsideRegions  = (1 << 0),
00662 
00667         VisualizeMeshBSPInsideRegions   = (1 << 1),
00668 
00674         VisualizeMeshBSPSingleRegion    = (1 << 8),
00675 
00677         VisualizeSliceBSPOutsideRegions = (1 << 16),
00678 
00680         VisualizeSliceBSPInsideRegions  = (1 << 17),
00681 
00683         VisualizeSliceBSPSingleRegion   = (1 << 24),
00684 
00685         VisualizeMeshBSPAllRegions      = VisualizeMeshBSPOutsideRegions | VisualizeMeshBSPInsideRegions
00686     };
00687 
00693     class Embedding
00694     {
00695     public:
00699         enum DataType
00700         {
00701             MaterialLibrary
00702         };
00703         
00707         virtual void    serialize(PxFileBuf& stream, Embedding::DataType type) const = 0;
00708         
00712         virtual void    deserialize(PxFileBuf& stream, Embedding::DataType type, uint32_t version) = 0;
00713     };
00714 
00718     class ConvexHull
00719     {
00720     protected:
00721         ConvexHull()
00722         {
00723         }
00724 
00725         virtual ~ConvexHull()
00726         {
00727         }
00728 
00729     public:
00733         virtual void                    buildFromPoints(const void* points, uint32_t numPoints, uint32_t pointStrideBytes) = 0;
00734 
00738         virtual const PxBounds3&        getBounds() const = 0;
00739 
00743         virtual float                   getVolume() const = 0;
00744 
00748         virtual uint32_t                getVertexCount() const = 0;
00749 
00754         virtual PxVec3                  getVertex(uint32_t vertexIndex) const = 0;
00755 
00759         virtual uint32_t                getEdgeCount() const = 0;
00760 
00766         virtual PxVec3                  getEdgeEndpoint(uint32_t edgeIndex, uint32_t whichEndpoint) const = 0;
00767 
00771         virtual uint32_t                getPlaneCount() const = 0;
00772 
00777         virtual PxPlane                 getPlane(uint32_t planeIndex) const = 0;
00778 
00806         virtual bool                    rayCast(float& in, float& out, const PxVec3& orig, const PxVec3& dir,
00807                                                 const PxTransform& localToWorldRT, const PxVec3& scale, PxVec3* normal = NULL) const = 0;
00816         virtual bool                    reduceHull(uint32_t maxVertexCount, uint32_t maxEdgeCount, uint32_t maxFaceCount, bool inflated) = 0;
00817 
00821         virtual void                    release() = 0;
00822     };
00823 
00831     virtual void                        clear(bool keepRoot = false) = 0;
00832 
00837     virtual int32_t                     maxDepth() const = 0;
00838 
00842     virtual uint32_t                    partCount() const = 0;
00843 
00847     virtual uint32_t                    chunkCount() const = 0;
00848 
00853     virtual int32_t*                    parentIndex(uint32_t chunkIndex) = 0;
00854 
00859     virtual uint64_t                    chunkUniqueID(uint32_t chunkIndex) = 0;
00860 
00864     virtual int32_t*                    partIndex(uint32_t chunkIndex) = 0;
00865 
00869     virtual PxVec3*                     instancedPositionOffset(uint32_t chunkIndex) = 0;
00870 
00874     virtual PxVec2*                     instancedUVOffset(uint32_t chunkIndex) = 0;
00875 
00880     virtual uint32_t                    meshTriangleCount(uint32_t partIndex) const = 0;
00881 
00886     virtual ExplicitRenderTriangle*     meshTriangles(uint32_t partIndex) = 0;
00887 
00891     virtual PxBounds3                   meshBounds(uint32_t partIndex) const = 0;
00892 
00896     virtual PxBounds3                   chunkBounds(uint32_t chunkIndex) const = 0;
00897 
00902     virtual uint32_t*                   chunkFlags(uint32_t chunkIndex) const = 0;
00903 
00907     virtual void                        buildCollisionGeometryForPart(uint32_t partIndex, const CollisionVolumeDesc& desc) = 0;
00908 
00916     virtual void                        buildCollisionGeometryForRootChunkParts(const CollisionDesc& desc, bool aggregateRootChunkParentCollision = true) = 0;
00917 
00921     virtual void                        reduceHulls(const CollisionDesc& desc, bool inflated) = 0;
00922 
00926     virtual uint32_t                    convexHullCount(uint32_t partIndex) const = 0;
00927 
00931     virtual const ConvexHull**          convexHulls(uint32_t partIndex) const = 0;
00932 
00936     virtual PxVec3*                     surfaceNormal(uint32_t partIndex) = 0;
00937 
00941     virtual const DisplacementMapVolume&    displacementMapVolume() const = 0;
00942 
00948     virtual uint32_t                    submeshCount() const = 0;
00949 
00953     virtual ExplicitSubmeshData*        submeshData(uint32_t submeshIndex) = 0;
00954 
00959     virtual uint32_t                    addSubmesh(const ExplicitSubmeshData& submeshData) = 0;
00960 
00969     virtual uint32_t                    getMaterialFrameCount() const = 0;
00970     virtual nvidia::MaterialFrame       getMaterialFrame(uint32_t index) const = 0; 
00971     virtual void                        setMaterialFrame(uint32_t index, const nvidia::MaterialFrame& materialFrame) = 0; 
00972     virtual uint32_t                    addMaterialFrame() = 0; 
00973 
00978     virtual void                        serialize(PxFileBuf& stream, Embedding& embedding) const = 0;
00979     
00984     virtual void                        deserialize(PxFileBuf& stream, Embedding& embedding) = 0;
00985 
00989     virtual void                        set(const ExplicitHierarchicalMesh& mesh) = 0;
00990 
00999     virtual void                        calculateMeshBSP(uint32_t randomSeed, IProgressListener* progressListener = NULL, const uint32_t* microgridSize = NULL, BSPOpenMode::Enum meshMode = BSPOpenMode::Automatic) = 0;
01000 
01004     virtual void                        replaceInteriorSubmeshes(uint32_t partIndex, uint32_t frameCount, uint32_t* frameIndices, uint32_t submeshIndex) = 0;
01005 
01011     virtual void                        visualize(RenderDebugInterface& debugRender, uint32_t flags, uint32_t index = 0) const = 0;
01012 
01016     virtual void                        release() = 0;
01017 
01018 protected:
01024     ExplicitHierarchicalMesh() {}
01025     virtual                             ~ExplicitHierarchicalMesh() {}
01026 
01027 private:
01029     ExplicitHierarchicalMesh&           operator = (const ExplicitHierarchicalMesh&)
01030     {
01031         return *this;
01032     }
01033 };
01034 
01035 
01036 PX_POP_PACK
01037 
01038 }
01039 } // end namespace nvidia
01040 
01041 
01042 #endif // EXPLICIT_HIERARCHICAL_MESH_H

Generated on Sat Dec 1 2018 15:52:05

Copyright © 2012-2018 NVIDIA Corporation, 2701 San Tomas Expressway, Santa Clara, CA 95050 U.S.A. All rights reserved.