diff options
| author | Marijn Tamis <[email protected]> | 2018-05-03 18:22:48 +0200 |
|---|---|---|
| committer | Marijn Tamis <[email protected]> | 2018-05-03 18:22:48 +0200 |
| commit | ca32c59a58d37c1822e185a2d5f3d0d3e8943593 (patch) | |
| tree | b06b9eec03f34344ef8fc31aa147b2714d3962ee /NvCloth/samples/SampleBase/renderer | |
| parent | Forced rename of platform folders in cmake dir. Git didn't pick this up before. (diff) | |
| download | nvcloth-ca32c59a58d37c1822e185a2d5f3d0d3e8943593.tar.xz nvcloth-ca32c59a58d37c1822e185a2d5f3d0d3e8943593.zip | |
NvCloth 1.1.4 Release. (24070740)
Diffstat (limited to 'NvCloth/samples/SampleBase/renderer')
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 |