diff options
| author | Bryan Galdrikian <[email protected]> | 2017-02-21 12:07:59 -0800 |
|---|---|---|
| committer | Bryan Galdrikian <[email protected]> | 2017-02-21 12:07:59 -0800 |
| commit | 446ce137c6823ba9eff273bdafdaf266287c7c98 (patch) | |
| tree | d20aab3e2ed08d7b3ca71c2f40db6a93ea00c459 /NvBlast/samples/SampleBase/blast | |
| download | archived-blast-1.0.0-beta.tar.xz archived-blast-1.0.0-beta.zip | |
first commitv1.0.0-beta
Diffstat (limited to 'NvBlast/samples/SampleBase/blast')
24 files changed, 3252 insertions, 0 deletions
diff --git a/NvBlast/samples/SampleBase/blast/BlastAsset.cpp b/NvBlast/samples/SampleBase/blast/BlastAsset.cpp new file mode 100644 index 0000000..b3b5d84 --- /dev/null +++ b/NvBlast/samples/SampleBase/blast/BlastAsset.cpp @@ -0,0 +1,28 @@ +/* +* Copyright (c) 2008-2015, 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 "BlastAsset.h" +#include "NvBlastExtPxAsset.h" +#include "NvBlastTkAsset.h" + + +BlastAsset::BlastAsset(Renderer& renderer) + : m_renderer(renderer) +{ +} + +void BlastAsset::validate() +{ +} + +size_t BlastAsset::getBlastAssetSize() const +{ + return m_pxAsset->getTkAsset().getDataSize(); +} diff --git a/NvBlast/samples/SampleBase/blast/BlastAsset.h b/NvBlast/samples/SampleBase/blast/BlastAsset.h new file mode 100644 index 0000000..4740791 --- /dev/null +++ b/NvBlast/samples/SampleBase/blast/BlastAsset.h @@ -0,0 +1,96 @@ +/* +* Copyright (c) 2008-2015, 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 BLAST_ASSET_H +#define BLAST_ASSET_H + +#include <memory> +#include "PxTransform.h" +#include "NvBlastTypes.h" + + +using namespace physx; + +class Renderer; +class BlastFamily; +class PhysXController; + +namespace Nv +{ +namespace Blast +{ +class ExtPxFamily; +class ExtPxAsset; +class ExtPxManager; +class TkGroup; +} +} + +using namespace Nv::Blast; + +typedef std::shared_ptr<BlastFamily> BlastFamilyPtr; + + +class BlastAsset +{ +public: + //////// ctor //////// + + BlastAsset(Renderer& renderer); + virtual ~BlastAsset() {} + + + //////// desc //////// + + /** + Descriptor with actor initial settings. + */ + struct ActorDesc + { + NvBlastID id; + PxTransform transform; + TkGroup* group; + }; + + + //////// abstract //////// + + virtual BlastFamilyPtr createFamily(PhysXController& physXConroller, ExtPxManager& pxManager, const ActorDesc& desc) = 0; + + + //////// data getters //////// + + const ExtPxAsset* getPxAsset() const + { + return m_pxAsset; + } + + size_t getBlastAssetSize() const; + + +protected: + //////// internal operations //////// + + void validate(); + + + //////// input data //////// + + Renderer& m_renderer; + + + //////// internal data //////// + + ExtPxAsset* m_pxAsset; +}; + + + +#endif //BLAST_ASSET_H
\ No newline at end of file diff --git a/NvBlast/samples/SampleBase/blast/BlastAssetBoxes.cpp b/NvBlast/samples/SampleBase/blast/BlastAssetBoxes.cpp new file mode 100644 index 0000000..dfa0138 --- /dev/null +++ b/NvBlast/samples/SampleBase/blast/BlastAssetBoxes.cpp @@ -0,0 +1,84 @@ +/* +* Copyright (c) 2008-2015, 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 "BlastAssetBoxes.h" +#include "BlastFamilyBoxes.h" +#include "NvBlastExtPxAsset.h" +#include "PxPhysics.h" +#include "cooking/PxCooking.h" + + +BlastAssetBoxes::BlastAssetBoxes(TkFramework& framework, PxPhysics& physics, PxCooking& cooking, Renderer& renderer, const Desc& desc) + : BlastAsset(renderer) +{ + // generate boxes slices procedurally + CubeAssetGenerator::generate(m_generatorAsset, desc.generatorSettings); + + // asset desc / tk asset + ExtPxAssetDesc assetDesc; + assetDesc.chunkDescs = &m_generatorAsset.solverChunks[0]; + assetDesc.chunkCount = (uint32_t)m_generatorAsset.solverChunks.size(); + assetDesc.bondDescs = m_generatorAsset.solverBonds.size() > 0 ? &m_generatorAsset.solverBonds[0] : nullptr; + assetDesc.bondCount = (uint32_t)m_generatorAsset.solverBonds.size(); + std::vector<uint8_t> bondFlags(assetDesc.bondCount); + std::fill(bondFlags.begin(), bondFlags.end(), desc.jointAllBonds ? 1 : 0); + assetDesc.bondFlags = bondFlags.data(); + + // box convex + PxVec3 vertices[8] = { { -1, -1, -1 }, { -1, -1, 1 }, { -1, 1, -1 }, { -1, 1, 1 }, { 1, -1, -1 }, { 1, -1, 1 }, { 1, 1, -1 }, { 1, 1, 1 } }; + PxConvexMeshDesc convexMeshDesc; + convexMeshDesc.points.count = 8; + convexMeshDesc.points.data = vertices; + convexMeshDesc.points.stride = sizeof(PxVec3); + convexMeshDesc.flags = PxConvexFlag::eCOMPUTE_CONVEX; + m_boxMesh = cooking.createConvexMesh(convexMeshDesc, physics.getPhysicsInsertionCallback()); + + // prepare chunks + const uint32_t chunkCount = (uint32_t)m_generatorAsset.solverChunks.size(); + std::vector<ExtPxAssetDesc::ChunkDesc> pxChunks(chunkCount); + std::vector<ExtPxAssetDesc::SubchunkDesc> pxSubchunks; + pxSubchunks.reserve(chunkCount); + for (uint32_t i = 0; i < m_generatorAsset.solverChunks.size(); i++) + { + uint32_t chunkID = m_generatorAsset.solverChunks[i].userData; + GeneratorAsset::BlastChunkCube& cube = m_generatorAsset.chunks[chunkID]; + PxVec3 position = *reinterpret_cast<PxVec3*>(&cube.position); + PxVec3 extents = *reinterpret_cast<PxVec3*>(&cube.extents); + ExtPxAssetDesc::ChunkDesc& chunk = pxChunks[chunkID]; + ExtPxAssetDesc::SubchunkDesc subchunk = + { + PxTransform(position), + PxConvexMeshGeometry(m_boxMesh, PxMeshScale(extents / 2)) + }; + pxSubchunks.push_back(subchunk); + chunk.subchunks = &pxSubchunks.back(); + chunk.subchunkCount = 1; + chunk.isStatic = (position.y - (extents.y - desc.generatorSettings.extents.y) / 2) <= desc.staticHeight; + } + + // create asset + assetDesc.pxChunks = pxChunks.data(); + m_pxAsset = ExtPxAsset::create(assetDesc, framework); + + validate(); +} + + +BlastAssetBoxes::~BlastAssetBoxes() +{ + m_boxMesh->release(); + m_pxAsset->release(); +} + + +BlastFamilyPtr BlastAssetBoxes::createFamily(PhysXController& physXConroller, ExtPxManager& pxManager, const ActorDesc& desc) +{ + return BlastFamilyPtr(new BlastFamilyBoxes(physXConroller, pxManager, m_renderer, *this, desc)); +} diff --git a/NvBlast/samples/SampleBase/blast/BlastAssetBoxes.h b/NvBlast/samples/SampleBase/blast/BlastAssetBoxes.h new file mode 100644 index 0000000..518e93d --- /dev/null +++ b/NvBlast/samples/SampleBase/blast/BlastAssetBoxes.h @@ -0,0 +1,56 @@ +/* +* Copyright (c) 2008-2015, 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 BLAST_ASSET_BOXES_H +#define BLAST_ASSET_BOXES_H + +#include "BlastAsset.h" +#include "AssetGenerator.h" +#include "PxConvexMesh.h" + + +namespace physx +{ +class PxPhysics; +class PxCooking; +} + +namespace Nv +{ +namespace Blast +{ +class TkFramework; +} +} + + +class BlastAssetBoxes : public BlastAsset +{ +public: + struct Desc + { + CubeAssetGenerator::Settings generatorSettings; + float staticHeight; + bool jointAllBonds; + }; + + BlastAssetBoxes(TkFramework& framework, PxPhysics& physics, PxCooking& cooking, Renderer& renderer, const Desc& desc); + virtual ~BlastAssetBoxes(); + + BlastFamilyPtr createFamily(PhysXController& physXConroller, ExtPxManager& pxManager, const ActorDesc& desc); + +private: + PxConvexMesh* m_boxMesh; + GeneratorAsset m_generatorAsset; +}; + + + +#endif //BLAST_ASSET_BOXES_H
\ No newline at end of file diff --git a/NvBlast/samples/SampleBase/blast/BlastAssetModel.cpp b/NvBlast/samples/SampleBase/blast/BlastAssetModel.cpp new file mode 100644 index 0000000..c42215e --- /dev/null +++ b/NvBlast/samples/SampleBase/blast/BlastAssetModel.cpp @@ -0,0 +1,64 @@ +/* +* Copyright (c) 2008-2015, 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 "BlastAssetModel.h" +#include "Renderer.h" +#include "BlastController.h" +#include "Utils.h" +#include "ResourceManager.h" +#include "PsFileBuffer.h" +#include "NvBlastExtPxAsset.h" +#include <sstream> + + +BlastAssetModel::BlastAssetModel(TkFramework& framework, PxPhysics& physics, PxCooking& cooking, Renderer& renderer, const char* modelName) + : BlastAsset(renderer) +{ + ResourceManager& resourceManager = m_renderer.getResourceManager(); + + // Physics Asset + std::ostringstream blastFileName; + blastFileName << modelName << ".bpxa"; + std::string path; + if (resourceManager.findFile(blastFileName.str(), path)) + { + PsFileBuffer fileBuf(path.c_str(), PxFileBuf::OPEN_READ_ONLY); + m_pxAsset = ExtPxAsset::deserialize(fileBuf, framework, physics); + ASSERT_PRINT(m_pxAsset != nullptr, "can't load bpxa file"); + } + else + { + ASSERT_PRINT(false, "wrong blastFilename"); + } + + // load obj file + std::ostringstream objFileName; + objFileName << modelName << ".obj"; + if (resourceManager.findFile(objFileName.str(), path)) + { + m_model = BlastModel::loadFromFileTinyLoader(path.c_str()); + if (!m_model) + { + ASSERT_PRINT(false, "obj load failed"); + } + } + else + { + ASSERT_PRINT(false, "wrong objFileName"); + } + + validate(); +} + + +BlastAssetModel::~BlastAssetModel() +{ + m_pxAsset->release(); +} diff --git a/NvBlast/samples/SampleBase/blast/BlastAssetModel.h b/NvBlast/samples/SampleBase/blast/BlastAssetModel.h new file mode 100644 index 0000000..03045d4 --- /dev/null +++ b/NvBlast/samples/SampleBase/blast/BlastAssetModel.h @@ -0,0 +1,55 @@ +/* +* Copyright (c) 2008-2015, 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 BLAST_ASSET_MODEL_H +#define BLAST_ASSET_MODEL_H + +#include "BlastAsset.h" +#include "BlastModel.h" + + +namespace physx +{ +class PxPhysics; +class PxCooking; +} + +namespace Nv +{ +namespace Blast +{ +class TkFramework; +} +} + + +class BlastAssetModel : public BlastAsset +{ +public: + //////// ctor //////// + + BlastAssetModel(TkFramework& framework, PxPhysics& physics, PxCooking& cooking, Renderer& renderer, const char* modelName); + virtual ~BlastAssetModel(); + + + //////// data getters //////// + + const BlastModel& getModel() const + { + return *m_model.get(); + } + +private: + //////// private internal data //////// + + BlastModelPtr m_model; +}; + +#endif //BLAST_ASSET_MODEL_H
\ No newline at end of file diff --git a/NvBlast/samples/SampleBase/blast/BlastAssetModelSimple.cpp b/NvBlast/samples/SampleBase/blast/BlastAssetModelSimple.cpp new file mode 100644 index 0000000..fa423e2 --- /dev/null +++ b/NvBlast/samples/SampleBase/blast/BlastAssetModelSimple.cpp @@ -0,0 +1,51 @@ +/* +* Copyright (c) 2008-2015, 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 "BlastAssetModelSimple.h" +#include "BlastFamilyModelSimple.h" +#include "CustomRenderMesh.h" +#include "Renderer.h" + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// BlastAssetModelSimple +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +BlastAssetModelSimple::BlastAssetModelSimple(TkFramework& framework, PxPhysics& physics, PxCooking& cooking, Renderer& renderer, const char* modelName) + : BlastAssetModel(framework, physics, cooking, renderer, modelName) +{ + // prepare materials + for (const BlastModel::Material& material : getModel().materials) + { + if (material.diffuseTexture.empty()) + m_renderMaterials.push_back(new RenderMaterial(renderer.getResourceManager(), "model_simple")); + else + m_renderMaterials.push_back(new RenderMaterial(renderer.getResourceManager(), "model_simple_textured", material.diffuseTexture.c_str())); + } + + validate(); +} + + +BlastAssetModelSimple::~BlastAssetModelSimple() +{ + // release materials + for (RenderMaterial* r : m_renderMaterials) + { + SAFE_DELETE(r); + } +} + + +BlastFamilyPtr BlastAssetModelSimple::createFamily(PhysXController& physXConroller, ExtPxManager& pxManager, const ActorDesc& desc) +{ + return BlastFamilyPtr(new BlastFamilyModelSimple(physXConroller, pxManager, m_renderer, *this, desc)); +} diff --git a/NvBlast/samples/SampleBase/blast/BlastAssetModelSimple.h b/NvBlast/samples/SampleBase/blast/BlastAssetModelSimple.h new file mode 100644 index 0000000..7e61616 --- /dev/null +++ b/NvBlast/samples/SampleBase/blast/BlastAssetModelSimple.h @@ -0,0 +1,47 @@ +/* +* Copyright (c) 2008-2015, 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 BLAST_ASSET_MODEL_SIMPLE_H +#define BLAST_ASSET_MODEL_SIMPLE_H + +#include "BlastAssetModel.h" + + +class RenderMaterial; + +class BlastAssetModelSimple : public BlastAssetModel +{ +public: + //////// ctor //////// + + BlastAssetModelSimple(TkFramework& framework, PxPhysics& physics, PxCooking& cooking, Renderer& renderer, const char* modelName); + virtual ~BlastAssetModelSimple(); + + + //////// interface implementation //////// + + virtual BlastFamilyPtr createFamily(PhysXController& physXConroller, ExtPxManager& pxManager, const ActorDesc& desc); + + + //////// data getters //////// + + const std::vector<RenderMaterial*>& getRenderMaterials() const + { + return m_renderMaterials; + } + + +private: + //////// private internal data //////// + + std::vector<RenderMaterial*> m_renderMaterials; +}; + +#endif //BLAST_ASSET_MODEL_SIMPLE_H
\ No newline at end of file diff --git a/NvBlast/samples/SampleBase/blast/BlastAssetModelSkinned.cpp b/NvBlast/samples/SampleBase/blast/BlastAssetModelSkinned.cpp new file mode 100644 index 0000000..6b96580 --- /dev/null +++ b/NvBlast/samples/SampleBase/blast/BlastAssetModelSkinned.cpp @@ -0,0 +1,42 @@ +/* +* Copyright (c) 2008-2015, 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 "BlastAssetModelSkinned.h" +#include "BlastFamilyModelSkinned.h" +#include "RenderMaterial.h" +#include "Renderer.h" + + +BlastAssetModelSkinned::BlastAssetModelSkinned(TkFramework& framework, PxPhysics& physics, PxCooking& cooking, Renderer& renderer, const char* modelName) + : BlastAssetModel(framework, physics, cooking, renderer, modelName) +{ + for (const BlastModel::Material& material : getModel().materials) + { + if (material.diffuseTexture.empty()) + m_renderMaterials.push_back(new RenderMaterial(renderer.getResourceManager(), "model_skinned")); + else + m_renderMaterials.push_back(new RenderMaterial(renderer.getResourceManager(), "model_skinned_textured", material.diffuseTexture.c_str())); + } + + validate(); +} + +BlastAssetModelSkinned::~BlastAssetModelSkinned() +{ + for (RenderMaterial* r : m_renderMaterials) + { + SAFE_DELETE(r); + } +} + +BlastFamilyPtr BlastAssetModelSkinned::createFamily(PhysXController& physXConroller, ExtPxManager& pxManager, const ActorDesc& desc) +{ + return BlastFamilyPtr(new BlastFamilyModelSkinned(physXConroller, pxManager, m_renderer, *this, desc)); +} diff --git a/NvBlast/samples/SampleBase/blast/BlastAssetModelSkinned.h b/NvBlast/samples/SampleBase/blast/BlastAssetModelSkinned.h new file mode 100644 index 0000000..149c8e8 --- /dev/null +++ b/NvBlast/samples/SampleBase/blast/BlastAssetModelSkinned.h @@ -0,0 +1,46 @@ +/* +* Copyright (c) 2008-2015, 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 BLAST_ASSET_MODEL_SKINNED_H +#define BLAST_ASSET_MODEL_SKINNED_H + +#include "BlastAssetModel.h" + +class RenderMaterial; + +class BlastAssetModelSkinned : public BlastAssetModel +{ +public: + //////// ctor //////// + + BlastAssetModelSkinned(TkFramework& framework, PxPhysics& physics, PxCooking& cooking, Renderer& renderer, const char* modelName); + virtual ~BlastAssetModelSkinned(); + + + //////// interface implementation //////// + + BlastFamilyPtr createFamily(PhysXController& physXConroller, ExtPxManager& pxManager, const ActorDesc& desc); + + + //////// public getter //////// + + const std::vector<RenderMaterial*>& getRenderMaterials() const + { + return m_renderMaterials; + } + + +private: + //////// internal data //////// + + std::vector<RenderMaterial*> m_renderMaterials; +}; + +#endif //BLAST_ASSET_MODEL_SKINNED_H
\ No newline at end of file diff --git a/NvBlast/samples/SampleBase/blast/BlastController.cpp b/NvBlast/samples/SampleBase/blast/BlastController.cpp new file mode 100644 index 0000000..77e2047 --- /dev/null +++ b/NvBlast/samples/SampleBase/blast/BlastController.cpp @@ -0,0 +1,477 @@ +/* +* Copyright (c) 2008-2015, 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 "BlastController.h" +#include "BlastFamily.h" +#include "BlastAsset.h" +#include "BlastReplay.h" +#include "PhysXController.h" +#include "SampleTime.h" +#include "SampleProfiler.h" +#include "Utils.h" +#include "Renderer.h" + +#include "NvBlast.h" +#include "NvBlastExtPxManager.h" +#include "NvBlastExtPxFamily.h" +#include "NvBlastExtPxActor.h" + +#include "NvBlastTkFramework.h" + +#include "PsString.h" +#include "PxTaskManager.h" +#include "PxDefaultCpuDispatcher.h" +#include "PxRigidBody.h" +#include "PxScene.h" +#include "PxRigidDynamic.h" +#include "PxDistanceJoint.h" + +#include <sstream> +#include <numeric> +#include <cstdlib> + +#include "imgui.h" + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// AllocatorCallback +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +class BlastAllocatorCallback : public PxAllocatorCallback +{ +public: + virtual void* allocate(size_t size, const char* typeName, const char* filename, int line) override + { + NV_UNUSED(typeName); + NV_UNUSED(filename); + NV_UNUSED(line); + return malloc(size); + } + + virtual void deallocate(void* ptr) override + { + free(ptr); + } +}; +BlastAllocatorCallback g_allocatorCallback; + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// ErrorCallback +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +class BlastErrorCallback : public PxErrorCallback +{ +public: + virtual void reportError(PxErrorCode::Enum code, const char* msg, const char* file, int line) override + { + std::stringstream str; + str << "NvBlastTk "; + bool critical = false; + switch (code) + { + case PxErrorCode::eNO_ERROR: critical = false; break; + case PxErrorCode::eDEBUG_INFO: str << "[Debug Info]"; critical = false; break; + case PxErrorCode::eDEBUG_WARNING: str << "[Debug Warning]"; critical = false; break; + case PxErrorCode::eINVALID_PARAMETER: str << "[Invalid Parameter]"; critical = true; break; + case PxErrorCode::eINVALID_OPERATION: str << "[Invalid Operation]"; critical = true; break; + case PxErrorCode::eOUT_OF_MEMORY: str << "[Out of] Memory"; critical = true; break; + case PxErrorCode::eINTERNAL_ERROR: str << "[Internal Error]"; critical = true; break; + case PxErrorCode::eABORT: str << "[Abort]"; critical = true; break; + case PxErrorCode::ePERF_WARNING: str << "[Perf Warning]"; critical = false; break; + default: PX_ALWAYS_ASSERT(); + } + str << file << "(" << line << "): " << msg << "\n"; + + std::string message = str.str(); + shdfnd::printString(message.c_str()); + PX_ASSERT_WITH_MESSAGE(!critical, message.c_str()); + } +}; +BlastErrorCallback g_errorCallback; + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Joint creation +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static physx::PxJoint* createPxJointCallback(ExtPxActor* actor0, const physx::PxTransform& localFrame0, ExtPxActor* actor1, const physx::PxTransform& localFrame1, physx::PxPhysics& physics, TkJoint& joint) +{ + PxDistanceJoint* pxJoint = PxDistanceJointCreate(physics, actor0 ? &actor0->getPhysXActor() : nullptr, localFrame0, actor1 ? &actor1->getPhysXActor() : nullptr, localFrame1); + pxJoint->setMaxDistance(1.0f); + return pxJoint; +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Controller +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +BlastController::BlastController() +: m_eventCallback(nullptr), debugRenderMode(BlastFamily::DEBUG_RENDER_DISABLED), m_impactDamageEnabled(true), +m_impactDamageToStressEnabled(false), m_rigidBodyLimitEnabled(true), m_rigidBodyLimit(40000), m_blastAssetsSize(0), debugRenderScale(0.01f) +{ + m_extImpactDamageManagerSettings.fragility = 500.0f; + + m_impactDamageToStressFactor = 0.01f; + m_draggingToStressFactor = 100.0f; +} + + +BlastController::~BlastController() +{ +} + +void BlastController::reinitialize() +{ + onSampleStop(); + onSampleStart(); +} + +void BlastController::onSampleStart() +{ + TkFrameworkDesc desc; + desc.allocatorCallback = &g_allocatorCallback; + desc.errorCallback = &g_errorCallback; + m_tkFramework = NvBlastTkFrameworkCreate(desc); + + m_replay = new BlastReplay(); + + m_taskManager = PxTaskManager::createTaskManager(g_errorCallback, getPhysXController().getCPUDispatcher(), 0); + + TkGroupDesc gdesc; + gdesc.pxTaskManager = m_taskManager; + m_tkGroup = m_tkFramework->createGroup(gdesc); + + m_extPxManager = ExtPxManager::create(getPhysXController().getPhysics(), *m_tkFramework, createPxJointCallback); + m_extPxManager->setActorCountLimit(m_rigidBodyLimitEnabled ? m_rigidBodyLimit : 0); + m_extImpactDamageManager = ExtImpactDamageManager::create(m_extPxManager, m_extImpactDamageManagerSettings); + m_eventCallback = new EventCallback(m_extImpactDamageManager); + + setImpactDamageEnabled(m_impactDamageEnabled, true); +} + + +void BlastController::onSampleStop() +{ + removeAllFamilies(); + + m_extImpactDamageManager->release(); + m_extPxManager->release(); + SAFE_DELETE(m_eventCallback); + + m_tkGroup->release(); + + delete m_replay; + + m_tkFramework->release(); + + m_taskManager->release(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Impact damage +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void BlastController::setImpactDamageEnabled(bool enabled, bool forceUpdate) +{ + if (m_impactDamageEnabled != enabled || forceUpdate) + { + m_impactDamageEnabled = enabled; + getPhysXController().getPhysXScene().setSimulationEventCallback(m_impactDamageEnabled ? m_eventCallback : nullptr); + refreshImpactDamageSettings(); + } +} + +bool BlastController::customImpactDamageFunction(void* data, ExtPxActor* actor, physx::PxShape* shape, physx::PxVec3 position, physx::PxVec3 force) +{ + return reinterpret_cast<BlastController*>(data)->stressDamage(actor, position, force); +} + +bool BlastController::stressDamage(ExtPxActor *actor, physx::PxVec3 position, physx::PxVec3 force) +{ + if (actor->getTkActor().getGraphNodeCount() > 1) + { + void* userData = actor->getFamily().userData; + if (userData) + { + ExtStressSolver* solver = reinterpret_cast<ExtStressSolver*>(userData); + solver->applyImpulse(*actor, position, force * m_impactDamageToStressFactor); + return true; + } + } + + return false; +} + +void BlastController::refreshImpactDamageSettings() +{ + m_extImpactDamageManagerSettings.damageFunction = m_impactDamageToStressEnabled ? customImpactDamageFunction : nullptr; + m_extImpactDamageManagerSettings.damageFunctionData = this; + m_extImpactDamageManager->setSettings(m_extImpactDamageManagerSettings); +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Stress +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void BlastController::updateDraggingStress() +{ + auto physxController = getPhysXController(); + auto actor = physxController.getDraggingActor(); + if (actor) + { + ExtPxActor* pxActor = m_extPxManager->getActorFromPhysXActor(*actor); + if (pxActor && pxActor->getTkActor().getGraphNodeCount() > 1 && pxActor->getPhysXActor().getRigidBodyFlags() & PxRigidBodyFlag::eKINEMATIC) + { + void* userData = pxActor->getFamily().userData; + if (userData) + { + ExtStressSolver* solver = reinterpret_cast<ExtStressSolver*>(userData); + PxTransform t(pxActor->getPhysXActor().getGlobalPose().getInverse()); + PxVec3 dragVector = t.rotate(physxController.getDragVector()); + const float factor = dragVector.magnitudeSquared() * m_draggingToStressFactor; + solver->applyImpulse(*pxActor, physxController.getDragActorHookLocalPoint(), dragVector.getNormalized() * factor); + } + } + } +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Stats +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +uint32_t BlastController::getActorCount() const +{ + return std::accumulate(m_families.begin(), m_families.end(), (uint32_t)0, [](uint32_t sum, const BlastFamilyPtr& a) + { + return sum += a->getActorCount(); + }); +} + +uint32_t BlastController::getTotalVisibleChunkCount() const +{ + return std::accumulate(m_families.begin(), m_families.end(), (uint32_t)0, [](uint32_t sum, const BlastFamilyPtr& a) + { + return sum += a->getTotalVisibleChunkCount(); + }); +} + +size_t BlastController::getFamilySize() const +{ + return std::accumulate(m_families.begin(), m_families.end(), (size_t)0, [](size_t sum, const BlastFamilyPtr& a) + { + return sum += a->getFamilySize(); + }); +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Time +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +const double Time::s_secondsPerTick = Time::getTickDuration(); + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Controller events +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void BlastController::Animate(double dt) +{ + PROFILER_SCOPED_FUNCTION(); + + PROFILER_BEGIN("Apply Impact Damage"); + m_extImpactDamageManager->applyDamage(); + PROFILER_END(); + + updateDraggingStress(); + + m_replay->update(); + + Time blastTime; + for (uint32_t i = 0; i < m_families.size(); ++i) + { + if (m_families[i]) + { + m_families[i]->updatePreSplit(dt); + } + } + + fillDebugRender(); + + PROFILER_BEGIN("Tk Group Process/Sync"); + m_tkGroup->process(); + m_tkGroup->sync(true); + PROFILER_END(); + + TkGroupStats gstats; + m_tkGroup->getStats(gstats); + + this->m_lastBlastTimers.blastDamageMaterial = NvBlastTicksToSeconds(gstats.timers.material); + this->m_lastBlastTimers.blastDamageFracture = NvBlastTicksToSeconds(gstats.timers.fracture); + this->m_lastBlastTimers.blastSplitIsland = NvBlastTicksToSeconds(gstats.timers.island); + this->m_lastBlastTimers.blastSplitPartition = NvBlastTicksToSeconds(gstats.timers.partition); + this->m_lastBlastTimers.blastSplitVisibility = NvBlastTicksToSeconds(gstats.timers.visibility); + + for (uint32_t i = 0; i < m_families.size(); ++i) + { + if (m_families[i]) + { + m_families[i]->updateAfterSplit(dt); + } + } +} + + +void BlastController::drawUI() +{ + // impact damage + bool impactEnabled = getImpactDamageEnabled(); + if (ImGui::Checkbox("Impact Damage", &impactEnabled)) + { + setImpactDamageEnabled(impactEnabled); + } + + if (ImGui::DragFloat("Fragility", &m_extImpactDamageManagerSettings.fragility)) + { + refreshImpactDamageSettings(); + } + + if (ImGui::Checkbox("Impact Damage To Stress Solver", &m_impactDamageToStressEnabled)) + { + refreshImpactDamageSettings(); + } + + ImGui::DragFloat("Impact Damage To Stress Factor", &m_impactDamageToStressFactor, 0.001f, 0.0f, 1000.0f, "%.4f"); + ImGui::DragFloat("Dragging To Stress Factor", &m_draggingToStressFactor, 0.1f, 0.0f, 1000.0f, "%.3f"); + + ImGui::Checkbox("Limit Rigid Body Count", &m_rigidBodyLimitEnabled); + if (m_rigidBodyLimitEnabled) + { + ImGui::DragInt("Rigid Body Limit", (int*)&m_rigidBodyLimit, 100, 1000, 100000); + } + m_extPxManager->setActorCountLimit(m_rigidBodyLimitEnabled ? m_rigidBodyLimit : 0); +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// actor management +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +BlastFamilyPtr BlastController::spawnFamily(BlastAsset* blastAsset, const BlastAsset::ActorDesc& desc) +{ + BlastFamilyPtr actor = blastAsset->createFamily(getPhysXController(), *m_extPxManager, desc); + m_families.push_back(actor); + recalculateAssetsSize(); + m_replay->addFamily(&actor->getFamily()->getTkFamily()); + return actor; +} + +void BlastController::removeFamily(BlastFamilyPtr actor) +{ + m_replay->removeFamily(&actor->getFamily()->getTkFamily()); + m_families.erase(std::remove(m_families.begin(), m_families.end(), actor), m_families.end()); + recalculateAssetsSize(); + getPhysXController().resetDragging(); +} + +void BlastController::removeAllFamilies() +{ + while (!m_families.empty()) + { + removeFamily(m_families.back()); + } + m_replay->reset(); +} + +void BlastController::recalculateAssetsSize() +{ + std::set<const BlastAsset*> uniquedAssets; + m_blastAssetsSize = 0; + for (uint32_t i = 0; i < m_families.size(); ++i) + { + if (uniquedAssets.find(&m_families[i]->getBlastAsset()) == uniquedAssets.end()) + { + m_blastAssetsSize += m_families[i]->getBlastAsset().getBlastAssetSize(); + uniquedAssets.insert(&m_families[i]->getBlastAsset()); + } + } +} + +void BlastController::blast(PxVec3 worldPos, float damageRadius, float explosiveImpulse, std::function<void(ExtPxActor*)> damageFunction) +{ + PROFILER_SCOPED_FUNCTION(); + + for (uint32_t i = 0; i < m_families.size(); ++i) + { + if (m_families[i]) + { + m_families[i]->blast(worldPos, damageRadius, explosiveImpulse, damageFunction); + } + } +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// context/log +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void BlastController::blastLog(int type, const char* msg, const char* file, int line) +{ + std::stringstream str; + bool critical = false; + switch (type) + { + case NvBlastMessage::Error: str << "[NvBlast ERROR] "; critical = true; break; + case NvBlastMessage::Warning: str << "[NvBlast WARNING] "; critical = true; break; + case NvBlastMessage::Info: str << "[NvBlast INFO] "; critical = false; break; + case NvBlastMessage::Debug: str << "[NvBlast DEBUG] "; critical = false; break; + } + str << file << "(" << line << "): " << msg << "\n"; + + std::string message = str.str(); + shdfnd::printString(message.c_str()); + PX_ASSERT_WITH_MESSAGE(!critical, message.c_str()); +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// debug render +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void BlastController::fillDebugRender() +{ + PROFILER_SCOPED_FUNCTION(); + + m_debugRenderBuffer.clear(); + + if (debugRenderMode != BlastFamily::DEBUG_RENDER_DISABLED) + { + getPhysXController().getPhysXScene().setVisualizationParameter(PxVisualizationParameter::eSCALE, 1); + for (uint32_t i = 0; i < m_families.size(); ++i) + { + m_families[i]->fillDebugRender(m_debugRenderBuffer, debugRenderMode, debugRenderScale); + } + } + else + { + getPhysXController().getPhysXScene().setVisualizationParameter(PxVisualizationParameter::eSCALE, 0); + } + + getRenderer().queueRenderBuffer(&m_debugRenderBuffer); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/NvBlast/samples/SampleBase/blast/BlastController.h b/NvBlast/samples/SampleBase/blast/BlastController.h new file mode 100644 index 0000000..f7f362f --- /dev/null +++ b/NvBlast/samples/SampleBase/blast/BlastController.h @@ -0,0 +1,239 @@ +/* +* Copyright (c) 2008-2015, 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 BLAST_CONTROLLER_H +#define BLAST_CONTROLLER_H + +#include "SampleManager.h" +#include "BlastFamily.h" +#include "DebugRenderBuffer.h" +#include "PxSimulationEventCallback.h" +#include "NvBlastExtImpactDamageManager.h" + +using namespace physx; + +class BlastAsset; +class BlastReplay; + +namespace physx +{ +class PxTaskManager; +} +namespace Nv +{ +namespace Blast +{ +class TkFramework; +} +} + + +struct BlastTimers +{ + double blastDamageMaterial; + double blastDamageFracture; + double blastSplitIsland; + double blastSplitPartition; + double blastSplitVisibility; +}; + +/** +Blast Controller. Entry point for all blast related code, keeps blast actors and controls them. +*/ +class BlastController : public ISampleController +{ +public: + //////// ctor //////// + + BlastController(); + virtual ~BlastController(); + + void reinitialize(); + + //////// controller callbacks //////// + + virtual void onSampleStart(); + virtual void onSampleStop(); + + virtual void Animate(double dt); + void drawUI(); + + + //////// public API //////// + + void blast(PxVec3 worldPos, float damageRadius, float explosiveImpulse, std::function<void(ExtPxActor*)> damageFunction); + + bool stressDamage(ExtPxActor *actor, PxVec3 position, PxVec3 force); + + BlastFamilyPtr spawnFamily(BlastAsset* blastAsset, const BlastAsset::ActorDesc& desc); + void removeFamily(BlastFamilyPtr actor); + void removeAllFamilies(); + + + //////// public static //////// + + static void blastLog(int type, const char* msg, const char* file, int line); + + + //////// public getters/setters //////// + + TkFramework& getTkFramework() const + { + return *m_tkFramework; + } + + TkGroup* getTkGroup() const + { + return m_tkGroup; + } + + ExtPxManager& getExtPxManager() const + { + return *m_extPxManager; + } + + ExtImpactDamageManager* getExtImpactDamageManager() const + { + return m_extImpactDamageManager; + } + + BlastReplay* getReplay() const + { + return m_replay; + } + + uint32_t getActorCount() const; + + uint32_t getTotalVisibleChunkCount() const; + + size_t getFamilySize() const; + + size_t getBlastAssetsSize() const + { + return m_blastAssetsSize; + } + + const BlastTimers& getLastBlastTimers() const + { + return m_lastBlastTimers; + } + + bool getImpactDamageEnabled() const + { + return m_impactDamageEnabled; + } + + void setImpactDamageEnabled(bool enabled, bool forceUpdate = false); + + ExtStressSolverSettings& getStressSolverSettings() + { + return m_extStressSolverSettings; + } + + float getLastStressDelta() const; + + //////// public variables for UI //////// + + BlastFamily::DebugRenderMode debugRenderMode; + float debugRenderScale; + + + //////// Filter shader enum //////// + + enum FilterDataAttributes + { + SUPPRESS_CONTACT_NOTIFY = 1, + }; + +private: + //////// impact damage event callback //////// + + class EventCallback : public PxSimulationEventCallback + { + public: + EventCallback(ExtImpactDamageManager* manager) : m_manager(manager) {} + + // implemented + virtual void onContact(const PxContactPairHeader& pairHeader, const PxContactPair* pairs, uint32_t nbPairs) + { + m_manager->onContact(pairHeader, pairs, nbPairs); + } + + private: + // unused + void onConstraintBreak(PxConstraintInfo*, PxU32) {} + void onWake(PxActor**, PxU32) {} + void onSleep(PxActor**, PxU32) {} + void onTrigger(PxTriggerPair*, PxU32) {} + void onAdvance(const PxRigidBody*const*, const PxTransform*, const PxU32) {} + + // data + ExtImpactDamageManager* m_manager; + }; + + + //////// private methods //////// + + void updateDraggingStress(); + + void refreshImpactDamageSettings(); + + void fillDebugRender(); + + void recalculateAssetsSize(); + + static bool customImpactDamageFunction(void* data, ExtPxActor* actor, PxShape* shape, PxVec3 position, PxVec3 force); + + + //////// used controllers //////// + + Renderer& getRenderer() const + { + return getManager()->getRenderer(); + } + + PhysXController& getPhysXController() const + { + return getManager()->getPhysXController(); + } + + + //////// internal data //////// + + PxTaskManager* m_taskManager; + TkFramework* m_tkFramework; + TkGroup* m_tkGroup; + ExtPxManager* m_extPxManager; + ExtImpactDamageManager* m_extImpactDamageManager; + ExtImpactSettings m_extImpactDamageManagerSettings; + EventCallback* m_eventCallback; + ExtStressSolverSettings m_extStressSolverSettings; + + std::vector<BlastFamilyPtr> m_families; + DebugRenderBuffer m_debugRenderBuffer; + + bool m_impactDamageEnabled; + bool m_impactDamageToStressEnabled; + + float m_impactDamageToStressFactor; + float m_draggingToStressFactor; + + bool m_rigidBodyLimitEnabled; + uint32_t m_rigidBodyLimit; + + BlastReplay* m_replay; + + BlastTimers m_lastBlastTimers; + + size_t m_blastAssetsSize; +}; + + +#endif diff --git a/NvBlast/samples/SampleBase/blast/BlastFamily.cpp b/NvBlast/samples/SampleBase/blast/BlastFamily.cpp new file mode 100644 index 0000000..95444b3 --- /dev/null +++ b/NvBlast/samples/SampleBase/blast/BlastFamily.cpp @@ -0,0 +1,570 @@ +/* +* Copyright (c) 2008-2015, 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 "BlastFamily.h" +#include "SampleProfiler.h" +#include "PhysXController.h" +#include "RenderUtils.h" +#include "SampleTime.h" +#include "UIHelpers.h" + +#include "NvBlast.h" +#include "NvBlastTkFamily.h" +#include "NvBlastTkActor.h" +#include "NvBlastTkAsset.h" +#include "NvBlastTkJoint.h" +#include "NvBlastExtPxAsset.h" +#include "NvBlastExtPxActor.h" +#include "NvBlastExtPxFamily.h" +#include "NvBlastExtPxManager.h" + +#include "PxRigidDynamic.h" +#include "PxScene.h" +#include "PxJoint.h" + + +const float RIGIDBODY_DENSITY = 2000.0f; + +const float BlastFamily::BOND_HEALTH_MAX = 1.0f; + +BlastFamily::BlastFamily(PhysXController& physXController, ExtPxManager& pxManager, const BlastAsset& blastAsset) + : m_physXController(physXController) + , m_pxManager(pxManager) + , m_blastAsset(blastAsset) + , m_listener(this) + , m_totalVisibleChunkCount(0) + , m_stressSolver(nullptr) +{ + m_settings.stressSolverEnabled = false; + m_settings.stressDamageEnabled = false; + + m_settings.material = { 3.0f, 0.1f, 0.2f, 1.5f + 1e-5f, 0.95f };; +} + +BlastFamily::~BlastFamily() +{ + if (m_stressSolver) + { + m_stressSolver->release(); + } + + m_pxFamily->unsubscribe(m_listener); + + m_pxFamily->release(); +} + +void BlastFamily::initialize(const BlastAsset::ActorDesc& desc) +{ + ExtPxFamilyDesc familyDesc; + familyDesc.actorDesc.initialBondHealths = nullptr; + familyDesc.actorDesc.initialSupportChunkHealths = nullptr; + familyDesc.actorDesc.uniformInitialBondHealth = BOND_HEALTH_MAX; + familyDesc.actorDesc.uniformInitialLowerSupportChunkHealth = 1.0f; + familyDesc.group = desc.group; + familyDesc.pxAsset = m_blastAsset.getPxAsset(); + m_pxFamily = m_pxManager.createFamily(familyDesc); + + m_tkFamily = &m_pxFamily->getTkFamily(); + m_tkFamily->setID(desc.id); + m_tkFamily->setMaterial(&m_settings.material); + + m_familySize = NvBlastFamilyGetSize(m_tkFamily->getFamilyLL(), nullptr); + + m_pxFamily->subscribe(m_listener); + + ExtPxSpawnSettings spawnSettings = { + &m_physXController.getPhysXScene(), + m_physXController.getDefaultMaterial(), + RIGIDBODY_DENSITY + }; + m_pxFamily->spawn(desc.transform, PxVec3(1.0f), spawnSettings); + + reloadStressSolver(); +} + +void BlastFamily::updatePreSplit(float dt) +{ + PROFILER_BEGIN("Stress Solver"); + // update stress + m_stressSolveTime = 0; + if (m_stressSolver) + { + Time t; + m_stressSolver->update(m_settings.stressDamageEnabled); + m_stressSolveTime += t.getElapsedSeconds(); + } + PROFILER_END(); + + // collect potential actors to health update + m_actorsToUpdateHealth.clear(); + for (const ExtPxActor* actor : m_actors) + { + if (actor->getTkActor().isPending()) + { + m_actorsToUpdateHealth.emplace(actor); + } + } +} + +void BlastFamily::updateAfterSplit(float dt) +{ + PROFILER_BEGIN("Actor Health Update"); + for (const ExtPxActor* actor : m_actors) + { + onActorUpdate(*actor); + + // update health if neccessary + if (m_actorsToUpdateHealth.find(actor) != m_actorsToUpdateHealth.end()) + { + onActorHealthUpdate(*actor); + } + } + PROFILER_END(); + + PROFILER_BEGIN("Actor Misc Update"); + onUpdate(); + PROFILER_END(); + + m_pxFamily->postSplitUpdate(); +} + +void BlastFamily::processActorCreated(ExtPxFamily&, ExtPxActor& actor) +{ + m_totalVisibleChunkCount += actor.getChunkCount(); + m_actors.emplace(&actor); + + onActorCreated(actor); + onActorHealthUpdate(actor); +} + +void BlastFamily::processActorDestroyed(ExtPxFamily&, ExtPxActor& actor) +{ + m_totalVisibleChunkCount -= actor.getChunkCount(); + m_physXController.notifyRigidDynamicDestroyed(&actor.getPhysXActor()); + + onActorDestroyed(actor); + + m_actors.erase(m_actors.find(&actor)); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Data Helpers +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +uint32_t BlastFamily::getActorCount() const +{ + return (uint32_t)m_tkFamily->getActorCount(); +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// UI +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void BlastFamily::drawUI() +{ + // Blast Material + ImGui::Spacing(); + ImGui::Text("Blast Material:"); + ImGui::DragFloat("singleChunkThreshold", &m_settings.material.singleChunkThreshold); + ImGui::DragFloat("graphChunkThreshold", &m_settings.material.graphChunkThreshold); + ImGui::DragFloat("bondNormalThreshold", &m_settings.material.bondNormalThreshold); + ImGui::DragFloat("bondTangentialThreshold", &m_settings.material.bondTangentialThreshold); + ImGui::DragFloat("damageAttenuation", &m_settings.material.damageAttenuation); + + ImGui::Spacing(); + + // Stress Solver Settings + if (ImGui::Checkbox("Stress Solver Enabled", &m_settings.stressSolverEnabled)) + { + reloadStressSolver(); + } + + if (m_settings.stressSolverEnabled) + { + // Settings + bool changed = false; + + changed |= ImGui::DragInt("Bond Iterations Per Frame", (int*)&m_settings.stressSolverSettings.bondIterationsPerFrame, 100, 0, 500000); + changed |= ImGui::DragFloat("Stress Linear Factor", &m_settings.stressSolverSettings.stressLinearFactor, 0.00001f, 0.0f, 10.0f, "%.6f"); + changed |= ImGui::DragFloat("Stress Angular Factor", &m_settings.stressSolverSettings.stressAngularFactor, 0.00001f, 0.0f, 10.0f, "%.6f"); + changed |= ImGui::SliderInt("Graph Reduction Level", (int*)&m_settings.stressSolverSettings.graphReductionLevel, 0, 32); + if (changed) + { + refreshStressSolverSettings(); + } + + ImGui::Checkbox("Stress Damage Enabled", &m_settings.stressDamageEnabled); + + if (ImGui::Button("Recalculate Stress")) + { + resetStress(); + } + } +} + +void BlastFamily::drawStatsUI() +{ + ImGui::PushStyleColor(ImGuiCol_Text, ImColor(10, 255, 10, 255)); + const ExtStressSolver* stressSolver = m_stressSolver; + if (stressSolver) + { + const float errorLinear = stressSolver->getStressErrorLinear(); + const float errorAngular = stressSolver->getStressErrorAngular(); + + ImGui::Text("Stress Bond Count: %d", stressSolver->getBondCount()); + ImGui::Text("Stress Iterations: %d (+%d)", stressSolver->getIterationCount(), stressSolver->getIterationsPerFrame()); + ImGui::Text("Stress Frames: %d", stressSolver->getFrameCount()); + ImGui::Text("Stress Error Lin / Ang: %.4f / %.4f", errorLinear, errorAngular); + ImGui::Text("Stress Solve Time: %.3f ms", m_stressSolveTime * 1000); + + // plot errors + { + static float scale = 1.0f; + scale = stressSolver->getFrameCount() <= 1 ? 1.0f : scale; + scale = std::max<float>(scale, errorLinear); + scale = std::max<float>(scale, errorAngular); + + static PlotLinesInstance<> linearErrorPlot; + linearErrorPlot.plot("Stress Linear Error", errorLinear, "error/frame", 0.0f, 1.0f * scale); + static PlotLinesInstance<> angularErrorPlot; + angularErrorPlot.plot("Stress Angular Error", errorAngular, "error/frame", 0.0f, 1.0f * scale); + } + } + else + { + ImGui::Text("No Stress Solver"); + } + ImGui::PopStyleColor(); +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Stress Solver +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void BlastFamily::setSettings(const Settings& settings) +{ + bool reloadStressSolverNeeded = (m_settings.stressSolverEnabled != settings.stressSolverEnabled); + + m_settings = settings; + refreshStressSolverSettings(); + + if (reloadStressSolverNeeded) + { + reloadStressSolver(); + } + + m_tkFamily->setMaterial(&m_settings.material); +} + +void BlastFamily::refreshStressSolverSettings() +{ + if (m_stressSolver) + { + m_stressSolver->setSettings(m_settings.stressSolverSettings); + } +} + +void BlastFamily::resetStress() +{ + m_stressSolver->reset(); +} + +void BlastFamily::reloadStressSolver() +{ + if (m_stressSolver) + { + m_stressSolver->release(); + m_stressSolver = nullptr; + } + + if (m_settings.stressSolverEnabled) + { + m_stressSolver = ExtStressSolver::create(*m_pxFamily, m_settings.stressSolverSettings); + m_pxFamily->userData = m_stressSolver; + } +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// debug render +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +const DirectX::XMFLOAT4 BOND_NORMAL_COLOR(0.0f, 0.8f, 1.0f, 1.0f); +const DirectX::XMFLOAT4 BOND_INVISIBLE_COLOR(0.65f, 0.16f, 0.16f, 1.0f); +const DirectX::XMFLOAT4 BOND_IMPULSE_LINEAR_COLOR(0.0f, 1.0f, 0.0f, 1.0f); +const DirectX::XMFLOAT4 BOND_IMPULSE_ANGULAR_COLOR(1.0f, 0.0f, 0.0f, 1.0f); +const DirectX::XMFLOAT4 JOINT_COLOR(0.5f, 0.6f, 7.0f, 1.0f); + + +inline void pushCentroid(std::vector<PxDebugLine>& lines, PxVec3 pos, PxU32 color, const float& area, const PxVec3& normal) +{ + // draw square of area 'area' rotated by normal + { + // build world rotation + PxVec3 n0(0, 0, 1); + PxVec3 n1 = normal; + PxVec3 axis = n0.cross(n1); + float d = n0.dot(n1); + PxQuat q(axis.x, axis.y, axis.z, 1.f + d); + q.normalize(); + float e = PxSqrt(1.0f / 2.0f); + float r = PxSqrt(area); + + // transform all 4 square points + PxTransform t(pos, q); + PxVec3 p0 = t.transform(PxVec3(-e, e, 0) * r); + PxVec3 p1 = t.transform(PxVec3( e, e, 0) * r); + PxVec3 p2 = t.transform(PxVec3( e, -e, 0) * r); + PxVec3 p3 = t.transform(PxVec3(-e, -e, 0) * r); + + // push square edges + lines.push_back(PxDebugLine(p0, p1, color)); + lines.push_back(PxDebugLine(p3, p2, color)); + lines.push_back(PxDebugLine(p1, p2, color)); + lines.push_back(PxDebugLine(p0, p3, color)); + } + + // draw normal + lines.push_back(PxDebugLine(pos, pos + normal * 0.5f, XMFLOAT4ToU32Color(BOND_NORMAL_COLOR))); +} + +inline DirectX::XMFLOAT4 bondHealthColor(float healthFraction) +{ + const DirectX::XMFLOAT4 BOND_HEALTHY_COLOR(0.0f, 1.0f, 0.0f, 1.0f); + const DirectX::XMFLOAT4 BOND_MID_COLOR(1.0f, 1.0f, 0.0f, 1.0f); + const DirectX::XMFLOAT4 BOND_BROKEN_COLOR(1.0f, 0.0f, 0.0f, 1.0f); + + return healthFraction < 0.5 ? XMFLOAT4Lerp(BOND_BROKEN_COLOR, BOND_MID_COLOR, 2.0f * healthFraction) : XMFLOAT4Lerp(BOND_MID_COLOR, BOND_HEALTHY_COLOR, 2.0f * healthFraction - 1.0f); +} + +void BlastFamily::fillDebugRender(DebugRenderBuffer& debugRenderBuffer, DebugRenderMode mode, float renderScale) +{ + const NvBlastChunk* chunks = m_tkFamily->getAsset()->getChunks(); + const NvBlastBond* bonds = m_tkFamily->getAsset()->getBonds(); + const NvBlastSupportGraph graph = m_tkFamily->getAsset()->getGraph(); + + for (const ExtPxActor* pxActor : m_actors) + { + TkActor& actor = pxActor->getTkActor(); + uint32_t lineStartIndex = (uint32_t)debugRenderBuffer.m_lines.size(); + + uint32_t nodeCount = actor.getGraphNodeCount(); + if (nodeCount == 0) // subsupport chunks don't have graph nodes + continue; + + std::vector<uint32_t> nodes(actor.getGraphNodeCount()); + actor.getGraphNodeIndices(nodes.data(), static_cast<uint32_t>(nodes.size())); + + if (DEBUG_RENDER_HEALTH_GRAPH <= mode && mode <= DEBUG_RENDER_HEALTH_GRAPH_CENTROIDS) + { + const float* bondHealths = actor.getBondHealths(); + + const ExtPxChunk* pxChunks = m_blastAsset.getPxAsset()->getChunks(); + + for (uint32_t node0 : nodes) + { + const uint32_t chunkIndex0 = graph.chunkIndices[node0]; + const NvBlastChunk& blastChunk0 = chunks[chunkIndex0]; + const ExtPxChunk& assetChunk0 = pxChunks[chunkIndex0]; + + for (uint32_t adjacencyIndex = graph.adjacencyPartition[node0]; adjacencyIndex < graph.adjacencyPartition[node0 + 1]; adjacencyIndex++) + { + uint32_t node1 = graph.adjacentNodeIndices[adjacencyIndex]; + const uint32_t chunkIndex1 = graph.chunkIndices[node1]; + const NvBlastChunk& blastChunk1 = chunks[chunkIndex1]; + const ExtPxChunk& assetChunk1 = pxChunks[chunkIndex1]; + if (node0 > node1) + continue; + + bool invisibleBond = assetChunk0.subchunkCount == 0 || assetChunk1.subchunkCount == 0; + + // health + uint32_t bondIndex = graph.adjacentBondIndices[adjacencyIndex]; + float healthVal = PxClamp(bondHealths[bondIndex] / BOND_HEALTH_MAX, 0.0f, 1.0f); + + DirectX::XMFLOAT4 color = bondHealthColor(healthVal); + + const NvBlastBond& solverBond = bonds[bondIndex]; + const PxVec3& centroid = reinterpret_cast<const PxVec3&>(solverBond.centroid); + + // centroid + if (mode == DEBUG_RENDER_HEALTH_GRAPH_CENTROIDS || mode == DEBUG_RENDER_CENTROIDS) + { + const PxVec3& normal = reinterpret_cast<const PxVec3&>(solverBond.normal); + pushCentroid(debugRenderBuffer.m_lines, centroid, XMFLOAT4ToU32Color(invisibleBond ? BOND_INVISIBLE_COLOR : color), solverBond.area, normal.getNormalized()); + } + + // chunk connection (bond) + if ((mode == DEBUG_RENDER_HEALTH_GRAPH || mode == DEBUG_RENDER_HEALTH_GRAPH_CENTROIDS) && !invisibleBond) + { + const PxVec3& c0 = reinterpret_cast<const PxVec3&>(blastChunk0.centroid); + const PxVec3& c1 = reinterpret_cast<const PxVec3&>(blastChunk1.centroid); + debugRenderBuffer.m_lines.push_back(PxDebugLine(c0, c1, XMFLOAT4ToU32Color(color))); + } + } + } + } + + // stress + if (DEBUG_RENDER_STRESS_GRAPH <= mode && mode <= DEBUG_RENDER_STRESS_GRAPH_BONDS_IMPULSES) + { + if (m_stressSolver) + { + m_stressSolver->fillDebugRender(nodes, debugRenderBuffer.m_lines, (ExtStressSolver::DebugRenderMode)(mode - DEBUG_RENDER_STRESS_GRAPH), renderScale); + } + } + + // transform all added lines from local to global + PxTransform localToGlobal = pxActor->getPhysXActor().getGlobalPose(); + for (uint32_t i = lineStartIndex; i < debugRenderBuffer.m_lines.size(); i++) + { + PxDebugLine& line = debugRenderBuffer.m_lines[i]; + line.pos0 = localToGlobal.transform(line.pos0); + line.pos1 = localToGlobal.transform(line.pos1); + } + } + + // joints debug render + if (mode == DEBUG_RENDER_JOINTS) + { + for (const ExtPxActor* pxActor : m_actors) + { + TkActor& actor = pxActor->getTkActor(); + const uint32_t jointCount = actor.getJointCount(); + if (jointCount > 0) + { + std::vector<TkJoint*> joints(jointCount); + actor.getJoints(joints.data(), jointCount); + for (auto joint : joints) + { + PxJoint* pxJoint = reinterpret_cast<PxJoint*>(joint->userData); + if (pxJoint) + { + PxRigidActor *actor0, *actor1; + pxJoint->getActors(actor0, actor1); + auto lp0 = pxJoint->getLocalPose(PxJointActorIndex::eACTOR0); + auto lp1 = pxJoint->getLocalPose(PxJointActorIndex::eACTOR1); + PxVec3 p0 = actor0 ? actor0->getGlobalPose().transform(lp0).p : lp0.p; + PxVec3 p1 = actor1 ? actor1->getGlobalPose().transform(lp1).p : lp1.p; + debugRenderBuffer.m_lines.push_back(PxDebugLine(p0, p1, XMFLOAT4ToU32Color(JOINT_COLOR))); + } + } + } + } + } +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// action!!! +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +class BlastOverlapCallback : public PxOverlapCallback +{ +public: + BlastOverlapCallback(ExtPxManager& pxManager, std::set<ExtPxActor*>& actorBuffer) + : m_pxManager(pxManager), m_actorBuffer(actorBuffer), PxOverlapCallback(m_hitBuffer, sizeof(m_hitBuffer) / sizeof(m_hitBuffer[0])) {} + + PxAgain processTouches(const PxOverlapHit* buffer, PxU32 nbHits) + { + for (PxU32 i = 0; i < nbHits; ++i) + { + PxRigidDynamic* rigidDynamic = buffer[i].actor->is<PxRigidDynamic>(); + if (rigidDynamic) + { + ExtPxActor* actor = m_pxManager.getActorFromPhysXActor(*rigidDynamic); + if (actor != nullptr) + { + m_actorBuffer.insert(actor); + } + } + } + return true; + } + +private: + ExtPxManager& m_pxManager; + std::set<ExtPxActor*>& m_actorBuffer; + PxOverlapHit m_hitBuffer[1000]; +}; + +void BlastFamily::blast(PxVec3 worldPos, float damageRadius, float explosiveImpulse, std::function<void(ExtPxActor*)> damageFunction) +{ + std::set<ExtPxActor*> actorsToDamage; +#if 1 + BlastOverlapCallback overlapCallback(m_pxManager, actorsToDamage); + m_physXController.getPhysXScene().overlap(PxSphereGeometry(damageRadius), PxTransform(worldPos), overlapCallback); +#else + for (std::map<NvBlastActor*, PhysXController::Actor*>::iterator it = m_actorsMap.begin(); it != m_actorsMap.end(); it++) + { + actorsToDamage.insert(it->first); + } +#endif + + for (auto actor : actorsToDamage) + { + damageFunction(actor); + } + + if (explosiveImpulse > 0.0f) + { + explode(worldPos, damageRadius, explosiveImpulse); + } +} + + +class ExplodeOverlapCallback : public PxOverlapCallback +{ +public: + ExplodeOverlapCallback(PxVec3 worldPos, float radius, float explosiveImpulse) + : m_worldPos(worldPos) + , m_radius(radius) + , m_explosiveImpulse(explosiveImpulse) + , PxOverlapCallback(m_hitBuffer, sizeof(m_hitBuffer) / sizeof(m_hitBuffer[0])) {} + + PxAgain processTouches(const PxOverlapHit* buffer, PxU32 nbHits) + { + for (PxU32 i = 0; i < nbHits; ++i) + { + PxRigidActor* actor = buffer[i].actor; + PxRigidDynamic* rigidDynamic = actor->is<PxRigidDynamic>(); + if (rigidDynamic && !(rigidDynamic->getRigidBodyFlags() & PxRigidBodyFlag::eKINEMATIC)) + { + if (m_actorBuffer.find(rigidDynamic) == m_actorBuffer.end()) + { + m_actorBuffer.insert(rigidDynamic); + PxVec3 dr = rigidDynamic->getGlobalPose().transform(rigidDynamic->getCMassLocalPose()).p - m_worldPos; + float distance = dr.magnitude(); + float factor = PxClamp(1.0f - (distance * distance) / (m_radius * m_radius), 0.0f, 1.0f); + float impulse = factor * m_explosiveImpulse * RIGIDBODY_DENSITY; + PxVec3 vel = dr.getNormalized() * impulse / rigidDynamic->getMass(); + rigidDynamic->setLinearVelocity(rigidDynamic->getLinearVelocity() + vel); + } + } + } + return true; + } + +private: + PxOverlapHit m_hitBuffer[1000]; + float m_explosiveImpulse; + std::set<PxRigidDynamic*> m_actorBuffer; + PxVec3 m_worldPos; + float m_radius; +}; + +void BlastFamily::explode(PxVec3 worldPos, float damageRadius, float explosiveImpulse) +{ + ExplodeOverlapCallback overlapCallback(worldPos, damageRadius, explosiveImpulse); + m_physXController.getPhysXScene().overlap(PxSphereGeometry(damageRadius), PxTransform(worldPos), overlapCallback); + +} diff --git a/NvBlast/samples/SampleBase/blast/BlastFamily.h b/NvBlast/samples/SampleBase/blast/BlastFamily.h new file mode 100644 index 0000000..0cb9bb3 --- /dev/null +++ b/NvBlast/samples/SampleBase/blast/BlastFamily.h @@ -0,0 +1,201 @@ +/* +* Copyright (c) 2008-2015, 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 BLAST_FAMILY_H +#define BLAST_FAMILY_H + +#include "BlastAsset.h" +#include "NvBlastExtPxListener.h" +#include "NvBlastExtStressSolver.h" +#include "NvBlastExtDamageShaders.h" +#include <functional> +#include <set> + + +class DebugRenderBuffer; + +namespace Nv +{ +namespace Blast +{ +class TkFamily; +class ExtPxManager; +} +} + + +/** +BlastFamily class represents 1 spawned BlastAsset, contains and manipulates all physx/blast actors spawned by fracturing it. +Abstract class, internal actor management functions are implementation dependent and so pure virtual. +*/ +class BlastFamily +{ +public: + + //////// public API //////// + + void blast(PxVec3 worldPos, float damageRadius, float explosiveImpulse, std::function<void(ExtPxActor*)> damageFunction); + void explode(PxVec3 worldPos, float damageRadius, float explosiveImpulse); + + void updatePreSplit(float dt); + void updateAfterSplit(float dt); + + void drawUI(); + void drawStatsUI(); + + + enum DebugRenderMode + { + DEBUG_RENDER_DISABLED, + DEBUG_RENDER_HEALTH_GRAPH, + DEBUG_RENDER_CENTROIDS, + DEBUG_RENDER_HEALTH_GRAPH_CENTROIDS, + DEBUG_RENDER_JOINTS, + DEBUG_RENDER_STRESS_GRAPH, + DEBUG_RENDER_STRESS_GRAPH_NODES_IMPULSES, + DEBUG_RENDER_STRESS_GRAPH_BONDS_IMPULSES, + + // count + DEBUG_RENDER_MODES_COUNT + }; + + void fillDebugRender(DebugRenderBuffer& debugRenderBuffer, DebugRenderMode mode, float renderScale); + + + //////// public getters //////// + + const ExtPxFamily* getFamily() const + { + return m_pxFamily; + } + + uint32_t getActorCount() const; + + uint32_t getTotalVisibleChunkCount() const + { + return m_totalVisibleChunkCount; + } + + size_t getFamilySize() const + { + return m_familySize; + } + + const BlastAsset& getBlastAsset() + { + return m_blastAsset; + } + + void resetStress(); + + void refreshStressSolverSettings(); + + void reloadStressSolver(); + + + //////// consts //////// + + static const float BOND_HEALTH_MAX; + + + //////// settings //////// + + struct Settings + { + bool stressSolverEnabled; + ExtStressSolverSettings stressSolverSettings; + bool stressDamageEnabled; + NvBlastExtMaterial material; + }; + + void setSettings(const Settings& settings); + + const Settings& getSettings() const + { + return m_settings; + } + + + //////// dtor //////// + + virtual ~BlastFamily(); + +protected: + + //////// ctor //////// + + BlastFamily(PhysXController& physXController, ExtPxManager& pxManager, const BlastAsset& blastAsset); + + void initialize(const BlastAsset::ActorDesc& desc); + + + //////// internal virtual callbacks //////// + + virtual void onActorCreated(const ExtPxActor& actor) = 0; + virtual void onActorUpdate(const ExtPxActor& actor) = 0; + virtual void onActorDestroyed(const ExtPxActor& actor) = 0; + virtual void onActorHealthUpdate(const ExtPxActor& pxActor) {}; + + virtual void onUpdate() {} + + + //////// protected data //////// + + PhysXController& m_physXController; + ExtPxManager& m_pxManager; + const BlastAsset& m_blastAsset; + +private: + + //////// physics listener //////// + + class PxManagerListener : public ExtPxListener + { + public: + PxManagerListener(BlastFamily* family) : m_family(family) {} + + virtual void onActorCreated(ExtPxFamily& family, ExtPxActor& actor) + { + m_family->processActorCreated(family, actor); + + } + + virtual void onActorDestroyed(ExtPxFamily& family, ExtPxActor& actor) + { + m_family->processActorDestroyed(family, actor); + } + private: + BlastFamily* m_family; + }; + + friend class PxManagerListener; + + //////// private methods //////// + + void processActorCreated(ExtPxFamily&, ExtPxActor& actor); + void processActorDestroyed(ExtPxFamily&, ExtPxActor& actor); + + + //////// private data //////// + + TkFamily* m_tkFamily; + ExtPxFamily* m_pxFamily; + PxManagerListener m_listener; + Settings m_settings; + size_t m_familySize; + uint32_t m_totalVisibleChunkCount; + ExtStressSolver* m_stressSolver; + double m_stressSolveTime; + std::set<ExtPxActor*> m_actors; + std::set<const ExtPxActor*> m_actorsToUpdateHealth; +}; + + +#endif //BLAST_FAMILY_H
\ No newline at end of file diff --git a/NvBlast/samples/SampleBase/blast/BlastFamilyBoxes.cpp b/NvBlast/samples/SampleBase/blast/BlastFamilyBoxes.cpp new file mode 100644 index 0000000..e8b7b27 --- /dev/null +++ b/NvBlast/samples/SampleBase/blast/BlastFamilyBoxes.cpp @@ -0,0 +1,92 @@ +/* +* Copyright (c) 2008-2015, 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 "BlastFamilyBoxes.h" +#include "NvBlastExtPxAsset.h" +#include "NvBlastExtPxActor.h" +#include "BlastAssetBoxes.h" +#include "Renderer.h" +#include "PhysXController.h" +#include "RenderUtils.h" +#include "PxRigidDynamic.h" + +using namespace physx; + + +BlastFamilyBoxes::BlastFamilyBoxes(PhysXController& physXController, ExtPxManager& pxManager, Renderer& renderer, const BlastAssetBoxes& blastAsset, const BlastAsset::ActorDesc& desc) + : BlastFamily(physXController, pxManager, blastAsset), m_renderer(renderer) +{ + // prepare renderables + IRenderMesh* boxRenderMesh = renderer.getPrimitiveRenderMesh(PrimitiveRenderMeshType::Box); + RenderMaterial* primitiveRenderMaterial = physXController.getPrimitiveRenderMaterial(); + + const ExtPxAsset* pxAsset = m_blastAsset.getPxAsset(); + const uint32_t chunkCount = pxAsset->getChunkCount(); + const ExtPxChunk* chunks = pxAsset->getChunks(); + const ExtPxSubchunk* subChunks = pxAsset->getSubchunks(); + m_chunkRenderables.resize(chunkCount); + for (uint32_t i = 0; i < chunkCount; i++) + { + Renderable* renderable = renderer.createRenderable(*boxRenderMesh, *primitiveRenderMaterial); + renderable->setHidden(true); + renderable->setScale(subChunks[chunks[i].firstSubchunkIndex].geometry.scale.scale); + m_chunkRenderables[i] = renderable; + } + + // initialize in position + initialize(desc); +} + +BlastFamilyBoxes::~BlastFamilyBoxes() +{ + for (uint32_t i = 0; i < m_chunkRenderables.size(); i++) + { + m_renderer.removeRenderable(m_chunkRenderables[i]); + } +} + +void BlastFamilyBoxes::onActorCreated(const ExtPxActor& actor) +{ + DirectX::XMFLOAT4 color = getRandomPastelColor(); + + const uint32_t* chunkIndices = actor.getChunkIndices(); + uint32_t chunkCount = actor.getChunkCount(); + for (uint32_t i = 0; i < chunkCount; i++) + { + const uint32_t chunkIndex = chunkIndices[i]; + m_chunkRenderables[chunkIndex]->setHidden(false); + m_chunkRenderables[chunkIndex]->setColor(color); + } +} + +void BlastFamilyBoxes::onActorUpdate(const ExtPxActor& actor) +{ + const ExtPxChunk* chunks = m_blastAsset.getPxAsset()->getChunks(); + const ExtPxSubchunk* subChunks = m_blastAsset.getPxAsset()->getSubchunks(); + const uint32_t* chunkIndices = actor.getChunkIndices(); + uint32_t chunkCount = actor.getChunkCount(); + for (uint32_t i = 0; i < chunkCount; i++) + { + const uint32_t chunkIndex = chunkIndices[i]; + m_chunkRenderables[chunkIndex]->setTransform(actor.getPhysXActor().getGlobalPose() * subChunks[chunks[chunkIndex].firstSubchunkIndex].transform); + } +} + +void BlastFamilyBoxes::onActorDestroyed(const ExtPxActor& actor) +{ + const uint32_t* chunkIndices = actor.getChunkIndices(); + uint32_t chunkCount = actor.getChunkCount(); + for (uint32_t i = 0; i < chunkCount; i++) + { + m_chunkRenderables[chunkIndices[i]]->setHidden(true); + } + +} diff --git a/NvBlast/samples/SampleBase/blast/BlastFamilyBoxes.h b/NvBlast/samples/SampleBase/blast/BlastFamilyBoxes.h new file mode 100644 index 0000000..1f70451 --- /dev/null +++ b/NvBlast/samples/SampleBase/blast/BlastFamilyBoxes.h @@ -0,0 +1,37 @@ +/* +* Copyright (c) 2008-2015, 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 BLAST_FAMILY_BOXES +#define BLAST_FAMILY_BOXES + +#include "BlastFamily.h" + +class BlastAssetBoxes; +class Renderable; + + +class BlastFamilyBoxes : public BlastFamily +{ +public: + BlastFamilyBoxes(PhysXController& physXController, ExtPxManager& pxManager, Renderer& renderer, const BlastAssetBoxes& blastAsset, const BlastAsset::ActorDesc& desc); + virtual ~BlastFamilyBoxes(); + +protected: + virtual void onActorCreated(const ExtPxActor& actor); + virtual void onActorUpdate(const ExtPxActor& actor); + virtual void onActorDestroyed(const ExtPxActor& actor); + +private: + Renderer& m_renderer; + std::vector<Renderable*> m_chunkRenderables; +}; + + +#endif //BLAST_FAMILY_BOXES
\ No newline at end of file diff --git a/NvBlast/samples/SampleBase/blast/BlastFamilyModelSimple.cpp b/NvBlast/samples/SampleBase/blast/BlastFamilyModelSimple.cpp new file mode 100644 index 0000000..858758f --- /dev/null +++ b/NvBlast/samples/SampleBase/blast/BlastFamilyModelSimple.cpp @@ -0,0 +1,335 @@ +/* +* Copyright (c) 2008-2015, 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 "BlastFamilyModelSimple.h" +#include "RenderUtils.h" +#include "DeviceManager.h" +#include "Renderer.h" +#include "NvBlastExtPxAsset.h" +#include "NvBlastExtPxActor.h" +#include "NvBlastTkActor.h" +#include "NvBlastTkAsset.h" +#include "PxRigidDynamic.h" + +using namespace physx; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// SimpleRenderMesh +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +class SimpleRenderMesh : public IRenderMesh +{ +public: + SimpleRenderMesh(const SimpleMesh* mesh) : m_mesh(mesh) + { + m_device = GetDeviceManager()->GetDevice(); + + 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 }); + m_inputDesc.push_back({ "TEXCOORD", 1, DXGI_FORMAT_R32_FLOAT, 1, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }); + + m_numVertices = static_cast<uint32_t>(mesh->vertices.size()); + m_numFaces = static_cast<uint32_t>(mesh->indices.size()); + + // VB + { + D3D11_SUBRESOURCE_DATA vertexBufferData; + ZeroMemory(&vertexBufferData, sizeof(vertexBufferData)); + vertexBufferData.pSysMem = mesh->vertices.data(); + + D3D11_BUFFER_DESC bufferDesc; + memset(&bufferDesc, 0, sizeof(D3D11_BUFFER_DESC)); + bufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; + bufferDesc.ByteWidth = sizeof(SimpleMesh::Vertex) * m_numVertices; + bufferDesc.CPUAccessFlags = 0; + bufferDesc.MiscFlags = 0; + bufferDesc.Usage = D3D11_USAGE_IMMUTABLE; + + V(m_device->CreateBuffer(&bufferDesc, &vertexBufferData, &m_vertexBuffer)); + } + + // Health Buffer + { + // fill with 1.0f initially + std::vector<float> healths(mesh->vertices.size()); + std::fill(healths.begin(), healths.end(), 1.0f); + + D3D11_SUBRESOURCE_DATA vertexBufferData; + ZeroMemory(&vertexBufferData, sizeof(vertexBufferData)); + vertexBufferData.pSysMem = healths.data(); + + D3D11_BUFFER_DESC bufferDesc; + memset(&bufferDesc, 0, sizeof(D3D11_BUFFER_DESC)); + bufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; + bufferDesc.ByteWidth = (uint32_t)(sizeof(float) * m_numVertices); + bufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + bufferDesc.MiscFlags = 0; + bufferDesc.Usage = D3D11_USAGE_DYNAMIC; + + V(m_device->CreateBuffer(&bufferDesc, &vertexBufferData, &m_healthBuffer)); + } + + // IB + if (m_numFaces) + { + D3D11_SUBRESOURCE_DATA indexBufferData; + + ZeroMemory(&indexBufferData, sizeof(indexBufferData)); + indexBufferData.pSysMem = mesh->indices.data(); + + D3D11_BUFFER_DESC bufferDesc; + + memset(&bufferDesc, 0, sizeof(D3D11_BUFFER_DESC)); + bufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER; + bufferDesc.ByteWidth = sizeof(uint16_t) * m_numFaces; + bufferDesc.CPUAccessFlags = 0; + bufferDesc.MiscFlags = 0; + bufferDesc.Usage = D3D11_USAGE_IMMUTABLE; + + V(m_device->CreateBuffer(&bufferDesc, &indexBufferData, &m_indexBuffer)); + } + } + + ~SimpleRenderMesh() + { + SAFE_RELEASE(m_healthBuffer); + SAFE_RELEASE(m_vertexBuffer); + SAFE_RELEASE(m_indexBuffer); + } + + + void render(ID3D11DeviceContext& context) const + { + context.IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + + UINT strides[2] = { sizeof(SimpleMesh::Vertex), sizeof(uint32_t) }; + UINT offsets[2] = { 0 }; + ID3D11Buffer* buffers[2] = { m_vertexBuffer, m_healthBuffer }; + context.IASetVertexBuffers(0, 2, buffers, strides, offsets); + + + context.IASetIndexBuffer(m_indexBuffer, DXGI_FORMAT_R16_UINT, 0); + + if (m_indexBuffer) + context.DrawIndexed(m_numFaces, 0, 0); + else + context.Draw(m_numVertices, 0); + } + + const std::vector<D3D11_INPUT_ELEMENT_DESC>& getInputElementDesc() const { return m_inputDesc; } + + const SimpleMesh* getMesh() { return m_mesh; } + + void updateHealths(const std::vector<float>& healths) + { + ID3D11DeviceContext* context; + m_device->GetImmediateContext(&context); + + // update buffer + { + D3D11_MAPPED_SUBRESOURCE mappedRead; + V(context->Map(m_healthBuffer, 0, D3D11_MAP_WRITE_DISCARD, NULL, &mappedRead)); + memcpy(mappedRead.pData, healths.data(), sizeof(float) * healths.size()); + context->Unmap(m_healthBuffer, 0); + } + + } + + +private: + + ID3D11Device* m_device; + + ID3D11Buffer* m_vertexBuffer; + ID3D11Buffer* m_healthBuffer; + ID3D11Buffer* m_indexBuffer; + uint32_t m_numFaces; + uint32_t m_numVertices; + + std::vector<D3D11_INPUT_ELEMENT_DESC> m_inputDesc; + + const SimpleMesh* m_mesh; +}; + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// BlastFamilyModelSimple +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +BlastFamilyModelSimple::BlastFamilyModelSimple(PhysXController& physXController, ExtPxManager& pxManager, Renderer& renderer, const BlastAssetModelSimple& blastAsset, const BlastAsset::ActorDesc& desc) + : BlastFamily(physXController, pxManager, blastAsset), m_renderer(renderer) +{ + // materials + auto materials = blastAsset.getRenderMaterials(); + + // model + const BlastModel& model = blastAsset.getModel(); + + // create render mesh for every BlastModel::Chunk::Mesh and renderable with it + const std::vector<BlastModel::Chunk>& modelChunks = model.chunks; + m_chunks.resize(modelChunks.size()); + for (uint32_t chunkIndex = 0; chunkIndex < modelChunks.size(); chunkIndex++) + { + const std::vector<BlastModel::Chunk::Mesh>& meshes = modelChunks[chunkIndex].meshes; + std::vector<SimpleRenderMesh*>& renderMeshes = m_chunks[chunkIndex].renderMeshes; + std::vector<Renderable*>& renderables = m_chunks[chunkIndex].renderables; + renderMeshes.resize(meshes.size()); + renderables.resize(meshes.size()); + for (uint32_t i = 0; i < meshes.size(); i++) + { + renderMeshes[i] = new SimpleRenderMesh(&meshes[i].mesh); + + uint32_t materialIndex = model.chunks[chunkIndex].meshes[i].materialIndex; + Renderable* renderable = renderer.createRenderable(*renderMeshes[i], *materials[materialIndex]); + renderable->setHidden(true); + renderables[i] = renderable; + + } + } + + // initialize in position + initialize(desc); +} + +BlastFamilyModelSimple::~BlastFamilyModelSimple() +{ + // release all chunks + for (uint32_t chunkIndex = 0; chunkIndex < m_chunks.size(); chunkIndex++) + { + std::vector<Renderable*>& renderables = m_chunks[chunkIndex].renderables; + for (uint32_t i = 0; i < m_chunks[chunkIndex].renderables.size(); i++) + { + m_renderer.removeRenderable(m_chunks[chunkIndex].renderables[i]); + SAFE_DELETE(m_chunks[chunkIndex].renderMeshes[i]); + } + } +} + +void BlastFamilyModelSimple::onActorCreated(const ExtPxActor& actor) +{ + // separate color for every material + std::vector<DirectX::XMFLOAT4> colors; + + const uint32_t* chunkIndices = actor.getChunkIndices(); + uint32_t chunkCount = actor.getChunkCount(); + for (uint32_t i = 0; i < chunkCount; i++) + { + uint32_t chunkIndex = chunkIndices[i]; + std::vector<Renderable*>& renderables = m_chunks[chunkIndex].renderables; + for (uint32_t r = 0; r < renderables.size(); r++) + { + if (colors.size() <= r) + colors.push_back(getRandomPastelColor()); + + renderables[r]->setHidden(false); + renderables[r]->setColor(colors[r]); + } + } +} + +void BlastFamilyModelSimple::onActorUpdate(const ExtPxActor& actor) +{ + const ExtPxChunk* chunks = m_blastAsset.getPxAsset()->getChunks(); + const ExtPxSubchunk* subChunks = m_blastAsset.getPxAsset()->getSubchunks(); + const uint32_t* chunkIndices = actor.getChunkIndices(); + uint32_t chunkCount = actor.getChunkCount(); + for (uint32_t i = 0; i < chunkCount; i++) + { + uint32_t chunkIndex = chunkIndices[i]; + std::vector<Renderable*>& renderables = m_chunks[chunkIndex].renderables; + for (Renderable* r : renderables) + { + r->setTransform(actor.getPhysXActor().getGlobalPose() * subChunks[chunks[chunkIndex].firstSubchunkIndex].transform); + } + } +} + +void BlastFamilyModelSimple::onActorDestroyed(const ExtPxActor& actor) +{ + const uint32_t* chunkIndices = actor.getChunkIndices(); + uint32_t chunkCount = actor.getChunkCount(); + for (uint32_t i = 0; i < chunkCount; i++) + { + uint32_t chunkIndex = chunkIndices[i]; + std::vector<Renderable*>& renderables = m_chunks[chunkIndex].renderables; + for (Renderable* r : renderables) + { + r->setHidden(true); + } + } +} + +void BlastFamilyModelSimple::onActorHealthUpdate(const ExtPxActor& actor) +{ + TkActor& tkActor = actor.getTkActor(); + const TkAsset* tkAsset = tkActor.getAsset(); + + const float* bondHealths = tkActor.getBondHealths(); + uint32_t nodeCount = tkActor.getGraphNodeCount(); + if (nodeCount == 0) // subsupport chunks don't have graph nodes + return; + + std::vector<uint32_t> nodes(tkActor.getGraphNodeCount()); + tkActor.getGraphNodeIndices(nodes.data(), static_cast<uint32_t>(nodes.size())); + + const NvBlastChunk* chunks = tkAsset->getChunks(); + const NvBlastBond* bonds = tkAsset->getBonds(); + + const NvBlastSupportGraph graph = tkAsset->getGraph(); + + std::vector<float> healthBuffer; + + for (uint32_t node0 : nodes) + { + uint32_t chunkIndex = graph.chunkIndices[node0]; + + if (chunkIndex >= m_chunks.size()) + continue; + + std::vector<SimpleRenderMesh*>& meshes = m_chunks[chunkIndex].renderMeshes; + const auto& renderables = m_chunks[chunkIndex].renderables; + for (uint32_t i = 0; i < meshes.size(); ++i) + { + if(renderables[i]->isHidden()) + continue; + + SimpleRenderMesh* renderMesh = meshes[i]; + + const SimpleMesh* mesh = renderMesh->getMesh(); + healthBuffer.resize(mesh->vertices.size()); + + for (uint32_t vertexIndex = 0; vertexIndex < mesh->vertices.size(); vertexIndex++) + { + PxVec3 position = mesh->vertices[vertexIndex].position; + float health = 0.0f; + float healthDenom = 0.0f; + + for (uint32_t adjacencyIndex = graph.adjacencyPartition[node0]; adjacencyIndex < graph.adjacencyPartition[node0 + 1]; adjacencyIndex++) + { + uint32_t node1 = graph.adjacentNodeIndices[adjacencyIndex]; + uint32_t bondIndex = graph.adjacentBondIndices[adjacencyIndex]; + float bondHealth = PxClamp(bondHealths[bondIndex] / BOND_HEALTH_MAX, 0.0f, 1.0f); + const NvBlastBond& solverBond = bonds[bondIndex]; + const PxVec3& centroid = reinterpret_cast<const PxVec3&>(solverBond.centroid); + + float factor = 1.0f / (centroid - position).magnitudeSquared(); + + health += bondHealth * factor; + healthDenom += factor; + } + + healthBuffer[vertexIndex] = healthDenom > 0.0f ? health / healthDenom : 1.0f; + } + + renderMesh->updateHealths(healthBuffer); + } + } +} diff --git a/NvBlast/samples/SampleBase/blast/BlastFamilyModelSimple.h b/NvBlast/samples/SampleBase/blast/BlastFamilyModelSimple.h new file mode 100644 index 0000000..7816690 --- /dev/null +++ b/NvBlast/samples/SampleBase/blast/BlastFamilyModelSimple.h @@ -0,0 +1,52 @@ +/* +* Copyright (c) 2008-2015, 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 BLAST_FAMILY_MODEL_SIMPLE_H +#define BLAST_FAMILY_MODEL_SIMPLE_H + +#include "BlastFamily.h" +#include "BlastAssetModelSimple.h" + +class SimpleRenderMesh; +class Renderable; +class Renderer; + +class BlastFamilyModelSimple : public BlastFamily +{ +public: + //////// ctor //////// + + BlastFamilyModelSimple(PhysXController& physXController, ExtPxManager& pxManager, Renderer& renderer, const BlastAssetModelSimple& blastAsset, const BlastAsset::ActorDesc& desc); + virtual ~BlastFamilyModelSimple(); + +protected: + //////// abstract implementation //////// + + virtual void onActorCreated(const ExtPxActor& actor); + virtual void onActorUpdate(const ExtPxActor& actor); + virtual void onActorDestroyed(const ExtPxActor& actor); + virtual void onActorHealthUpdate(const ExtPxActor& pxActor); + +private: + //////// internal data //////// + + Renderer& m_renderer; + + struct Chunk + { + std::vector<SimpleRenderMesh*> renderMeshes; + std::vector<Renderable*> renderables; + }; + + std::vector<Chunk> m_chunks; +}; + + +#endif //BLAST_FAMILY_MODEL_SIMPLE_H
\ No newline at end of file diff --git a/NvBlast/samples/SampleBase/blast/BlastFamilyModelSkinned.cpp b/NvBlast/samples/SampleBase/blast/BlastFamilyModelSkinned.cpp new file mode 100644 index 0000000..c0731d5 --- /dev/null +++ b/NvBlast/samples/SampleBase/blast/BlastFamilyModelSkinned.cpp @@ -0,0 +1,167 @@ +/* +* Copyright (c) 2008-2015, 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 "BlastFamilyModelSkinned.h" +#include "RenderUtils.h" +#include "Renderer.h" +#include "SkinnedRenderMesh.h" +#include "NvBlastExtPxAsset.h" +#include "NvBlastExtPxActor.h" +#include "NvBlastTkActor.h" +#include "PxRigidDynamic.h" + +using namespace physx; + +BlastFamilyModelSkinned::BlastFamilyModelSkinned(PhysXController& physXController, ExtPxManager& pxManager, Renderer& renderer, const BlastAssetModelSkinned& blastAsset, const BlastAsset::ActorDesc& desc) + : BlastFamily(physXController, pxManager, blastAsset), m_renderer(renderer), m_visibleActorsDirty(true) +{ + // materials + auto materials = blastAsset.getRenderMaterials(); + + const BlastModel& model = blastAsset.getModel(); + + // finalize (create) sub model + auto finalizeSubModelFunction = [&](SubModel* subModel, std::vector<const SimpleMesh*>& subModelMeshes, RenderMaterial& renderMaterial) + { + subModel->skinnedRenderMesh = new SkinnedRenderMesh(subModelMeshes); + subModel->renderable = renderer.createRenderable(*subModel->skinnedRenderMesh, renderMaterial); + subModel->renderable->setColor(getRandomPastelColor()); + }; + + // create at least one submodel per every material (if mesh count is too high, more then one sub model per material to be created) + SubModel* subModel = nullptr; + std::vector<const SimpleMesh*> subModelMeshes; + subModelMeshes.reserve(model.chunks.size()); + for (uint32_t materialIndex = 0; materialIndex < model.materials.size(); materialIndex++) + { + for (uint32_t chunkIndex = 0; chunkIndex < model.chunks.size(); chunkIndex++) + { + const BlastModel::Chunk& chunk = model.chunks[chunkIndex]; + for (const BlastModel::Chunk::Mesh& mesh : chunk.meshes) + { + if (mesh.materialIndex == materialIndex) + { + // init new submodel? + if (subModel == nullptr) + { + m_subModels.push_back(SubModel()); + subModel = &m_subModels.back(); + subModel->chunkIdToBoneMap.resize(model.chunks.size()); + std::fill(subModel->chunkIdToBoneMap.begin(), subModel->chunkIdToBoneMap.end(), SubModel::INVALID_BONE_ID); + subModelMeshes.clear(); + } + + // add mesh to map and list + subModel->chunkIdToBoneMap[chunkIndex] = (uint32_t)subModelMeshes.size(); + subModelMeshes.push_back(&(mesh.mesh)); + + // mesh reached limit? + if (subModelMeshes.size() == SkinnedRenderMesh::MeshesCountMax) + { + finalizeSubModelFunction(subModel, subModelMeshes, *materials[materialIndex]); + subModel = nullptr; + } + } + } + } + + // finalize subModel for this material + if (subModel && subModelMeshes.size() > 0) + { + finalizeSubModelFunction(subModel, subModelMeshes, *materials[materialIndex]); + subModel = nullptr; + } + } + + // reserve for scratch + m_visibleBones.reserve(model.chunks.size()); + m_visibleBoneTransforms.reserve(model.chunks.size()); + + // initialize in position + initialize(desc); +} + +BlastFamilyModelSkinned::~BlastFamilyModelSkinned() +{ + for (uint32_t subModelIndex = 0; subModelIndex < m_subModels.size(); subModelIndex++) + { + m_renderer.removeRenderable(m_subModels[subModelIndex].renderable); + SAFE_DELETE(m_subModels[subModelIndex].skinnedRenderMesh); + } +} + +void BlastFamilyModelSkinned::onActorCreated(const ExtPxActor& actor) +{ + m_visibleActors.insert(&actor); + m_visibleActorsDirty = true; +} + +void BlastFamilyModelSkinned::onActorUpdate(const ExtPxActor& actor) +{ +} + +void BlastFamilyModelSkinned::onActorDestroyed(const ExtPxActor& actor) +{ + m_visibleActors.erase(&actor); + m_visibleActorsDirty = true; +} + +void BlastFamilyModelSkinned::onUpdate() +{ + // visible actors changed this frame? + if (m_visibleActorsDirty) + { + for (const SubModel& model : m_subModels) + { + // pass visible chunks list to render mesh + m_visibleBones.clear(); + for (const ExtPxActor* actor : m_visibleActors) + { + const uint32_t* chunkIndices = actor->getChunkIndices(); + uint32_t chunkCount = actor->getChunkCount(); + for (uint32_t i = 0; i < chunkCount; ++i) + { + uint32_t chunkIndex = chunkIndices[i]; + uint32_t boneIndex = model.chunkIdToBoneMap[chunkIndex]; + if (boneIndex != SubModel::INVALID_BONE_ID) + { + m_visibleBones.push_back(boneIndex); + } + } + } + model.skinnedRenderMesh->updateVisibleMeshes(m_visibleBones); + } + + m_visibleActorsDirty = false; + } + + // update and pass chunk transforms + const ExtPxChunk* chunks = m_blastAsset.getPxAsset()->getChunks(); + const ExtPxSubchunk* subChunks = m_blastAsset.getPxAsset()->getSubchunks(); + for (const SubModel& model : m_subModels) + { + m_visibleBoneTransforms.clear(); + for (const ExtPxActor* actor : m_visibleActors) + { + const uint32_t* chunkIndices = actor->getChunkIndices(); + uint32_t chunkCount = actor->getChunkCount(); + for (uint32_t i = 0; i < chunkCount; ++i) + { + uint32_t chunkIndex = chunkIndices[i]; + uint32_t boneIndex = model.chunkIdToBoneMap[chunkIndex]; + if (boneIndex != SubModel::INVALID_BONE_ID) + { + m_visibleBoneTransforms.push_back(PxMat44(actor->getPhysXActor().getGlobalPose() * subChunks[chunks[chunkIndex].firstSubchunkIndex].transform)); + } + } + } + model.skinnedRenderMesh->updateVisibleMeshTransforms(m_visibleBoneTransforms); + } +} diff --git a/NvBlast/samples/SampleBase/blast/BlastFamilyModelSkinned.h b/NvBlast/samples/SampleBase/blast/BlastFamilyModelSkinned.h new file mode 100644 index 0000000..aeb9353 --- /dev/null +++ b/NvBlast/samples/SampleBase/blast/BlastFamilyModelSkinned.h @@ -0,0 +1,63 @@ +/* +* Copyright (c) 2008-2015, 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 BLAST_FAMILY_MODEL_SKINNED_H +#define BLAST_FAMILY_MODEL_SKINNED_H + +#include "BlastFamily.h" +#include "BlastAssetModelSkinned.h" + +class SkinnedRenderMesh; +class Renderable; + +class BlastFamilyModelSkinned : public BlastFamily +{ +public: + //////// ctor //////// + + BlastFamilyModelSkinned(PhysXController& physXController, ExtPxManager& pxManager, Renderer& renderer, const BlastAssetModelSkinned& blastAsset, const BlastAsset::ActorDesc& desc); + virtual ~BlastFamilyModelSkinned(); + +protected: + //////// abstract implementation //////// + + virtual void onActorCreated(const ExtPxActor& actor); + virtual void onActorUpdate(const ExtPxActor& actor); + virtual void onActorDestroyed(const ExtPxActor& actor); + + virtual void onUpdate(); + +private: + //////// internal data //////// + + Renderer& m_renderer; + + struct SubModel + { + static const uint32_t INVALID_BONE_ID = ~(uint32_t)0; + + Renderable* renderable = nullptr; + SkinnedRenderMesh* skinnedRenderMesh = nullptr; + std::vector<uint32_t> chunkIdToBoneMap; + }; + std::vector<SubModel> m_subModels; + + std::set<const ExtPxActor*> m_visibleActors; + bool m_visibleActorsDirty; + + //////// scratch buffers //////// + + std::vector<uint32_t> m_visibleBones; + std::vector<PxMat44> m_visibleBoneTransforms; + +}; + + +#endif //BLAST_FAMILY_MODEL_SKINNED_H
\ No newline at end of file diff --git a/NvBlast/samples/SampleBase/blast/BlastModel.cpp b/NvBlast/samples/SampleBase/blast/BlastModel.cpp new file mode 100644 index 0000000..88ddf3c --- /dev/null +++ b/NvBlast/samples/SampleBase/blast/BlastModel.cpp @@ -0,0 +1,160 @@ +/* +* Copyright (c) 2008-2015, 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 "BlastModel.h" + +#define TINYOBJLOADER_IMPLEMENTATION +#include "tiny_obj_loader.h" + + +using namespace physx; + +BlastModelPtr BlastModel::loadFromFileTinyLoader(const char* path) +{ + std::shared_ptr<BlastModel> model = std::shared_ptr<BlastModel>(new BlastModel()); + + std::vector<tinyobj::shape_t> shapes; + std::vector<tinyobj::material_t> mats; + std::string err; + std::string mtlPath; + for (size_t i = strnlen(path, 255) - 1; i >= 0; --i) + { + if (path[i] == '\\') + { + mtlPath.resize(i + 2, 0); + strncpy(&mtlPath[0], path, i + 1); + break; + } + } + + + bool ret = tinyobj::LoadObj(shapes, mats, err, path, mtlPath.data()); + + // can't load? + if (!ret) + return false; + + // one submodel per material + uint32_t materialsCount = (uint32_t)mats.size(); + model->materials.resize(materialsCount); + + // fill submodel materials + for (uint32_t i = 0; i < materialsCount; i++) + { + tinyobj::material_t *pMaterial = &mats[i]; + + if (!pMaterial->diffuse_texname.empty()) + { + model->materials[i].diffuseTexture = pMaterial->diffuse_texname; + } + } + + // estimate + model->chunks.reserve(shapes.size() / materialsCount + 1); + + if (shapes.size() > 0) + { + uint32_t meshIndex = 0; + for (uint32_t m = 0; m < shapes.size(); m++) + { + tinyobj::shape_t& pMesh = shapes[m]; + uint32_t materialIndex; + uint32_t chunkIndex; + sscanf(pMesh.name.data(), "%d_%d", &chunkIndex, &materialIndex); + if (model->chunks.size() <= chunkIndex) + { + model->chunks.resize(chunkIndex + 1); + } + model->chunks[chunkIndex].meshes.push_back(Chunk::Mesh()); + Chunk::Mesh& mesh = model->chunks[chunkIndex].meshes.back(); + + mesh.materialIndex = materialIndex; + SimpleMesh& chunkMesh = mesh.mesh; + + PxVec3 emin(FLT_MAX, FLT_MAX, FLT_MAX); + PxVec3 emax(FLT_MIN, FLT_MIN, FLT_MIN); + + + + // create an index buffer + chunkMesh.indices.resize(pMesh.mesh.indices.size()); + + // Check if all faces are triangles + bool allTriangles = true; + for (uint32_t i = 0; i < pMesh.mesh.num_vertices.size(); ++i) + { + if (pMesh.mesh.num_vertices[i] != 3) + { + allTriangles = false; + break; + } + } + + if (pMesh.mesh.indices.size() > 0 && allTriangles) + { + for (uint32_t i = 0; i < pMesh.mesh.indices.size(); i += 3) + { + chunkMesh.indices[i] = (uint16_t)pMesh.mesh.indices[i + 2]; + chunkMesh.indices[i + 1] = (uint16_t)pMesh.mesh.indices[i + 1]; + chunkMesh.indices[i + 2] = (uint16_t)pMesh.mesh.indices[i]; + } + } + // create vertex buffer + chunkMesh.vertices.resize(pMesh.mesh.positions.size() / 3); + // copy positions + uint32_t indexer = 0; + for (uint32_t i = 0; i < pMesh.mesh.positions.size() / 3; i++) + { + chunkMesh.vertices[i].position.x = pMesh.mesh.positions[indexer]; + chunkMesh.vertices[i].position.y = pMesh.mesh.positions[indexer + 1]; + chunkMesh.vertices[i].position.z = pMesh.mesh.positions[indexer + 2]; + indexer += 3; + // calc min/max + emin = emin.minimum(chunkMesh.vertices[i].position); + emax = emax.maximum(chunkMesh.vertices[i].position); + } + + // copy normals + if (pMesh.mesh.normals.size() > 0) + { + indexer = 0; + for (uint32_t i = 0; i < pMesh.mesh.normals.size() / 3; i++) + { + chunkMesh.vertices[i].normal.x = pMesh.mesh.normals[indexer]; + chunkMesh.vertices[i].normal.y = pMesh.mesh.normals[indexer + 1]; + chunkMesh.vertices[i].normal.z = pMesh.mesh.normals[indexer + 2]; + + indexer += 3; + } + } + + // copy uv + if (pMesh.mesh.texcoords.size() > 0) + { + indexer = 0; + for (uint32_t i = 0; i < pMesh.mesh.texcoords.size() / 2; i++) + { + chunkMesh.vertices[i].uv.x = pMesh.mesh.texcoords[indexer]; + chunkMesh.vertices[i].uv.y = pMesh.mesh.texcoords[indexer + 1]; + indexer += 2; + } + } + + // assign extents + chunkMesh.extents = (emax - emin) * 0.5f; + + // get the center + chunkMesh.center = emin + chunkMesh.extents; + + } + } + + return model; +} diff --git a/NvBlast/samples/SampleBase/blast/BlastModel.h b/NvBlast/samples/SampleBase/blast/BlastModel.h new file mode 100644 index 0000000..2ef39f6 --- /dev/null +++ b/NvBlast/samples/SampleBase/blast/BlastModel.h @@ -0,0 +1,55 @@ +/* +* Copyright (c) 2016-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 BLAST_MODEL_H +#define BLAST_MODEL_H + +#include "Mesh.h" +#include <vector> +#include <memory> + + +class BlastModel; +typedef std::shared_ptr<const BlastModel> BlastModelPtr; + +/** +BlastModel struct represents graphic model. +Now only loading from .obj file is supported. +Can have >=0 materials +Every chunk can have multiple meshes (1 for every material) +*/ +class BlastModel +{ +public: + struct Material + { + std::string diffuseTexture; + }; + + struct Chunk + { + struct Mesh + { + uint32_t materialIndex; + SimpleMesh mesh; + }; + + std::vector<Mesh> meshes; + }; + + std::vector<Material> materials; + std::vector<Chunk> chunks; + + static BlastModelPtr loadFromFileTinyLoader(const char* path); +private: + BlastModel() {} +}; + +#endif // ifndef BLAST_MODEL_H
\ No newline at end of file diff --git a/NvBlast/samples/SampleBase/blast/BlastReplay.cpp b/NvBlast/samples/SampleBase/blast/BlastReplay.cpp new file mode 100644 index 0000000..13d2b33 --- /dev/null +++ b/NvBlast/samples/SampleBase/blast/BlastReplay.cpp @@ -0,0 +1,160 @@ +/* +* Copyright (c) 2008-2015, 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 "BlastReplay.h" +#include "NvBlastTk.h" +#include "NvBlastExtPxManager.h" +#include "NvBlastExtPxFamily.h" +#include "SampleProfiler.h" + + +using namespace std::chrono; + +BlastReplay::BlastReplay() : m_sync(nullptr) +{ + m_sync = ExtSync::create(); + reset(); +} + +BlastReplay::~BlastReplay() +{ + m_sync->release(); + clearBuffer(); +} + +void BlastReplay::addFamily(TkFamily* family) +{ + family->addListener(*m_sync); +} + +void BlastReplay::removeFamily(TkFamily* family) +{ + family->removeListener(*m_sync); +} + +void BlastReplay::startRecording(ExtPxManager& manager, bool syncFamily, bool syncPhysics) +{ + if (isRecording()) + return; + + m_sync->releaseSyncBuffer(); + + if (syncFamily || syncPhysics) + { + std::vector<ExtPxFamily*> families(manager.getFamilyCount()); + manager.getFamilies(families.data(), (uint32_t)families.size()); + for (ExtPxFamily* family : families) + { + if (syncPhysics) + { + m_sync->syncFamily(*family); + } + else if (syncFamily) + { + m_sync->syncFamily(family->getTkFamily()); + } + } + } + + m_isRecording = true; +} + +void BlastReplay::stopRecording() +{ + if (!isRecording()) + return; + + const ExtSyncEvent*const* buffer; + uint32_t size; + m_sync->acquireSyncBuffer(buffer, size); + + clearBuffer(); + m_buffer.resize(size); + for (uint32_t i = 0; i < size; ++i) + { + m_buffer[i] = buffer[i]->clone(); + } + + // TODO: sort by ts ? make sure? + //m_buffer.sort + + m_sync->releaseSyncBuffer(); + + m_isRecording = false; +} + +void BlastReplay::startPlayback(ExtPxManager& manager, TkGroup* group) +{ + if (isPlaying() || !hasRecord()) + return; + + m_isPlaying = true; + m_startTime = steady_clock::now(); + m_nextEventIndex = 0; + m_firstEventTs = m_buffer[0]->timestamp; + m_pxManager = &manager; + m_group = group; +} + +void BlastReplay::stopPlayback() +{ + if (!isPlaying()) + return; + + m_isPlaying = false; + m_pxManager = nullptr; + m_group = nullptr; +} + +void BlastReplay::update() +{ + if (isPlaying()) + { + PROFILER_SCOPED_FUNCTION(); + + auto now = steady_clock::now(); + auto mil = duration_cast<milliseconds>((now - m_startTime)); + bool stop = true; + while (m_nextEventIndex < m_buffer.size()) + { + const ExtSyncEvent* e = m_buffer[m_nextEventIndex]; + auto t = e->timestamp - m_firstEventTs; + if (t < (uint64_t)mil.count()) + { + m_sync->applySyncBuffer(m_pxManager->getFramework(), &e, 1, m_group, m_pxManager); + m_nextEventIndex++; + } + else + { + stop = false; + break; + } + } + + if (stop) + stopPlayback(); + } +} + +void BlastReplay::reset() +{ + m_isPlaying = false; + m_isRecording = false; + m_sync->releaseSyncBuffer(); +} + +void BlastReplay::clearBuffer() +{ + for (auto e : m_buffer) + { + e->release(); + } + m_buffer.clear(); +} diff --git a/NvBlast/samples/SampleBase/blast/BlastReplay.h b/NvBlast/samples/SampleBase/blast/BlastReplay.h new file mode 100644 index 0000000..a85c996 --- /dev/null +++ b/NvBlast/samples/SampleBase/blast/BlastReplay.h @@ -0,0 +1,75 @@ +/* +* Copyright (c) 2016-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 BLAST_REPLAY_H +#define BLAST_REPLAY_H + +#include "NvBlastExtSync.h" +#include <chrono> + +using namespace Nv::Blast; + +class BlastReplay +{ +public: + BlastReplay(); + ~BlastReplay(); + + bool isRecording() const + { + return m_isRecording; + } + + bool isPlaying() const + { + return m_isPlaying; + } + + bool hasRecord() const + { + return m_buffer.size() > 0; + } + + size_t getEventCount() const + { + return isRecording() ? m_sync->getSyncBufferSize() : m_buffer.size(); + } + + uint32_t getCurrentEventIndex() const + { + return m_nextEventIndex; + } + + void addFamily(TkFamily* family); + void removeFamily(TkFamily* family); + + void startRecording(ExtPxManager& manager, bool syncFamily, bool syncPhysics); + void stopRecording(); + void startPlayback(ExtPxManager& manager, TkGroup* group); + void stopPlayback(); + void update(); + void reset(); + +private: + void clearBuffer(); + + ExtPxManager* m_pxManager; + TkGroup* m_group; + std::chrono::steady_clock::time_point m_startTime; + uint64_t m_firstEventTs; + uint32_t m_nextEventIndex; + bool m_isRecording; + bool m_isPlaying; + ExtSync* m_sync; + std::vector<ExtSyncEvent*> m_buffer; +}; + + +#endif // ifndef BLAST_REPLAY_H
\ No newline at end of file |