aboutsummaryrefslogtreecommitdiff
path: root/NvCloth/samples/SampleBase/renderer
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/renderer
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/renderer')
-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
16 files changed, 1480 insertions, 62 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