diff options
| author | Marijn Tamis <[email protected]> | 2018-05-03 18:22:48 +0200 |
|---|---|---|
| committer | Marijn Tamis <[email protected]> | 2018-05-03 18:22:48 +0200 |
| commit | ca32c59a58d37c1822e185a2d5f3d0d3e8943593 (patch) | |
| tree | b06b9eec03f34344ef8fc31aa147b2714d3962ee /NvCloth/samples/SampleBase | |
| parent | Forced rename of platform folders in cmake dir. Git didn't pick this up before. (diff) | |
| download | nvcloth-ca32c59a58d37c1822e185a2d5f3d0d3e8943593.tar.xz nvcloth-ca32c59a58d37c1822e185a2d5f3d0d3e8943593.zip | |
NvCloth 1.1.4 Release. (24070740)
Diffstat (limited to 'NvCloth/samples/SampleBase')
49 files changed, 4279 insertions, 174 deletions
diff --git a/NvCloth/samples/SampleBase/renderer/ClothRenderMesh.cpp b/NvCloth/samples/SampleBase/renderer/ClothRenderMesh.cpp index 1183bdb..492324a 100644 --- a/NvCloth/samples/SampleBase/renderer/ClothRenderMesh.cpp +++ b/NvCloth/samples/SampleBase/renderer/ClothRenderMesh.cpp @@ -34,15 +34,21 @@ void gatherIndices(std::vector<uint16_t>& indices, indices.push_back(static_cast<uint16_t>(tIt.ptr()[1])); indices.push_back(static_cast<uint16_t>(tIt.ptr()[2])); } - qIt = PxMakeIterator(reinterpret_cast<const T*>(quads.data), quads.stride); - for (PxU32 i = 0; i < quads.count; ++i, ++qIt) + + //Only do quads in case there wasn't triangle data provided + // otherwise we risk to render triangles double + if(indices.size() == 0) { - indices.push_back(static_cast<uint16_t>(qIt.ptr()[0])); - indices.push_back(static_cast<uint16_t>(qIt.ptr()[1])); - indices.push_back(static_cast<uint16_t>(qIt.ptr()[2])); - indices.push_back(static_cast<uint16_t>(qIt.ptr()[0])); - indices.push_back(static_cast<uint16_t>(qIt.ptr()[2])); - indices.push_back(static_cast<uint16_t>(qIt.ptr()[3])); + qIt = PxMakeIterator(reinterpret_cast<const T*>(quads.data), quads.stride); + for (PxU32 i = 0; i < quads.count; ++i, ++qIt) + { + indices.push_back(static_cast<uint16_t>(qIt.ptr()[0])); + indices.push_back(static_cast<uint16_t>(qIt.ptr()[1])); + indices.push_back(static_cast<uint16_t>(qIt.ptr()[2])); + indices.push_back(static_cast<uint16_t>(qIt.ptr()[0])); + indices.push_back(static_cast<uint16_t>(qIt.ptr()[2])); + indices.push_back(static_cast<uint16_t>(qIt.ptr()[3])); + } } } @@ -147,6 +153,9 @@ void ClothRenderMesh::initialize(const void* vertices, uint32_t numVertices, uin V(mDevice->CreateBuffer(&bufferDesc, &indexBufferData, &mIndexBuffer)); } + std::vector<uint32_t> offsets; + offsets.push_back(0); + setSubmeshOffsets(offsets); } ClothRenderMesh::~ClothRenderMesh() @@ -197,7 +206,7 @@ void ClothRenderMesh::update(const PxVec3* positions, uint32_t numVertices) } } -void ClothRenderMesh::render(ID3D11DeviceContext& context) const +void ClothRenderMesh::render(ID3D11DeviceContext& context, int submesh) const { context.IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); @@ -207,8 +216,11 @@ void ClothRenderMesh::render(ID3D11DeviceContext& context) const context.IASetIndexBuffer(mIndexBuffer, DXGI_FORMAT_R16_UINT, 0); + int firstIndex = mSubmeshOffsets[submesh]; + int indexCount = mSubmeshOffsets[submesh+1] - firstIndex; + if (mIndexBuffer) - context.DrawIndexed(mNumFaces*3, 0, 0); + context.DrawIndexed(indexCount, firstIndex, 0); else context.Draw(mNumVertices, 0); }
\ No newline at end of file diff --git a/NvCloth/samples/SampleBase/renderer/ClothRenderMesh.h b/NvCloth/samples/SampleBase/renderer/ClothRenderMesh.h index 1af0027..ec33e9d 100644 --- a/NvCloth/samples/SampleBase/renderer/ClothRenderMesh.h +++ b/NvCloth/samples/SampleBase/renderer/ClothRenderMesh.h @@ -28,7 +28,7 @@ struct Vertex }; /** -Simple �loth render mesh +Simple cloth render mesh */ class ClothRenderMesh : public IRenderMesh { @@ -41,7 +41,14 @@ public: void update(const PxVec3* positions, uint32_t numVertices); const std::vector<D3D11_INPUT_ELEMENT_DESC>& getInputElementDesc() const { return mInputDesc; } - void render(ID3D11DeviceContext& context) const; + void render(ID3D11DeviceContext& context, int submesh) const; + + void setSubmeshOffsets(std::vector<uint32_t>const & offsets) + { + mSubmeshOffsets = offsets; + m_submeshCount = (int)offsets.size(); + mSubmeshOffsets.push_back((uint32_t)mIndices.size()); + } protected: ClothRenderMesh(); @@ -56,6 +63,7 @@ private: std::vector<Vertex> mVertices; std::vector<uint16_t> mIndices; + std::vector<uint32_t> mSubmeshOffsets; uint32_t mNumFaces; uint32_t mNumVertices; diff --git a/NvCloth/samples/SampleBase/renderer/CustomRenderMesh.cpp b/NvCloth/samples/SampleBase/renderer/CustomRenderMesh.cpp index 8dfbbb6..c634921 100644 --- a/NvCloth/samples/SampleBase/renderer/CustomRenderMesh.cpp +++ b/NvCloth/samples/SampleBase/renderer/CustomRenderMesh.cpp @@ -9,7 +9,7 @@ */ #include "CustomRenderMesh.h" - +#include <assert.h> CustomRenderMesh::CustomRenderMesh() : m_indexBuffer(nullptr) @@ -17,14 +17,14 @@ CustomRenderMesh::CustomRenderMesh() } CustomRenderMesh::CustomRenderMesh(const void* vertices, uint32_t numVertices, uint32_t vertexSize, - std::vector<D3D11_INPUT_ELEMENT_DESC>& inputDesc, const uint16_t* faces, uint32_t numFaces) + std::vector<D3D11_INPUT_ELEMENT_DESC>& inputDesc, const uint16_t* faces, uint32_t numFaces, int flags) : m_indexBuffer(nullptr) { - initialize(vertices, numVertices, vertexSize, inputDesc, faces, numFaces); + initialize(vertices, numVertices, vertexSize, inputDesc, faces, numFaces, flags); } void CustomRenderMesh::initialize(const void* vertices, uint32_t numVertices, uint32_t vertexSize, - std::vector<D3D11_INPUT_ELEMENT_DESC>& inputDesc, const uint16_t* faces, uint32_t numFaces) + std::vector<D3D11_INPUT_ELEMENT_DESC>& inputDesc, const uint16_t* faces, uint32_t numFaces, int flags) { ID3D11Device* device = GetDeviceManager()->GetDevice(); @@ -32,6 +32,8 @@ void CustomRenderMesh::initialize(const void* vertices, uint32_t numVertices, ui m_numVertices = numVertices; m_vertexSize = vertexSize; m_numFaces = numFaces; + m_vertexCapacity = max(1,numVertices); + m_indexCapacity = max(1,numFaces); // VB { @@ -40,16 +42,26 @@ void CustomRenderMesh::initialize(const void* vertices, uint32_t numVertices, ui ZeroMemory(&vertexBufferData, sizeof(vertexBufferData)); vertexBufferData.pSysMem = vertices; + void* backupBuffer = nullptr; + if(vertices == nullptr) + { + void* backupBuffer = malloc(vertexSize * m_vertexCapacity); + vertexBufferData.pSysMem = backupBuffer; + } + D3D11_BUFFER_DESC bufferDesc; memset(&bufferDesc, 0, sizeof(D3D11_BUFFER_DESC)); bufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; - bufferDesc.ByteWidth = vertexSize * numVertices; - bufferDesc.CPUAccessFlags = 0; + bufferDesc.ByteWidth = vertexSize * m_vertexCapacity; + bufferDesc.CPUAccessFlags = (flags&DYNAMIC_VERTEX_BUFFER) ? D3D11_CPU_ACCESS_WRITE : 0; bufferDesc.MiscFlags = 0; - bufferDesc.Usage = D3D11_USAGE_IMMUTABLE; + bufferDesc.Usage = (flags&DYNAMIC_VERTEX_BUFFER) ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_IMMUTABLE; V(device->CreateBuffer(&bufferDesc, &vertexBufferData, &m_vertexBuffer)); + + if(vertices = nullptr) + free(backupBuffer); } // IB @@ -60,16 +72,26 @@ void CustomRenderMesh::initialize(const void* vertices, uint32_t numVertices, ui ZeroMemory(&indexBufferData, sizeof(indexBufferData)); indexBufferData.pSysMem = faces; + void* backupBuffer = nullptr; + if(faces == nullptr) + { + void* backupBuffer = malloc(sizeof(uint16_t) * m_indexCapacity); + indexBufferData.pSysMem = backupBuffer; + } + D3D11_BUFFER_DESC bufferDesc; memset(&bufferDesc, 0, sizeof(D3D11_BUFFER_DESC)); bufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER; - bufferDesc.ByteWidth = sizeof(uint16_t) * numFaces; + bufferDesc.ByteWidth = sizeof(uint16_t) * m_indexCapacity; bufferDesc.CPUAccessFlags = 0; bufferDesc.MiscFlags = 0; bufferDesc.Usage = D3D11_USAGE_IMMUTABLE; V(device->CreateBuffer(&bufferDesc, &indexBufferData, &m_indexBuffer)); + + if(faces = nullptr) + free(backupBuffer); } } @@ -79,8 +101,26 @@ CustomRenderMesh::~CustomRenderMesh() SAFE_RELEASE(m_indexBuffer); } +void CustomRenderMesh::updateVertices(const void* vertices, uint32_t numVertices, uint32_t vertexSize) +{ + assert(numVertices <= m_vertexCapacity); + assert(vertexSize == m_vertexSize); + ID3D11Device* device = GetDeviceManager()->GetDevice(); + ID3D11DeviceContext* context; + device->GetImmediateContext(&context); + + D3D11_MAPPED_SUBRESOURCE mappedResource; + ZeroMemory(&mappedResource, sizeof(D3D11_MAPPED_SUBRESOURCE)); + + context->Map(m_vertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource); + // Update the vertex buffer here. + memcpy(mappedResource.pData, vertices, vertexSize*numVertices); + // Reenable GPU access to the vertex buffer data. + context->Unmap(m_vertexBuffer, 0); +} + -void CustomRenderMesh::render(ID3D11DeviceContext& context) const +void CustomRenderMesh::render(ID3D11DeviceContext& context, int submesh) const { context.IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); diff --git a/NvCloth/samples/SampleBase/renderer/CustomRenderMesh.h b/NvCloth/samples/SampleBase/renderer/CustomRenderMesh.h index c00eafc..93eb322 100644 --- a/NvCloth/samples/SampleBase/renderer/CustomRenderMesh.h +++ b/NvCloth/samples/SampleBase/renderer/CustomRenderMesh.h @@ -18,16 +18,27 @@ class CustomRenderMesh : public IRenderMesh { public: const std::vector<D3D11_INPUT_ELEMENT_DESC>& getInputElementDesc() const { return m_inputDesc; } - void render(ID3D11DeviceContext& context) const; + void render(ID3D11DeviceContext& context, int submesh) const; + + //flags + enum + { + DYNAMIC_VERTEX_BUFFER = 1 + }; CustomRenderMesh(const void* vertices, uint32_t numVertices, uint32_t vertexSize, - std::vector<D3D11_INPUT_ELEMENT_DESC>& inputDesc, const uint16_t* faces = nullptr, uint32_t numFaces = 0); + std::vector<D3D11_INPUT_ELEMENT_DESC>& inputDesc, const uint16_t* faces = nullptr, uint32_t numFaces = 0, int flags = 0); virtual ~CustomRenderMesh(); + void updateVertices(const void* vertices, uint32_t numVertices, uint32_t vertexSize); + uint32_t getVertexCapacity() const { return m_vertexCapacity; } + uint32_t getIndexCapacity() const { return m_indexCapacity; } + + protected: CustomRenderMesh(); void initialize(const void* vertices, uint32_t numVertices, uint32_t vertexSize, - std::vector<D3D11_INPUT_ELEMENT_DESC>& inputDesc, const uint16_t* faces, uint32_t numFaces); + std::vector<D3D11_INPUT_ELEMENT_DESC>& inputDesc, const uint16_t* faces, uint32_t numFaces, int flags); private: ID3D11Buffer* m_vertexBuffer; @@ -35,6 +46,8 @@ private: uint32_t m_numFaces; uint32_t m_numVertices; uint32_t m_vertexSize; + uint32_t m_vertexCapacity; + uint32_t m_indexCapacity; std::vector<D3D11_INPUT_ELEMENT_DESC> m_inputDesc; }; diff --git a/NvCloth/samples/SampleBase/renderer/Mesh.h b/NvCloth/samples/SampleBase/renderer/Mesh.h index cdc595d..3a2885b 100644 --- a/NvCloth/samples/SampleBase/renderer/Mesh.h +++ b/NvCloth/samples/SampleBase/renderer/Mesh.h @@ -18,7 +18,7 @@ class Mesh { - virtual uint32_t getVertexStride() = 0; + virtual uint32_t getVertexStride() const = 0; // ... TBD }; @@ -38,7 +38,7 @@ public: physx::PxVec2 uv; }; - virtual uint32_t getVertexStride() { return sizeof(Vertex); } + virtual uint32_t getVertexStride() const { return sizeof(Vertex); } std::vector<Vertex> vertices; std::vector<uint16_t> indices; diff --git a/NvCloth/samples/SampleBase/renderer/Model.cpp b/NvCloth/samples/SampleBase/renderer/Model.cpp new file mode 100644 index 0000000..4609cd5 --- /dev/null +++ b/NvCloth/samples/SampleBase/renderer/Model.cpp @@ -0,0 +1,520 @@ +/* +* Copyright (c) 2008-2017, 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 "Model.h" +#include <assimp/Importer.hpp> +#include <assimp/scene.h> +#include <assimp/postprocess.h> +#include <assert.h> +#include <Renderer.h> +#include "RenderMaterial.h" +#include "Renderable.h" + +SimpleMesh SkinnedMesh::convertToSimpleMesh() const +{ + SimpleMesh ret; + ret.center = center; + ret.extents = extents; + ret.indices = indices; + for(int i = 0; i<(int)vertices.size(); i++) + { + SimpleMesh::Vertex v; + v.position = vertices[i].position; + v.normal = vertices[i].normal; + v.uv = vertices[i].uv; + ret.vertices.push_back(v); + } + return ret; +} + +// assimp type to physx type conversions +physx::PxMat44 pxmat44(aiMatrix4x4 in) +{ + auto m = physx::PxMat44(in[0]); + return m.getTranspose(); +} +physx::PxQuat pxquat(aiQuaternion const in) +{ + return physx::PxQuat(in.x, in.y, in.z, in.w); +} +physx::PxVec3 pxvec3(aiVector3D const in) +{ + return physx::PxVec3(in.x, in.y, in.z); +} + +Model::~Model() +{ + for(int i = 0; i < (int)mRenderMaterials.size(); i++) + delete mRenderMaterials[i]; +} + +void Model::loadModel(const char* file) +{ + Assimp::Importer importer; + + std::string filename = file; + + const aiScene* scene = importer.ReadFile(filename, + aiProcess_Triangulate + | aiProcess_JoinIdenticalVertices // probably not needed + | aiProcess_SortByPType // separate non polygon data + //| aiProcess_GenNormals + | aiProcess_GenSmoothNormals // ignored if normals are defined in the file + | aiProcess_LimitBoneWeights // limit to 4 bones per vertex + | aiProcess_FlipWindingOrder + | aiProcess_FlipUVs + ); + + if(scene == nullptr) + printf("assimp loading error: %s\n", importer.GetErrorString()); + + assert(scene != nullptr); + + auto transform = scene->mRootNode->mTransformation; + mInverseRootTransform = pxmat44(transform.Inverse()); + + // Load (bone) nodes + processNode(*scene->mRootNode); + + // Load materials + for(int materialIt = 0; materialIt < (int)scene->mNumMaterials; materialIt++) + { + const aiMaterial& imaterial = *scene->mMaterials[materialIt]; + + if(mRenderer == nullptr) //can't load textures when mRenderer is not available + continue; + + //load textures + if(imaterial.GetTextureCount(aiTextureType_DIFFUSE) > 0) + { + aiString texturePath; + if (imaterial.GetTexture(aiTextureType_DIFFUSE, 0, &texturePath, NULL, NULL, NULL, NULL, NULL) == AI_SUCCESS) + { + auto mat = new RenderMaterial(mRenderer->getResourceManager(), "weighted_model_skinned_textured", texturePath.C_Str()); + mRenderMaterials.push_back(mat); + } + } + } + + // submeshes + for(int meshIt = 0; meshIt < (int)scene->mNumMeshes; meshIt++) + { + const aiMesh* imesh = scene->mMeshes[meshIt]; + const aiVector3D* vertexPositions = &imesh->mVertices[0]; + const aiVector3D* vertexNormals = &imesh->mNormals[0]; + const aiVector3D* vertexTextureCoords0 = imesh->HasTextureCoords(0) ? &((imesh->mTextureCoords[0])[0]) : nullptr; + + //printf("submesh %d: %s\n", meshIt, imesh->mName.C_Str()); + + mSubmeshes.push_back(SkinnedMesh()); + SkinnedMesh& mesh = mSubmeshes.back(); + mesh.vertices.resize(imesh->mNumVertices, SkinnedMesh::Vertex::getDefaultVertex()); + + mesh.mMaterialId = imesh->mMaterialIndex; + + // vertices + for(int i = 0; i < (int)imesh->mNumVertices; i++) + { + mesh.vertices[i].position.x = vertexPositions[i].x; + mesh.vertices[i].position.y = vertexPositions[i].y; + mesh.vertices[i].position.z = vertexPositions[i].z; + mesh.vertices[i].normal.x = vertexNormals[i].x; + mesh.vertices[i].normal.y = vertexNormals[i].y; + mesh.vertices[i].normal.z = vertexNormals[i].z; + if(vertexTextureCoords0 != nullptr) + { + mesh.vertices[i].uv.x = vertexTextureCoords0[i].x; + mesh.vertices[i].uv.y = vertexTextureCoords0[i].y; + } + } + + // triangle indices + mesh.indices.resize(imesh->mNumFaces * 3); + for(int i = 0; i < (int)imesh->mNumFaces; i++) + { + const aiFace& face = imesh->mFaces[i]; + assert(face.mNumIndices == 3); // we only deal with triangles here. Use aiProcess_Triangulate at import + + mesh.indices[i * 3 + 0] = face.mIndices[0]; + mesh.indices[i * 3 + 1] = face.mIndices[1]; + mesh.indices[i * 3 + 2] = face.mIndices[2]; + } + + mesh.center = physx::PxVec3(0.0f, 0.0f, 0.0f); + mesh.extents = physx::PxVec3(1.0f, 1.0f, 1.0f); //TODO + + mesh.mBoneOffsets.resize(mNodes.size(), physx::PxMat44(physx::PxIdentity)); + + //submesh bones + if(imesh->HasBones()) + { + + for(int boneIt = 0; boneIt < (int)imesh->mNumBones; boneIt++) + { + const aiBone& bone = *imesh->mBones[boneIt]; + int boneIndex = getNodeIdByName(bone.mName.C_Str()); + //printf("bone %d %s\n", boneIt, bone.mName.C_Str()); + + //store bone weights in vertex data + for(int i = 0; i < (int)bone.mNumWeights; i++) + { + const aiVertexWeight& weight = bone.mWeights[i]; + assert(weight.mVertexId < mesh.vertices.size()); + + auto& vertex = mesh.vertices[weight.mVertexId]; + for(int j = 0; j < 4; j++) + { + if(vertex.boneIndices[j] == -1) + { + vertex.boneIndices[j] = boneIndex; + vertex.boneWeights[j] = weight.mWeight; + break; + } + } + } + + mesh.mBoneOffsets[getNodeIdByName(bone.mName.C_Str())] = pxmat44(bone.mOffsetMatrix); + } + } + + //set all unused bone indices to 0 so we never go out of bounds + for(int j = 0; j < (int)mesh.vertices.size(); j++) + { + for(int k = 0; k < 4; k++) + { + if(mesh.vertices[j].boneIndices[k] == -1) + { + mesh.vertices[j].boneIndices[k] = 0; + mesh.vertices[j].boneWeights[k] = 0.0f; + } + } + } + } + + // animation + if(scene->HasAnimations()) + { + for(int animationIt = 0; animationIt < (int)scene->mNumAnimations; animationIt++) + { + const aiAnimation& ianimation = *scene->mAnimations[animationIt]; + + mAnimations.push_back(Animation()); + Animation& animation = mAnimations.back(); + + mAnimationNameMap[ianimation.mName.C_Str()] = (int)mAnimations.size() - 1; + //printf("animation %d %s\n", animationIt, ianimation.mName.C_Str()); + + animation.mDuration = ianimation.mDuration/ianimation.mTicksPerSecond; + animation.mLoop = true; + + animation.mBoneTimelines.resize(mNodes.size()); + + //animation contains 1 channel per (bone) node + for(int channelIt = 0; channelIt < (int)ianimation.mNumChannels; channelIt++) + { + const aiNodeAnim& channel = *ianimation.mChannels[channelIt]; + int nodeId = getNodeIdByName(channel.mNodeName.C_Str()); + assert(nodeId < (int)animation.mBoneTimelines.size()); + BoneTimeline& timeline = animation.mBoneTimelines[nodeId]; + + // position keyframes + for(int frame = 0; frame < (int)channel.mNumPositionKeys; frame++) + { + VariableFramerateKeyframeSequence<physx::PxVec3>::Key key; + key.mTime = channel.mPositionKeys[frame].mTime / ianimation.mTicksPerSecond; + key.mValue = pxvec3(channel.mPositionKeys[frame].mValue); + + timeline.mPositionKeys.mKeys.push_back(key); + } + if(channel.mNumPositionKeys == 1) + { + //duplicate last key for single frame animations so simplify interpolation code + timeline.mPositionKeys.mKeys.push_back(timeline.mPositionKeys.mKeys.back()); + timeline.mPositionKeys.mKeys.back().mTime += 1.0f; + } + + // rotation keyframes + for(int frame = 0; frame < (int)channel.mNumRotationKeys; frame++) + { + VariableFramerateKeyframeSequence<physx::PxQuat>::Key key; + key.mTime = channel.mRotationKeys[frame].mTime / ianimation.mTicksPerSecond; + key.mValue = pxquat(channel.mRotationKeys[frame].mValue); + + timeline.mRotationKeys.mKeys.push_back(key); + } + if(channel.mNumRotationKeys == 1) + { + timeline.mRotationKeys.mKeys.push_back(timeline.mRotationKeys.mKeys.back()); + timeline.mRotationKeys.mKeys.back().mTime += 1.0f; + } + + // scale keyframes + for(int frame = 0; frame < (int)channel.mNumScalingKeys; frame++) + { + VariableFramerateKeyframeSequence<physx::PxVec3>::Key key; + key.mTime = channel.mScalingKeys[frame].mTime / ianimation.mTicksPerSecond; + key.mValue = pxvec3(channel.mScalingKeys[frame].mValue); + + timeline.mScaleKeys.mKeys.push_back(key); + } + if(channel.mNumScalingKeys == 1) + { + timeline.mScaleKeys.mKeys.push_back(timeline.mScaleKeys.mKeys.back()); + timeline.mScaleKeys.mKeys.back().mTime += 1.0f; + } + } + + //set all missing bones to identity transform + for(int i = 0; i<(int)animation.mBoneTimelines.size(); i++) + { + if(animation.mBoneTimelines[i].mPositionKeys.mKeys.size() == 0) + { + animation.mBoneTimelines[i] = BoneTimeline::getDefaultTimeline(); + } + } + } + } +} + +int Model::processNode(const aiNode& inode, int parrent, int depth) +{ + //recursively build node tree + mNodes.push_back(Node()); + Node& node = mNodes.back(); + node.mName = inode.mName.C_Str(); + node.mTransform = pxmat44(inode.mTransformation); + node.mParrentNodeId = parrent; + + int thisId = (int)mNodes.size()-1; + mNodeNameMap[node.mName] = thisId; + + //printf("%.*s|- [%d] %s\n", depth, " . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .", + // thisId,inode.mName.C_Str()); + + node.mMeshIds.resize(inode.mNumMeshes); + for(int i = 0; i < (int)inode.mNumMeshes; i++) + { + node.mMeshIds[i] = inode.mMeshes[i]; + mSubmeshInstances.push_back({(int)inode.mMeshes[i], thisId}); + //printf("[%d]", node.mMeshIds[i]); + } + + //if(inode.mNumMeshes) + // printf("\n"); + + for(int i = 0; i < (int)inode.mNumChildren; i++) + { + //'node' is invalid, use thisId instead + mNodes[thisId].mChildNodeIds.push_back( + processNode(*inode.mChildren[i], thisId, depth + 1) + ); + + } + return thisId; +}; + +void Model::setRenderableMaterials(Renderable* renderable) +{ + if(getRenderMaterialCount()) + { + renderable->clearMaterials(); + for(int submeshId = 0; submeshId < getSubMeshCount(); submeshId++) + { + renderable->addMaterial(*getRenderMaterial(getSubMesh(submeshId).mMaterialId)); + } + } +} + +void Model::updateModelInstance(ModelInstance& instance, physx::PxMat44 transform) const +{ + instance.mNodes.resize(mNodes.size()); + //allocate the combined transform memory so it can be used as scratch space later on without allocation overhead + instance.mNodeTransormsWithBoneOffset.resize(mNodes.size()); + { + const Animation& animation = mAnimations[instance.mAnimationIndex]; + const Node& node = mNodes[0]; + auto& instanceNode = instance.mNodes[0]; + + physx::PxMat44 localTransform = animation.getBoneMatrix(instance.mAnimationTime, 0, + instanceNode.mPositionFrameGuess, instanceNode.mRotationFrameGuess, instanceNode.mScaleFrameGuess); + + instanceNode.mTransform = transform * localTransform; + } + + const Node& node = mNodes[0]; + for(int i = 0; i < (int)node.mChildNodeIds.size(); i++) + recurseModelInstanceNode(instance, node.mChildNodeIds[i]); +} + +void Model::recurseModelInstanceNode(ModelInstance& instance, int nodeIndex, int depth) const +{ + const Animation& animation = mAnimations[instance.mAnimationIndex]; + + const Node& node = mNodes[nodeIndex]; + auto& instanceNode = instance.mNodes[nodeIndex]; + auto& instanceParrentNode = instance.mNodes[node.mParrentNodeId]; + + physx::PxMat44 localTransform = animation.getBoneMatrix(instance.mAnimationTime, nodeIndex, + instanceNode.mPositionFrameGuess, instanceNode.mRotationFrameGuess, instanceNode.mScaleFrameGuess); + + instanceNode.mTransform = instanceParrentNode.mTransform * localTransform; + + //printf("%.*s|- %s\n", depth, " . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .", + // node.mName.c_str()); + + + for(int i = 0; i < (int)node.mChildNodeIds.size(); i++) + recurseModelInstanceNode(instance, node.mChildNodeIds[i], depth + 1); +} + +void Model::updateCollisionSpheres(physx::PxVec4* spheres, uint32_t* sphereNodes, int sphereCount, physx::PxVec4* sphereOffsets, ModelInstance const& instance, physx::PxMat44& transform) const +{ + /* + //ignore sphereOffsets + for(int sphereId = 0; sphereId < sphereCount; sphereId++) + { + spheres[sphereId] = transform.transform(instance.mNodes[sphereRootNode + sphereId].mTransform.transform(physx::PxVec4(sphereOffsets[sphereId].getXYZ(), 1.0f))); + spheres[sphereId].w = sphereOffsets[sphereId].w; + }*/ + + //take sphereOffsets into account in bone space + for(int i = 0; i < sphereCount; i++) + { + int sphereId = sphereNodes[i]; + physx::PxVec3 pos = transform.transform(instance.mNodes[sphereId].mTransform.transform(physx::PxVec4(sphereOffsets[sphereId].getXYZ(), 1.0f))).getXYZ(); + physx::PxQuat orientation; + { + int parrent = mNodes[sphereId].mParrentNodeId; + physx::PxVec3 u = physx::PxVec3(0.0f, 1.0f, 0.0f); + physx::PxVec3 v = + pos - + transform.transform(instance.mNodes[parrent].mTransform.transform(physx::PxVec4(sphereOffsets[parrent].getXYZ(), 1.0f))).getXYZ(); + + + v.normalize(); + + if(u.dot(v) < -0.9999) + orientation = physx::PxQuat(physx::PxTwoPi, physx::PxVec3(1.0f, 0.0f, 0.0f)); + else if(u.dot(v) > 0.9999) + orientation = physx::PxQuat(0.0f, physx::PxVec3(1.0f, 0.0f, 0.0f)); + else + { + physx::PxVec3 half = u + v; + half.normalize(); + physx::PxVec3 imaginary = u.cross(half); + orientation = physx::PxQuat(imaginary.x, imaginary.y, imaginary.z, u.dot(half)); + } + } + pos += orientation.rotate(sphereOffsets[sphereId].getXYZ()); + spheres[i] = physx::PxVec4(pos, sphereOffsets[sphereId].w); + } +} + +std::vector<uint32_t> Model::getCollisionCapsules(int sphereRootNode) const +{ + std::vector<uint32_t> capsules; + for(int i = sphereRootNode + 1; i < (int)mNodes.size(); i++) + { + if(mNodes[i].mParrentNodeId < sphereRootNode) + continue; + capsules.push_back(mNodes[i].mParrentNodeId - sphereRootNode); + capsules.push_back(i - sphereRootNode); + } + return capsules; +} + +void SkinnedModel::initialize(Model * model) +{ + mModel = model; + mSubmeshes.resize(mModel->getTotalSubmeshInstanceCount()); + for(int i = 0; i < (int)mSubmeshes.size(); i++) + { + Model::SubmeshInstance submeshInstance = mModel->getSubMeshInstance(i); + SkinnedMesh const& skinnedMesh = mModel->getSubMesh(submeshInstance.mSubmeshId); + mSubmeshes[i].vertices.resize(skinnedMesh.vertices.size()); + mSubmeshes[i].indices = skinnedMesh.indices; + } +} +void SkinnedModel::updateMeshes(JobManager* jobManager) +{ + assert(mSubmeshes.size() == mModel->getTotalSubmeshInstanceCount()); + for(int submeshId = 0; submeshId < (int)mSubmeshes.size(); submeshId++) + { + Model::SubmeshInstance submeshInstance = mModel->getSubMeshInstance(submeshId); + SkinnedMesh const& skinnedMesh = mModel->getSubMesh(submeshInstance.mSubmeshId); + SimpleMesh& outMesh = mSubmeshes[submeshId]; + assert(mSubmeshes[submeshId].vertices.size() == skinnedMesh.vertices.size()); + + physx::PxMat44* boneTransformBoneOffest = mModelInstance.mNodeTransormsWithBoneOffset.data(); + for(int i = 0; i < (int)skinnedMesh.mBoneOffsets.size(); i++) + { + boneTransformBoneOffest[i] = mModelInstance.mNodes[i].mTransform * skinnedMesh.mBoneOffsets[i]; + } + + const int jobCount = 16; + + auto jobFunction = [&](int jobId) + { + int vertexCount = (int)skinnedMesh.vertices.size(); + int jobSize = vertexCount / jobCount + 1; + int vertexId = jobSize * jobId; + int lastVertexId = min(jobSize * (jobId + 1), (int)skinnedMesh.vertices.size()); + for(; vertexId < lastVertexId; vertexId++) + { + SkinnedMesh::Vertex inVertex = skinnedMesh.vertices[vertexId]; + SimpleMesh::Vertex outVertex; + outVertex.position = physx::PxVec3(0.0f, 0.0f, 0.0f); + outVertex.normal = physx::PxVec3(0.0f, 0.0f, 0.0f); + outVertex.uv = inVertex.uv; + + for(int i = 0; i < 4; i++) + { + const physx::PxMat44 transform = boneTransformBoneOffest[inVertex.boneIndices[i]]; + outVertex.position += transform.transform(inVertex.position) * inVertex.boneWeights[i]; + outVertex.normal += transform.transform(physx::PxVec4(inVertex.normal, 0.0f)).getXYZ() * inVertex.boneWeights[i]; + } + outVertex.normal.normalizeFast(); + outMesh.vertices[vertexId] = outVertex; + } + }; + + if(jobManager != nullptr) + { + jobManager->ParallelLoop<jobCount>(jobFunction); + } + else + { + for(int i = 0; i < jobCount; i++) + jobFunction(i); + } + } +} + +void SkinnedModel::updateMeshesToBindPose() +{ + assert(mSubmeshes.size() == mModel->getTotalSubmeshInstanceCount()); + for(int submeshId = 0; submeshId < (int)mSubmeshes.size(); submeshId++) + { + Model::SubmeshInstance submeshInstance = mModel->getSubMeshInstance(submeshId); + SkinnedMesh const& skinnedMesh = mModel->getSubMesh(submeshInstance.mSubmeshId); + SimpleMesh& outMesh = mSubmeshes[submeshId]; + assert(mSubmeshes[submeshId].vertices.size() == skinnedMesh.vertices.size()); + + for(int vertexId = 0; vertexId < (int)skinnedMesh.vertices.size(); vertexId++) + { + SkinnedMesh::Vertex inVertex = skinnedMesh.vertices[vertexId]; + SimpleMesh::Vertex outVertex; + outVertex.position = inVertex.position; + outVertex.normal = inVertex.normal; + outVertex.uv = inVertex.uv; + + outMesh.vertices[vertexId] = outVertex; + } + } +}
\ No newline at end of file diff --git a/NvCloth/samples/SampleBase/renderer/Model.h b/NvCloth/samples/SampleBase/renderer/Model.h new file mode 100644 index 0000000..c98cfac --- /dev/null +++ b/NvCloth/samples/SampleBase/renderer/Model.h @@ -0,0 +1,421 @@ +/* +* Copyright (c) 2008-2017, NVIDIA CORPORATION. All rights reserved. +* +* NVIDIA CORPORATION and its licensors retain all intellectual property +* and proprietary rights in and to this software, related documentation +* and any modifications thereto. Any use, reproduction, disclosure or +* distribution of this software and related documentation without an express +* license agreement from NVIDIA CORPORATION is strictly prohibited. +*/ +#ifndef MODEL_H +#define MODEL_H + +#include <type_traits> +#include <vector> +#include <foundation/PxVec2.h> +#include <foundation/PxVec3.h> +#include <foundation/PxVec4.h> +#include <foundation/PxQuat.h> +#include <foundation/PxMat44.h> +#include <Mesh.h> +#include <string> +#include <map> +#include <assert.h> +#include <PxMath.h> +#include "renderer/Mesh.h" +#include "utils/JobManager.h" + +class RenderMaterial; +class Renderer; + +/** +Skinned mesh: position + normal + uv + 4 bone weights + 4 bone indices +*/ +class SkinnedMesh : public Mesh +{ +public: + + class Vertex + { + public: + physx::PxVec3 position; + physx::PxVec3 normal; + physx::PxVec2 uv; + physx::PxVec4 boneWeights; + uint32_t boneIndices[4]; //limited to 4 bones per vertex which should be enough for game assets + + static Vertex getDefaultVertex() + { + Vertex v; + v.position = physx::PxVec3(0.0f, 0.0f, 0.0f); + v.normal = physx::PxVec3(0.0f, 0.0f, 0.0f); + v.uv = physx::PxVec2(0.0f, 0.0f); + v.boneWeights = physx::PxVec4(0.0f, 0.0f, 0.0f, 0.0f); + v.boneIndices[0] = -1; + v.boneIndices[1] = -1; + v.boneIndices[2] = -1; + v.boneIndices[3] = -1; + return v; + } + }; + + virtual uint32_t getVertexStride() const { return sizeof(Vertex); } + + std::vector<Vertex> vertices; + std::vector<uint16_t> indices; + + physx::PxVec3 extents; + physx::PxVec3 center; + + //Transforms mesh space to bone space in bind pose + //This list is the same size and order as Model::mNodes + std::vector<physx::PxMat44> mBoneOffsets; + + int mMaterialId; + + SimpleMesh convertToSimpleMesh() const; +}; + +/** +Not used +*/ +template <typename T> +struct FixedFramerateKeyframeSequence +{ + T getFrameLerp(float frame) + { + int roundedFrame = frame; + float lerp = frame - (float)roundedFrame; + + return (1.0f - lerp)*getFrame(roundedFrame) + lerp*getFrame(roundedFrame + 1); + } + T getFrameSlerp(float frame) + { + int roundedFrame = frame; + float lerp = frame - (float)roundedFrame; + + T a = getFrame(roundedFrame); + T b = getFrame(roundedFrame + 1); + return (a*(1.0f - lerp) + b*lerp).getNormalized(); + } + T getFrame(int frame) + { + frame = physx::PxMin(physx::PxMax(0, frame), (int)mKeys.size()); + return *(mKeys.data() + frame); + } + std::vector<T> mKeys; +}; + +/** +Contains a list of keyframes sorted by time with quick get methods and interpolation +*/ +template <typename T> +struct VariableFramerateKeyframeSequence +{ + /// Get the linear interpolated keyframe at 'time' + /// frameGuess is a hint provided by the user to reduce the linear search time, + /// the hint will be overwritten with the last found keyframe + T getFrameLerp(float time, int& frameGuess) const + { + getFrameIndex(time, frameGuess); + Key a = mKeys[frameGuess]; + Key b = mKeys[physx::PxMin(frameGuess + 1, (int)mKeys.size() - 1)]; //Min for one shot anim, should be % for loop // TODO + + float div = (b.mTime - a.mTime); + float lerp = (time - a.mTime) / div; + if(div < 0.0000001) //hack to fix nans if a==b for one keyframe animations, or the last frame + lerp = 1.0f; + + return (a.mValue*(1.0f - lerp) + b.mValue*lerp); + } + + // Get the spherical linear interpolated keyframe at 'time' + T getFrameSlerp(float time, int& frameGuess) const + { + getFrameIndex(time, frameGuess); + Key a = mKeys[frameGuess]; + Key b = mKeys[physx::PxMin(frameGuess + 1, (int)mKeys.size() - 1)]; + + float dot = a.mValue.dot(b.mValue); + if(dot < 0.0f) + { + b.mValue = -b.mValue; + dot = -dot; + } + + float div = (b.mTime - a.mTime); + float lerp = (time - a.mTime) / div; + if(div < 0.0000001) //hack to fix nans if a==b for one keyframe animations, or the last frame + lerp = 1.0f; + + if(dot > 0.99f) + { + return (a.mValue*(1.0f - lerp) + b.mValue*lerp).getNormalized(); + } + + dot = physx::PxMin(physx::PxMax(-1.0f, dot),1.0f); + float theta_a = acosf(dot); + float theta = theta_a*lerp; + physx::PxQuat q = b.mValue - a.mValue*dot; + q.normalize(); + return a.mValue*cosf(theta) + q*sinf(theta); + } + // Get the frame index for the keyfrime at or before 'time' + void getFrameIndex(float time, int& frameGuess) const + { + //clamp to key range + frameGuess = physx::PxMin((int)mKeys.size() - 1, physx::PxMax(frameGuess, 0)); + + int begin = 0; + int last = (int)mKeys.size() - 1; + + //Loop forward until we are past 'time' + while((mKeys.data() + frameGuess)->mTime < time && frameGuess != last) + { + frameGuess++; + } + //Loop backwards until we are before 'time' + while((mKeys.data() + frameGuess)->mTime > time && frameGuess != begin) + { + frameGuess--; + } + } + + struct Key + { + float mTime; + T mValue; + }; + float mStartTime, mEndTime; + std::vector<Key> mKeys; +}; + +/** +BoneTimeline contains KeyframeSequences for position, rotation, and scale with easy access functions to get interpolated the interpolated bone matrix +*/ +struct BoneTimeline +{ + VariableFramerateKeyframeSequence<physx::PxVec3> mPositionKeys; + VariableFramerateKeyframeSequence<physx::PxQuat> mRotationKeys; + VariableFramerateKeyframeSequence<physx::PxVec3> mScaleKeys; + + //Helper to initialize a timeline with identity transforms + static BoneTimeline getDefaultTimeline() + { + BoneTimeline t; + t.mPositionKeys.mKeys.push_back({0.0f,physx::PxVec3(0.0f, 0.0f, 0.0f)}); + t.mRotationKeys.mKeys.push_back({0.0f,physx::PxQuat(1.0f,0.0f, 0.0f, 0.0f)}); + t.mScaleKeys.mKeys.push_back({0.0f,physx::PxVec3(1.0f, 1.0f, 1.0f)}); + + t.mPositionKeys.mKeys.push_back({1.0f,physx::PxVec3(0.0f, 0.0f, 0.0f)}); + t.mRotationKeys.mKeys.push_back({1.0f,physx::PxQuat(1.0f,0.0f, 0.0f, 0.0f)}); + t.mScaleKeys.mKeys.push_back({1.0f,physx::PxVec3(1.0f, 1.0f, 1.0f)}); + return t; + } + + /// Get the interpolated bone matrix at 'time' + /// The *FrameGuess arguments are hints provided by the user to reduce the linear search time, + /// the hints will be overwritten with the last found keyframe + physx::PxMat44 getBoneMatrix(float time, int& positionFrameGuess, int& rotationFrameGuess, int& scaleFrameGuess) const + { + physx::PxVec3 translation = mPositionKeys.getFrameLerp(time, positionFrameGuess); + physx::PxQuat rotation = mRotationKeys.getFrameSlerp(time, rotationFrameGuess); + physx::PxVec3 scale = mScaleKeys.getFrameLerp(time,scaleFrameGuess); + + return physx::PxMat44(physx::PxTransform(translation, rotation)) * physx::PxMat44(physx::PxVec4(scale, 1.0f)); + } +}; +static_assert(std::is_nothrow_move_constructible<BoneTimeline>::value, "moves are more expensive if this fails"); + +/** +Animation contains all BoneTimelines for a single animation +*/ +struct Animation +{ +public: + physx::PxMat44 getBoneMatrix(float time, int boneIndex, int& positionFrameGuess, int& rotationFrameGuess, int& scaleFrameGuess) const + { + //if(mLoop) //TODO + { + float unused; + time = modf(time/mDuration, &unused) * mDuration; + } + return mBoneTimelines[boneIndex].getBoneMatrix(time, positionFrameGuess, rotationFrameGuess, scaleFrameGuess); + } + + float mDuration; + bool mLoop; //Todo, implement different animation types for loop, oneshot etc. + + //This list is the same size and order as Model::mNodes + std::vector<BoneTimeline> mBoneTimelines; +}; +static_assert(std::is_nothrow_move_constructible<Animation>::value, "moves are more expensive if this fails"); + +struct aiNode; +struct ModelInstance; +class Renderable; + +/** +Model contains all the data needed to render and animate a model +*/ +class Model +{ +public: + Model() { mRenderer = nullptr; } + ~Model(); + + /// Load model from file (with animations) using assimp + void loadModel(const char* file); + void loadModel(std::string const file) { loadModel(file.c_str()); } + + void setRenderer(Renderer* renderer) {mRenderer = renderer;} + + /// Bone Node + struct Node + { + physx::PxMat44 mTransform; // TODO should not be needed + + //Id's are indices into mNodes (and similar lists like SkinnedMesh::mBoneOffsets) + std::vector<int> mChildNodeIds; + int mParrentNodeId; + std::vector<int> mMeshIds; + std::string mName; + }; + struct SubmeshInstance + { + int mSubmeshId; + int mParrentNodeId; + }; + + const Node& getNode(int id) { return mNodes[id]; } + int getNodeCount() const { return (int)mNodes.size(); } + const char* getNodeName(int id) { return mNodes[id].mName.c_str(); } + int getNodeIdByName(std::string name) { assert(mNodeNameMap.count(name)); return mNodeNameMap[name]; } + int getNodeIdByNameWithErrorCode(std::string name) { if(mNodeNameMap.count(name)==0) return -1; return mNodeNameMap[name]; } + int getAnimationIdByName(std::string name) { assert(mAnimationNameMap.count(name)); return mAnimationNameMap[name]; } + RenderMaterial* getRenderMaterial(int id) const { return mRenderMaterials[id]; } + int getRenderMaterialCount() const { return (int)mRenderMaterials.size(); } + + /// Sets renderable materials to match this model + /// renderable should not outlive model. + void setRenderableMaterials(Renderable* renderable); + + /// Updates the transforms of the nodes in 'instance' + void updateModelInstance(ModelInstance& instance, physx::PxMat44 transform = physx::PxMat44(physx::PxIdentity)) const; + + void updateCollisionSpheres(physx::PxVec4* spheres, uint32_t* sphereNodes, int sphereCount, physx::PxVec4* sphereOffsets, ModelInstance const& instance, physx::PxMat44& transform) const; + std::vector<uint32_t> getCollisionCapsules(int sphereRootNode) const; + + /// Calls lambda 'function(nodeId, parrentNodeId)' for all nodes except the root + template <typename T> + void traverseNodes(T function) + { + Node& node = mNodes[0]; + for(int i = 0; i < (int)node.mChildNodeIds.size(); i++) + { + function(node.mChildNodeIds[i], 0); + recuresTraverseNodes(function, node.mChildNodeIds[i]); + } + } + + const SkinnedMesh& getSubMesh(int id) const { return mSubmeshes[id]; } + int getSubMeshCount()const { return (int)mSubmeshes.size(); } + const SubmeshInstance& getSubMeshInstance(int id) const { return mSubmeshInstances[id]; } + int getTotalSubmeshInstanceCount()const { return (int)mSubmeshInstances.size(); } + + int getTotalVertexCount() const + { + int count = 0; + for(int i = 0; i < (int)mSubmeshes.size(); i++) + count += (int)getSubMesh(i).vertices.size(); + return count; + } + int getTotalIndexCount() const + { + int count = 0; + for(int i = 0; i < (int)mSubmeshes.size(); i++) + count += (int)getSubMesh(i).indices.size(); + return count; + } + +private: + /// recursive processing of assimp nodes while loading a model + int processNode(const aiNode& node, int parrent = -1, int depth = 0); + + /// recursive part of updateModelInstance() + void recurseModelInstanceNode(ModelInstance& instance, int nodeIndex, int depth = 1) const; + + /// recursive part of traverseNodes() + template <typename T> + void recuresTraverseNodes(T function, int nodeIndex) + { + Node& node = mNodes[nodeIndex]; + + for(int i = 0; i < (int)node.mChildNodeIds.size(); i++) + { + function(node.mChildNodeIds[i], node.mParrentNodeId); + recuresTraverseNodes(function, node.mChildNodeIds[i]); + } + } + + Renderer* mRenderer; + + std::vector<SkinnedMesh> mSubmeshes; + std::vector<Animation> mAnimations; + std::map<std::string, int> mAnimationNameMap; + std::vector<Node> mNodes; + std::map<std::string, int> mNodeNameMap; + std::vector<RenderMaterial*> mRenderMaterials; + + std::vector<SubmeshInstance> mSubmeshInstances; + + physx::PxMat44 mInverseRootTransform; +}; + +/** +ModelInstane keeps track of the node/bone transforms of a model + so the same Model can be used to render multiple instances with different animations +*/ +struct ModelInstance +{ + // Bone node + struct Node + { + physx::PxMat44 mTransform; //transformation from bone space to model space + int mPositionFrameGuess; + int mRotationFrameGuess; + int mScaleFrameGuess; + bool mHidden; + + Node() + { + mTransform = physx::PxMat44(physx::PxIdentity); + mPositionFrameGuess = 0; + mRotationFrameGuess = 0; + mScaleFrameGuess = 0; + mHidden = false; + } + }; + + //This list is the same size and order as Model::mNodes + std::vector<Node> mNodes; + std::vector<physx::PxMat44> mNodeTransormsWithBoneOffset; + float mAnimationTime; //current time inside the animation, advance this with dt each frame + int mAnimationIndex; //which animation we are running +}; + +/** +SkinnedModel is a container for vertex data resulting from software skinning +*/ +struct SkinnedModel +{ + std::vector<SimpleMesh> mSubmeshes; + ModelInstance mModelInstance; + Model* mModel; + + void initialize(Model * model); + void updateMeshes(JobManager* jobManager = nullptr); + void updateMeshesToBindPose(); +}; + +#endif
\ No newline at end of file diff --git a/NvCloth/samples/SampleBase/renderer/PrimitiveRenderMesh.cpp b/NvCloth/samples/SampleBase/renderer/PrimitiveRenderMesh.cpp index 3d324f8..0032865 100644 --- a/NvCloth/samples/SampleBase/renderer/PrimitiveRenderMesh.cpp +++ b/NvCloth/samples/SampleBase/renderer/PrimitiveRenderMesh.cpp @@ -23,7 +23,7 @@ PrimitiveRenderMesh::PrimitiveRenderMesh(const float v[], UINT numVertices) layout.push_back({ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }); layout.push_back({ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }); - initialize(v, numVertices, sizeof(float) * 6, layout, nullptr, 0); + initialize(v, numVertices, sizeof(float) * 6, layout, nullptr, 0, 0); } @@ -103,7 +103,7 @@ PlaneRenderMesh::PlaneRenderMesh() layout.push_back({ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }); layout.push_back({ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0 }); - initialize(planeVertices, sizeof(planeVertices) / (8 * sizeof(planeVertices[0])), sizeof(float) * 8, layout, nullptr, 0); + initialize(planeVertices, sizeof(planeVertices) / (8 * sizeof(planeVertices[0])), sizeof(float) * 8, layout, nullptr, 0, 0); } @@ -196,7 +196,7 @@ SphereRenderMesh::SphereRenderMesh() layout.push_back({ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }); layout.push_back({ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }); - initialize(vertices.data(), (uint32_t)vertices.size(), sizeof(SphereVertex), layout, g_sphereIndices, g_numSphereIndices); + initialize(vertices.data(), (uint32_t)vertices.size(), sizeof(SphereVertex), layout, g_sphereIndices, g_numSphereIndices, 0); } diff --git a/NvCloth/samples/SampleBase/renderer/Renderable.cpp b/NvCloth/samples/SampleBase/renderer/Renderable.cpp index 51a151d..c0560fd 100644 --- a/NvCloth/samples/SampleBase/renderer/Renderable.cpp +++ b/NvCloth/samples/SampleBase/renderer/Renderable.cpp @@ -14,35 +14,56 @@ const DirectX::XMFLOAT4 DEFAULT_COLOR(0.5f, 0.5f, 0.5f, 1.0f); -Renderable::Renderable(IRenderMesh& mesh, RenderMaterial& material) : m_mesh(mesh), m_scale(1, 1, 1), m_color(DEFAULT_COLOR), m_hidden(false), m_transform(PxIdentity) +Renderable::Renderable(IRenderMesh& mesh, RenderMaterial& material) : m_mesh(mesh), m_scale(1, 1, 1), m_hidden(false), m_transform(PxIdentity) { + setColor(DEFAULT_COLOR); setMaterial(material); } void Renderable::setMaterial(RenderMaterial& material) { - m_materialInstance = material.getMaterialInstance(&m_mesh); + m_materialInstances.clear(); + m_materialInstances.push_back(material.getMaterialInstance(&m_mesh)); +} + +void Renderable::addMaterial(RenderMaterial& material) +{ + m_materialInstances.push_back(material.getMaterialInstance(&m_mesh)); } void Renderable::render(Renderer& renderer, bool depthStencilOnly) const { - if (!m_materialInstance->isValid()) + for(int submeshId = 0; submeshId < m_mesh.getSubMeshCount(); submeshId++) { - PX_ALWAYS_ASSERT(); - return; - } + if(m_mesh.isRenderSubmeshHidden(submeshId)) + continue; + //physx::PxMat44 submeshTransform = m_mesh.getRenderSubmeshTransform(submeshId); - m_materialInstance->bind(*renderer.m_context, 0, depthStencilOnly); + auto& material = m_materialInstances[submeshId%getMaterialCount()]; + if(!material->isValid()) + { + PX_ALWAYS_ASSERT(); + return; + } - // setup object CB - { - D3D11_MAPPED_SUBRESOURCE mappedResource; - renderer.m_context->Map(renderer.m_objectCB, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource); - Renderer::CBObject* objectBuffer = (Renderer::CBObject*)mappedResource.pData; - objectBuffer->world = PxMat44ToXMMATRIX(getModelMatrix()); - objectBuffer->color = getColor(); - renderer.m_context->Unmap(renderer.m_objectCB, 0); - } + //skip transparent submeshes in the depthStensil pass + if(depthStencilOnly && !(material->getMaterial().getBlending() == RenderMaterial::BLEND_NONE)) + continue; + + material->bind(*renderer.m_context, 0, depthStencilOnly); - m_mesh.render(*renderer.m_context); + // setup object CB + { + D3D11_MAPPED_SUBRESOURCE mappedResource; + renderer.m_context->Map(renderer.m_objectCB, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource); + Renderer::CBObject* objectBuffer = (Renderer::CBObject*)mappedResource.pData; + objectBuffer->world = PxMat44ToXMMATRIX(getModelMatrix()); + objectBuffer->color = getColor(submeshId % getColorCount()); + objectBuffer->boneoffsetoffset = getBoneCount()*(1+submeshId); + objectBuffer->submesh = submeshId; + renderer.m_context->Unmap(renderer.m_objectCB, 0); + } + + m_mesh.render(*renderer.m_context, submeshId); + } } diff --git a/NvCloth/samples/SampleBase/renderer/Renderable.h b/NvCloth/samples/SampleBase/renderer/Renderable.h index da94144..2cd534d 100644 --- a/NvCloth/samples/SampleBase/renderer/Renderable.h +++ b/NvCloth/samples/SampleBase/renderer/Renderable.h @@ -27,14 +27,22 @@ RenderMesh interface, used by Renderable class IRenderMesh { public: + IRenderMesh(){ m_submeshCount = 1; } virtual ~IRenderMesh() {} virtual const std::vector<D3D11_INPUT_ELEMENT_DESC>& getInputElementDesc() const = 0; - virtual void render(ID3D11DeviceContext& context) const = 0; + virtual void render(ID3D11DeviceContext& context, int submesh) const = 0; + virtual physx::PxMat44 getRenderSubmeshTransform(int submesh) { return physx::PxMat44(physx::PxIdentity); } + virtual bool isRenderSubmeshHidden(int submesh) { return false; } + + virtual int getBoneCount() const { return 0; } + int getSubMeshCount() const { return m_submeshCount; } +protected: + int m_submeshCount; }; /** Renderable, represents single object renderer by Renderer. -Basically Renderable = RenderMaterial + RenderMesh +Basically Renderable = RenderMaterial(s) + RenderMesh(es) */ class Renderable { @@ -42,6 +50,10 @@ public: //////// public API //////// void setMaterial(RenderMaterial& material); + void clearMaterials() { m_materialInstances.clear(); } + void addMaterial(RenderMaterial& material); + RenderMaterial& getMaterial(int id) const { return m_materialInstances[id]->getMaterial(); } + int getMaterialCount() const { return (int)m_materialInstances.size(); } PxMat44 getModelMatrix() const { @@ -70,12 +82,17 @@ public: void setColor(DirectX::XMFLOAT4 color) { - m_color = color; + m_colors.clear(); + m_colors.push_back(color); } - DirectX::XMFLOAT4 getColor() const + void addColor(DirectX::XMFLOAT4 color) { - return m_color; + m_colors.push_back(color); } + DirectX::XMFLOAT4& getColor(int id) { return m_colors[id]; } + DirectX::XMFLOAT4 const& getColor(int id) const { return m_colors[id]; } + int getColorCount() const { return (int)m_colors.size(); } + int getBoneCount() const { return m_mesh.getBoneCount(); } void setHidden(bool hidden) { @@ -89,11 +106,14 @@ public: bool isTransparent() const { - return !(m_materialInstance->getMaterial().getBlending() == RenderMaterial::BLEND_NONE); + for(int i = 0; i < (int)m_materialInstances.size(); i++) + { + if(!(m_materialInstances[i]->getMaterial().getBlending() == RenderMaterial::BLEND_NONE)) + return true; + } + return false; } - RenderMaterial& getMaterial() const { return m_materialInstance->getMaterial(); } - private: //////// methods used by Renderer //////// @@ -116,11 +136,11 @@ private: //////// internal data //////// - DirectX::XMFLOAT4 m_color; + std::vector<DirectX::XMFLOAT4> m_colors; PxTransform m_transform; PxVec3 m_scale; - RenderMaterial::InstancePtr m_materialInstance; + std::vector<RenderMaterial::InstancePtr> m_materialInstances; IRenderMesh& m_mesh; bool m_hidden; }; diff --git a/NvCloth/samples/SampleBase/renderer/Renderer.cpp b/NvCloth/samples/SampleBase/renderer/Renderer.cpp index 48685d9..dffcebd 100644 --- a/NvCloth/samples/SampleBase/renderer/Renderer.cpp +++ b/NvCloth/samples/SampleBase/renderer/Renderer.cpp @@ -443,10 +443,55 @@ void Renderer::Render(ID3D11Device* /*device*/, ID3D11DeviceContext* ctx, ID3D11 ctx->RSSetViewports(1, &m_viewport); // render debug render buffers - while (m_queuedRenderBuffers.size() > 0) { - render(m_queuedRenderBuffers.back()); - m_queuedRenderBuffers.pop_back(); + float fAspectRatio = m_screenWidth / m_screenHeight; + + // Hack projection matrix to add a depth bias to all render buffers + // This might not be the behavior we want if we use the render buffers for other purposes than + // debug lines, but it works for now. + // Fill Camera constant buffer + { + float depthOffset = 0.005f; + // Temporarily change camera state so we can use the same projection matrix calculation. + // We don't want to mess with the DXUT library right now, but it would be cleaner to have support for this from m_camera + m_camera.SetProjParams(DirectX::XM_PIDIV4, fAspectRatio, CAMERA_CLIP_NEAR + depthOffset, CAMERA_CLIP_FAR + depthOffset); + + // copied from the beginning of Renderer::Render() + // needed matrices + DirectX::XMMATRIX viewMatrix = m_camera.GetViewMatrix(); + DirectX::XMMATRIX projMatrix = m_camera.GetProjMatrix(); + DirectX::XMMATRIX projMatrixInv = DirectX::XMMatrixInverse(NULL, projMatrix); + DirectX::XMMATRIX viewProjMatrix = viewMatrix * projMatrix; + + //same as // Opaque render + D3D11_MAPPED_SUBRESOURCE mappedResource; + ctx->Map(m_cameraCB, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource); + CBCamera* cameraBuffer = (CBCamera*)mappedResource.pData; + cameraBuffer->viewProjection = viewProjMatrix; + cameraBuffer->projectionInv = projMatrixInv; + DirectX::XMStoreFloat3(&(cameraBuffer->viewPos), m_camera.GetEyePt()); + ctx->Unmap(m_cameraCB, 0); + } + + while(m_queuedRenderBuffers.size() > 0) + { + render(m_queuedRenderBuffers.back()); + m_queuedRenderBuffers.pop_back(); + } + + //reset camera state + m_camera.SetProjParams(DirectX::XM_PIDIV4, fAspectRatio, CAMERA_CLIP_NEAR, CAMERA_CLIP_FAR); + + // Fill Camera constant buffer (make sure that the depth bias is reset) + { + D3D11_MAPPED_SUBRESOURCE mappedResource; + ctx->Map(m_cameraCB, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource); + CBCamera* cameraBuffer = (CBCamera*)mappedResource.pData; + cameraBuffer->viewProjection = viewProjMatrix; + cameraBuffer->projectionInv = projMatrixInv; + DirectX::XMStoreFloat3(&(cameraBuffer->viewPos), m_camera.GetEyePt()); + ctx->Unmap(m_cameraCB, 0); + } } // Transparency render @@ -680,7 +725,8 @@ void Renderer::reloadShaders() std::set<RenderMaterial*> materials; for (auto it = m_renderables.begin(); it != m_renderables.end(); it++) { - materials.emplace(&((*it)->getMaterial())); + for(int i = 0; i<(*it)->getMaterialCount(); i++) + materials.emplace(&((*it)->getMaterial(i))); } for (std::set<RenderMaterial*>::iterator it = materials.begin(); it != materials.end(); it++) { diff --git a/NvCloth/samples/SampleBase/renderer/Renderer.h b/NvCloth/samples/SampleBase/renderer/Renderer.h index 3dc8e56..131d577 100644 --- a/NvCloth/samples/SampleBase/renderer/Renderer.h +++ b/NvCloth/samples/SampleBase/renderer/Renderer.h @@ -178,6 +178,8 @@ class Renderer : public ISampleController { DirectX::XMMATRIX world; DirectX::XMFLOAT4 color; + uint32_t boneoffsetoffset; + uint32_t submesh; }; diff --git a/NvCloth/samples/SampleBase/renderer/SkinnedRenderMesh.cpp b/NvCloth/samples/SampleBase/renderer/SkinnedRenderMesh.cpp index 9f0b022..deb518d 100644 --- a/NvCloth/samples/SampleBase/renderer/SkinnedRenderMesh.cpp +++ b/NvCloth/samples/SampleBase/renderer/SkinnedRenderMesh.cpp @@ -200,7 +200,7 @@ void SkinnedRenderMesh::updateVisibleMeshTransforms(std::vector<PxMat44>& transf } } -void SkinnedRenderMesh::render(ID3D11DeviceContext& context) const +void SkinnedRenderMesh::render(ID3D11DeviceContext& context, int submesh) const { context.IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); diff --git a/NvCloth/samples/SampleBase/renderer/SkinnedRenderMesh.h b/NvCloth/samples/SampleBase/renderer/SkinnedRenderMesh.h index cc30e89..a228e9d 100644 --- a/NvCloth/samples/SampleBase/renderer/SkinnedRenderMesh.h +++ b/NvCloth/samples/SampleBase/renderer/SkinnedRenderMesh.h @@ -47,7 +47,7 @@ public: //////// IRenderMesh implementation //////// virtual const std::vector<D3D11_INPUT_ELEMENT_DESC>& getInputElementDesc() const { return m_inputDesc; } - virtual void render(ID3D11DeviceContext& context) const; + virtual void render(ID3D11DeviceContext& context, int submesh) const; private: //////// internal data //////// diff --git a/NvCloth/samples/SampleBase/renderer/WeightedSkinRenderMesh.cpp b/NvCloth/samples/SampleBase/renderer/WeightedSkinRenderMesh.cpp new file mode 100644 index 0000000..b0fc4f1 --- /dev/null +++ b/NvCloth/samples/SampleBase/renderer/WeightedSkinRenderMesh.cpp @@ -0,0 +1,219 @@ +/* +* Copyright (c) 2008-2017, 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 "WeightedSkinRenderMesh.h" +#include "Renderer.h" +#include "Model.h" + +WeightedSkinRenderMesh::WeightedSkinRenderMesh(const Model* model) +{ + PX_ASSERT_WITH_MESSAGE(meshes.size() <= MeshesCountMax, "meshes.size() have to be <= WeightedSkinRenderMesh::MeshesCountMax"); + + m_model = model; + m_modelInstance.mAnimationIndex = 0; + m_modelInstance.mAnimationTime = 0.0f; + + m_device = GetDeviceManager()->GetDevice(); + + // input element desc setup + m_inputDesc.push_back({ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }); + m_inputDesc.push_back({ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }); + m_inputDesc.push_back({ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0 }); //uv + m_inputDesc.push_back({ "TEXCOORD", 1, DXGI_FORMAT_R32G32B32A32_UINT, 0, 48, D3D11_INPUT_PER_VERTEX_DATA, 0 }); //bone Indices + m_inputDesc.push_back({ "TEXCOORD", 2, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 32, D3D11_INPUT_PER_VERTEX_DATA, 0}); //bone weights + + ID3D11DeviceContext* context; + m_device->GetImmediateContext(&context); + + // reserve VB + uint32_t verticesTotal = model->getTotalVertexCount(); + std::vector<SkinnedMesh::Vertex> vertexBuffer; + vertexBuffer.reserve(verticesTotal); + + // reserve IB + uint32_t indicesTotal = model->getTotalIndexCount(); + m_indices.reserve(indicesTotal); + + // fill VB, IB, MeshInfo + m_meshesInfo.resize(model->getSubMeshCount()); + m_submeshCount = model->getTotalSubmeshInstanceCount(); + for (int meshIndex = 0; meshIndex < m_submeshCount; ++meshIndex) + { + const SkinnedMesh* mesh = &model->getSubMesh(meshIndex); + MeshInfo& meshInfo = m_meshesInfo[meshIndex]; + + meshInfo.firstVertex = (uint32_t)vertexBuffer.size(); + vertexBuffer.insert(vertexBuffer.end(), mesh->vertices.begin(), mesh->vertices.end()); + meshInfo.verticesCount = (uint32_t)mesh->vertices.size(); + + meshInfo.firstIndex = (uint32_t)m_indices.size(); + uint32_t indexOffset = meshInfo.firstVertex; + for (uint32_t index : mesh->indices) + { + m_indices.push_back((uint32_t)index); + } + meshInfo.indicesCount = (uint32_t)mesh->indices.size(); + } + + // vertex buffer + { + D3D11_SUBRESOURCE_DATA vertexBufferData; + + ZeroMemory(&vertexBufferData, sizeof(vertexBufferData)); + vertexBufferData.pSysMem = vertexBuffer.data(); + + D3D11_BUFFER_DESC bufferDesc; + + memset(&bufferDesc, 0, sizeof(D3D11_BUFFER_DESC)); + bufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; + bufferDesc.ByteWidth = (uint32_t)(sizeof(SkinnedMesh::Vertex) * vertexBuffer.size()); + bufferDesc.CPUAccessFlags = 0; + bufferDesc.MiscFlags = 0; + bufferDesc.Usage = D3D11_USAGE_IMMUTABLE; + + V(m_device->CreateBuffer(&bufferDesc, &vertexBufferData, &m_vertexBuffer)); + } + + // index buffer + { + D3D11_BUFFER_DESC bufferDesc; + + memset(&bufferDesc, 0, sizeof(D3D11_BUFFER_DESC)); + bufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER; + bufferDesc.ByteWidth = (uint32_t)(sizeof(uint32_t) * m_indices.size()); + bufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + bufferDesc.MiscFlags = 0; + bufferDesc.Usage = D3D11_USAGE_DYNAMIC; + + V(m_device->CreateBuffer(&bufferDesc, nullptr, &m_indexBuffer)); + } + + // bone texture + { + D3D11_TEXTURE2D_DESC desc; + ZeroMemory(&desc, sizeof(desc)); + desc.Width = 4; + desc.Height = (uint32_t)model->getNodeCount()*(1+model->getSubMeshCount()); //transforms and bind pose transforms + desc.MipLevels = 1; + desc.ArraySize = 1; + desc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT; + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + desc.Usage = D3D11_USAGE_DYNAMIC; + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + + V(m_device->CreateTexture2D(&desc, nullptr, &m_boneTexture)); + } + + // bone texture SRV + { + D3D11_SHADER_RESOURCE_VIEW_DESC desc; + ZeroMemory(&desc, sizeof(desc)); + desc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT; + desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + desc.Texture2D.MipLevels = 1; + desc.Texture2D.MostDetailedMip = 0; + V(m_device->CreateShaderResourceView(m_boneTexture, &desc, &m_boneTextureSRV)); + } + + // update index buffer + { + D3D11_MAPPED_SUBRESOURCE mappedRead; + V(context->Map(m_indexBuffer, 0, D3D11_MAP_WRITE_DISCARD, NULL, &mappedRead)); + + uint32_t* indexBuffer = (uint32_t*)mappedRead.pData; + uint32_t indexCount = 0; + for(const MeshInfo& info : m_meshesInfo) + { + memcpy(indexBuffer + indexCount, &m_indices[info.firstIndex], info.indicesCount * sizeof(uint32_t)); + indexCount += info.indicesCount; + } + context->Unmap(m_indexBuffer, 0); + m_indexCount = indexCount; + PX_ASSERT(m_indexCount % 3 == 0); + } +} + +WeightedSkinRenderMesh::~WeightedSkinRenderMesh() +{ + SAFE_RELEASE(m_vertexBuffer); + SAFE_RELEASE(m_indexBuffer); + SAFE_RELEASE(m_boneTexture); + SAFE_RELEASE(m_boneTextureSRV); +} + +void WeightedSkinRenderMesh::updateVisibleMeshTransforms(float deltatime, physx::PxMat44 transform) +{ + m_modelInstance.mAnimationTime += deltatime; + m_model->updateModelInstance(m_modelInstance, transform); + + + ID3D11DeviceContext* context; + m_device->GetImmediateContext(&context); + + // update bone transform texture + { + D3D11_MAPPED_SUBRESOURCE mappedRead; + V(context->Map(m_boneTexture, 0, D3D11_MAP_WRITE_DISCARD, NULL, &mappedRead)); + for (uint32_t i = 0; i < m_modelInstance.mNodes.size(); ++i) + { + std::memcpy((uint8_t*)mappedRead.pData + i * mappedRead.RowPitch, &m_modelInstance.mNodes[i].mTransform, sizeof(PxMat44)); + } + + //probably should separate this in to a different texture as it doesn't change + for(int submeshId = 0; submeshId < m_model->getSubMeshCount(); submeshId++) + { + auto& submesh = m_model->getSubMesh(submeshId); + int offset = (1 + submeshId) * (int)m_modelInstance.mNodes.size(); + for(int i = 0; i < m_model->getNodeCount(); ++i) + { + std::memcpy((uint8_t*)mappedRead.pData + (i+offset) * mappedRead.RowPitch, &submesh.mBoneOffsets[i], sizeof(PxMat44)); + } + } + context->Unmap(m_boneTexture, 0); + } +} + +int WeightedSkinRenderMesh::getBoneCount() const +{ + return (int)m_modelInstance.mNodes.size(); +} + +void WeightedSkinRenderMesh::render(ID3D11DeviceContext& context, int submesh) const +{ + Model::SubmeshInstance submeshInstance = m_model->getSubMeshInstance(submesh); + + context.IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + + UINT strides[1] = { sizeof(SkinnedMesh::Vertex)}; + UINT offsets[1] = { 0 }; + ID3D11Buffer* buffers[1] = { m_vertexBuffer }; + context.IASetVertexBuffers(0, 1, buffers, strides, offsets); + + context.IASetIndexBuffer(m_indexBuffer, DXGI_FORMAT_R32_UINT, 0); + + context.VSSetShaderResources(1, 1, &m_boneTextureSRV); + + context.DrawIndexed(m_meshesInfo[submeshInstance.mSubmeshId].indicesCount, m_meshesInfo[submeshInstance.mSubmeshId].firstIndex, m_meshesInfo[submeshInstance.mSubmeshId].firstVertex); +} + +physx::PxMat44 WeightedSkinRenderMesh::getRenderSubmeshTransform(int submesh) +{ + Model::SubmeshInstance submeshInstance = m_model->getSubMeshInstance(submesh); + return m_modelInstance.mNodes[submeshInstance.mParrentNodeId].mTransform; +} + +bool WeightedSkinRenderMesh::isRenderSubmeshHidden(int submesh) +{ + Model::SubmeshInstance submeshInstance = m_model->getSubMeshInstance(submesh); + return m_modelInstance.mNodes[submeshInstance.mParrentNodeId].mHidden; +}
\ No newline at end of file diff --git a/NvCloth/samples/SampleBase/renderer/WeightedSkinRenderMesh.h b/NvCloth/samples/SampleBase/renderer/WeightedSkinRenderMesh.h new file mode 100644 index 0000000..014e28c --- /dev/null +++ b/NvCloth/samples/SampleBase/renderer/WeightedSkinRenderMesh.h @@ -0,0 +1,96 @@ +/* +* Copyright (c) 2008-2017, NVIDIA CORPORATION. All rights reserved. +* +* NVIDIA CORPORATION and its licensors retain all intellectual property +* and proprietary rights in and to this software, related documentation +* and any modifications thereto. Any use, reproduction, disclosure or +* distribution of this software and related documentation without an express +* license agreement from NVIDIA CORPORATION is strictly prohibited. +*/ + +#ifndef SKINNED_RENDER_MESH_H +#define SKINNED_RENDER_MESH_H + +#include "Utils.h" +#include <DirectXMath.h> + +#include <vector> +#include "Renderable.h" +#include "Mesh.h" +#include "Model.h" + +class Model; +struct ModelInstance; +/** +WeightedSkinRenderMesh: + bone indices are passed as vertex input, + bone transforms are stored in texture + max bone meshes count: WeightedSkinRenderMesh::MeshesCountMax +*/ +class WeightedSkinRenderMesh : public IRenderMesh +{ +public: + //////// ctor //////// + + WeightedSkinRenderMesh(const Model* model); + ~WeightedSkinRenderMesh(); + + + //////// const //////// + + static const uint32_t MeshesCountMax = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION; + + + //////// public API //////// + + //void updateVisibleMeshes(const std::vector<uint32_t>& visibleMeshes); + void updateVisibleMeshTransforms(float deltatime, physx::PxMat44 transform = physx::PxMat44(physx::PxIdentity)); + + int getBoneCount() const override; + + + //////// IRenderMesh implementation //////// + + virtual const std::vector<D3D11_INPUT_ELEMENT_DESC>& getInputElementDesc() const { return m_inputDesc; } + virtual void render(ID3D11DeviceContext& context, int submesh) const; + virtual physx::PxMat44 getRenderSubmeshTransform(int submesh); + virtual bool isRenderSubmeshHidden(int submesh); + + ModelInstance const * getModelInstance() const { return &m_modelInstance; } + ModelInstance * getModelInstance() { return &m_modelInstance; } + +private: + //////// internal data //////// + + struct MeshInfo + { + uint32_t firstIndex; + uint32_t indicesCount; + + uint32_t firstVertex; + uint32_t verticesCount; + }; + + std::vector<D3D11_INPUT_ELEMENT_DESC> m_inputDesc; + + ID3D11Device* m_device; + + ID3D11Buffer* m_vertexBuffer; + //ID3D11Buffer* m_boneIndexBuffer; + ID3D11Buffer* m_indexBuffer; + ID3D11Texture2D* m_boneTexture; + ID3D11ShaderResourceView* m_boneTextureSRV; + + uint32_t m_indexCount; + + std::vector<MeshInfo> m_meshesInfo; + std::vector<uint32_t> m_indices; + + + const Model* m_model; //we do not own this + ModelInstance m_modelInstance; +}; + + + +#endif //SKINNED_RENDER_MESH_H
\ No newline at end of file diff --git a/NvCloth/samples/SampleBase/scene/Scene.cpp b/NvCloth/samples/SampleBase/scene/Scene.cpp index 00e23ae..183b146 100644 --- a/NvCloth/samples/SampleBase/scene/Scene.cpp +++ b/NvCloth/samples/SampleBase/scene/Scene.cpp @@ -19,7 +19,8 @@ #include <foundation/PxVec2.h> std::vector<SceneFactory> Scene::sSceneFactories; -unsigned int Scene::mDebugVisualizationFlags; +unsigned int Scene::mDebugVisualizationFlags = 0; +bool Scene::mDebugVisualizationUpdateRequested = true; Scene::SceneDebugRenderParams Scene::sSceneDebugRenderParams; Scene::~Scene() @@ -78,6 +79,13 @@ void Scene::UpdateParticleDragging(float dt) tmp = physx::PxMat44((float*)&tmp2.r); physx::PxMat44 invMatrix = tmp; + tmp = modelMatrix; + tmp2 = DirectX::XMMatrixInverse(nullptr, DirectX::XMMATRIX(tmp.front())); + tmp = physx::PxMat44((float*)&tmp2.r); + physx::PxMat44 invModelMatrix = tmp; + + float clothScale = modelMatrix.transform(physx::PxVec4(1.0f, 1.0f, 1.0f, 0)).magnitude()/ 1.732; + physx::PxVec3 particleWorld = modelMatrix.transform(particles[mDraggingParticle.mParticleIndex].getXYZ()); physx::PxVec4 mousePointWorldT = invMatrix.transform(physx::PxVec4(mousePoint.x, mousePoint.y, 1.0, 1.0)); physx::PxVec3 mousePointWorld = mousePointWorldT.getXYZ() / mousePointWorldT.w; @@ -91,14 +99,20 @@ void Scene::UpdateParticleDragging(float dt) if(offset.magnitudeSquared() > 2.5f*2.5f) offset = offset.getNormalized()*2.5f; + offset = invModelMatrix.transform(physx::PxVec4(offset,0.0f)).getXYZ(); + for(int i = 0; i < (int)particles.size(); i++) { - physx::PxVec4 p = particles[i]; + physx::PxVec4 p = modelMatrix.transform(particles[i]); float dist = (p.getXYZ() - particleWorld).magnitude(); if(p.w > 0.0f) //Only move dynamic points { - float weight = max(0.0,min(1.0,0.4-dist)); + const float softSelectionRadius = 0.4f; + const float maxWeight = 0.4f; + float weight = max(0.0,min(1.0,1.0f-(dist/softSelectionRadius)))*maxWeight; + if(weight <= 0.0f) + continue; physx::PxVec3 point0(prevParticles[i].x, prevParticles[i].y, prevParticles[i].z); point0 = point0 - weight*offset; point0 = point0*0.99f + p.getXYZ()*0.01f; @@ -112,6 +126,44 @@ void Scene::UpdateParticleDragging(float dt) bool Scene::HandleEvent(UINT uMsg, WPARAM wParam, LPARAM lParam) { + if (uMsg == WM_KEYDOWN) + { + int iKeyPressed = static_cast<int>(wParam); + + switch (iKeyPressed) + { + case 'T': + mDebugVisualizationFlags ^= DEBUG_VIS_TETHERS; + break; + case 'C': + mDebugVisualizationFlags ^= DEBUG_VIS_CONSTRAINTS; + break; + case 'F': + mDebugVisualizationFlags ^= DEBUG_VIS_CONSTRAINTS_STIFFNESS; + break; + case 'R': + mDebugVisualizationFlags ^= DEBUG_VIS_CONSTRAINT_ERROR; + break; + case 'X': + mDebugVisualizationFlags ^= DEBUG_VIS_BOUNDING_BOX; + break; + case 'Z': + mDebugVisualizationFlags ^= DEBUG_VIS_DISTANCE_CONSTRAINTS; + break; + case 'L': + mDebugVisualizationFlags ^= DEBUG_VIS_POSITION_DELTA; + break; + case 'N': + mDebugVisualizationFlags ^= DEBUG_VIS_NORMALS; + break; + default: + return false; + } + + mDebugVisualizationUpdateRequested = true; + return true; + } + auto camera = mSceneController->getRenderer().getCamera(); auto m1 = camera.GetViewMatrix(); auto m2 = camera.GetProjMatrix(); @@ -183,6 +235,7 @@ bool Scene::HandlePickingEvent(UINT uMsg, WPARAM wParam, LPARAM lParam, physx::P { physx::PxVec4 p = particles[i]; physx::PxVec4 point(p.x, p.y, p.z, 1.0f); + point = modelMatrix.transform(point); float dist = mouseRayDir.dot(point.getXYZ() - mouseRayStart); float offset = (point.getXYZ() - (dist*mouseRayDir + mouseRayStart)).magnitude(); @@ -271,6 +324,38 @@ void Scene::drawUI() cloth->setLinearInertia(physx::PxVec3(f, f, f)); } { + physx::PxVec3 f3 = cloth->getAngularInertia(); + if(ImGui::DragFloat3("Angular Inertia", &f3.x, 0.02f, 0.0f, 1.0f, "%.2f")) + cloth->setAngularInertia(f3); + float f = f3.maxElement(); + if(ImGui::DragFloat("Angular Inertia xyz", &f, 0.02f, 0.0f, 1.0f, "%.2f")) + cloth->setAngularInertia(physx::PxVec3(f, f, f)); + } + { + physx::PxVec3 f3 = cloth->getCentrifugalInertia(); + if(ImGui::DragFloat3("Centrifugal Inertia", &f3.x, 0.02f, 0.0f, 1.0f, "%.2f")) + cloth->setCentrifugalInertia(f3); + float f = f3.maxElement(); + if(ImGui::DragFloat("Centrifugal Inertia xyz", &f, 0.02f, 0.0f, 1.0f, "%.2f")) + cloth->setCentrifugalInertia(physx::PxVec3(f, f, f)); + } + { + physx::PxVec3 f3 = cloth->getLinearDrag(); + if(ImGui::DragFloat3("Linear Drag", &f3.x, 0.02f, 0.0f, 1.0f, "%.2f")) + cloth->setLinearDrag(f3); + float f = f3.maxElement(); + if(ImGui::DragFloat("Linear Drag xyz", &f, 0.02f, 0.0f, 1.0f, "%.2f")) + cloth->setLinearDrag(physx::PxVec3(f, f, f)); + } + { + physx::PxVec3 f3 = cloth->getAngularDrag(); + if(ImGui::DragFloat3("Angular Drag", &f3.x, 0.02f, 0.0f, 1.0f, "%.2f")) + cloth->setAngularDrag(f3); + float f = f3.maxElement(); + if(ImGui::DragFloat("Angular Drag xyz", &f, 0.02f, 0.0f, 1.0f, "%.2f")) + cloth->setAngularDrag(physx::PxVec3(f, f, f)); + } + { float f = cloth->getMotionConstraintScale(); if(ImGui::DragFloat("Motion Constraint Scale", &f, 0.08f, 0.0f, 4.0f, "%.2f")) cloth->setMotionConstraintScaleBias(f, cloth->getMotionConstraintBias()); @@ -325,21 +410,23 @@ void Scene::drawUI() if(ImGui::TreeNode("Debug Visualization")) { - ImGui::CheckboxFlags("Tethers", &mDebugVisualizationFlags, DEBUG_VIS_TETHERS); - ImGui::CheckboxFlags("Constraints", &mDebugVisualizationFlags, DEBUG_VIS_CONSTRAINTS); + auto old = mDebugVisualizationFlags; + ImGui::CheckboxFlags("Tethers (T)", &mDebugVisualizationFlags, DEBUG_VIS_TETHERS); + ImGui::CheckboxFlags("Constraints (C)", &mDebugVisualizationFlags, DEBUG_VIS_CONSTRAINTS); if(mDebugVisualizationFlags&DEBUG_VIS_CONSTRAINTS) { ImGui::DragInt("Start Constraint Phase Range", &sSceneDebugRenderParams.mVisiblePhaseRangeBegin, 0.05, 0, 30); ImGui::DragInt("End", &sSceneDebugRenderParams.mVisiblePhaseRangeEnd, 0.05, 0, 30); } - ImGui::CheckboxFlags("Constraint Stiffness", &mDebugVisualizationFlags, DEBUG_VIS_CONSTRAINTS_STIFFNESS); - - ImGui::CheckboxFlags("Constraint Error", &mDebugVisualizationFlags, DEBUG_VIS_CONSTRAINT_ERROR); - ImGui::CheckboxFlags("Position Delta", &mDebugVisualizationFlags, DEBUG_VIS_POSITION_DELTA); - ImGui::CheckboxFlags("Bounding Box", &mDebugVisualizationFlags, DEBUG_VIS_BOUNDING_BOX); - ImGui::CheckboxFlags("Distance Constraints", &mDebugVisualizationFlags, DEBUG_VIS_DISTANCE_CONSTRAINTS); - + ImGui::CheckboxFlags("Constraint Stiffness (F)", &mDebugVisualizationFlags, DEBUG_VIS_CONSTRAINTS_STIFFNESS); + ImGui::CheckboxFlags("Constraint Error (R)", &mDebugVisualizationFlags, DEBUG_VIS_CONSTRAINT_ERROR); + ImGui::CheckboxFlags("Position Delta (L)", &mDebugVisualizationFlags, DEBUG_VIS_POSITION_DELTA); + ImGui::CheckboxFlags("Bounding Box (X)", &mDebugVisualizationFlags, DEBUG_VIS_BOUNDING_BOX); + ImGui::CheckboxFlags("Distance Constraints (Z)", &mDebugVisualizationFlags, DEBUG_VIS_DISTANCE_CONSTRAINTS); ImGui::TreePop(); + + if(old != mDebugVisualizationFlags) + mDebugVisualizationUpdateRequested = true; } static int activeSolver = 0; @@ -395,7 +482,13 @@ void Scene::drawDebugVisualization() DebugRenderBoundingBox(); if(mDebugVisualizationFlags & DEBUG_VIS_DISTANCE_CONSTRAINTS) DebugRenderDistanceConstraints(); - + + mDebugVisualizationUpdateRequested = false; +} + +bool Scene::debugVisualizationUpdateRequested() const +{ + return mDebugVisualizationUpdateRequested; } namespace @@ -478,6 +571,11 @@ void Scene::untrackRenderable(Renderable* renderable) untrackT(mRenderableList, renderable); } +void Scene::trackCleanupCallback(std::function<void(void)> cleanupCallback) +{ + trackT(mCleanupCallbackList, cleanupCallback); +} + void Scene::autoDeinitialize() { //Remove all cloths from solvers @@ -518,6 +616,13 @@ void Scene::autoDeinitialize() mSceneController->getRenderer().removeRenderable(it); } mRenderableList.clear(); + + //Run all cleanup callbacks + for(auto it : mCleanupCallbackList) + { + it(); + } + mCleanupCallbackList.clear(); } void Scene::doSimulationStep(float dt) @@ -892,4 +997,4 @@ void Scene::DebugRenderBoundingBox() dbl->addLine(transform,c - dy + dz + dx,c + dy - dz - dx, 0x007777); dbl->addLine(transform,c - dy + dz - dx,c + dy - dz + dx, 0x007777); } -}
\ No newline at end of file +} diff --git a/NvCloth/samples/SampleBase/scene/Scene.h b/NvCloth/samples/SampleBase/scene/Scene.h index 4ef2bed..be8b5a1 100644 --- a/NvCloth/samples/SampleBase/scene/Scene.h +++ b/NvCloth/samples/SampleBase/scene/Scene.h @@ -15,6 +15,7 @@ #include <map> #include "scene/SceneController.h" #include "foundation/PxMat44.h" +#include <functional> namespace nv { namespace cloth @@ -44,13 +45,15 @@ public: Scene(SceneController* sceneController):mSceneController(sceneController) {} virtual ~Scene(); - virtual void Animate(double dt) { doSimulationStep(dt); drawDebugVisualization(); } + virtual void Animate(double dt) { doSimulationStep(dt); } void UpdateParticleDragging(float dt); virtual bool HandleEvent(UINT uMsg, WPARAM wParam, LPARAM lParam); bool HandlePickingEvent(UINT uMsg, WPARAM wParam, LPARAM lParam, physx::PxMat44 viewProjectionMatrix); virtual void drawUI(); + virtual bool drawSubScenes() { return false; } //returns true if scene needs to be reinitialized virtual void drawStatsUI() {} virtual void drawDebugVisualization(); + bool debugVisualizationUpdateRequested() const; virtual void onInitialize() = 0; virtual void onTerminate() { autoDeinitialize(); } @@ -82,6 +85,9 @@ protected: void trackRenderable(Renderable* renderMesh); void untrackRenderable(Renderable* renderMesh); + //add a callback that is run at the end of autoDeinitialize + void trackCleanupCallback(std::function<void(void)> cleanupCallback); + void autoDeinitialize(); @@ -111,6 +117,7 @@ private: std::vector<nv::cloth::Fabric*> mFabricList; std::map<ClothActor*, nv::cloth::Solver*> mClothSolverMap; std::vector<Renderable*> mRenderableList; + std::vector<std::function<void(void)>> mCleanupCallbackList; static std::vector<SceneFactory> sSceneFactories; @@ -131,6 +138,7 @@ private: }; static unsigned int mDebugVisualizationFlags; + static bool mDebugVisualizationUpdateRequested; struct SceneDebugRenderParams { diff --git a/NvCloth/samples/SampleBase/scene/SceneController.cpp b/NvCloth/samples/SampleBase/scene/SceneController.cpp index fa35d5d..2eedfe4 100644 --- a/NvCloth/samples/SampleBase/scene/SceneController.cpp +++ b/NvCloth/samples/SampleBase/scene/SceneController.cpp @@ -76,6 +76,8 @@ void SceneController::onSampleStart() mPhysXPrimitiveRenderMaterial = new RenderMaterial(getRenderer().getResourceManager(), "physx_primitive", ""); mPhysXPlaneRenderMaterial = new RenderMaterial(getRenderer().getResourceManager(), "physx_primitive_plane", ""); + mWeightedModelSkinRenderMaterial = new RenderMaterial(getRenderer().getResourceManager(), "weighted_model_skinned", ""); + /*{ IRenderMesh* mesh = getRenderer().getPrimitiveRenderMesh(PrimitiveRenderMeshType::Plane); @@ -184,13 +186,20 @@ void SceneController::changeScene(int index) mActiveScene = Scene::CreateScene(0, this); } mActiveScene->onInitialize(); + mDebugLineRenderBuffer->clear(); + mActiveScene->drawDebugVisualization(); } void SceneController::Animate(double dt) { if(mPaused && (mSingleStep <= 0)) { - //Re render debug lines from last frame + if(mActiveScene->debugVisualizationUpdateRequested()) + { + mDebugLineRenderBuffer->clear(); + mActiveScene->drawDebugVisualization(); + } + //else Re render debug lines from last frame getRenderer().queueRenderBuffer(mDebugLineRenderBuffer); return; } @@ -200,8 +209,6 @@ void SceneController::Animate(double dt) dt = 1.0 / 60.0; } - mDebugLineRenderBuffer->clear(); - mLeftOverTime += dt * mTimeScale; double simulationStep = 0.0; @@ -211,6 +218,8 @@ void SceneController::Animate(double dt) simulationStep = 1.0 / 60.0; mActiveScene->Animate(simulationStep); + mDebugLineRenderBuffer->clear(); + mActiveScene->drawDebugVisualization(); getRenderer().queueRenderBuffer(mDebugLineRenderBuffer); } @@ -267,6 +276,10 @@ void SceneController::drawUI() for(int i = 0; i < Scene::GetSceneCount(); i++) { pressed = pressed | ImGui::RadioButton(Scene::GetSceneName(i), &mActiveSceneIndex, i); + if(i == mActiveSceneIndex) + { + pressed = pressed | mActiveScene->drawSubScenes(); + } } if(pressed) changeScene(mActiveSceneIndex); diff --git a/NvCloth/samples/SampleBase/scene/SceneController.h b/NvCloth/samples/SampleBase/scene/SceneController.h index 9f4f428..d8042bb 100644 --- a/NvCloth/samples/SampleBase/scene/SceneController.h +++ b/NvCloth/samples/SampleBase/scene/SceneController.h @@ -92,6 +92,11 @@ public: return mPhysXPlaneRenderMaterial; } + RenderMaterial* getDefaultWeightedModelSkinMaterial() + { + return mWeightedModelSkinRenderMaterial; + } + DebugLineRenderBuffer* getDebugLineRenderBuffer() { return mDebugLineRenderBuffer; @@ -106,6 +111,7 @@ private: RenderMaterial* mPhysXPrimitiveRenderMaterial; RenderMaterial* mPhysXPlaneRenderMaterial; + RenderMaterial* mWeightedModelSkinRenderMaterial; Renderable* mPlane; std::vector<Renderable*> mBoxes; @@ -132,8 +138,7 @@ private: double mLeftOverTime; bool mPaused; - int mSingleStep; - + int mSingleStep; }; #endif
\ No newline at end of file diff --git a/NvCloth/samples/SampleBase/scene/scenes/CCDScene2.cpp b/NvCloth/samples/SampleBase/scene/scenes/CCDScene2.cpp new file mode 100644 index 0000000..ba50f1d --- /dev/null +++ b/NvCloth/samples/SampleBase/scene/scenes/CCDScene2.cpp @@ -0,0 +1,155 @@ +/* +* Copyright (c) 2008-2017, 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 "CCDScene2.h" +#include "scene/SceneController.h" +#include <NvClothExt/ClothFabricCooker.h> +#include "ClothMeshGenerator.h" +#include <NvCloth/Fabric.h> +#include <NvCloth/Solver.h> +#include <NvCloth/Cloth.h> +#include <NvCloth/Factory.h> +#include "Renderer.h" +#include "renderer/RenderUtils.h" +#include "windows.h" +#include "utils/MeshGenerator.h" + +DECLARE_SCENE_NAME(CCDScene2, "CCD Scene 2") + +void CCDScene2::Animate(double dt) +{ + static float time = 0.0f; + time += dt*1.5f; //19x + + physx::PxTransform invTranslation(-mOffset - physx::PxVec3(0.f, 10.f, -2.f)); + physx::PxTransform rotation(physx::PxQuat(cosf(time)*physx::PxHalfPi-physx::PxHalfPi,physx::PxVec3(0.0f,1.0f,0.0f))); + physx::PxTransform translation(mOffset + physx::PxVec3(0.f, 10.f, -2.f) + physx::PxVec3(0.0f,0.0f,10.0f*sinf(time))); + physx::PxTransform totalTransform = translation.transform(rotation.transform(invTranslation)); + + physx::PxVec4 spheres[2] = { + physx::PxVec4(totalTransform.transform(physx::PxVec3(-4.f,10.f,0.f) + mOffset),1.0), + physx::PxVec4(totalTransform.transform(physx::PxVec3( 4.f,10.f,0.f) + mOffset),0.5)}; + + mClothActor[0]->mCloth->setSpheres(nv::cloth::Range<physx::PxVec4>(spheres, spheres + 2), 0, mClothActor[0]->mCloth->getNumSpheres()); + + mCollisionMehs->setTransform(totalTransform); + + Scene::Animate(dt); +} + +void CCDScene2::initializeCloth(int index, physx::PxVec3 offset) +{ + mOffset = offset; + + /////////////////////////////////////////////////////////////////////// + ClothMeshData clothMesh; + + physx::PxMat44 transform = PxTransform(PxVec3(0.f, 13.f, 0.f)+ mOffset, PxQuat(0, PxVec3(1.f, 0.f, 0.f))); + clothMesh.GeneratePlaneCloth(5.f, 6.f, 69, 79, false, transform); + clothMesh.AttachClothPlaneByAngles(69, 79); + clothMesh.SetInvMasses(0.5f + (float)index * 2.0f); + + mClothActor[index] = new ClothActor; + nv::cloth::ClothMeshDesc meshDesc = clothMesh.GetClothMeshDesc(); + { + mClothActor[index]->mClothRenderMesh = new ClothRenderMesh(meshDesc); + mClothActor[index]->mClothRenderable = getSceneController()->getRenderer().createRenderable(*(static_cast<IRenderMesh*>(mClothActor[index]->mClothRenderMesh)), *getSceneController()->getDefaultMaterial()); + + float r, g, b; + r = index == 0 ? 1.0f : 0.3f; + g = index == 1 ? 1.0f : 0.3f; + b = index == 2 ? 1.0f : 0.3f; + + mClothActor[index]->mClothRenderable->setColor(DirectX::XMFLOAT4(r, g, b, 1.0f)); + } + + nv::cloth::Vector<int32_t>::Type phaseTypeInfo; + mFabric[index] = NvClothCookFabricFromMesh(getSceneController()->getFactory(), meshDesc, physx::PxVec3(0.0f, 0.0f, 1.0f), &phaseTypeInfo, false); + trackFabric(mFabric[index]); + + // Initialize start positions and masses for the actual cloth instance + // (note: the particle/vertex positions do not have to match the mesh description here. Set the positions to the initial shape of this cloth instance) + std::vector<physx::PxVec4> particlesCopy; + particlesCopy.resize(clothMesh.mVertices.size()); + + physx::PxVec3 clothOffset = transform.getPosition(); + for(int i = 0; i < (int)clothMesh.mVertices.size(); i++) + { + // To put attachment point closer to each other + if(clothMesh.mInvMasses[i] < 1e-6) + clothMesh.mVertices[i] = (clothMesh.mVertices[i] - clothOffset)*0.95f + clothOffset; + + particlesCopy[i] = physx::PxVec4(clothMesh.mVertices[i], clothMesh.mInvMasses[i]); // w component is 1/mass, or 0.0f for anchored/fixed particles + } + + // Create the cloth from the initial positions/masses and the fabric + mClothActor[index]->mCloth = getSceneController()->getFactory()->createCloth(nv::cloth::Range<physx::PxVec4>(&particlesCopy[0], &particlesCopy[0] + particlesCopy.size()), *mFabric[index]); + particlesCopy.clear(); particlesCopy.shrink_to_fit(); + + mClothActor[index]->mCloth->setGravity(physx::PxVec3(0.0f, -9.8f, 0.0f)); + mClothActor[index]->mCloth->setDamping(physx::PxVec3(0.1f, 0.1f, 0.1f)); + mClothActor[index]->mCloth->setSelfCollisionDistance(0.07f); + //mClothActor[index]->mCloth->enableContinuousCollision(true); + //mClothActor[index]->mCloth->setFriction(1.0f); + + + physx::PxVec4 spheres[2] = {physx::PxVec4(physx::PxVec3(-4.f,10.f,0.f) + mOffset,1.0), + physx::PxVec4(physx::PxVec3( 4.f,10.f,0.f) + mOffset,0.5)}; + + mClothActor[index]->mCloth->setSpheres(nv::cloth::Range<physx::PxVec4>(spheres, spheres + 2), 0, mClothActor[index]->mCloth->getNumSpheres()); + + uint32_t caps[4]; + caps[0] = 0; + caps[1] = 1; + + mClothActor[index]->mCloth->setCapsules(nv::cloth::Range<uint32_t>(caps, caps + 2), 0, mClothActor[index]->mCloth->getNumCapsules()); + + //create render mesh + auto mesh = MeshGenerator::generateCollisionCapsules(spheres,2,caps,2,-0.05f); + auto renderMesh = new MeshGenerator::MeshGeneratorRenderMesh(mesh); + mCollisionMehs = getSceneController()->getRenderer().createRenderable(*renderMesh, *getSceneController()->getDefaultMaterial()); + trackRenderable(mCollisionMehs); + + // Setup phase configs + std::vector<nv::cloth::PhaseConfig> phases(mFabric[index]->getNumPhases()); + for(int i = 0; i < (int)phases.size(); i++) + { + phases[i].mPhaseIndex = i; + phases[i].mStiffness = 1.0f; + phases[i].mStiffnessMultiplier = 1.0f; + phases[i].mCompressionLimit = 1.0f; + phases[i].mStretchLimit = 1.0f; + } + mClothActor[index]->mCloth->setPhaseConfig(nv::cloth::Range<nv::cloth::PhaseConfig>(&phases.front(), &phases.back())); + mClothActor[index]->mCloth->setDragCoefficient(0.5f); + mClothActor[index]->mCloth->setLiftCoefficient(0.6f); + mClothActor[index]->mCloth->setSolverFrequency(30); + + trackClothActor(mClothActor[index]); + + // Add the cloth to the solver for simulation + addClothToSolver(mClothActor[index], mSolver); +} + +void CCDScene2::onInitialize() +{ + mSolver = getSceneController()->getFactory()->createSolver(); + trackSolver(mSolver); + + initializeCloth(0, physx::PxVec3(0.0f, 0.0f, 0.0f)); + + { + IRenderMesh* mesh = getSceneController()->getRenderer().getPrimitiveRenderMesh(PrimitiveRenderMeshType::Plane); + Renderable* plane = getSceneController()->getRenderer().createRenderable(*mesh, *getSceneController()->getDefaultPlaneMaterial()); + plane->setTransform(PxTransform(PxVec3(0.f, 0.f, 0.f), PxQuat(PxPiDivTwo, PxVec3(0.f, 0.f, 1.f)))); + plane->setScale(PxVec3(1000.f)); + trackRenderable(plane); + } +} diff --git a/NvCloth/samples/SampleBase/scene/scenes/CCDScene2.h b/NvCloth/samples/SampleBase/scene/scenes/CCDScene2.h new file mode 100644 index 0000000..8c27a63 --- /dev/null +++ b/NvCloth/samples/SampleBase/scene/scenes/CCDScene2.h @@ -0,0 +1,37 @@ +/* +* Copyright (c) 2008-2017, NVIDIA CORPORATION. All rights reserved. +* +* NVIDIA CORPORATION and its licensors retain all intellectual property +* and proprietary rights in and to this software, related documentation +* and any modifications thereto. Any use, reproduction, disclosure or +* distribution of this software and related documentation without an express +* license agreement from NVIDIA CORPORATION is strictly prohibited. +*/ + +#ifndef CCD_SCENE_H +#define CCD_SCENE_H + +#include "scene/Scene.h" +#include <foundation/PxVec3.h> + +class CCDScene2 : public Scene +{ +public: + + CCDScene2(SceneController* sceneController): Scene(sceneController) {} + + virtual void Animate(double dt) override; + void initializeCloth(int index, physx::PxVec3 offset); + virtual void onInitialize() override; + +private: + nv::cloth::Fabric* mFabric[1]; + nv::cloth::Solver* mSolver; + ClothActor* mClothActor[1]; + + physx::PxVec3 mOffset; + Renderable* mCollisionMehs; +}; + + +#endif
\ No newline at end of file diff --git a/NvCloth/samples/SampleBase/scene/scenes/InterCollisionScene.cpp b/NvCloth/samples/SampleBase/scene/scenes/InterCollisionScene.cpp index 0dd1aa7..12ff7ce 100644 --- a/NvCloth/samples/SampleBase/scene/scenes/InterCollisionScene.cpp +++ b/NvCloth/samples/SampleBase/scene/scenes/InterCollisionScene.cpp @@ -117,6 +117,8 @@ void InterCollisionScene::onInitialize() initializeCloth(0, posTrans); posTrans.setPosition(physx::PxVec3(0.0f, 0.8f, -1.2f)); initializeCloth(1, posTrans); + posTrans.setPosition(physx::PxVec3(0.0f, 1.6f, -1.4f)); + initializeCloth(2, posTrans); { IRenderMesh* mesh = getSceneController()->getRenderer().getPrimitiveRenderMesh(PrimitiveRenderMeshType::Plane); diff --git a/NvCloth/samples/SampleBase/scene/scenes/InterCollisionScene.h b/NvCloth/samples/SampleBase/scene/scenes/InterCollisionScene.h index 1ef0b45..98ea3ef 100644 --- a/NvCloth/samples/SampleBase/scene/scenes/InterCollisionScene.h +++ b/NvCloth/samples/SampleBase/scene/scenes/InterCollisionScene.h @@ -24,9 +24,9 @@ public: virtual void onInitialize() override; private: - nv::cloth::Fabric* mFabric[2]; + nv::cloth::Fabric* mFabric[3]; nv::cloth::Solver* mSolver; - ClothActor* mClothActor[2]; + ClothActor* mClothActor[3]; }; diff --git a/NvCloth/samples/SampleBase/scene/scenes/SelfCollisionScene.cpp b/NvCloth/samples/SampleBase/scene/scenes/SelfCollisionScene.cpp index 89fed39..f2c368e 100644 --- a/NvCloth/samples/SampleBase/scene/scenes/SelfCollisionScene.cpp +++ b/NvCloth/samples/SampleBase/scene/scenes/SelfCollisionScene.cpp @@ -61,7 +61,7 @@ void SelfCollisionScene::initializeCloth(int index, physx::PxMat44 transform) particlesCopy.resize(clothMesh.mVertices.size()); physx::PxVec3 clothOffset = transform.getPosition(); - for(int i = 0; i < (int)clothMesh.mVertices.size(); i++) + for (int i = 0; i < (int)clothMesh.mVertices.size(); i++) { // To put attachment point closer to each other if(clothMesh.mInvMasses[i] < 1e-6) @@ -79,9 +79,10 @@ void SelfCollisionScene::initializeCloth(int index, physx::PxMat44 transform) nv::cloth::Range<const physx::PxVec4> planesR(&planes[0], &planes[0] + planes.size()); mClothActor[index]->mCloth->setPlanes(planesR, 0, mClothActor[index]->mCloth->getNumPlanes()); + std::vector<uint32_t> indices; indices.resize(planes.size()); - for(int i = 0; i < (int)indices.size(); i++) + for (int i = 0; i < (int)indices.size(); i++) indices[i] = 1 << i; nv::cloth::Range<uint32_t> cind(&indices[0], &indices[0] + indices.size()); mClothActor[index]->mCloth->setConvexes(cind, 0, mClothActor[index]->mCloth->getNumConvexes()); @@ -97,20 +98,21 @@ void SelfCollisionScene::initializeCloth(int index, physx::PxMat44 transform) std::vector<uint32_t> selfCollisionIndices; //only enable every other particle for self collision - for(int y = 0; y < 5*h + 1; y++) - for(int x = 0; x < 5 * w + 1; x++) - if((x & 1) ^ (y & 1)) selfCollisionIndices.push_back(x + y*(5 * w + 1)); + for (int y = 0; y < 5*h + 1; y++) + for (int x = 0; x < 5 * w + 1; x++) + if ((x & 1) ^ (y & 1)) selfCollisionIndices.push_back(x + y*(5 * w + 1)); - for(int y = 0; y < 5 * h2 + 1; y++) - for(int x = 0; x < 5 * w2 + 1; x++) - if((x & 1) ^ (y & 1)) selfCollisionIndices.push_back(FirstParticleIndexCloth2 + x + y*(5 * w2 + 1)); + for (int y = 0; y < 5 * h2 + 1; y++) + for (int x = 0; x < 5 * w2 + 1; x++) + if ((x & 1) ^ (y & 1)) + selfCollisionIndices.push_back(FirstParticleIndexCloth2 + x + y*(5 * w2 + 1)); nv::cloth::Range<uint32_t> selfCollisionIndicesRange (&selfCollisionIndices[0], &selfCollisionIndices[0] + selfCollisionIndices.size()); mClothActor[index]->mCloth->setSelfCollisionIndices(selfCollisionIndicesRange); // Setup phase configs std::vector<nv::cloth::PhaseConfig> phases(mFabric[index]->getNumPhases()); - for(int i = 0; i < (int)phases.size(); i++) + for (int i = 0; i < (int)phases.size(); i++) { phases[i].mPhaseIndex = i; phases[i].mStiffness = 1.0f; @@ -137,6 +139,7 @@ void SelfCollisionScene::onInitialize() return true; } );*/ + trackSolver(mSolver); physx::PxMat44 posTrans(physx::PxIdentity); diff --git a/NvCloth/samples/SampleBase/scene/scenes/TeleportScene.cpp b/NvCloth/samples/SampleBase/scene/scenes/TeleportScene.cpp new file mode 100644 index 0000000..ed4d044 --- /dev/null +++ b/NvCloth/samples/SampleBase/scene/scenes/TeleportScene.cpp @@ -0,0 +1,148 @@ +/* +* Copyright (c) 2008-2017, 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 "TeleportScene.h" +#include "scene/SceneController.h" +#include <NvClothExt/ClothFabricCooker.h> +#include "ClothMeshGenerator.h" +#include <NvCloth/Fabric.h> +#include <NvCloth/Solver.h> +#include <NvCloth/Cloth.h> +#include <NvCloth/Factory.h> +#include "Renderer.h" +#include "renderer/RenderUtils.h" +#include "windows.h" + +DECLARE_SCENE_NAME(TeleportScene, "Teleport Scene") + +void TeleportScene::Animate(double dt) +{ + mTime += dt; + if(mTime > 4.0f) + { + TeleportScene::teleport(); + } + physx::PxVec3 position(0.0f,0.0f, mTime*-25.0f); + physx::PxQuat rotation(mTime * physx::PxPi * 0.5f, physx::PxVec3(0.0f, 1.0f, 0.0f)); + + mClothActor[0]->mCloth->setTranslation(position); + mClothActor[0]->mCloth->setRotation(rotation); + + //Disable setTransform on the renderable so we can clearly see any jumps and discontinuities from the teleport + //mClothActor[0]->mClothRenderable->setTransform(physx::PxTransform(position + physx::PxVec3(-4.f, 0.f, 0.f), rotation)); + + Scene::Animate(dt); +} + +void TeleportScene::teleport() +{ + mTime = 0.0f; + physx::PxVec3 position(0.0f, 0.0f, mTime*-10.0f); + physx::PxQuat rotation(mTime, physx::PxVec3(0.0f, 1.0f, 0.0f)); + + //mClothActor[0]->mCloth->teleport(-mClothActor[0]->mCloth->getTranslation()); + mClothActor[0]->mCloth->teleportToLocation(position, rotation); + //mClothActor[0]->mCloth->clearInertia(); + mClothActor[0]->mCloth->ignoreVelocityDiscontinuity(); +} + +void TeleportScene::initializeCloth(int index, physx::PxVec3 offset) +{ + /////////////////////////////////////////////////////////////////////// + ClothMeshData clothMesh; + + physx::PxMat44 transform = PxTransform(PxVec3(0.f, 13.f, 0.f)+ offset, PxQuat(PxPi / 2.0f, PxVec3(1.f, 0.f, 0.f))); + clothMesh.GeneratePlaneCloth(5.f, 6.f, 39, 49, false, transform); + clothMesh.AttachClothPlaneByAngles(39, 49); + clothMesh.SetInvMasses(0.5f + (float)index * 2.0f); + + mClothActor[index] = new ClothActor; + nv::cloth::ClothMeshDesc meshDesc = clothMesh.GetClothMeshDesc(); + { + mClothActor[index]->mClothRenderMesh = new ClothRenderMesh(meshDesc); + mClothActor[index]->mClothRenderable = getSceneController()->getRenderer().createRenderable(*(static_cast<IRenderMesh*>(mClothActor[index]->mClothRenderMesh)), *getSceneController()->getDefaultMaterial()); + + float r, g, b; + r = index == 0 ? 1.0f : 0.3f; + g = index == 1 ? 1.0f : 0.3f; + b = index == 2 ? 1.0f : 0.3f; + + mClothActor[index]->mClothRenderable->setColor(DirectX::XMFLOAT4(r, g, b, 1.0f)); + } + + nv::cloth::Vector<int32_t>::Type phaseTypeInfo; + mFabric[index] = NvClothCookFabricFromMesh(getSceneController()->getFactory(), meshDesc, physx::PxVec3(0.0f, 0.0f, 1.0f), &phaseTypeInfo, false); + trackFabric(mFabric[index]); + + // Initialize start positions and masses for the actual cloth instance + // (note: the particle/vertex positions do not have to match the mesh description here. Set the positions to the initial shape of this cloth instance) + std::vector<physx::PxVec4> particlesCopy; + particlesCopy.resize(clothMesh.mVertices.size()); + + physx::PxVec3 clothOffset = transform.getPosition(); + for (int i = 0; i < (int)clothMesh.mVertices.size(); i++) + { + // To put attachment point closer to each other + if(clothMesh.mInvMasses[i] < 1e-6) + clothMesh.mVertices[i] = (clothMesh.mVertices[i] - clothOffset)*0.95f + clothOffset; + + particlesCopy[i] = physx::PxVec4(clothMesh.mVertices[i], clothMesh.mInvMasses[i]); // w component is 1/mass, or 0.0f for anchored/fixed particles + } + + if(index == 1) + { + mAttachmentVertices[0] = 0; + mAttachmentVertices[1] = 39; + mAttachmentVertexOriginalPositions[0] = particlesCopy[mAttachmentVertices[0]]; + mAttachmentVertexOriginalPositions[1] = particlesCopy[mAttachmentVertices[1]]; + } + + // Create the cloth from the initial positions/masses and the fabric + mClothActor[index]->mCloth = getSceneController()->getFactory()->createCloth(nv::cloth::Range<physx::PxVec4>(&particlesCopy[0], &particlesCopy[0] + particlesCopy.size()), *mFabric[index]); + particlesCopy.clear(); particlesCopy.shrink_to_fit(); + + mClothActor[index]->mCloth->setGravity(physx::PxVec3(0.0f, -9.8f, 0.0f)); + + // Setup phase configs + std::vector<nv::cloth::PhaseConfig> phases(mFabric[index]->getNumPhases()); + for (int i = 0; i < (int)phases.size(); i++) + { + phases[i].mPhaseIndex = i; + phases[i].mStiffness = 1.0f; + phases[i].mStiffnessMultiplier = 1.0f; + phases[i].mCompressionLimit = 1.0f; + phases[i].mStretchLimit = 1.0f; + } + mClothActor[index]->mCloth->setPhaseConfig(nv::cloth::Range<nv::cloth::PhaseConfig>(&phases.front(), &phases.back())); + mClothActor[index]->mCloth->setDragCoefficient(0.1f); + mClothActor[index]->mCloth->setLiftCoefficient(0.2f); + + mSolver[index] = getSceneController()->getFactory()->createSolver(); + trackSolver(mSolver[index]); + trackClothActor(mClothActor[index]); + + // Add the cloth to the solver for simulation + addClothToSolver(mClothActor[index], mSolver[index]); +} + +void TeleportScene::onInitialize() +{ + initializeCloth(0, physx::PxVec3(0.0f, 0.0f, 0.0f)); + + mTime = 0.0f; + + { + IRenderMesh* mesh = getSceneController()->getRenderer().getPrimitiveRenderMesh(PrimitiveRenderMeshType::Plane); + Renderable* plane = getSceneController()->getRenderer().createRenderable(*mesh, *getSceneController()->getDefaultPlaneMaterial()); + plane->setTransform(PxTransform(PxVec3(0.f, 0.f, 0.f), PxQuat(PxPiDivTwo, PxVec3(0.f, 0.f, 1.f)))); + plane->setScale(PxVec3(1000.f)); + trackRenderable(plane); + } +} diff --git a/NvCloth/samples/SampleBase/scene/scenes/TeleportScene.h b/NvCloth/samples/SampleBase/scene/scenes/TeleportScene.h new file mode 100644 index 0000000..668dfb0 --- /dev/null +++ b/NvCloth/samples/SampleBase/scene/scenes/TeleportScene.h @@ -0,0 +1,41 @@ +/* +* Copyright (c) 2008-2017, NVIDIA CORPORATION. All rights reserved. +* +* NVIDIA CORPORATION and its licensors retain all intellectual property +* and proprietary rights in and to this software, related documentation +* and any modifications thereto. Any use, reproduction, disclosure or +* distribution of this software and related documentation without an express +* license agreement from NVIDIA CORPORATION is strictly prohibited. +*/ + +#ifndef TELEPORT_SCENE_H +#define TELEPORT_SCENE_H + +#include "scene/Scene.h" +#include <foundation/PxVec3.h> + +class TeleportScene : public Scene +{ +public: + + TeleportScene(SceneController* sceneController): Scene(sceneController) {} + + virtual void Animate(double dt) override; + void initializeCloth(int index, physx::PxVec3 offset); + virtual void onInitialize() override; + void teleport(); + +private: + nv::cloth::Fabric* mFabric[1]; + nv::cloth::Solver* mSolver[1]; + ClothActor* mClothActor[1]; + + int mAttachmentVertices[1]; + physx::PxVec4 mAttachmentVertexOriginalPositions[1]; + + float mTime; + +}; + + +#endif
\ No newline at end of file diff --git a/NvCloth/samples/SampleBase/scene/scenes/TimeStepScene.cpp b/NvCloth/samples/SampleBase/scene/scenes/TimeStepScene.cpp new file mode 100644 index 0000000..82ac9fe --- /dev/null +++ b/NvCloth/samples/SampleBase/scene/scenes/TimeStepScene.cpp @@ -0,0 +1,114 @@ +/* +* Copyright (c) 2008-2017, 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 "TimeStepScene.h" +#include "scene/SceneController.h" +#include <NvClothExt/ClothFabricCooker.h> +#include "ClothMeshGenerator.h" +#include <NvCloth/Fabric.h> +#include <NvCloth/Solver.h> +#include <NvCloth/Cloth.h> +#include <NvCloth/Factory.h> +#include "Renderer.h" +#include "renderer/RenderUtils.h" + +DECLARE_SCENE_NAME(TimeStepScene, "Time Step Scene") + +void TimeStepScene::initializeCloth(int index, physx::PxVec3 offset) +{ + ClothMeshData clothMesh; + + physx::PxMat44 transform = PxTransform(PxVec3(0.f, 13.f, 0.f) + offset, PxQuat(PxPi / 2.f, PxVec3(1.f, 0.f, 0.f))); + clothMesh.GeneratePlaneCloth(4.f, 4.f, 1, 1, false, transform); + clothMesh.AttachClothPlaneByAngles(1, 1); + + mClothActor[index] = new ClothActor; + nv::cloth::ClothMeshDesc meshDesc = clothMesh.GetClothMeshDesc(); + { + mClothActor[index]->mClothRenderMesh = new ClothRenderMesh(meshDesc); + mClothActor[index]->mClothRenderable = getSceneController()->getRenderer().createRenderable(*(static_cast<IRenderMesh*>(mClothActor[index]->mClothRenderMesh)), *getSceneController()->getDefaultMaterial()); + mClothActor[index]->mClothRenderable->setColor(getRandomPastelColor()); + } + + nv::cloth::Vector<int32_t>::Type phaseTypeInfo; + mFabric[index] = NvClothCookFabricFromMesh(getSceneController()->getFactory(), meshDesc, physx::PxVec3(0.0f, 0.0f, 1.0f), &phaseTypeInfo, false); + trackFabric(mFabric[index]); + + // Initialize start positions and masses for the actual cloth instance + // (note: the particle/vertex positions do not have to match the mesh description here. Set the positions to the initial shape of this cloth instance) + std::vector<physx::PxVec4> particlesCopy; + particlesCopy.resize(clothMesh.mVertices.size()); + + physx::PxVec3 clothOffset = transform.getPosition(); + for (int i = 0; i < (int)clothMesh.mVertices.size(); i++) + { + // To put attachment point closer to each other + if(clothMesh.mInvMasses[i] < 1e-6) + clothMesh.mVertices[i] = (clothMesh.mVertices[i] - clothOffset) * 1.0f + clothOffset; + + particlesCopy[i] = physx::PxVec4(clothMesh.mVertices[i], 0.5f * clothMesh.mInvMasses[i]); // w component is 1/mass, or 0.0f for anchored/fixed particles + } + + // Create the cloth from the initial positions/masses and the fabric + mClothActor[index]->mCloth = getSceneController()->getFactory()->createCloth(nv::cloth::Range<physx::PxVec4>(&particlesCopy[0], &particlesCopy[0] + particlesCopy.size()), *mFabric[index]); + particlesCopy.clear(); particlesCopy.shrink_to_fit(); + + mClothActor[index]->mCloth->setGravity(physx::PxVec3(0.0f, -9.8f*0.0f, 0.0f)); + mClothActor[index]->mCloth->setTetherConstraintStiffness(0.01); + mClothActor[index]->mCloth->setStiffnessFrequency(10); + mClothActor[index]->mCloth->setTetherConstraintScale(0.7); + if(index == 0) + { + mClothActor[index]->mCloth->setSolverFrequency(60); + } + else + { + mClothActor[index]->mCloth->setSolverFrequency(300); + } + + //actual spring frequency of the 300hz cloth should be about 2.25x faster instead + // of 4.5x if the stiffness compensation is used (Log stiffness). + + // Setup phase configs + std::vector<nv::cloth::PhaseConfig> phases(mFabric[index]->getNumPhases()); + for (int i = 0; i < (int)phases.size(); i++) + { + phases[i].mPhaseIndex = i; + phases[i].mStiffness = 0.0f; + phases[i].mStiffnessMultiplier = 1.0f; + phases[i].mCompressionLimit = 1.0f; + phases[i].mStretchLimit = 1.0f; + } + mClothActor[index]->mCloth->setPhaseConfig(nv::cloth::Range<nv::cloth::PhaseConfig>(&phases.front(), &phases.back())); + + trackClothActor(mClothActor[index]); + + // Add the cloth to the solver for simulation + addClothToSolver(mClothActor[index], mSolver); +} + +void TimeStepScene::onInitialize() +{ + mSolver = getSceneController()->getFactory()->createSolver(); + trackSolver(mSolver); + + initializeCloth(0,physx::PxVec3(-7.0f, 2.0f, 0.0f)); + initializeCloth(1, physx::PxVec3(2.0f, 2.0f, 0.0f)); + + mTime = 0.0f; + + { + IRenderMesh* mesh = getSceneController()->getRenderer().getPrimitiveRenderMesh(PrimitiveRenderMeshType::Plane); + Renderable* plane = getSceneController()->getRenderer().createRenderable(*mesh, *getSceneController()->getDefaultPlaneMaterial()); + plane->setTransform(PxTransform(PxVec3(0.f, 0.f, 0.f), PxQuat(PxPiDivTwo, PxVec3(0.f, 0.f, 1.f)))); + plane->setScale(PxVec3(1000.f)); + trackRenderable(plane); + } +} diff --git a/NvCloth/samples/SampleBase/scene/scenes/TimeStepScene.h b/NvCloth/samples/SampleBase/scene/scenes/TimeStepScene.h new file mode 100644 index 0000000..82b0034 --- /dev/null +++ b/NvCloth/samples/SampleBase/scene/scenes/TimeStepScene.h @@ -0,0 +1,36 @@ +/* +* Copyright (c) 2008-2017, NVIDIA CORPORATION. All rights reserved. +* +* NVIDIA CORPORATION and its licensors retain all intellectual property +* and proprietary rights in and to this software, related documentation +* and any modifications thereto. Any use, reproduction, disclosure or +* distribution of this software and related documentation without an express +* license agreement from NVIDIA CORPORATION is strictly prohibited. +*/ + +#ifndef TETHER_SCENE_H +#define TETHER_SCENE_H + +#include "scene/Scene.h" +#include <foundation/PxVec3.h> + +class TimeStepScene : public Scene +{ +public: + + TimeStepScene(SceneController* sceneController): Scene(sceneController) {} + + void initializeCloth(int index, physx::PxVec3 offset); + virtual void onInitialize() override; + +private: + nv::cloth::Fabric* mFabric[2]; + nv::cloth::Solver* mSolver; + ClothActor* mClothActor[2]; + + float mTime; + +}; + + +#endif
\ No newline at end of file diff --git a/NvCloth/samples/SampleBase/scene/scenes/VirtualParticleScene.cpp b/NvCloth/samples/SampleBase/scene/scenes/VirtualParticleScene.cpp new file mode 100644 index 0000000..a2fa3a5 --- /dev/null +++ b/NvCloth/samples/SampleBase/scene/scenes/VirtualParticleScene.cpp @@ -0,0 +1,171 @@ +/* +* Copyright (c) 2008-2017, 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 "VirtualParticleScene.h" +#include "scene/SceneController.h" +#include <NvClothExt/ClothFabricCooker.h> +#include "ClothMeshGenerator.h" +#include <NvCloth/Fabric.h> +#include <NvCloth/Solver.h> +#include <NvCloth/Cloth.h> +#include <NvCloth/Factory.h> +#include "Renderer.h" +#include "renderer/RenderUtils.h" +#include "windows.h" +#include "utils/MeshGenerator.h" + +DECLARE_SCENE_NAME(VirtualParticleScene, "Virtual Particle Scene") + +void VirtualParticleScene::Animate(double dt) +{ + static float time = 0.0f; + time += dt*0.75; + + physx::PxTransform invTranslation(-mOffset - physx::PxVec3(0.f, 10.f, -2.f)); + physx::PxTransform rotation(physx::PxQuat(cosf(time)*physx::PxHalfPi-physx::PxHalfPi,physx::PxVec3(0.0f,1.0f,0.0f))); + physx::PxTransform translation(mOffset + physx::PxVec3(0.f, 10.f, -2.f) + physx::PxVec3(0.0f,0.0f,10.0f*sinf(time))); + physx::PxTransform totalTransform = translation.transform(rotation.transform(invTranslation)); + + physx::PxVec4 spheres[2] = { + physx::PxVec4(totalTransform.transform(physx::PxVec3(-4.f,10.f,0.f) + mOffset),1.5), + physx::PxVec4(totalTransform.transform(physx::PxVec3( 4.f,10.f,0.f) + mOffset),1.5)}; + + mClothActor[0]->mCloth->setSpheres(nv::cloth::Range<physx::PxVec4>(spheres, spheres + 2), 0, mClothActor[0]->mCloth->getNumSpheres()); + + mCollisionMehs->setTransform(totalTransform); + + Scene::Animate(dt); +} + +void VirtualParticleScene::initializeCloth(int index, physx::PxVec3 offset) +{ + mOffset = offset; + + /////////////////////////////////////////////////////////////////////// + ClothMeshData clothMesh; + + physx::PxMat44 transform = PxTransform(PxVec3(0.f, 13.f, 0.f)+ mOffset, PxQuat(0, PxVec3(1.f, 0.f, 0.f))); + clothMesh.GeneratePlaneCloth(5.f, 6.f, 9, 1, false, transform); + clothMesh.AttachClothPlaneByAngles(8, 1); + //clothMesh.SetInvMasses(0.5f + (float)index * 2.0f); + + mClothActor[index] = new ClothActor; + nv::cloth::ClothMeshDesc meshDesc = clothMesh.GetClothMeshDesc(); + { + mClothActor[index]->mClothRenderMesh = new ClothRenderMesh(meshDesc); + mClothActor[index]->mClothRenderable = getSceneController()->getRenderer().createRenderable(*(static_cast<IRenderMesh*>(mClothActor[index]->mClothRenderMesh)), *getSceneController()->getDefaultMaterial()); + + float r, g, b; + r = index == 0 ? 1.0f : 0.3f; + g = index == 1 ? 1.0f : 0.3f; + b = index == 2 ? 1.0f : 0.3f; + + mClothActor[index]->mClothRenderable->setColor(DirectX::XMFLOAT4(r, g, b, 1.0f)); + } + + nv::cloth::Vector<int32_t>::Type phaseTypeInfo; + mFabric[index] = NvClothCookFabricFromMesh(getSceneController()->getFactory(), meshDesc, physx::PxVec3(0.0f, 0.0f, 1.0f), &phaseTypeInfo, false); + trackFabric(mFabric[index]); + + // Initialize start positions and masses for the actual cloth instance + // (note: the particle/vertex positions do not have to match the mesh description here. Set the positions to the initial shape of this cloth instance) + std::vector<physx::PxVec4> particlesCopy; + particlesCopy.resize(clothMesh.mVertices.size()); + + physx::PxVec3 clothOffset = transform.getPosition(); + for(int i = 0; i < (int)clothMesh.mVertices.size(); i++) + { + // To put attachment point closer to each other + if(clothMesh.mInvMasses[i] < 1e-6) + clothMesh.mVertices[i] = (clothMesh.mVertices[i] - clothOffset)*0.95f + clothOffset; + + particlesCopy[i] = physx::PxVec4(clothMesh.mVertices[i], clothMesh.mInvMasses[i]); // w component is 1/mass, or 0.0f for anchored/fixed particles + } + + // Create the cloth from the initial positions/masses and the fabric + mClothActor[index]->mCloth = getSceneController()->getFactory()->createCloth(nv::cloth::Range<physx::PxVec4>(&particlesCopy[0], &particlesCopy[0] + particlesCopy.size()), *mFabric[index]); + particlesCopy.clear(); particlesCopy.shrink_to_fit(); + + mClothActor[index]->mCloth->setGravity(physx::PxVec3(0.0f, -50.0f, 0.0f)); + mClothActor[index]->mCloth->setDamping(physx::PxVec3(0.1f, 0.1f, 0.1f)); + //mClothActor[index]->mCloth->setSelfCollisionDistance(0.07f); + //mClothActor[index]->mCloth->enableContinuousCollision(true); + //mClothActor[index]->mCloth->setFriction(1.0f); + typedef uint32_t arraytype[4]; + + arraytype * virtualParticleIndices = new uint32_t[clothMesh.mTriangles.size()][4]; + for(int i = 0; i < (int)clothMesh.mTriangles.size(); i++) + { + virtualParticleIndices[i][0] = clothMesh.mTriangles[i].a; + virtualParticleIndices[i][1] = clothMesh.mTriangles[i].b; + virtualParticleIndices[i][2] = clothMesh.mTriangles[i].c; + virtualParticleIndices[i][3] = 0; + } + + physx::PxVec3 weights[1] = + { + physx::PxVec3(1.0f,1.0f,1.0f) / 3.0f + }; + + mClothActor[index]->mCloth->setVirtualParticles(nv::cloth::Range<const uint32_t[4]>(virtualParticleIndices, (virtualParticleIndices+ clothMesh.mTriangles.size())), nv::cloth::Range<physx::PxVec3>(weights, weights+1)); + + + physx::PxVec4 spheres[2] = {physx::PxVec4(physx::PxVec3(-4.f,10.f,0.f) + mOffset,1.5), + physx::PxVec4(physx::PxVec3( 4.f,10.f,0.f) + mOffset,1.5)}; + + mClothActor[index]->mCloth->setSpheres(nv::cloth::Range<physx::PxVec4>(spheres, spheres + 2), 0, mClothActor[index]->mCloth->getNumSpheres()); + + uint32_t caps[4]; + caps[0] = 0; + caps[1] = 1; + + mClothActor[index]->mCloth->setCapsules(nv::cloth::Range<uint32_t>(caps, caps + 2), 0, mClothActor[index]->mCloth->getNumCapsules()); + + //create render mesh + auto mesh = MeshGenerator::generateCollisionCapsules(spheres,2,caps,2,-0.05f); + auto renderMesh = new MeshGenerator::MeshGeneratorRenderMesh(mesh); + mCollisionMehs = getSceneController()->getRenderer().createRenderable(*renderMesh, *getSceneController()->getDefaultMaterial()); + trackRenderable(mCollisionMehs); + + // Setup phase configs + std::vector<nv::cloth::PhaseConfig> phases(mFabric[index]->getNumPhases()); + for(int i = 0; i < (int)phases.size(); i++) + { + phases[i].mPhaseIndex = i; + phases[i].mStiffness = 1.0f; + phases[i].mStiffnessMultiplier = 1.0f; + phases[i].mCompressionLimit = 1.0f; + phases[i].mStretchLimit = 1.0f; + } + mClothActor[index]->mCloth->setPhaseConfig(nv::cloth::Range<nv::cloth::PhaseConfig>(&phases.front(), &phases.back())); + mClothActor[index]->mCloth->setDragCoefficient(0.5f); + mClothActor[index]->mCloth->setLiftCoefficient(0.6f); + + trackClothActor(mClothActor[index]); + + // Add the cloth to the solver for simulation + addClothToSolver(mClothActor[index], mSolver); +} + +void VirtualParticleScene::onInitialize() +{ + mSolver = getSceneController()->getFactory()->createSolver(); + trackSolver(mSolver); + + initializeCloth(0, physx::PxVec3(0.0f, 0.0f, 0.0f)); + + { + IRenderMesh* mesh = getSceneController()->getRenderer().getPrimitiveRenderMesh(PrimitiveRenderMeshType::Plane); + Renderable* plane = getSceneController()->getRenderer().createRenderable(*mesh, *getSceneController()->getDefaultPlaneMaterial()); + plane->setTransform(PxTransform(PxVec3(0.f, 0.f, 0.f), PxQuat(PxPiDivTwo, PxVec3(0.f, 0.f, 1.f)))); + plane->setScale(PxVec3(1000.f)); + trackRenderable(plane); + } +} diff --git a/NvCloth/samples/SampleBase/scene/scenes/VirtualParticleScene.h b/NvCloth/samples/SampleBase/scene/scenes/VirtualParticleScene.h new file mode 100644 index 0000000..38fa66b --- /dev/null +++ b/NvCloth/samples/SampleBase/scene/scenes/VirtualParticleScene.h @@ -0,0 +1,37 @@ +/* +* Copyright (c) 2008-2017, NVIDIA CORPORATION. All rights reserved. +* +* NVIDIA CORPORATION and its licensors retain all intellectual property +* and proprietary rights in and to this software, related documentation +* and any modifications thereto. Any use, reproduction, disclosure or +* distribution of this software and related documentation without an express +* license agreement from NVIDIA CORPORATION is strictly prohibited. +*/ + +#ifndef CCD_SCENE_H +#define CCD_SCENE_H + +#include "scene/Scene.h" +#include <foundation/PxVec3.h> + +class VirtualParticleScene : public Scene +{ +public: + + VirtualParticleScene(SceneController* sceneController): Scene(sceneController) {} + + virtual void Animate(double dt) override; + void initializeCloth(int index, physx::PxVec3 offset); + virtual void onInitialize() override; + +private: + nv::cloth::Fabric* mFabric[1]; + nv::cloth::Solver* mSolver; + ClothActor* mClothActor[1]; + + physx::PxVec3 mOffset; + Renderable* mCollisionMehs; +}; + + +#endif
\ No newline at end of file diff --git a/NvCloth/samples/SampleBase/task/PxCpuDispatcher.h b/NvCloth/samples/SampleBase/task/PxCpuDispatcher.h new file mode 100644 index 0000000..ffd5cfd --- /dev/null +++ b/NvCloth/samples/SampleBase/task/PxCpuDispatcher.h @@ -0,0 +1,79 @@ +// 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-2017 NVIDIA Corporation. All rights reserved. + +#ifndef PXTASK_PXCPUDISPATCHER_H +#define PXTASK_PXCPUDISPATCHER_H + +#include "task/PxTaskDefine.h" +#include "foundation/PxSimpleTypes.h" + +namespace physx +{ + +class PxBaseTask; + +/** + \brief A CpuDispatcher is responsible for scheduling the execution of tasks passed to it by the SDK. + + A typical implementation would for example use a thread pool with the dispatcher + pushing tasks onto worker thread queues or a global queue. + + @see PxBaseTask + @see PxTask + @see PxTaskManager +*/ +class PxCpuDispatcher +{ +public: + /** + \brief Called by the TaskManager when a task is to be queued for execution. + + Upon receiving a task, the dispatcher should schedule the task + to run when resource is available. After the task has been run, + it should call the release() method and discard it's pointer. + + \param[in] task The task to be run. + + @see PxBaseTask + */ + virtual void submitTask( PxBaseTask& task ) = 0; + + /** + \brief Returns the number of available worker threads for this dispatcher. + + The SDK will use this count to control how many tasks are submitted. By + matching the number of tasks with the number of execution units task + overhead can be reduced. + */ + virtual uint32_t getWorkerCount() const = 0; + + virtual ~PxCpuDispatcher() {} +}; + +} // end physx namespace + +#endif // PXTASK_PXCPUDISPATCHER_H diff --git a/NvCloth/samples/SampleBase/task/PxGpuDispatcher.h b/NvCloth/samples/SampleBase/task/PxGpuDispatcher.h new file mode 100644 index 0000000..6cd4c3b --- /dev/null +++ b/NvCloth/samples/SampleBase/task/PxGpuDispatcher.h @@ -0,0 +1,248 @@ +// 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-2017 NVIDIA Corporation. All rights reserved. + +#ifndef PXTASK_PXGPUDISPATCHER_H +#define PXTASK_PXGPUDISPATCHER_H + +#include "task/PxTaskDefine.h" +#include "task/PxTask.h" + +/* forward decl to avoid including <cuda.h> */ +typedef struct CUstream_st* CUstream; + +namespace physx +{ + +struct PxGpuCopyDesc; +class PxCudaContextManager; + +PX_PUSH_PACK_DEFAULT + +class PxTaskManager; + +/** \brief A GpuTask dispatcher + * + * A PxGpuDispatcher executes GpuTasks submitted by one or more TaskManagers (one + * or more scenes). It maintains a CPU worker thread which waits on GpuTask + * "groups" to be submitted. The submission API is explicitly sessioned so that + * GpuTasks are dispatched together as a group whenever possible to improve + * parallelism on the GPU. + * + * A PxGpuDispatcher cannot be allocated ad-hoc, they are created as a result of + * creating a PxCudaContextManager. Every PxCudaContextManager has a PxGpuDispatcher + * instance that can be queried. In this way, each PxGpuDispatcher is tied to + * exactly one CUDA context. + * + * A scene will use CPU fallback Tasks for GpuTasks if the PxTaskManager provided + * to it does not have a PxGpuDispatcher. For this reason, the PxGpuDispatcher must + * be assigned to the PxTaskManager before the PxTaskManager is given to a scene. + * + * Multiple TaskManagers may safely share a single PxGpuDispatcher instance, thus + * enabling scenes to share a CUDA context. + * + * Only failureDetected() is intended for use by the user. The rest of the + * nvGpuDispatcher public methods are reserved for internal use by only both + * TaskManagers and GpuTasks. + */ +class PxGpuDispatcher +{ +public: + /** \brief Record the start of a simulation step + * + * A PxTaskManager calls this function to record the beginning of a simulation + * step. The PxGpuDispatcher uses this notification to initialize the + * profiler state. + */ + virtual void startSimulation() = 0; + + /** \brief Record the start of a GpuTask batch submission + * + * A PxTaskManager calls this function to notify the PxGpuDispatcher that one or + * more GpuTasks are about to be submitted for execution. The PxGpuDispatcher + * will not read the incoming task queue until it receives one finishGroup() + * call for each startGroup() call. This is to ensure as many GpuTasks as + * possible are executed together as a group, generating optimal parallelism + * on the GPU. + */ + virtual void startGroup() = 0; + + /** \brief Submit a GpuTask for execution + * + * Submitted tasks are pushed onto an incoming queue. The PxGpuDispatcher + * will take the contents of this queue every time the pending group count + * reaches 0 and run the group of submitted GpuTasks as an interleaved + * group. + */ + virtual void submitTask(PxTask& task) = 0; + + /** \brief Record the end of a GpuTask batch submission + * + * A PxTaskManager calls this function to notify the PxGpuDispatcher that it is + * done submitting a group of GpuTasks (GpuTasks which were all make ready + * to run by the same prerequisite dependency becoming resolved). If no + * other group submissions are in progress, the PxGpuDispatcher will execute + * the set of ready tasks. + */ + virtual void finishGroup() = 0; + + /** \brief Add a CUDA completion prerequisite dependency to a task + * + * A GpuTask calls this function to add a prerequisite dependency on another + * task (usually a CpuTask) preventing that task from starting until all of + * the CUDA kernels and copies already launched have been completed. The + * PxGpuDispatcher will increment that task's reference count, blocking its + * execution, until the CUDA work is complete. + * + * This is generally only required when a CPU task is expecting the results + * of the CUDA kernels to have been copied into host memory. + * + * This mechanism is not at all not required to ensure CUDA kernels and + * copies are issued in the correct order. Kernel issue order is determined + * by normal task dependencies. The rule of thumb is to only use a blocking + * completion prerequisite if the task in question depends on a completed + * GPU->Host DMA. + * + * The PxGpuDispatcher issues a blocking event record to CUDA for the purposes + * of tracking the already submitted CUDA work. When this event is + * resolved, the PxGpuDispatcher manually decrements the reference count of + * the specified task, allowing it to execute (assuming it does not have + * other pending prerequisites). + */ + virtual void addCompletionPrereq(PxBaseTask& task) = 0; + + /** \brief Retrieve the PxCudaContextManager associated with this + * PxGpuDispatcher + * + * Every PxCudaContextManager has one PxGpuDispatcher, and every PxGpuDispatcher + * has one PxCudaContextManager. + */ + virtual PxCudaContextManager* getCudaContextManager() = 0; + + /** \brief Record the end of a simulation frame + * + * A PxTaskManager calls this function to record the completion of its + * dependency graph. If profiling is enabled, the PxGpuDispatcher will + * trigger the retrieval of profiling data from the GPU at this point. + */ + virtual void stopSimulation() = 0; + + /** \brief Returns true if a CUDA call has returned a non-recoverable error + * + * A return value of true indicates a fatal error has occurred. To protect + * itself, the PxGpuDispatcher enters a fall through mode that allows GpuTasks + * to complete without being executed. This allows simulations to continue + * but leaves GPU content static or corrupted. + * + * The user may try to recover from these failures by deleting GPU content + * so the visual artifacts are minimized. But there is no way to recover + * the state of the GPU actors before the failure. Once a CUDA context is + * in this state, the only recourse is to create a new CUDA context, a new + * scene, and start over. + * + * This is our "Best Effort" attempt to not turn a soft failure into a hard + * failure because continued use of a CUDA context after it has returned an + * error will usually result in a driver reset. However if the initial + * failure was serious enough, a reset may have already occurred by the time + * we learn of it. + */ + virtual bool failureDetected() const = 0; + + /** \brief Force the PxGpuDispatcher into failure mode + * + * This API should be used if user code detects a non-recoverable CUDA + * error. This ensures the PxGpuDispatcher does not launch any further + * CUDA work. Subsequent calls to failureDetected() will return true. + */ + virtual void forceFailureMode() = 0; + + /** \brief Launch a copy kernel with arbitrary number of copy commands + * + * This method is intended to be called from Kernel GpuTasks, but it can + * function outside of that context as well. + * + * If count is 1, the descriptor is passed to the kernel as arguments, so it + * may be declared on the stack. + * + * If count is greater than 1, the kernel will read the descriptors out of + * host memory. Because of this, the descriptor array must be located in + * page locked (pinned) memory. The provided descriptors may be modified by + * this method (converting host pointers to their GPU mapped equivalents) + * and should be considered *owned* by CUDA until the current batch of work + * has completed, so descriptor arrays should not be freed or modified until + * you have received a completion notification. + * + * If your GPU does not support mapping of page locked memory (SM>=1.1), + * this function degrades to calling CUDA copy methods. + */ + virtual void launchCopyKernel(PxGpuCopyDesc* desc, uint32_t count, CUstream stream) = 0; + + /** \brief Query pre launch task that runs before launching gpu kernels. + * + * This is part of an optional feature to schedule multiple gpu features + * at the same time to get kernels to run in parallel. + * \note Do *not* set the continuation on the returned task, but use addPreLaunchDependent(). + */ + virtual PxBaseTask& getPreLaunchTask() = 0; + + /** \brief Adds a gpu launch task that gets executed after the pre launch task. + * + * This is part of an optional feature to schedule multiple gpu features + * at the same time to get kernels to run in parallel. + * \note Each call adds a reference to the pre-launch task. + */ + virtual void addPreLaunchDependent(PxBaseTask& dependent) = 0; + + /** \brief Query post launch task that runs after the gpu is done. + * + * This is part of an optional feature to schedule multiple gpu features + * at the same time to get kernels to run in parallel. + * \note Do *not* set the continuation on the returned task, but use addPostLaunchDependent(). + */ + virtual PxBaseTask& getPostLaunchTask() = 0; + + /** \brief Adds a task that gets executed after the post launch task. + * + * This is part of an optional feature to schedule multiple gpu features + * at the same time to get kernels to run in parallel. + * \note Each call adds a reference to the pre-launch task. + */ + virtual void addPostLaunchDependent(PxBaseTask& dependent) = 0; + +protected: + /** \brief protected destructor + * + * GpuDispatchers are allocated and freed by their PxCudaContextManager. + */ + virtual ~PxGpuDispatcher() {} +}; + +PX_POP_PACK + +} // end physx namespace + + +#endif // PXTASK_PXGPUDISPATCHER_H diff --git a/NvCloth/samples/SampleBase/task/PxGpuTask.h b/NvCloth/samples/SampleBase/task/PxGpuTask.h new file mode 100644 index 0000000..197fc3d --- /dev/null +++ b/NvCloth/samples/SampleBase/task/PxGpuTask.h @@ -0,0 +1,118 @@ +// 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-2017 NVIDIA Corporation. All rights reserved. + + +#ifndef PXTASK_PXGPUTASK_H +#define PXTASK_PXGPUTASK_H + +#include "task/PxTaskDefine.h" +#include "task/PxTask.h" +#include "task/PxGpuDispatcher.h" + +namespace physx +{ + +PX_PUSH_PACK_DEFAULT + +/** \brief Define the 'flavor' of a PxGpuTask + * + * Each PxGpuTask should have a specific function; either copying data to the + * device, running kernels on that data, or copying data from the device. + * + * For optimal performance, the dispatcher should run all available HtoD tasks + * before running all Kernel tasks, and all Kernel tasks before running any DtoH + * tasks. This provides maximal kernel overlap and the least number of CUDA + * flushes. + */ +struct PxGpuTaskHint +{ + /// \brief Enums for the type of GPU task + enum Enum + { + HostToDevice, + Kernel, + DeviceToHost, + + NUM_GPU_TASK_HINTS + }; +}; + +/** + * \brief PxTask implementation for launching CUDA work + */ +class PxGpuTask : public PxTask +{ +public: + PxGpuTask() : mComp(NULL) {} + + /** + * \brief iterative "run" function for a PxGpuTask + * + * The GpuDispatcher acquires the CUDA context for the duration of this + * function call, and it is highly recommended that the PxGpuTask use the + * provided CUstream for all kernels. + * + * kernelIndex will be 0 for the initial call and incremented before each + * subsequent call. Once launchInstance() returns false, its PxGpuTask is + * considered completed and is released. + */ + virtual bool launchInstance(CUstream stream, int kernelIndex) = 0; + + /** + * \brief Returns a hint indicating the function of this task + */ + virtual PxGpuTaskHint::Enum getTaskHint() const = 0; + + /** + * \brief Specify a task that will have its reference count decremented + * when this task is released + */ + void setCompletionTask(PxBaseTask& task) + { + mComp = &task; + } + + void release() + { + if (mComp) + { + mComp->removeReference(); + mComp = NULL; + } + PxTask::release(); + } + +protected: + /// \brief A pointer to the completion task + PxBaseTask* mComp; +}; + +PX_POP_PACK + +} // end physx namespace + +#endif // PXTASK_PXGPUTASK_H diff --git a/NvCloth/samples/SampleBase/task/PxTask.h b/NvCloth/samples/SampleBase/task/PxTask.h new file mode 100644 index 0000000..85d91da --- /dev/null +++ b/NvCloth/samples/SampleBase/task/PxTask.h @@ -0,0 +1,335 @@ +// 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-2017 NVIDIA Corporation. All rights reserved. + +#ifndef PXTASK_PXTASK_H +#define PXTASK_PXTASK_H + +#include "task/PxTaskDefine.h" +#include "task/PxTaskManager.h" +#include "task/PxCpuDispatcher.h" +#include "task/PxGpuDispatcher.h" +#include "foundation/PxAssert.h" + +namespace physx +{ + +/** + * \brief Base class of all task types + * + * PxBaseTask defines a runnable reference counted task with built-in profiling. + */ +class PxBaseTask +{ +public: + PxBaseTask() : mContextID(0), mTm(NULL) {} + virtual ~PxBaseTask() {} + + /** + * \brief The user-implemented run method where the task's work should be performed + * + * run() methods must be thread safe, stack friendly (no alloca, etc), and + * must never block. + */ + virtual void run() = 0; + + /** + * \brief Return a user-provided task name for profiling purposes. + * + * It does not have to be unique, but unique names are helpful. + * + * \return The name of this task + */ + virtual const char* getName() const = 0; + + //! \brief Implemented by derived implementation classes + virtual void addReference() = 0; + //! \brief Implemented by derived implementation classes + virtual void removeReference() = 0; + //! \brief Implemented by derived implementation classes + virtual int32_t getReference() const = 0; + + /** \brief Implemented by derived implementation classes + * + * A task may assume in its release() method that the task system no longer holds + * references to it - so it may safely run its destructor, recycle itself, etc. + * provided no additional user references to the task exist + */ + virtual void release() = 0; + + /** + * \brief Return PxTaskManager to which this task was submitted + * + * Note, can return NULL if task was not submitted, or has been + * completed. + */ + PX_FORCE_INLINE PxTaskManager* getTaskManager() const + { + return mTm; + } + + PX_FORCE_INLINE void setContextId(PxU64 id) { mContextID = id; } + PX_FORCE_INLINE PxU64 getContextId() const { return mContextID; } + +protected: + PxU64 mContextID; //!< Context ID for profiler interface + PxTaskManager* mTm; //!< Owning PxTaskManager instance + + friend class PxTaskMgr; +}; + + +/** + * \brief A PxBaseTask implementation with deferred execution and full dependencies + * + * A PxTask must be submitted to a PxTaskManager to to be executed, Tasks may + * optionally be named when they are submitted. + */ +class PxTask : public PxBaseTask +{ +public: + PxTask() : mTaskID(0) {} + virtual ~PxTask() {} + + //! \brief Release method implementation + virtual void release() + { + PX_ASSERT(mTm); + + // clear mTm before calling taskCompleted() for safety + PxTaskManager* save = mTm; + mTm = NULL; + save->taskCompleted( *this ); + } + + //! \brief Inform the PxTaskManager this task must finish before the given + // task is allowed to start. + PX_INLINE void finishBefore( PxTaskID taskID ) + { + PX_ASSERT(mTm); + mTm->finishBefore( *this, taskID); + } + + //! \brief Inform the PxTaskManager this task cannot start until the given + // task has completed. + PX_INLINE void startAfter( PxTaskID taskID ) + { + PX_ASSERT(mTm); + mTm->startAfter( *this, taskID ); + } + + /** + * \brief Manually increment this task's reference count. The task will + * not be allowed to run until removeReference() is called. + */ + PX_INLINE void addReference() + { + PX_ASSERT(mTm); + mTm->addReference( mTaskID ); + } + + /** + * \brief Manually decrement this task's reference count. If the reference + * count reaches zero, the task will be dispatched. + */ + PX_INLINE void removeReference() + { + PX_ASSERT(mTm); + mTm->decrReference( mTaskID ); + } + + /** + * \brief Return the ref-count for this task + */ + PX_INLINE int32_t getReference() const + { + return mTm->getReference( mTaskID ); + } + + /** + * \brief Return the unique ID for this task + */ + PX_INLINE PxTaskID getTaskID() const + { + return mTaskID; + } + + /** + * \brief Called by PxTaskManager at submission time for initialization + * + * Perform simulation step initialization here. + */ + virtual void submitted() + { + mStreamIndex = 0; + mPreSyncRequired = false; + } + + /** + * \brief Specify that the GpuTask sync flag be set + */ + PX_INLINE void requestSyncPoint() + { + mPreSyncRequired = true; + } + + +protected: + PxTaskID mTaskID; //!< ID assigned at submission + uint32_t mStreamIndex; //!< GpuTask CUDA stream index + bool mPreSyncRequired; //!< GpuTask sync flag + + friend class PxTaskMgr; + friend class PxGpuWorkerThread; +}; + + +/** + * \brief A PxBaseTask implementation with immediate execution and simple dependencies + * + * A PxLightCpuTask bypasses the PxTaskManager launch dependencies and will be + * submitted directly to your scene's CpuDispatcher. When the run() function + * completes, it will decrement the reference count of the specified + * continuation task. + * + * You must use a full-blown PxTask if you want your task to be resolved + * by another PxTask, or you need more than a single dependency to be + * resolved when your task completes, or your task will not run on the + * CpuDispatcher. + */ +class PxLightCpuTask : public PxBaseTask +{ +public: + PxLightCpuTask() + : mCont( NULL ) + , mRefCount( 0 ) + { + } + virtual ~PxLightCpuTask() + { + mTm = NULL; + } + + /** + * \brief Initialize this task and specify the task that will have its ref count decremented on completion. + * + * Submission is deferred until the task's mRefCount is decremented to zero. + * Note that we only use the PxTaskManager to query the appropriate dispatcher. + * + * \param[in] tm The PxTaskManager this task is managed by + * \param[in] c The task to be executed when this task has finished running + */ + PX_INLINE void setContinuation(PxTaskManager& tm, PxBaseTask* c) + { + PX_ASSERT( mRefCount == 0 ); + mRefCount = 1; + mCont = c; + mTm = &tm; + if( mCont ) + { + mCont->addReference(); + } + } + + /** + * \brief Initialize this task and specify the task that will have its ref count decremented on completion. + * + * This overload of setContinuation() queries the PxTaskManager from the continuation + * task, which cannot be NULL. + * \param[in] c The task to be executed after this task has finished running + */ + PX_INLINE void setContinuation( PxBaseTask* c ) + { + PX_ASSERT( c ); + PX_ASSERT( mRefCount == 0 ); + mRefCount = 1; + mCont = c; + if( mCont ) + { + mCont->addReference(); + mTm = mCont->getTaskManager(); + PX_ASSERT( mTm ); + } + } + + /** + * \brief Retrieves continuation task + */ + PX_INLINE PxBaseTask* getContinuation() const + { + return mCont; + } + + /** + * \brief Manually decrement this task's reference count. If the reference + * count reaches zero, the task will be dispatched. + */ + PX_INLINE void removeReference() + { + mTm->decrReference(*this); + } + + /** \brief Return the ref-count for this task */ + PX_INLINE int32_t getReference() const + { + return mRefCount; + } + + /** + * \brief Manually increment this task's reference count. The task will + * not be allowed to run until removeReference() is called. + */ + PX_INLINE void addReference() + { + mTm->addReference(*this); + } + + /** + * \brief called by CpuDispatcher after run method has completed + * + * Decrements the continuation task's reference count, if specified. + */ + PX_INLINE void release() + { + if( mCont ) + { + mCont->removeReference(); + } + } + +protected: + + PxBaseTask* mCont; //!< Continuation task, can be NULL + volatile int32_t mRefCount; //!< PxTask is dispatched when reaches 0 + + friend class PxTaskMgr; +}; + + +}// end physx namespace + + +#endif // PXTASK_PXTASK_H diff --git a/NvCloth/samples/SampleBase/task/PxTaskDefine.h b/NvCloth/samples/SampleBase/task/PxTaskDefine.h new file mode 100644 index 0000000..c1daea7 --- /dev/null +++ b/NvCloth/samples/SampleBase/task/PxTaskDefine.h @@ -0,0 +1,37 @@ +// 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-2017 NVIDIA Corporation. All rights reserved. + +#ifndef PXTASK_PXTASKDEFINE_H +#define PXTASK_PXTASKDEFINE_H + +#include "foundation/PxPreprocessor.h" + +#ifndef PX_SUPPORT_PXTASK_PROFILING +#define PX_SUPPORT_PXTASK_PROFILING 1 +#endif + +#endif // PXTASK_PXTASKDEFINE_H diff --git a/NvCloth/samples/SampleBase/task/PxTaskManager.h b/NvCloth/samples/SampleBase/task/PxTaskManager.h new file mode 100644 index 0000000..f40f7b1 --- /dev/null +++ b/NvCloth/samples/SampleBase/task/PxTaskManager.h @@ -0,0 +1,231 @@ +// 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-2017 NVIDIA Corporation. All rights reserved. + +#ifndef PXTASK_PXTASKMANAGER_H +#define PXTASK_PXTASKMANAGER_H + +#include "task/PxTaskDefine.h" +#include "foundation/PxSimpleTypes.h" +#include "foundation/PxErrorCallback.h" + +namespace physx +{ + +PX_PUSH_PACK_DEFAULT + +class PxBaseTask; +class PxTask; +class PxLightCpuTask; +typedef unsigned int PxTaskID; + +/** +\brief Identifies the type of each heavyweight PxTask object + +\note This enum type is only used by PxTask and GpuTask objects, LightCpuTasks do not use this enum. + +@see PxTask +@see PxLightCpuTask +*/ +struct PxTaskType +{ + /** + * \brief Identifies the type of each heavyweight PxTask object + */ + enum Enum + { + TT_CPU, //!< PxTask will be run on the CPU + TT_GPU, //!< PxTask will be run on the GPU + TT_NOT_PRESENT, //!< Return code when attempting to find a task that does not exist + TT_COMPLETED //!< PxTask execution has been completed + }; +}; + +class PxCpuDispatcher; +class PxGpuDispatcher; + +/** + \brief The PxTaskManager interface + + A PxTaskManager instance holds references to user-provided dispatcher objects, when tasks are + submitted the PxTaskManager routes them to the appropriate dispatcher and handles task profiling if enabled. + Users should not implement the PxTaskManager interface, the SDK creates it's own concrete PxTaskManager object + per-scene which users can configure by passing dispatcher objects into the PxSceneDesc. + + + @see CpuDispatcher + @see PxGpuDispatcher + +*/ +class PxTaskManager +{ +public: + + /** + \brief Set the user-provided dispatcher object for CPU tasks + + \param[in] ref The dispatcher object. + + @see CpuDispatcher + */ + virtual void setCpuDispatcher(PxCpuDispatcher& ref) = 0; + + /** + \brief Set the user-provided dispatcher object for GPU tasks + + \param[in] ref The dispatcher object. + + @see PxGpuDispatcher + */ + virtual void setGpuDispatcher(PxGpuDispatcher& ref) = 0; + + /** + \brief Get the user-provided dispatcher object for CPU tasks + + \return The CPU dispatcher object. + + @see CpuDispatcher + */ + virtual PxCpuDispatcher* getCpuDispatcher() const = 0; + + /** + \brief Get the user-provided dispatcher object for GPU tasks + + \return The GPU dispatcher object. + + @see PxGpuDispatcher + */ + virtual PxGpuDispatcher* getGpuDispatcher() const = 0; + + /** + \brief Reset any dependencies between Tasks + + \note Will be called at the start of every frame before tasks are submitted. + + @see PxTask + */ + virtual void resetDependencies() = 0; + + /** + \brief Called by the owning scene to start the task graph. + + \note All tasks with with ref count of 1 will be dispatched. + + @see PxTask + */ + virtual void startSimulation() = 0; + + /** + \brief Called by the owning scene at the end of a simulation step to synchronize the PxGpuDispatcher + + @see PxGpuDispatcher + */ + virtual void stopSimulation() = 0; + + /** + \brief Called by the worker threads to inform the PxTaskManager that a task has completed processing + + \param[in] task The task which has been completed + */ + virtual void taskCompleted(PxTask& task) = 0; + + /** + \brief Retrieve a task by name + + \param[in] name The unique name of a task + \return The ID of the task with that name, or TT_NOT_PRESENT if not found + */ + virtual PxTaskID getNamedTask(const char* name) = 0; + + /** + \brief Submit a task with a unique name. + + \param[in] task The task to be executed + \param[in] name The unique name of a task + \param[in] type The type of the task (default TT_CPU) + \return The ID of the task with that name, or TT_NOT_PRESENT if not found + + */ + virtual PxTaskID submitNamedTask(PxTask* task, const char* name, PxTaskType::Enum type = PxTaskType::TT_CPU) = 0; + + /** + \brief Submit an unnamed task. + + \param[in] task The task to be executed + \param[in] type The type of the task (default TT_CPU) + + \return The ID of the task with that name, or TT_NOT_PRESENT if not found + */ + virtual PxTaskID submitUnnamedTask(PxTask& task, PxTaskType::Enum type = PxTaskType::TT_CPU) = 0; + + /** + \brief Retrieve a task given a task ID + + \param[in] id The ID of the task to return, a valid ID must be passed or results are undefined + + \return The task associated with the ID + */ + virtual PxTask* getTaskFromID(PxTaskID id) = 0; + + /** + \brief Release the PxTaskManager object, referenced dispatchers will not be released + */ + virtual void release() = 0; + + /** + \brief Construct a new PxTaskManager instance with the given [optional] dispatchers + */ + static PxTaskManager* createTaskManager(PxErrorCallback& errorCallback, PxCpuDispatcher* = 0, PxGpuDispatcher* = 0); + +protected: + virtual ~PxTaskManager() {} + + /*! \cond PRIVATE */ + + virtual void finishBefore(PxTask& task, PxTaskID taskID) = 0; + virtual void startAfter(PxTask& task, PxTaskID taskID) = 0; + + virtual void addReference(PxTaskID taskID) = 0; + virtual void decrReference(PxTaskID taskID) = 0; + virtual int32_t getReference(PxTaskID taskID) const = 0; + + virtual void decrReference(PxLightCpuTask&) = 0; + virtual void addReference(PxLightCpuTask&) = 0; + + /*! \endcond */ + + friend class PxBaseTask; + friend class PxTask; + friend class PxLightCpuTask; + friend class PxGpuWorkerThread; +}; + +PX_POP_PACK + +} // end physx namespace + + +#endif // PXTASK_PXTASKMANAGER_H diff --git a/NvCloth/samples/SampleBase/ui/CommonUIController.cpp b/NvCloth/samples/SampleBase/ui/CommonUIController.cpp index 492e3e9..29470d2 100644 --- a/NvCloth/samples/SampleBase/ui/CommonUIController.cpp +++ b/NvCloth/samples/SampleBase/ui/CommonUIController.cpp @@ -31,6 +31,7 @@ inline float memorySizeOutput(const char*& prefix, float value) CommonUIController::CommonUIController() { + m_drawGui = true; } HRESULT CommonUIController::DeviceCreated(ID3D11Device* pDevice) @@ -104,6 +105,10 @@ LRESULT CommonUIController::MsgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM getRenderer().reloadShaders(); break; } + case VK_OEM_3: //`~ key + { + m_drawGui = !m_drawGui; + } default: break; } @@ -123,7 +128,8 @@ void CommonUIController::Animate(double fElapsedTimeSeconds) void CommonUIController::Render(ID3D11Device*, ID3D11DeviceContext*, ID3D11RenderTargetView*, ID3D11DepthStencilView*) { ImGui_ImplDX11_NewFrame(); - drawUI(); + if(m_drawGui) + drawUI(); ImGui::Render(); } diff --git a/NvCloth/samples/SampleBase/ui/CommonUIController.h b/NvCloth/samples/SampleBase/ui/CommonUIController.h index 47953b7..04b7a20 100644 --- a/NvCloth/samples/SampleBase/ui/CommonUIController.h +++ b/NvCloth/samples/SampleBase/ui/CommonUIController.h @@ -85,7 +85,7 @@ class CommonUIController : public ISampleController std::queue<DelayedCall> m_delayedCalls; float m_dt; - + bool m_drawGui; }; #endif
\ No newline at end of file diff --git a/NvCloth/samples/SampleBase/utils/AnimatedModelUtilities.cpp b/NvCloth/samples/SampleBase/utils/AnimatedModelUtilities.cpp new file mode 100644 index 0000000..9bb15f8 --- /dev/null +++ b/NvCloth/samples/SampleBase/utils/AnimatedModelUtilities.cpp @@ -0,0 +1,102 @@ +/* +* Copyright (c) 2008-2017, 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 "DataStream.h" +#include "renderer/Model.h" +#include "scene/Scene.h" +#include "utils/DebugLineRenderBuffer.h" +#include <map> + +bool saveBoneCapsuleData(std::string filepath, Model* model, + std::vector<physx::PxVec4>& sphereOffsets, std::vector<uint32_t>& activeSpheres, std::vector<uint32_t>& capsuleNodes) +{ + DataStream stream; + uint32_t sphereOffsetCount = (uint32_t)sphereOffsets.size(); + stream.write(sphereOffsetCount); + for(int i = 0; i < (int)sphereOffsets.size(); i++) + { + stream.write(std::string(model->getNodeName(i))); + stream.write(sphereOffsets[i]); + } + uint32_t activeSphereCount = (uint32_t)activeSpheres.size(); + stream.write(activeSphereCount); + for(int i = 0; i < (int)activeSpheres.size(); i++) + { + stream.write(activeSpheres[i]); + } + uint32_t capsuleNodeCount = (uint32_t)capsuleNodes.size(); + stream.write(capsuleNodeCount); + for(int i = 0; i < (int)capsuleNodes.size(); i++) + { + stream.write(capsuleNodes[i]); + } + + stream.saveToFile(filepath.c_str()); + return true; +} + +bool loadBoneCapsuleData(std::string filepath, Model* model, + std::vector<physx::PxVec4>& sphereOffsets, std::vector<uint32_t>& activeSpheres, std::vector<uint32_t>& capsuleNodes) +{ + if(!DataStream::fileExists(filepath.c_str())) + return false; + + assert(sphereOffsets.size() == model->getNodeCount()); + + DataStream stream(filepath.c_str()); + std::map<int, int> fileNodeIdToModelNodeId; + uint32_t sphereOffsetCount = stream.read<uint32_t>(); + for(int i = 0; i < (int)sphereOffsetCount; i++) + { + auto nodeName = stream.read<std::string>(); + int modelNode = model->getNodeIdByNameWithErrorCode(nodeName); + fileNodeIdToModelNodeId[i] = modelNode; + if(modelNode == -1) + { + stream.read<physx::PxVec4>(); + printf("Warning: node [%d] \"%s\" not found in model (%s)\n", i, nodeName.c_str(), filepath.c_str()); + continue; + } + sphereOffsets[modelNode] = stream.read<physx::PxVec4>(); + } + activeSpheres.resize(stream.read<uint32_t>()); + for(int i = 0; i < (int)activeSpheres.size(); i++) + { + int fileNode = stream.read<uint32_t>(); + if(fileNodeIdToModelNodeId[fileNode] == -1) + continue; + activeSpheres[i] = fileNodeIdToModelNodeId[fileNode]; + } + capsuleNodes.resize(stream.read<uint32_t>()); + for(int i = 0; i < (int)capsuleNodes.size(); i++) + { + int fileNode = stream.read<uint32_t>(); + if(fileNodeIdToModelNodeId[fileNode] == -1) + continue; + capsuleNodes[i] = fileNodeIdToModelNodeId[fileNode]; + } + return true; +} + +void renderBoneLines(Scene* scene, Model* model, ModelInstance* instance, physx::PxMat44 transform, float scale) +{ + DebugLineRenderBuffer* dbl = scene->getSceneController()->getDebugLineRenderBuffer(); + + //Render bone lines + model->traverseNodes([&transform, scale, instance, dbl](int nodeId, int parrentId) + { + physx::PxVec3 a = instance->mNodes[parrentId].mTransform.transform(physx::PxVec3(0.0f, 0.0f, 0.0f)); + physx::PxVec3 b = instance->mNodes[nodeId].mTransform.transform(physx::PxVec3(0.0f, 0.0f, 0.0f)); + a = transform.transform(a * scale); + b = transform.transform(b * scale); + dbl->addLine(a, b, 0xFFFFFFFF); + }); +}
\ No newline at end of file diff --git a/NvCloth/samples/SampleBase/utils/AnimatedModelUtilities.h b/NvCloth/samples/SampleBase/utils/AnimatedModelUtilities.h new file mode 100644 index 0000000..efe04b2 --- /dev/null +++ b/NvCloth/samples/SampleBase/utils/AnimatedModelUtilities.h @@ -0,0 +1,26 @@ +/* +* Copyright (c) 2008-2017, 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. +*/ + +#pragma once +#include <vector> +#include <string> +#include <PxVec4.h> +#include <PxVec3.h> +#include <PxMat44.h> +class Model; +class Scene; + +bool saveBoneCapsuleData(std::string filepath, Model* model, + std::vector<physx::PxVec4>& sphereOffsets, std::vector<uint32_t>& activeSpheres, std::vector<uint32_t>& capsuleNodes); + +bool loadBoneCapsuleData(std::string filepath, Model* model, + std::vector<physx::PxVec4>& sphereOffsets, std::vector<uint32_t>& activeSpheres, std::vector<uint32_t>& capsuleNodes); + +void renderBoneLines(Scene* scene, Model* model, ModelInstance* instance, physx::PxMat44 transform = physx::PxMat44(physx::PxIdentity), float scale = 1.0f);
\ No newline at end of file diff --git a/NvCloth/samples/SampleBase/utils/ClothMeshGenerator.cpp b/NvCloth/samples/SampleBase/utils/ClothMeshGenerator.cpp index 495ba15..a0c0d3a 100644 --- a/NvCloth/samples/SampleBase/utils/ClothMeshGenerator.cpp +++ b/NvCloth/samples/SampleBase/utils/ClothMeshGenerator.cpp @@ -10,6 +10,25 @@ #include "ClothMeshGenerator.h" +#include "PsMathUtils.h" + +#include <fstream> +#include <iterator> +#include <algorithm> +#include <assert.h> + +namespace +{ + template<typename T> + std::vector<T> readValuesFromFile(const std::string& path) + { + std::ifstream inputFile(path); + std::vector<T> data{ std::istream_iterator<T>{inputFile}, { } }; + return std::move(data); + } +} // end of anonymous namespace + + void ClothMeshData::Clear() { mVertices.clear(); @@ -34,8 +53,6 @@ GeneratePlaneCloth(x,y,2,2) generates: */ -// Submesh submesh; - Clear(); mVertices.resize((segmentsX + 1) * (segmentsY + 1)); mInvMasses.resize((segmentsX + 1) * (segmentsY + 1)); @@ -144,6 +161,90 @@ GeneratePlaneCloth(x,y,2,2) generates: } } +void ClothMeshData::GenerateCylinderWave(float radiusTop, float radiusBottom, float height, float frequency, float ampitudeTop, float ampitudeBottom, int segmentsX, int segmentsY, physx::PxMat44 transform, bool attachTop, bool attachBottom, bool createQuads, int missingXsegments) +{ + Clear(); + int particleXsegments = segmentsX - std::max(0, missingXsegments - 1); + int triangleXsegments = segmentsX - missingXsegments; + assert(missingXsegments < segmentsX); + mVertices.resize((particleXsegments + 0) * (segmentsY + 1)); + mInvMasses.resize((particleXsegments + 0) * (segmentsY + 1)); + mTriangles.resize(triangleXsegments * segmentsY * 2); + if (createQuads) + mQuads.resize(triangleXsegments * segmentsY); + + mMesh.vertices.resize(mVertices.size()); + mMesh.indices.resize(3 * mTriangles.size()); + + float slopeX; + float slopeY; + { + float y = height; + float x = radiusBottom - radiusTop; + float l = sqrtf(x*x + y*y); + slopeY = x / l; + slopeX = y / l; + } + + // Vertices + for (int y = 0; y < segmentsY + 1; y++) + { + float h = height - (float)y / (float)segmentsY * height - 0.5f*height; + float ynorm = (float)y / (float)(segmentsY - 1); + float w = ynorm; + float r = radiusBottom * w + (1.0f - w) * radiusTop; + for (int x = 0; x < particleXsegments; x++) + { + float theta = (float)x / (float)segmentsX * physx::PxTwoPi; + float rw = r + cosf(frequency*theta)*(ampitudeBottom * w + (1.0f - w) * ampitudeTop); + mVertices[x + y * particleXsegments] = transform.transform(physx::PxVec3(sinf(theta)*rw, h, cosf(theta)*rw)); + mInvMasses[x + y * particleXsegments] = (y == 0 && attachTop || y == segmentsY && attachBottom) ? 0.0f : 1.0f; + + mMesh.vertices[x + y * particleXsegments].position = mVertices[x + y * particleXsegments]; + mMesh.vertices[x + y * particleXsegments].uv = physx::PxVec2((float)x / (float)particleXsegments, (float)y / (float)segmentsY); + // Not the correct normal, but we recalculate it anyway when updating the cloth mesh + mMesh.vertices[x + y * particleXsegments].normal = physx::PxVec3(cosf(theta)*slopeX, slopeY, -sinf(theta)*slopeX); + } + } + + if (createQuads) + { + // Quads + for (int y = 0; y < segmentsY; y++) + { + for (int x = 0; x < triangleXsegments; x++) + { + mQuads[(x + y * triangleXsegments)] = Quad((uint32_t)(x + 0) + (y + 0) * (particleXsegments), + (uint32_t)((x + 1) % particleXsegments) + (y + 0) * (particleXsegments), + (uint32_t)((x + 1) % particleXsegments) + (y + 1) * (particleXsegments), + (uint32_t)((x + 0) % particleXsegments) + (y + 1) * (particleXsegments)); + } + } + } + + // Triangles + for (int y = 0; y < segmentsY; y++) + { + for (int x = 0; x < triangleXsegments; x++) + { + mTriangles[(x + y * triangleXsegments) * 2 + 0] = Triangle((uint32_t)((x + 1) % particleXsegments) + (y + 1) * (particleXsegments), + (uint32_t)((x + 1) % particleXsegments) + (y + 0) * (particleXsegments), + (uint32_t)(x + 0) + (y + 0) * (particleXsegments)); + + mTriangles[(x + y * triangleXsegments) * 2 + 1] = Triangle((uint32_t)((x + 0) % particleXsegments) + (y + 1) * (particleXsegments), + (uint32_t)((x + 1) % particleXsegments) + (y + 1) * (particleXsegments), + (uint32_t)(x + 0) + (y + 0) * (particleXsegments)); + } + } + + for (int i = 0; i < (int)mTriangles.size(); i++) + { + mMesh.indices[3 * i + 0] = mTriangles[i].a; + mMesh.indices[3 * i + 1] = mTriangles[i].b; + mMesh.indices[3 * i + 2] = mTriangles[i].c; + } +} + void ClothMeshData::AttachClothPlaneByAngles(int segmentsX, int segmentsY, bool attachByWidth) { for (int y = 0; y < segmentsY + 1; y++) @@ -161,6 +262,105 @@ void ClothMeshData::AttachClothPlaneBySide(int segmentsX, int segmentsY, bool at mInvMasses[x + y * (segmentsX + 1)] = 0.0f; } +void ClothMeshData::AttachClothUsingTopVertices(float thresholdY) +{ + int topVertexIndex = -1; + physx::PxVec3 topVertex(-1e30f, -1e30f, -1e30f); + + for (int i = 0; i < (int)mVertices.size(); ++i) + { + if (mVertices[i].y > topVertex.y) + { + topVertex = mVertices[i]; + topVertexIndex = i; + } + } + NV_CLOTH_ASSERT(topVertexIndex >= 0); + + for (int i = 0; i < (int)mVertices.size(); ++i) + { + if (topVertex.y - mVertices[i].y < thresholdY) + { + mInvMasses[i] = 0.0f; + } + } +} + +bool ClothMeshData::ReadClothFromFile(const std::string& verticesPath, const std::string& indicesPath, physx::PxMat44 transform) +{ + std::vector<float> verticesXYZ = readValuesFromFile<float>(verticesPath); + std::vector<uint32_t> indices = readValuesFromFile<uint32_t>(indicesPath); + + if(verticesXYZ.size() < 3*3 || indices.size() < 3) + return false; + + return InitializeFromData<float,uint32_t>(ToBoundedData(verticesXYZ), ToBoundedData(indices), transform); +} + +template<typename PositionType, typename IndexType> +bool ClothMeshData::InitializeFromData(nv::cloth::BoundedData positions, nv::cloth::BoundedData indices, physx::PxMat44 transform) +{ + if(positions.count < 3 || indices.count < 3) + return false; + + NV_CLOTH_ASSERT(sizeof(PositionType) != physx::PxVec3 || positions.count % 3 == 0); + NV_CLOTH_ASSERT(indices.count % 3 == 0); + + auto numVertices = (sizeof(PositionType) == sizeof(physx::PxVec3)) ? positions.count : positions.count / 3; + const auto numTriangles = indices.count / 3; + + Clear(); + mVertices.resize(numVertices); + mInvMasses.resize(numVertices); + mTriangles.resize(numTriangles); + + // Quads not supported yet + //mQuads.resize(numTriangles / 2); + + mMesh.vertices.resize(mVertices.size()); + mMesh.indices.resize(3 * mTriangles.size()); + + for(int i = 0; i < (int)numVertices; ++i) + { + physx::PxVec3 pos; + if(sizeof(PositionType) == sizeof(physx::PxVec3)) + pos = positions.at<physx::PxVec3>(i); + else + pos = physx::PxVec3(positions.at<float>(i * 3), positions.at<float>(i * 3 + 1), positions.at<float>(i * 3 + 2)); + + pos = transform.transform(pos); + + mVertices[i] = pos; + mInvMasses[i] = 1.0f; + + mMesh.vertices[i].position = pos; + mMesh.vertices[i].normal = transform.transform(physx::PxVec3(0.f, 1.f, 0.f)); // TODO + mMesh.vertices[i].uv = physx::PxVec2(0.0f, 0.0f); // TODO + } + + for(int i = 0; i < (int)numTriangles; ++i) + { + mTriangles[i] = Triangle( + indices.at<IndexType>(i * 3), + indices.at<IndexType>(i * 3 + 1), + indices.at<IndexType>(i * 3 + 2) + ); + } + + for(int i = 0; i < (int)numTriangles; i++) + { + mMesh.indices[3 * i + 0] = mTriangles[i].a; + mMesh.indices[3 * i + 1] = mTriangles[i].b; + mMesh.indices[3 * i + 2] = mTriangles[i].c; + } + + return true; +} +template bool ClothMeshData::InitializeFromData<float,uint16_t>(nv::cloth::BoundedData positions, nv::cloth::BoundedData indices, physx::PxMat44 transform); +template bool ClothMeshData::InitializeFromData<float,uint32_t>(nv::cloth::BoundedData positions, nv::cloth::BoundedData indices, physx::PxMat44 transform); +template bool ClothMeshData::InitializeFromData<physx::PxVec3,uint16_t>(nv::cloth::BoundedData positions, nv::cloth::BoundedData indices, physx::PxMat44 transform); +template bool ClothMeshData::InitializeFromData<physx::PxVec3,uint32_t>(nv::cloth::BoundedData positions, nv::cloth::BoundedData indices, physx::PxMat44 transform); + void ClothMeshData::SetInvMasses(float invMass) { // Doesn't modify attached vertices @@ -178,17 +378,6 @@ void ClothMeshData::SetInvMassesFromDensity(float density) mInvMasses[i] = 1.f / density; } -template <typename T> -nv::cloth::BoundedData ToBoundedData(T& vector) -{ - nv::cloth::BoundedData d; - d.data = &vector[0]; - d.stride = sizeof(vector[0]); - d.count = (physx::PxU32)vector.size(); - - return d; -} - nv::cloth::ClothMeshDesc ClothMeshData::GetClothMeshDesc() { nv::cloth::ClothMeshDesc d; @@ -218,7 +407,7 @@ void ClothMeshData::Merge(const ClothMeshData& other) mUvs.insert(mUvs.end(), other.mUvs.begin(), other.mUvs.end()); mInvMasses.insert(mInvMasses.end(), other.mInvMasses.begin(), other.mInvMasses.end()); - mMesh.vertices.insert(mMesh.vertices.end(), mMesh.vertices.begin(), mMesh.vertices.end()); + mMesh.vertices.insert(mMesh.vertices.end(), other.mMesh.vertices.begin(), other.mMesh.vertices.end()); for(const auto& t : other.mTriangles) { diff --git a/NvCloth/samples/SampleBase/utils/ClothMeshGenerator.h b/NvCloth/samples/SampleBase/utils/ClothMeshGenerator.h index 1db4115..a765fee 100644 --- a/NvCloth/samples/SampleBase/utils/ClothMeshGenerator.h +++ b/NvCloth/samples/SampleBase/utils/ClothMeshGenerator.h @@ -18,6 +18,17 @@ struct ClothMeshData { + template <typename T> + static nv::cloth::BoundedData ToBoundedData(T& vector) + { + nv::cloth::BoundedData d; + d.data = &vector[0]; + d.stride = sizeof(vector[0]); + d.count = (physx::PxU32)vector.size(); + + return d; + } + struct Triangle { Triangle(){} @@ -46,10 +57,18 @@ struct ClothMeshData void Clear(); void GeneratePlaneCloth(float width, float height, int segmentsX, int segmentsY, bool createQuads = false, physx::PxMat44 transform = physx::PxIdentity, bool alternatingDiagonals = true, int zigzag = 0); - + void GenerateCylinderWave(float radiusTop, float radiusBottom, float height, float frequency, float ampitudeTop, float ampitudeBottom, int segmentsX, int segmentsY, physx::PxMat44 transform = physx::PxIdentity, bool attachTop = false, bool attachBottom = false, bool createQuads = false, int missingXsegments = 0); void AttachClothPlaneByAngles(int segmentsX, int segmentsY, bool attachByWidth = true); void AttachClothPlaneBySide(int segmentsX, int segmentsY, bool attachByWidth = true); + bool ReadClothFromFile(const std::string& verticesPath, const std::string& indicesPath, physx::PxMat44 transform = physx::PxIdentity); + + //positions as float (3 elements per position) + template<typename PositionType = float, typename IndexType = uint16_t> + bool InitializeFromData(nv::cloth::BoundedData positions, nv::cloth::BoundedData indices, physx::PxMat44 transform = physx::PxMat44(physx::PxIdentity)); + + void AttachClothUsingTopVertices(float thresholdY = 0.5f); + void SetInvMasses(float invMass); void SetInvMassesFromDensity(float density); // Todo diff --git a/NvCloth/samples/SampleBase/utils/DataStream.cpp b/NvCloth/samples/SampleBase/utils/DataStream.cpp new file mode 100644 index 0000000..c61badc --- /dev/null +++ b/NvCloth/samples/SampleBase/utils/DataStream.cpp @@ -0,0 +1,67 @@ +/* +* Copyright (c) 2008-2017, 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 "DataStream.h" +#include <fstream> + +DataStream::DataStream() + : + mReadPos(0), + mWritePos(0) +{ + +} +DataStream::DataStream(void* data, int sizeInBytes) + : + mReadPos(0), + mWritePos(0) +{ + write(data, sizeInBytes); +} +DataStream::DataStream(const char* filename) + : + mReadPos(0), + mWritePos(0) +{ + std::ifstream file; + file.open(filename,std::ifstream::binary | std::ios::ate); + assert(file.good()); + + std::streamsize size = file.tellg(); + mBuffer.resize(size); + file.seekg(0, std::ios::beg); + file.read(reinterpret_cast<char*>(mBuffer.data()), size); + + mWritePos = size; + file.close(); +} + +bool DataStream::fileExists(const char* filename) +{ + std::ifstream file; + file.open(filename, std::ifstream::binary | std::ios::ate); + return file.good(); +} + +void DataStream::saveToFile(const char* filename) +{ + std::ofstream file(filename, std::fstream::binary | std::fstream::trunc); + file.write(reinterpret_cast<char*>(mBuffer.data()), mBuffer.size()); + file.flush(); + file.close(); +} + +void DataStream::write(void* data, int sizeInBytes) +{ + if((int)mBuffer.size() < mWritePos + sizeInBytes) + mBuffer.resize(mWritePos + sizeInBytes); + memcpy(&mBuffer[mWritePos], data, sizeInBytes); + mWritePos += sizeInBytes; +}
\ No newline at end of file diff --git a/NvCloth/samples/SampleBase/utils/DataStream.h b/NvCloth/samples/SampleBase/utils/DataStream.h new file mode 100644 index 0000000..c47da39 --- /dev/null +++ b/NvCloth/samples/SampleBase/utils/DataStream.h @@ -0,0 +1,72 @@ +/* +* Copyright (c) 2008-2017, 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. +*/ + +#pragma once +#include <vector> +#include <string> +#include <assert.h> + +class DataStream +{ +public: + DataStream(); + DataStream(void* data, int sizeInBytes); + DataStream(const char* filename); + static bool fileExists(const char* filename); + + void saveToFile(const char* filename); + + template<typename T> + void write(T& value) + { + if(mBuffer.size() < mWritePos + sizeof(T)) + mBuffer.resize(mWritePos + sizeof(T)); + memcpy(&mBuffer[mWritePos], &value, sizeof(T)); + mWritePos += sizeof(T); + } + template<> + void write<std::string>(std::string& value) + { + int len = (int)value.length(); + if(mBuffer.size() < mWritePos + len + sizeof(int)) + mBuffer.resize(mWritePos + len + sizeof(int)); + memcpy(&mBuffer[mWritePos], &len, sizeof(int)); + mWritePos += sizeof(int); + memcpy(&mBuffer[mWritePos], value.c_str(), len); + mWritePos += len; + } + + void write(void* data, int sizeInBytes); + + template<typename T> + T read() + { + T value; + assert(mReadPos + sizeof(T) <= mBuffer.size()); + memcpy(&value, &mBuffer[mReadPos], sizeof(T)); + mReadPos += sizeof(T); + return value; + } + template<> + std::string read<std::string>() + { + int len = read<int>(); + std::string value; + value.resize(len); + assert(mReadPos + len < (int)mBuffer.size()); + memcpy(&value[0], &mBuffer[mReadPos], len); + mReadPos += len; + return value; + } +private: + std::vector<unsigned char> mBuffer; + int mReadPos; + int mWritePos; +};
\ No newline at end of file diff --git a/NvCloth/samples/SampleBase/utils/JobManager.cpp b/NvCloth/samples/SampleBase/utils/JobManager.cpp index 2a18d1c..a1e8454 100644 --- a/NvCloth/samples/SampleBase/utils/JobManager.cpp +++ b/NvCloth/samples/SampleBase/utils/JobManager.cpp @@ -41,9 +41,10 @@ void Job::Execute() else ExecuteInternal(); - mFinishedLock.lock(); - mFinished = true; - mFinishedLock.unlock(); + { + std::lock_guard<std::mutex> lock(mFinishedLock); + mFinished = true; + } mFinishedEvent.notify_one(); } @@ -53,16 +54,19 @@ void Job::AddReference() } void Job::RemoveReference() { - if (0 == --mRefCount) + int refCount = --mRefCount; + if (0 == refCount) { mParent->Submit(this); } + assert(refCount >= 0); } void Job::Wait() { std::unique_lock<std::mutex> lock(mFinishedLock); mFinishedEvent.wait(lock, [this](){return mFinished;} ); + lock.unlock(); return; } @@ -115,9 +119,12 @@ void MultithreadedSolverHelper::StartSimulation(float dt) if (mSolver->getSimulationChunkCount() != mSimulationChunkJobs.size()) { - mSimulationChunkJobs.resize(mSolver->getSimulationChunkCount(), Job()); + mSimulationChunkJobs.resize(mSolver->getSimulationChunkCount(), JobDependency()); for (int j = 0; j < mSolver->getSimulationChunkCount(); j++) - mSimulationChunkJobs[j].Initialize(mJobManager, [this, j](Job*) {mSolver->simulateChunk(j); mEndSimulationJob.RemoveReference(); }); + { + mSimulationChunkJobs[j].Initialize(mJobManager, [this, j](Job*) {mSolver->simulateChunk(j); }); + mSimulationChunkJobs[j].SetDependentJob(&mEndSimulationJob); + } } else { diff --git a/NvCloth/samples/SampleBase/utils/JobManager.h b/NvCloth/samples/SampleBase/utils/JobManager.h index 648fa33..7bb88c9 100644 --- a/NvCloth/samples/SampleBase/utils/JobManager.h +++ b/NvCloth/samples/SampleBase/utils/JobManager.h @@ -23,8 +23,8 @@ #include <queue> #include <atomic> -#include <task/PxTaskManager.h> -#include <task/PxTask.h> +#include "task/PxTaskManager.h" +#include "task/PxTask.h" namespace nv { @@ -78,9 +78,10 @@ class Job public: Job() = default; Job(const Job&); + ~Job() { mValid = false; } void Initialize(JobManager* parent, std::function<void(Job*)> function = std::function<void(Job*)>(), int refcount = 1); void Reset(int refcount = 1); //Call this before reusing a job that doesn't need to be reinitialized - void Execute(); + virtual void Execute(); void AddReference(); void RemoveReference(); void Wait(); //Block until job is finished @@ -94,6 +95,22 @@ private: bool mFinished; std::mutex mFinishedLock; std::condition_variable mFinishedEvent; + bool mValid = true; +}; + +//this Job is a dependency to another job +class JobDependency : public Job +{ +public: + void SetDependentJob(Job* job) { mDependendJob = job; } + virtual void Execute() override + { + auto dependendJob = mDependendJob; + Job::Execute(); + dependendJob->RemoveReference(); + } +private: + Job* mDependendJob; }; class JobManager @@ -134,10 +151,11 @@ public: function(i);*/ Job finalJob; finalJob.Initialize(this, std::function<void(Job*)>(), count); - Job jobs[count]; + JobDependency jobs[count]; for(int j = 0; j < count; j++) { - jobs[j].Initialize(this, [j, &finalJob, function](Job*) {function(j); finalJob.RemoveReference(); }); + jobs[j].Initialize(this, [j, &finalJob, function](Job*) {function(j); }); + jobs[j].SetDependentJob(&finalJob); jobs[j].RemoveReference(); } finalJob.Wait(); @@ -166,7 +184,7 @@ public: private: Job mStartSimulationJob; Job mEndSimulationJob; - std::vector<Job> mSimulationChunkJobs; + std::vector<JobDependency> mSimulationChunkJobs; float mDt; diff --git a/NvCloth/samples/SampleBase/utils/MeshGenerator.cpp b/NvCloth/samples/SampleBase/utils/MeshGenerator.cpp index 5541cc3..ccb3bf3 100644 --- a/NvCloth/samples/SampleBase/utils/MeshGenerator.cpp +++ b/NvCloth/samples/SampleBase/utils/MeshGenerator.cpp @@ -387,56 +387,28 @@ Mesh generateCone(physx::PxVec4 a, physx::PxVec4 b, int segments, float grow, bo //sphere a with smaller radius float cRadius = aRadius - bRadius; - physx::PxVec3 cCenter = aCenter; - - //sphere in between the a and b - physx::PxVec3 dCenter = (aCenter+bCenter)*0.5f; - float dRadius = (aCenter - bCenter).magnitude()*0.5f; - - //intersection between c and d to get tangent point - float iRadius; - physx::PxVec3 iCenter = IntersectSpheres(&iRadius, dCenter, dRadius, cCenter, cRadius); - physx::PxVec3 iPoint = iCenter + basis[0] * iRadius; //tangent point on c - physx::PxVec3 offset = (iPoint - aCenter).getNormalized(); //offset direction - - physx::PxVec3 aPoint = aCenter + offset*aRadius; - aCenter = (aPoint - aCenter).dot(basis[2])*basis[2] + aCenter; - aRadius = (aPoint - aCenter).magnitude(); - physx::PxVec3 bPoint = bCenter + offset*bRadius; - bCenter = (bPoint - aCenter).dot(basis[2])*basis[2] + aCenter; - bRadius = (bPoint - bCenter).magnitude(); - - - } - - //old code, probably wrong - /* - physx::PxVec3 pa = aCenter + aRadius*basis[0]; - physx::PxVec3 pb = bCenter + bRadius*basis[0]; - physx::PxVec3 dir = pb - pa; - - //construct plane containing pa and pb, with normal perpendicular to basis[0] - physx::PxVec3 n = basis[2].cross(dir); - physx::PxVec3 n2 = dir.cross(n); - - //line plane intersection - physx::PxVec3 focusPoint = aCenter + ((pa - aCenter).dot(n2)) / basis[2].dot(n2) * basis[2]; - - { - float focusDistance = (focusPoint - aCenter).magnitude(); - //make circle with center in mid point between focusPoint and aCenter - physx::PxVec3 cCenter = (focusPoint + aCenter)*0.5f; - float cRadius = focusDistance*0.5f; - - aCenter = IntersectSpheres(&aRadius, aCenter, aRadius, cCenter, cRadius); + if(cRadius > 0.00001) + { + physx::PxVec3 cCenter = aCenter; + + //sphere in between the a and b + physx::PxVec3 dCenter = (aCenter + bCenter)*0.5f; + float dRadius = (aCenter - bCenter).magnitude()*0.5f; + + //intersection between c and d to get tangent point + float iRadius; + physx::PxVec3 iCenter = IntersectSpheres(&iRadius, dCenter, dRadius, cCenter, cRadius); + physx::PxVec3 iPoint = iCenter + basis[0] * iRadius; //tangent point on c + physx::PxVec3 offset = (iPoint - aCenter).getNormalized(); //offset direction + + physx::PxVec3 aPoint = aCenter + offset*aRadius; + aCenter = (aPoint - aCenter).dot(basis[2])*basis[2] + aCenter; + aRadius = (aPoint - aCenter).magnitude(); + physx::PxVec3 bPoint = bCenter + offset*bRadius; + bCenter = (bPoint - aCenter).dot(basis[2])*basis[2] + aCenter; + bRadius = (bPoint - bCenter).magnitude(); + } } - - { - float focusDistance = (focusPoint - bCenter).magnitude(); - physx::PxVec3 cCenter = (focusPoint + bCenter)*0.5f; - float cRadius = focusDistance*0.5f; - bCenter = IntersectSpheres(&bRadius, bCenter, bRadius, cCenter, cRadius); - }*/ } @@ -491,7 +463,7 @@ Mesh generateCollisionCapsules(physx::PxVec4* spheres, int sphereCount, uint32_t Mesh finalMesh; for(int i = 0; i < sphereCount; i++) { - Mesh sphere = generateIcosahedron(spheres[i].w+ grow, 4); + Mesh sphere = generateIcosahedron(spheres[i].w+ grow, 2); sphere.applyTransfom(physx::PxTransform(spheres[i].getXYZ())); finalMesh.merge(sphere); } @@ -504,6 +476,272 @@ Mesh generateCollisionCapsules(physx::PxVec4* spheres, int sphereCount, uint32_t return finalMesh; } +::SimpleMesh generateFastSphere(int segmentsX, int segmentY, physx::PxMat44 transform) +{ + SimpleMesh mesh; + const int xSegments = segmentsX; + const int ySegments = segmentY; + + { + //bottom + SimpleMesh::Vertex v; + v.position = physx::PxVec3(0.0f, -1.0f, 0.0f); + v.normal = transform.rotate(physx::PxVec4(v.position, 0.0f)).getXYZ(); + v.position = transform.transform(v.position); + v.uv = physx::PxVec2(0.0f, 0.0f); + mesh.vertices.push_back(v); + } + + //middle + for(int y = 1; y < ySegments; y++) + { + for(int x = 0; x < xSegments; x++) + { + float xf = (float)x / (xSegments - 1.0f); + float yaw = xf*physx::PxTwoPi; + float yf = (float)y / (ySegments); + float pitch = (yf - 0.5f)*physx::PxPi; + + SimpleMesh::Vertex v; + v.position = physx::PxVec3(cos(yaw)*cos(pitch), sin(pitch), sin(yaw)*cos(pitch)); + v.uv = physx::PxVec2(xf, yf); + v.normal = transform.rotate(physx::PxVec4(v.position, 0.0f)).getXYZ(); + v.position = transform.transform(v.position); + mesh.vertices.push_back(v); + } + } + + { + //top + SimpleMesh::Vertex v; + v.position = physx::PxVec3(0.0f, 1.0f, 0.0f); + v.normal = transform.rotate(physx::PxVec4(v.position,0.0f)).getXYZ(); + v.position = transform.transform(v.position); + v.uv = physx::PxVec2(0.0f, 1.0f); + mesh.vertices.push_back(v); + } + + //bottom cap + for(int x = 0; x < xSegments; x++) + { + mesh.indices.push_back(0); + mesh.indices.push_back(1 + x); + mesh.indices.push_back(1 + (x + 1) % xSegments); + } + + const auto RingVertex = [xSegments, ySegments](int x, int y) + { + return 1 + y*xSegments + x%xSegments; + }; + + //middle + for(int y = 0; y < ySegments - 2; y++) + { + for(int x = 0; x < xSegments; x++) + { + mesh.indices.push_back(RingVertex(x, y)); + mesh.indices.push_back(RingVertex(x + 1, y)); + mesh.indices.push_back(RingVertex(x, y + 1)); + + mesh.indices.push_back(RingVertex(x + 1, y)); + mesh.indices.push_back(RingVertex(x + 1, y + 1)); + mesh.indices.push_back(RingVertex(x, y + 1)); + } + } + + //bottom cap + for(int x = 0; x < xSegments; x++) + { + mesh.indices.push_back((uint16_t)mesh.vertices.size() - 1); + mesh.indices.push_back(RingVertex(x, ySegments - 2)); + mesh.indices.push_back(RingVertex(x + 1, ySegments - 2)); + } + + return mesh; +} + +::SimpleMesh generateFastCylinder(int segmentsX, int segmentY, physx::PxMat44 transform) +{ + SimpleMesh mesh; + const int xSegments = segmentsX; + const int ySegments = segmentY; + + + //middle + for(int y = 0; y < ySegments+1; y++) + { + for(int x = 0; x < xSegments; x++) + { + float xf = (float)x / (xSegments - 1.0f); + float yaw = xf*physx::PxTwoPi; + float yf = (float)y / (ySegments) * 2.0f - 1.0f; + + SimpleMesh::Vertex v; + v.position = physx::PxVec3(cos(yaw), yf, sin(yaw)); + v.uv = physx::PxVec2(xf, yf); + v.normal = transform.rotate(physx::PxVec4(physx::PxVec3(cos(yaw), 0.0f, sin(yaw)), 0.0f)).getXYZ(); + v.position = transform.transform(v.position); + mesh.vertices.push_back(v); + } + } + + + const auto RingVertex = [xSegments, ySegments](int x, int y) + { + return y*xSegments + x%xSegments; + }; + + //middle + for(int y = 0; y < ySegments; y++) + { + for(int x = 0; x < xSegments; x++) + { + mesh.indices.push_back(RingVertex(x, y)); + mesh.indices.push_back(RingVertex(x + 1, y)); + mesh.indices.push_back(RingVertex(x, y + 1)); + + mesh.indices.push_back(RingVertex(x + 1, y)); + mesh.indices.push_back(RingVertex(x + 1, y + 1)); + mesh.indices.push_back(RingVertex(x, y + 1)); + } + } + + return mesh; +} + +::SimpleMesh generateCollisionCapsulesFast(physx::PxVec4* spheres, int sphereCount, uint32_t* indices, int indexCount, float grow) +{ + static ::SimpleMesh sphere = generateFastSphere(24,12,physx::PxTransform(physx::PxVec3(0.0f, 0.0f, 0.0f), physx::PxQuat(0.0f, physx::PxVec3(0.0f, 1.0f, 0.0f)))); + static ::SimpleMesh cylinder = generateFastCylinder(24, 1, physx::PxTransform(physx::PxVec3(0.0f, 1.0f, 0.0f), physx::PxQuat(0.0f, physx::PxVec3(0.0f, 1.0f, 0.0f)))); + + ::SimpleMesh mesh; + mesh.vertices.resize(sphere.vertices.size()*sphereCount + cylinder.vertices.size()*(indexCount / 2)); + mesh.indices.resize(sphere.indices.size()*sphereCount + cylinder.indices.size()*(indexCount / 2)); + + int nextVertex = 0; + int nextIndex = 0; + for(int i = 0; i < sphereCount; i++) + { + int baseIndex = nextVertex; + physx::PxMat44 transform = + physx::PxMat44(physx::PxMat33(physx::PxIdentity), spheres[i].getXYZ()) + * physx::PxMat44(PxVec4(spheres[i].w + grow, spheres[i].w + grow, spheres[i].w + grow, 1.0f)); + + for(int vi = 0; vi<(int)sphere.vertices.size(); vi++) + { + SimpleMesh::Vertex v = sphere.vertices[vi]; + v.normal = transform.rotate(physx::PxVec4(v.normal, 0.0f)).getXYZ(); + v.position = transform.transform(v.position); + mesh.vertices[nextVertex++] = v; + } + + for(int ii = 0; ii < (int)sphere.indices.size(); ii++) + { + mesh.indices[nextIndex++] = sphere.indices[ii]+ baseIndex; + } + } + + for(int i = 0; i < indexCount; i+=2) + { + int baseIndex = nextVertex; + + physx::PxVec3 spherePosA = spheres[indices[i]].getXYZ(); + physx::PxVec3 spherePosB = spheres[indices[i+1]].getXYZ(); + float sphereRadiusA = spheres[indices[i]].w + grow; + float sphereRadiusB = spheres[indices[i + 1]].w + grow; + + if(sphereRadiusA < sphereRadiusB) + { + std::swap(sphereRadiusA, sphereRadiusB); + std::swap(spherePosA, spherePosB); + } + + { + //http://jwilson.coe.uga.edu/emt669/Student.Folders/Kertscher.Jeff/Essay.3/Tangents.html + + //sphere a with smaller radius + float cRadius = sphereRadiusA - sphereRadiusB; + if(cRadius > 0.00001) + { + physx::PxVec3 basis[3]; + basis[2] = spherePosB - spherePosA; + basis[2].normalize(); + computeBasis(basis[2], &basis[0], &basis[1]); + + physx::PxVec3 cCenter = spherePosA; + + //sphere in between the a and b + physx::PxVec3 dCenter = (spherePosA + spherePosB)*0.5f; + float dRadius = (spherePosA - spherePosB).magnitude()*0.5f; + + //intersection between c and d to get tangent point + float iRadius; + physx::PxVec3 iCenter = IntersectSpheres(&iRadius, dCenter, dRadius, cCenter, cRadius); + physx::PxVec3 iPoint = iCenter + basis[0] * iRadius; //tangent point on c + physx::PxVec3 offset = (iPoint - spherePosA).getNormalized(); //offset direction + + physx::PxVec3 aPoint = spherePosA + offset*sphereRadiusA; + spherePosA = (aPoint - spherePosA).dot(basis[2])*basis[2] + spherePosA; + sphereRadiusA = (aPoint - spherePosA).magnitude(); + physx::PxVec3 bPoint = spherePosB + offset*sphereRadiusB; + spherePosB = (bPoint - spherePosA).dot(basis[2])*basis[2] + spherePosA; + sphereRadiusB = (bPoint - spherePosB).magnitude(); + } + } + + float length = (spherePosB - spherePosA).magnitude(); + + + physx::PxMat44 scaleA = physx::PxMat44(PxVec4(sphereRadiusA, length/2.0f, sphereRadiusA+grow, 1.0f)); + physx::PxMat44 scaleB = physx::PxMat44(PxVec4(sphereRadiusB, length/2.0f, sphereRadiusB, 1.0f)); + + physx::PxQuat orientation; + { + physx::PxVec3 u = physx::PxVec3(0.0f, 1.0f, 0.0f); + physx::PxVec3 v = spherePosB - spherePosA; + v.normalize(); + + if(u.dot(v) < -0.9999) + orientation = physx::PxQuat(physx::PxTwoPi, physx::PxVec3(1.0f, 0.0f, 0.0f)); + else if(u.dot(v) > 0.9999) + orientation = physx::PxQuat(0.0f, physx::PxVec3(1.0f, 0.0f, 0.0f)); + else + { + physx::PxVec3 half = u + v; + half.normalize(); + physx::PxVec3 imaginary = u.cross(half); + orientation = physx::PxQuat(imaginary.x, imaginary.y, imaginary.z, u.dot(half)); + } + } + + physx::PxMat44 transform = physx::PxMat44(physx::PxTransform(spherePosA, orientation))*scaleA; + + int firstRing = (int)cylinder.vertices.size() / 2; + for(int vi = 0; vi<firstRing; vi++) + { + SimpleMesh::Vertex v = cylinder.vertices[vi]; + v.normal = transform.rotate(physx::PxVec4(v.normal, 0.0f)).getXYZ(); + v.position = transform.transform(v.position); + mesh.vertices[nextVertex++] = v; + } + transform = physx::PxMat44(physx::PxTransform(spherePosA, orientation))*scaleB; + for(int vi = firstRing; vi<(int)cylinder.vertices.size(); vi++) + { + SimpleMesh::Vertex v = cylinder.vertices[vi]; + v.normal = transform.rotate(physx::PxVec4(v.normal, 0.0f)).getXYZ(); + v.position = transform.transform(v.position); + mesh.vertices[nextVertex++] = v; + } + + for(int ii = 0; ii < (int)cylinder.indices.size(); ii++) + { + mesh.indices[nextIndex++] = cylinder.indices[ii] + baseIndex; + } + } + + return mesh; +} + uint32_t generateConvexPolyhedronPlanes(int segmentsX, int segmentsY, physx::PxVec3 center, float radius, std::vector<physx::PxVec4>* planes) { int offset = 0; @@ -546,7 +784,7 @@ MeshGeneratorRenderMesh::MeshGeneratorRenderMesh(const Mesh mesh) layout.push_back({"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0}); layout.push_back({"NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0}); - initialize(vertices, (uint32_t)vertexCount, sizeof(RenderVertex), layout, indices, indexCount); + initialize(vertices, (uint32_t)vertexCount, sizeof(RenderVertex), layout, indices, indexCount, 0); delete vertices; delete indices; @@ -567,11 +805,22 @@ MeshGeneratorRenderMeshSmooth::MeshGeneratorRenderMeshSmooth(const Mesh mesh) layout.push_back({"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0}); layout.push_back({"NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0}); - initialize(vertices, (uint32_t)vertexCount, sizeof(RenderVertex), layout, indices, indexCount); + initialize(vertices, (uint32_t)vertexCount, sizeof(RenderVertex), layout, indices, indexCount, 0); delete vertices; delete indices; } + +MeshGeneratorRenderMeshSmooth::MeshGeneratorRenderMeshSmooth(const ::SimpleMesh mesh, int flags) +{ + std::vector<D3D11_INPUT_ELEMENT_DESC> layout; + layout.push_back({"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0}); + layout.push_back({"NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0}); + layout.push_back({"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0}); + + initialize(mesh.vertices.data(), (uint32_t)mesh.vertices.size(), sizeof(::SimpleMesh::Vertex), layout, mesh.indices.data(), (uint32_t)mesh.indices.size(), flags); +} + MeshGeneratorRenderMeshSmooth::~MeshGeneratorRenderMeshSmooth() { diff --git a/NvCloth/samples/SampleBase/utils/MeshGenerator.h b/NvCloth/samples/SampleBase/utils/MeshGenerator.h index 4f4b0c9..2e0344e 100644 --- a/NvCloth/samples/SampleBase/utils/MeshGenerator.h +++ b/NvCloth/samples/SampleBase/utils/MeshGenerator.h @@ -14,6 +14,7 @@ #include <vector> #include "renderer/CustomRenderMesh.h" #include <foundation/PxVec3.h> +#include "renderer/Mesh.h" namespace MeshGenerator { @@ -92,6 +93,13 @@ Mesh generateCone(physx::PxVec4 a, physx::PxVec4 b, int segments, float grow, bo Mesh generateCollisionConvex(physx::PxVec4* planes, uint32_t mask, float grow, bool flip); Mesh generateCollisionCapsules(physx::PxVec4* spheres, int sphereCount, uint32_t* indices, int indexCount, float grow); +//Generates simple meshes with smooth shading +::SimpleMesh generateFastSphere(int segmentsX, int segmentY, physx::PxMat44 transform); +::SimpleMesh generateFastCylinder(int segmentsX, int segmentY, physx::PxMat44 transform); //no caps + +//Combines cashed spheres and cylinders to generate the capsules +::SimpleMesh generateCollisionCapsulesFast(physx::PxVec4* spheres, int sphereCount, uint32_t* indices, int indexCount, float grow); + uint32_t generateConvexPolyhedronPlanes(int segmentsX, int segmentsY, physx::PxVec3 center, float radius, std::vector<physx::PxVec4>* planes); class MeshGeneratorRenderMesh : public CustomRenderMesh @@ -105,6 +113,7 @@ class MeshGeneratorRenderMeshSmooth : public CustomRenderMesh { public: MeshGeneratorRenderMeshSmooth(const Mesh mesh); + MeshGeneratorRenderMeshSmooth(const ::SimpleMesh mesh, int flags = 0); //flags from CustomRenderMesh virtual ~MeshGeneratorRenderMeshSmooth(); }; |