aboutsummaryrefslogtreecommitdiff
path: root/NvCloth/samples/SampleBase
diff options
context:
space:
mode:
authorMarijn Tamis <[email protected]>2018-05-03 18:22:48 +0200
committerMarijn Tamis <[email protected]>2018-05-03 18:22:48 +0200
commitca32c59a58d37c1822e185a2d5f3d0d3e8943593 (patch)
treeb06b9eec03f34344ef8fc31aa147b2714d3962ee /NvCloth/samples/SampleBase
parentForced rename of platform folders in cmake dir. Git didn't pick this up before. (diff)
downloadnvcloth-ca32c59a58d37c1822e185a2d5f3d0d3e8943593.tar.xz
nvcloth-ca32c59a58d37c1822e185a2d5f3d0d3e8943593.zip
NvCloth 1.1.4 Release. (24070740)
Diffstat (limited to 'NvCloth/samples/SampleBase')
-rw-r--r--NvCloth/samples/SampleBase/renderer/ClothRenderMesh.cpp32
-rw-r--r--NvCloth/samples/SampleBase/renderer/ClothRenderMesh.h12
-rw-r--r--NvCloth/samples/SampleBase/renderer/CustomRenderMesh.cpp58
-rw-r--r--NvCloth/samples/SampleBase/renderer/CustomRenderMesh.h19
-rw-r--r--NvCloth/samples/SampleBase/renderer/Mesh.h4
-rw-r--r--NvCloth/samples/SampleBase/renderer/Model.cpp520
-rw-r--r--NvCloth/samples/SampleBase/renderer/Model.h421
-rw-r--r--NvCloth/samples/SampleBase/renderer/PrimitiveRenderMesh.cpp6
-rw-r--r--NvCloth/samples/SampleBase/renderer/Renderable.cpp55
-rw-r--r--NvCloth/samples/SampleBase/renderer/Renderable.h40
-rw-r--r--NvCloth/samples/SampleBase/renderer/Renderer.cpp54
-rw-r--r--NvCloth/samples/SampleBase/renderer/Renderer.h2
-rw-r--r--NvCloth/samples/SampleBase/renderer/SkinnedRenderMesh.cpp2
-rw-r--r--NvCloth/samples/SampleBase/renderer/SkinnedRenderMesh.h2
-rw-r--r--NvCloth/samples/SampleBase/renderer/WeightedSkinRenderMesh.cpp219
-rw-r--r--NvCloth/samples/SampleBase/renderer/WeightedSkinRenderMesh.h96
-rw-r--r--NvCloth/samples/SampleBase/scene/Scene.cpp133
-rw-r--r--NvCloth/samples/SampleBase/scene/Scene.h10
-rw-r--r--NvCloth/samples/SampleBase/scene/SceneController.cpp19
-rw-r--r--NvCloth/samples/SampleBase/scene/SceneController.h9
-rw-r--r--NvCloth/samples/SampleBase/scene/scenes/CCDScene2.cpp155
-rw-r--r--NvCloth/samples/SampleBase/scene/scenes/CCDScene2.h37
-rw-r--r--NvCloth/samples/SampleBase/scene/scenes/InterCollisionScene.cpp2
-rw-r--r--NvCloth/samples/SampleBase/scene/scenes/InterCollisionScene.h4
-rw-r--r--NvCloth/samples/SampleBase/scene/scenes/SelfCollisionScene.cpp21
-rw-r--r--NvCloth/samples/SampleBase/scene/scenes/TeleportScene.cpp148
-rw-r--r--NvCloth/samples/SampleBase/scene/scenes/TeleportScene.h41
-rw-r--r--NvCloth/samples/SampleBase/scene/scenes/TimeStepScene.cpp114
-rw-r--r--NvCloth/samples/SampleBase/scene/scenes/TimeStepScene.h36
-rw-r--r--NvCloth/samples/SampleBase/scene/scenes/VirtualParticleScene.cpp171
-rw-r--r--NvCloth/samples/SampleBase/scene/scenes/VirtualParticleScene.h37
-rw-r--r--NvCloth/samples/SampleBase/task/PxCpuDispatcher.h79
-rw-r--r--NvCloth/samples/SampleBase/task/PxGpuDispatcher.h248
-rw-r--r--NvCloth/samples/SampleBase/task/PxGpuTask.h118
-rw-r--r--NvCloth/samples/SampleBase/task/PxTask.h335
-rw-r--r--NvCloth/samples/SampleBase/task/PxTaskDefine.h37
-rw-r--r--NvCloth/samples/SampleBase/task/PxTaskManager.h231
-rw-r--r--NvCloth/samples/SampleBase/ui/CommonUIController.cpp8
-rw-r--r--NvCloth/samples/SampleBase/ui/CommonUIController.h2
-rw-r--r--NvCloth/samples/SampleBase/utils/AnimatedModelUtilities.cpp102
-rw-r--r--NvCloth/samples/SampleBase/utils/AnimatedModelUtilities.h26
-rw-r--r--NvCloth/samples/SampleBase/utils/ClothMeshGenerator.cpp217
-rw-r--r--NvCloth/samples/SampleBase/utils/ClothMeshGenerator.h21
-rw-r--r--NvCloth/samples/SampleBase/utils/DataStream.cpp67
-rw-r--r--NvCloth/samples/SampleBase/utils/DataStream.h72
-rw-r--r--NvCloth/samples/SampleBase/utils/JobManager.cpp19
-rw-r--r--NvCloth/samples/SampleBase/utils/JobManager.h30
-rw-r--r--NvCloth/samples/SampleBase/utils/MeshGenerator.cpp353
-rw-r--r--NvCloth/samples/SampleBase/utils/MeshGenerator.h9
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();
};