// This code contains NVIDIA Confidential Information and is disclosed to you // under a form of NVIDIA software license agreement provided separately to you. // // Notice // NVIDIA Corporation and its licensors retain all intellectual property and // proprietary rights in and to this software and related documentation and // any modifications thereto. Any use, reproduction, disclosure, or // distribution of this software and related documentation without an express // license agreement from NVIDIA Corporation is strictly prohibited. // // ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES // NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO // THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, // MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. // // Information and code furnished is believed to be accurate and reliable. // However, NVIDIA Corporation assumes no responsibility for the consequences of use of such // information or for any infringement of patents or other rights of third parties that may // result from its use. No license is granted by implication or otherwise under any patent // or patent rights of NVIDIA Corporation. Details are subject to change without notice. // This code supersedes and replaces all information previously supplied. // NVIDIA Corporation products are not authorized for use as critical // components in life support devices or systems without express written approval of // NVIDIA Corporation. // // Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. #include "SceneController.h" #include "RenderUtils.h" #include "Utils.h" #include "BlastAssetBoxes.h" #include "BlastAssetModelSimple.h" #include "BlastAssetModelSkinned.h" #include "NvBlastExtPxAsset.h" #include "NvBlastExtPxFamily.h" #include "NvBlastExtPxManager.h" #include "SampleAssetListParser.h" #include "BlastReplay.h" #include "Renderer.h" #include "BlastController.h" #include "CommonUIController.h" #include "PhysXController.h" #include "PxRigidDynamic.h" #include #include "PxInputDataFromPxFileBuf.h" #include #include #include #include //////// Simple hash function //////// static NvBlastID generateIDFromString(const char* str) { uint32_t h[4] = { 5381, 5381, 5381, 5381 }; int i = 0; for (const char* ptr = str; *ptr; i = ((i + 1) & 3), ++ptr) { h[i] = ((h[i] << 5) + h[i]) ^ static_cast(*ptr); } return *reinterpret_cast(h); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Scenes Setup /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// const DirectX::XMFLOAT4 PICK_POINTER_ACTIVE_COLOR(1.0f, 0.f, 0.f, 0.6f); const float RIGIDBODY_DENSITY = 2000.0f; class SingleSceneAsset; class SceneAsset { public: SceneAsset() : spawnCount(0) {} virtual ~SceneAsset() {} void initialize(Scene* scene) { m_scene = scene; } virtual const char* getID() const = 0; virtual const char* getName() const = 0; virtual void load() = 0; virtual void unload() = 0; virtual bool isLoaded() const = 0; virtual void spawn(PxVec3 shift) = 0; virtual ImVec4 getUIColor() const { return ImGui::GetStyle().Colors[ImGuiCol_Text]; } uint32_t spawnCount; protected: Scene* m_scene; }; class SceneActor { public: SceneActor() : removeOnReload(false) {} virtual ~SceneActor() {} virtual const char* getName() const = 0; virtual const char* getSubname(int subindex) const { return nullptr; } virtual ImVec4 getUIColor() const = 0; virtual void drawUI(int subindex) {} virtual void drawStatsUI(int subindex) {} virtual uint32_t getSubactorCount() const { return 0; } virtual PxVec3 getSpawnShift() const { return PxVec3(PxZero); } virtual void reload() {} virtual void removeSubactor(int subindex) {} bool removeOnReload; }; class Scene { public: struct ActorIndex { int index; int subindex; ActorIndex() { reset(); } ActorIndex(int i, int s = -1) : index(i), subindex(s) {} bool operator==(const ActorIndex& other) const { return index == other.index && subindex == other.subindex; } void reset() { index = -1; subindex = -1; } }; Scene(Renderer& renderer, PhysXController& physXController, BlastController& blastController, CommonUIController& commonUIController) : m_renderer(renderer), m_physXController(physXController), m_blastController(blastController), m_commonUIController(commonUIController) { } ~Scene() { removeAllSceneActors(); for (uint32_t i = 0; i < m_assets.size(); i++) { SAFE_DELETE(m_assets[i]); } m_assets.clear(); m_assetsByID.clear(); m_tkAssetMap.clear(); } void addAsset(SceneAsset* asset) { m_assets.push_back(asset); asset->initialize(this); m_assetsByID[asset->getID()] = asset; } void drawUI() { /////////////////////////////////////////////////////////////////////////////////////////// // Assets Selection /////////////////////////////////////////////////////////////////////////////////////////// { static int mode = 0; ImGui::RadioButton("Replace", &mode, 0); ImGui::SameLine(); ImGui::RadioButton("Append", &mode, 1); ImGui::ListBoxHeader("Assets", (int)m_assets.size()); for (uint32_t i = 0; i < m_assets.size(); ++i) { ImVec4 color = m_assets[i]->getUIColor(); color.w = color.w * (m_assets[i]->isLoaded() ? 1.0f : 0.5f); ImGui::PushStyleColor(ImGuiCol_Text, color); if (ImGui::Selectable(m_assets[i]->getName(), m_lastSpawnedAsset == i)) { m_lastSpawnedAsset = i; if (mode == 0) { removeAllSceneActors(); } m_commonUIController.addDelayedCall([=]() { spawnAsset(m_lastSpawnedAsset); }, "Loading Asset"); } ImGui::PopStyleColor(); } ImGui::ListBoxFooter(); } ImGui::Spacing(); ImGui::Separator(); /////////////////////////////////////////////////////////////////////////////////////////// // Actors Selection /////////////////////////////////////////////////////////////////////////////////////////// { // actor's list { int itemCount = 0; for (size_t i = 0; i < m_sceneActors.size(); ++i) { itemCount += 1 + m_sceneActors[i]->getSubactorCount(); } ImGui::ListBoxHeader("Scene Actors", itemCount); for (int i = 0; i < (int)m_sceneActors.size(); ++i) { ImVec4 color = m_sceneActors[i]->getUIColor(); ImGui::PushStyleColor(ImGuiCol_Text, color); const bool isSelected = (m_selectedActor.index == i); ImGui::PushID(i); if (ImGui::Selectable(m_sceneActors[i]->getName(), isSelected && m_selectedActor.subindex == -1)) { setSelectedActor(i); } for (int s = 0; s < (int)m_sceneActors[i]->getSubactorCount(); ++s) { ImGui::PushID(s); if (ImGui::Selectable(m_sceneActors[i]->getSubname(s), isSelected && m_selectedActor.subindex == s)) { setSelectedActor(i, s); } ImGui::PopID(); } ImGui::PopID(); ImGui::PopStyleColor(); } ImGui::ListBoxFooter(); } SceneActor* selectedActor = getSelectedActor(); if (selectedActor) { if (ImGui::Button("Remove")) { removeSceneActor(m_selectedActor); } ImGui::SameLine(); if (ImGui::Button("Reload")) { selectedActor->reload(); } ImGui::SameLine(); } if (ImGui::Button("Remove All")) { removeAllSceneActors(); } ImGui::SameLine(); if (ImGui::Button("Reload All (R)")) { reloadAllActors(); } } ImGui::Spacing(); ImGui::Spacing(); /////////////////////////////////////////////////////////////////////////////////////////// // Selected Actor /////////////////////////////////////////////////////////////////////////////////////////// { SceneActor* selectedActor = getSelectedActor(); if (selectedActor) { ImGui::Text("Selected Actor: "); ImGui::SameLine(); ImGui::PushStyleColor(ImGuiCol_Text, ImColor(40, 200, 80, 255)); ImGui::Text(m_selectedActor.subindex >= 0 ? selectedActor->getSubname(m_selectedActor.subindex) : selectedActor->getName()); ImGui::PopStyleColor(); ImGui::Spacing(); selectedActor->drawUI(m_selectedActor.subindex); } else { ImGui::Text("No Selected Actor"); } } } void drawStatsUI() { SceneActor* selectedActor = getSelectedActor(); if (selectedActor) { selectedActor->drawStatsUI(m_selectedActor.subindex); } } void spawnAsset(int32_t num) { m_lastSpawnedAsset = physx::PxClamp(num, -1, (uint32_t)m_assets.size() - 1); if (m_lastSpawnedAsset < 0) { return; } PxVec3 shift(PxZero); for (SceneActor* a : m_sceneActors) { shift += a->getSpawnShift(); } SceneAsset* asset = m_assets[m_lastSpawnedAsset]; asset->spawn(shift); } void addSceneActor(SceneActor* actor) { m_sceneActors.push_back(actor); if (!getSelectedActor()) { setSelectedActor((uint32_t)m_sceneActors.size() - 1); } } void removeSceneActor(ActorIndex actorIndex) { SceneActor* actor = getActorByIndex(actorIndex.index); if (actorIndex.subindex < 0) { delete actor; m_sceneActors.erase(std::remove(m_sceneActors.begin(), m_sceneActors.end(), actor)); } else { actor->removeSubactor(actorIndex.subindex); if (actor->getSubactorCount() == 0) { removeSceneActor(ActorIndex(actorIndex.index, -1)); return; } } SceneActor* selectedActor = getActorByIndex(m_selectedActor.index); if (selectedActor == nullptr) { if (!m_sceneActors.empty()) { setSelectedActor((uint32_t)m_sceneActors.size() - 1); } } else { int subactorCount = selectedActor->getSubactorCount(); if (m_selectedActor.subindex >= subactorCount || (m_selectedActor.subindex < 0 && subactorCount > 0)) { setSelectedActor(m_selectedActor.index, subactorCount - 1); } } } void removeAllSceneActors() { for (SceneActor* a : m_sceneActors) { delete a; } m_sceneActors.clear(); setSelectedActor(-1); } void setSelectedActor(int index, int subindex = -1) { m_selectedActor.index = physx::PxClamp(index, -1, (uint32_t)m_sceneActors.size() - 1); m_selectedActor.subindex = subindex; } SceneActor* getSelectedActor() const { return getActorByIndex(m_selectedActor.index); } SceneActor* getActorByIndex(int index) const { return (index >= 0 && index < (int)m_sceneActors.size()) ? m_sceneActors[index] : nullptr; } int releaseAll() { removeAllSceneActors(); for (size_t i = 0; i < m_assets.size(); ++i) { m_assets[i]->unload(); } const int currentAsset = m_lastSpawnedAsset; m_lastSpawnedAsset = -1; return currentAsset; } void reloadAllActors() { SceneActor* selectedActor = getSelectedActor(); ActorIndex selectIndex(0); for (uint32_t i = 0; i < m_sceneActors.size(); i++) { if (m_sceneActors[i]->removeOnReload) { removeSceneActor(ActorIndex(i)); i--; } } for (uint32_t i = 0; i < m_sceneActors.size(); i++) { if (m_sceneActors[i] == selectedActor) { selectIndex.index = i; } m_sceneActors[i]->reload(); } setSelectedActor(selectIndex.index, selectIndex.subindex); } void registerTkAsset(const TkAsset& tkAsset, SingleSceneAsset* asset) { m_tkAssetMap[&tkAsset] = asset; } void unregisterTkAsset(const TkAsset& tkAsset) { m_tkAssetMap.erase(&tkAsset); } SingleSceneAsset* findSingleSceneAsset(const TkAsset& tkAsset) { auto entry = m_tkAssetMap.find(&tkAsset); return entry != m_tkAssetMap.end() ? entry->second : nullptr; } SceneAsset* findSceneAsset(const std::string& id) { auto entry = m_assetsByID.find(id); return entry != m_assetsByID.end() ? entry->second : nullptr; } //////// used controllers //////// Renderer& getRenderer() const { return m_renderer; } PhysXController& getPhysXController() const { return m_physXController; } BlastController& getBlastController() const { return m_blastController; } CommonUIController& getCommonUIController() const { return m_commonUIController; } private: Renderer& m_renderer; PhysXController& m_physXController; BlastController& m_blastController; CommonUIController& m_commonUIController; std::vector m_assets; std::vector m_sceneActors; std::map m_tkAssetMap; std::map m_assetsByID; int m_lastSpawnedAsset; ActorIndex m_selectedActor; }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Assets /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// class SingleSceneActor; class SingleSceneAsset : public SceneAsset { public: SingleSceneAsset() : m_asset(nullptr) {} virtual ~SingleSceneAsset() { unload(); } virtual void spawn(PxVec3 shift) override; virtual void load() override { if (!m_asset) { m_asset = createAsset(); m_scene->registerTkAsset(m_asset->getPxAsset()->getTkAsset(), this); } } virtual void unload() override { if (m_asset) { m_scene->unregisterTkAsset(m_asset->getPxAsset()->getTkAsset()); delete m_asset; m_asset = nullptr; } } virtual bool isLoaded() const override { return m_asset != nullptr; } BlastAsset* getAsset() const { return m_asset; } virtual PxTransform getInitialTransform() = 0; protected: virtual BlastAsset* createAsset() = 0; private: BlastAsset* m_asset; }; class ModelSceneAsset : public SingleSceneAsset { public: ModelSceneAsset() {} virtual const char* getID() const override{ return desc.id.c_str(); } virtual const char* getName() const override { return desc.name.c_str(); } AssetList::ModelAsset desc; virtual PxTransform getInitialTransform() { return desc.transform; } }; class SimpleModelSceneAsset : public ModelSceneAsset { public: virtual BlastAsset* createAsset() { return new BlastAssetModelSimple(m_scene->getBlastController().getTkFramework(), m_scene->getPhysXController().getPhysics(), m_scene->getPhysXController().getCooking(), *m_scene->getBlastController().getExtSerialization(), m_scene->getRenderer(), desc.file.c_str()); } virtual ImVec4 getUIColor() const override { return ImColor(255, 255, 200, 255); } }; class SkinnedModelSceneAsset : public ModelSceneAsset { public: virtual BlastAsset* createAsset() { return new BlastAssetModelSkinned(m_scene->getBlastController().getTkFramework(), m_scene->getPhysXController().getPhysics(), m_scene->getPhysXController().getCooking(), *m_scene->getBlastController().getExtSerialization(), m_scene->getRenderer(), desc.file.c_str()); } virtual ImVec4 getUIColor() const override { return ImColor(255, 200, 255, 255); } }; class BoxesSceneAsset : public SingleSceneAsset { public: BoxesSceneAsset(const AssetList::BoxAsset& d) : desc(d) { for (uint32_t lv = 0; lv < desc.levels.size(); ++lv) { const AssetList::BoxAsset::Level& level = desc.levels[lv]; NvBlastChunkDesc::Flags fl = (level.isSupport) ? NvBlastChunkDesc::Flags::SupportFlag : NvBlastChunkDesc::Flags::NoFlags; assetDesc.generatorSettings.depths.push_back({ GeneratorAsset::Vec3(level.x, level.y, level.z), fl }); } assetDesc.generatorSettings.extents = GeneratorAsset::Vec3(desc.extents.x, desc.extents.y, desc.extents.z); assetDesc.staticHeight = desc.staticHeight; assetDesc.jointAllBonds = desc.jointAllBonds; assetDesc.generatorSettings.bondFlags = (CubeAssetGenerator::BondFlags)desc.bondFlags; } virtual ImVec4 getUIColor() const override { return ImColor(255, 200, 200, 255); } virtual const char* getID() const override { return desc.id.c_str(); } virtual const char* getName() const override { return desc.name.c_str(); } AssetList::BoxAsset desc; BlastAssetBoxes::Desc assetDesc; virtual BlastAsset* createAsset() { return new BlastAssetBoxes(m_scene->getBlastController().getTkFramework(), m_scene->getPhysXController().getPhysics(), m_scene->getPhysXController().getCooking(), m_scene->getRenderer(), assetDesc); } virtual PxTransform getInitialTransform() { return PxTransform(PxVec3(0, assetDesc.generatorSettings.extents.y / 2, 0)); } }; class CompositeSceneAsset : public SceneAsset { public: CompositeSceneAsset(const AssetList::CompositeAsset& desc) : m_desc(desc) { } virtual ~CompositeSceneAsset() { unload(); } virtual ImVec4 getUIColor() const override { return ImColor(200, 255, 255, 255); } virtual const char* getID() const override { return m_desc.id.c_str(); } virtual const char* getName() const override { return m_desc.name.c_str(); } virtual void spawn(PxVec3 shift) override; virtual void load() override { if (!isLoaded()) { // load dependent assets for (const auto& assetRef : m_desc.assetRefs) { SceneAsset* asset = m_scene->findSceneAsset(assetRef.id); if (asset) { asset->load(); m_sceneAssets.push_back(static_cast(asset)); } else { m_scene->getCommonUIController().addPopupMessage("Error", "Wrong asset dependency on composite"); m_sceneAssets.clear(); return; } } // check joints for (const auto& joint : m_desc.joints) { bool ok = (joint.assetIndices[0] >= 0 || joint.assetIndices[1] >= 0); for (char k = 0; k < 2 && ok; ++k) { if (joint.assetIndices[k] < 0) continue; ok &= (joint.assetIndices[k] < (int32_t)m_sceneAssets.size()); if (!ok) break; ok &= joint.chunkIndices[k] < m_sceneAssets[joint.assetIndices[k]]->getAsset()->getPxAsset()->getTkAsset().getChunkCount(); } if (!ok) { m_scene->getCommonUIController().addPopupMessage("Error", "Wrong joint on composite"); m_sceneAssets.clear(); return; } } } } virtual void unload() override { m_sceneAssets.clear(); } virtual bool isLoaded() const override { return !m_sceneAssets.empty(); } virtual PxTransform getInitialTransform() { return m_desc.transform; } const AssetList::CompositeAsset& getDesc() const { return m_desc; } const std::vector& getSceneAssets() const { return m_sceneAssets; } private: AssetList::CompositeAsset m_desc; std::vector m_sceneAssets; }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Scene Actors /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// class SingleSceneActor : public SceneActor { public: SingleSceneActor(Scene& scene, SingleSceneAsset* asset, PxVec3 shift) : m_scene(scene) , m_asset(asset) , m_shift(shift) { m_index = m_asset->spawnCount++; spawn(); } SingleSceneActor::~SingleSceneActor() { remove(); } virtual const char* getName() const override { return m_name.c_str(); } virtual const char* getSubname(int) const override { return nullptr; } virtual ImVec4 getUIColor() const override { return m_asset->getUIColor(); } virtual void drawUI(int) override { m_actor->drawUI(); } virtual void drawStatsUI(int) override { m_actor->drawStatsUI(); } virtual PxVec3 getSpawnShift() const override { return PxVec3(-20, 0, 0); } virtual void reload() override { auto settings = m_actor->getSettings(); remove(); spawn(); m_actor->setSettings(settings); } private: void remove() { m_scene.getBlastController().removeFamily(m_actor); m_actor = nullptr; } void spawn() { std::ostringstream str; str << m_asset->getName(); if (m_index) str << " (" << m_index << ")"; m_name = str.str(); PxTransform pose = m_asset->getInitialTransform(); pose.p += m_shift; BlastAsset::ActorDesc actorDesc = { actorDesc.id = generateIDFromString(m_name.c_str()), pose, m_scene.getBlastController().getTkGroup() }; m_actor = m_scene.getBlastController().spawnFamily(m_asset->getAsset(), actorDesc); } Scene& m_scene; BlastFamilyPtr m_actor; SingleSceneAsset* m_asset; PxVec3 m_shift; uint32_t m_index; std::string m_name; }; class CompositeSceneActor : public SceneActor { public: CompositeSceneActor(Scene& scene, CompositeSceneAsset* asset, PxVec3 shift) : m_scene(scene) , m_asset(asset) , m_shift(shift) { m_index = m_asset->spawnCount++; spawn(); } CompositeSceneActor::~CompositeSceneActor() { remove(); } virtual uint32_t getSubactorCount() const { return (uint32_t)m_actors.size(); } virtual const char* getName() const override { return m_name.c_str(); } virtual const char* getSubname(int subindex) const override { return m_actors[subindex].name.c_str(); } virtual ImVec4 getUIColor() const override { return m_asset->getUIColor(); } virtual void drawUI(int subindex) override { if (subindex >= 0) { m_actors[subindex].actor->drawUI(); } else { ImGui::Text("Select subactor to edit settings."); } } virtual void drawStatsUI(int subindex) override { if (subindex >= 0) { m_actors[subindex].actor->drawStatsUI(); } } virtual PxVec3 getSpawnShift() const override { return PxVec3(-20, 0, 0); } virtual void reload() override { std::map settings; for (uint32_t i = 0; i < m_actors.size(); ++i) { settings[m_actors[i].initialIndex] = m_actors[i].actor->getSettings(); } remove(); spawn(); for (uint32_t i = 0; i < m_actors.size(); ++i) { if (settings.find(i) != settings.end()) { m_actors[i].actor->setSettings(settings[i]); } } } virtual void removeSubactor(int subindex) { if (subindex >= 0 && subindex < (int)m_actors.size()) { m_scene.getBlastController().removeFamily(m_actors[subindex].actor); m_actors[subindex] = m_actors.back(); m_actors.resize(m_actors.size() - 1); } } private: void remove() { for (uint32_t i = 0; i < m_actors.size(); ++i) { m_scene.getBlastController().removeFamily(m_actors[i].actor); } m_actors.clear(); } void spawn() { std::ostringstream str; str << m_asset->getName(); if (m_index) str << " (" << m_index << ")"; m_name = str.str(); const AssetList::CompositeAsset& assetDesc = m_asset->getDesc(); const std::vector& sceneAssets = m_asset->getSceneAssets(); const uint32_t actorCount = (uint32_t)sceneAssets.size(); m_actors.resize(actorCount); for (uint32_t i = 0; i < actorCount; ++i) { std::ostringstream str; str << " -> " << i << "." << sceneAssets[i]->getName(); m_actors[i].name = str.str(); } ExtPxManager& pxManager = m_scene.getBlastController().getExtPxManager(); for (uint32_t i = 0; i < actorCount; ++i) { PxTransform pose = m_asset->getInitialTransform(); pose.p += m_shift; pose = assetDesc.assetRefs[i].transform.transform(pose); BlastAsset::ActorDesc actorDesc = { generateIDFromString(m_actors[i].name.c_str()), pose, m_scene.getBlastController().getTkGroup() }; m_actors[i].actor = m_scene.getBlastController().spawnFamily(sceneAssets[i]->getAsset(), actorDesc); m_actors[i].initialIndex = i; } for (const auto& joint : assetDesc.joints) { TkJointDesc jointDesc; for (char k = 0; k < 2; ++k) { jointDesc.attachPositions[k] = joint.attachPositions[k]; jointDesc.chunkIndices[k] = joint.chunkIndices[k]; jointDesc.families[k] = (joint.assetIndices[k] < 0) ? nullptr : &m_actors[joint.assetIndices[k]].actor->getFamily()->getTkFamily(); } TkJoint* joint = pxManager.getFramework().createJoint(jointDesc); if (joint) { pxManager.createJoint(*joint); } else { m_scene.getCommonUIController().addPopupMessage("Error", "Some joints can't be created"); } } } struct Subactor { BlastFamilyPtr actor; uint32_t initialIndex; std::string name; }; Scene& m_scene; std::vector m_actors; CompositeSceneAsset* m_asset; PxVec3 m_shift; uint32_t m_index; std::string m_name; }; class PhysXSceneActor : public SceneActor { public: PhysXSceneActor(PhysXController& physXController, PhysXController::Actor* actor, const char* name) : m_physXController(physXController) , m_actor(actor) , m_name(name) { } PhysXSceneActor::~PhysXSceneActor() { m_physXController.removePhysXPrimitive(m_actor); } virtual const char* getName() const override { return m_name; } virtual ImVec4 getUIColor() const override { return ImColor(255, 100, 100, 255); } private: PhysXController& m_physXController; PhysXController::Actor* m_actor; const char* m_name; }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Assets Implementation /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SingleSceneAsset::spawn(PxVec3 shift) { load(); SingleSceneActor* actor = new SingleSceneActor(*m_scene, this, shift); m_scene->addSceneActor(actor); } void CompositeSceneAsset::spawn(PxVec3 shift) { load(); if (isLoaded()) { CompositeSceneActor* actor = new CompositeSceneActor(*m_scene, this, shift); m_scene->addSceneActor(actor); } } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // PackmanConfigParser /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// class PackmanConfigParser : public physx::shdfnd::FastXml::Callback { public: std::vector> dependencies; protected: // encountered a comment in the XML virtual bool processComment(const char* /*comment*/) { return true; } virtual bool processClose(const char* elementName, unsigned int /*depth*/, bool& /*isError*/) { return true; } // return true to continue processing the XML document, false to skip. virtual bool processElement(const char* elementName, // name of the element const char* elementData, // element data, null if none const physx::shdfnd::FastXml::AttributePairs& attr, int /*lineno*/) // line number in the source XML file { if (::strcmp(elementName, "dependency") == 0) { dependencies.resize(dependencies.size() + 1); for (int i = 0; i < attr.getNbAttr(); ++i) { if (::strcmp(attr.getKey(i), "name") == 0) { dependencies.back().first = std::string(attr.getValue(i)); } else if (::strcmp(attr.getKey(i), "version") == 0) { dependencies.back().second = std::string(attr.getValue(i)); } } } return true; } }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Controller /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// SceneController::SceneController() : m_cubeScale(1.0f), m_cubeThrowDownTime(-1.f) { m_scene = NULL; } SceneController::~SceneController() { } void SceneController::onSampleStart() { // setup camera CFirstPersonCamera* camera = &getRenderer().getCamera(); DirectX::XMVECTORF32 lookAtPt = { 0, 10, 0, 0 }; DirectX::XMVECTORF32 eyePt = { 0, 20, 60, 0 }; camera->SetViewParams(eyePt, lookAtPt); camera->SetRotateButtons(false, false, true, false); camera->SetEnablePositionMovement(true); // setup scene m_scene = new Scene(getRenderer(), getPhysXController(), getBlastController(), getCommonUIController()); const SampleConfig& config = getManager()->getConfig(); // add packman repo to search dirs bool packmanResourcesAdded = false; if (const char* packmanPath = std::getenv("PM_PACKAGES_ROOT")) { const char* RESOURCES_CONFIG_FILE = "resources.xml"; std::string path; if (getRenderer().getResourceManager().findFile(RESOURCES_CONFIG_FILE, path)) { if (getRenderer().getResourceManager().addSearchDir("..\\..\\samples\\resources\\BlastSampleResources\\") || getRenderer().getResourceManager().addSearchDir("..\\..\\..\\samples\\resources\\BlastSampleResources\\")) { packmanResourcesAdded = true; } } } if (!packmanResourcesAdded) { getManager()->getCommonUIController().addPopupMessage("Error", "BlastSampleResources package wasn't found. Consider running download_sample_resources.bat in root folder.", 5.0f); } // parse asset file AssetList assetList; if (!config.assetsFile.empty()) { std::string path; if (getRenderer().getResourceManager().findFile(config.assetsFile, path)) { parseAssetList(assetList, path); } } // add both asset file and asset list from config addAssets(config.additionalAssetList, true); // only used for command line assets addAssets(assetList, packmanResourcesAdded); // prepare scene spawnAsset(0); } void SceneController::addAssets(const AssetList& assetList, bool loadModels) { if (loadModels) { for (const auto& model : assetList.models) { ModelSceneAsset* asset; if (!model.isSkinned) { asset = new SimpleModelSceneAsset(); } else { asset = new SkinnedModelSceneAsset(); } asset->desc = model; m_scene->addAsset(asset); } for (const auto& composite : assetList.composites) { m_scene->addAsset(new CompositeSceneAsset(composite)); } } for (const auto& box : assetList.boxes) { BoxesSceneAsset* asset = new BoxesSceneAsset(box); m_scene->addAsset(asset); } } void SceneController::onInitialize() { } void SceneController::onSampleStop() { if (NULL != m_scene) { delete m_scene; m_scene = nullptr; } } void SceneController::onTerminate() { } void SceneController::Animate(double dt) { } LRESULT SceneController::MsgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { if (uMsg == WM_KEYDOWN) { int iKeyPressed = static_cast(wParam); switch (iKeyPressed) { case 'R': m_scene->reloadAllActors(); return 0; case 'F': if (m_cubeThrowDownTime == -1.f) { m_cubeThrowDownTime = ImGui::GetTime(); } return 0; default: break; } } else if (uMsg == WM_KEYUP) { int iKeyPressed = static_cast(wParam); switch (iKeyPressed) { case 'F': throwCube(); m_cubeThrowDownTime = -1.f; return 0; default: break; } } return 1; } void SceneController::drawUI() { /////////////////////////////////////////////////////////////////////////////////////////// // Scene UI /////////////////////////////////////////////////////////////////////////////////////////// m_scene->drawUI(); ImGui::Spacing(); ImGui::Spacing(); ImGui::Separator(); ImGui::Spacing(); ImGui::Spacing(); /////////////////////////////////////////////////////////////////////////////////////////// // Replay /////////////////////////////////////////////////////////////////////////////////////////// { ImGui::Text("Replay Control:"); BlastReplay* replay = getBlastController().getReplay(); if (replay->isRecording()) { auto getAnimStr = []() { const uint32_t count = 5; const uint64_t periodMS = 150; static char str[count + 1] = ""; for (uint32_t i = 0; i < count; i++) { uint64_t ts = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); str[i] = (i == (ts % (periodMS * count)) / periodMS) ? '*' : ' '; } return str; }; ImGui::Text("State: Recording [%s] | Events: %d", getAnimStr(), replay->getEventCount()); if (ImGui::Button("Stop Recording")) { replay->stopRecording(); } } else if (replay->isPlaying()) { ImGui::Text("State: Playing | Events: %d / %d", replay->getCurrentEventIndex(), replay->getEventCount()); if (ImGui::Button("Stop Playing")) { replay->stopPlayback(); } } else { ImGui::Text("State: Idle | Events: %d", replay->getEventCount()); static bool syncFamilies = true; static bool syncPhysics = true; ImGui::Checkbox("Sync Initial Actors", &syncFamilies); if (ImGui::Checkbox("Sync Initial Transforms", &syncPhysics)) { syncFamilies = syncPhysics; } if (ImGui::Button("Start Recording")) { replay->startRecording(getBlastController().getExtPxManager(), syncFamilies, syncPhysics); } if (replay->hasRecord()) { static bool reload = false; if (ImGui::Button("Start Playback")) { if (reload) m_scene->reloadAllActors(); replay->startPlayback(getBlastController().getExtPxManager(), getBlastController().getTkGroup()); } ImGui::SameLine(); ImGui::Checkbox("Reload Scene On Playback", &reload); } } } ImGui::Spacing(); ImGui::Spacing(); ImGui::Separator(); /////////////////////////////////////////////////////////////////////////////////////////// // Cube /////////////////////////////////////////////////////////////////////////////////////////// { ImGui::Text("Thrown Cube Params (F)"); ImGui::DragFloat("Cube Size", &m_cubeScale, 1.0f, 0.0f, 100.0f); ImGui::Text("Cube Speed (hold F): %1.f", getCubeSpeed()); } } void SceneController::drawStatsUI() { m_scene->drawStatsUI(); } float SceneController::getCubeSpeed() { const float CUBE_VELOCITY_SPEED_MIN = 70; const float CUBE_VELOCITY_CHARGE_PER_SECOND = 300; return m_cubeThrowDownTime > 0 ? CUBE_VELOCITY_SPEED_MIN + (ImGui::GetTime() - m_cubeThrowDownTime) * CUBE_VELOCITY_CHARGE_PER_SECOND : 0.f; } void SceneController::throwCube() { const float CUBE_DENSITY = 20000.0f; CFirstPersonCamera* camera = &getRenderer().getCamera(); PxVec3 eyePos = XMVECTORToPxVec4(camera->GetEyePt()).getXYZ(); PxVec3 lookAtPos = XMVECTORToPxVec4(camera->GetLookAtPt()).getXYZ(); PhysXController::Actor* cube = getPhysXController().spawnPhysXPrimitiveBox(PxTransform(eyePos), PxVec3(m_cubeScale, m_cubeScale, m_cubeScale), CUBE_DENSITY); PxRigidDynamic* rigidDynamic = cube->getActor()->is(); cube->setColor(DirectX::XMFLOAT4(1, 0, 0, 1)); PxVec3 dir = (lookAtPos - eyePos).getNormalized(); rigidDynamic->setLinearVelocity(dir * getCubeSpeed()); PhysXSceneActor* actor = new PhysXSceneActor(getPhysXController(), cube, "Cube"); actor->removeOnReload = true; m_scene->addSceneActor(actor); } void SceneController::spawnAsset(int32_t num) { m_scene->spawnAsset(num); } int SceneController::releaseAll() { return m_scene->releaseAll(); }