Fix some import bugs / add support for separated model + anim file
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,138 @@
|
|||||||
|
#ifndef MODELDOCUTILS_H
|
||||||
|
#define MODELDOCUTILS_H
|
||||||
|
|
||||||
|
#include "destrum/Util/ModelDoc.h"
|
||||||
|
|
||||||
|
#include "spdlog/spdlog.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <initializer_list>
|
||||||
|
#include <optional>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace ModelDocUtils {
|
||||||
|
|
||||||
|
inline const ModelDoc::MeshPrimitive& GetFirstPrimitiveOrThrow(
|
||||||
|
const ModelDoc::ModelAsset& model,
|
||||||
|
const std::string& debugName
|
||||||
|
) {
|
||||||
|
if (model.primitives.empty()) {
|
||||||
|
throw std::runtime_error("ModelDoc loaded no mesh primitives for: " + debugName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return model.primitives.front();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const ModelDoc::MeshPrimitive& GetFirstSkinnedPrimitiveOrFirstOrThrow(
|
||||||
|
const ModelDoc::ModelAsset& model,
|
||||||
|
const std::string& debugName
|
||||||
|
) {
|
||||||
|
if (model.primitives.empty()) {
|
||||||
|
throw std::runtime_error("ModelDoc loaded no mesh primitives for: " + debugName);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto it = std::find_if(
|
||||||
|
model.primitives.begin(),
|
||||||
|
model.primitives.end(),
|
||||||
|
[](const ModelDoc::MeshPrimitive& primitive) {
|
||||||
|
return primitive.skinned;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return it != model.primitives.end() ? *it : model.primitives.front();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline glm::vec3 GetImportedBaseColor(
|
||||||
|
const ModelDoc::ModelAsset& model,
|
||||||
|
const ModelDoc::MeshPrimitive& primitive,
|
||||||
|
const glm::vec3& fallback = glm::vec3(1.0f)
|
||||||
|
) {
|
||||||
|
if (primitive.materialIndex == ModelDoc::InvalidIndex ||
|
||||||
|
primitive.materialIndex >= model.materials.size()) {
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
return glm::vec3(model.materials[primitive.materialIndex].baseColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string GetImportedMaterialName(
|
||||||
|
const ModelDoc::ModelAsset& model,
|
||||||
|
const ModelDoc::MeshPrimitive& primitive,
|
||||||
|
const std::string& fallback
|
||||||
|
) {
|
||||||
|
if (primitive.materialIndex == ModelDoc::InvalidIndex ||
|
||||||
|
primitive.materialIndex >= model.materials.size()) {
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& material = model.materials[primitive.materialIndex];
|
||||||
|
return material.name.empty() ? fallback : material.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::optional<std::string> FindFirstTexturePath(
|
||||||
|
const ModelDoc::ModelAsset& model,
|
||||||
|
const ModelDoc::MeshPrimitive& primitive,
|
||||||
|
std::initializer_list<ModelDoc::TextureSemantic> preferredSemantics
|
||||||
|
) {
|
||||||
|
if (primitive.materialIndex == ModelDoc::InvalidIndex ||
|
||||||
|
primitive.materialIndex >= model.materials.size()) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& material = model.materials[primitive.materialIndex];
|
||||||
|
|
||||||
|
for (const auto semantic : preferredSemantics) {
|
||||||
|
for (const auto& texture : material.textures) {
|
||||||
|
// gfxDevice.loadImageFromFile currently expects a real file path.
|
||||||
|
// Embedded GLB textures like "*0" need extraction/support in GfxDevice first.
|
||||||
|
if (texture.semantic == semantic && !texture.embedded && !texture.path.empty()) {
|
||||||
|
return texture.path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::filesystem::path PickTexturePath(
|
||||||
|
const ModelDoc::ModelAsset& model,
|
||||||
|
const ModelDoc::MeshPrimitive& primitive,
|
||||||
|
const std::filesystem::path& fallbackPath
|
||||||
|
) {
|
||||||
|
const auto importedTexturePath = FindFirstTexturePath(
|
||||||
|
model,
|
||||||
|
primitive,
|
||||||
|
{
|
||||||
|
ModelDoc::TextureSemantic::BaseColor,
|
||||||
|
ModelDoc::TextureSemantic::Diffuse
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (importedTexturePath.has_value()) {
|
||||||
|
return std::filesystem::path(*importedTexturePath);
|
||||||
|
}
|
||||||
|
spdlog::info("Using fallback path {}", fallbackPath.string());
|
||||||
|
return fallbackPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void LogModelDocSummary(
|
||||||
|
const ModelDoc::ModelAsset& model,
|
||||||
|
const std::string& label
|
||||||
|
) {
|
||||||
|
spdlog::info(
|
||||||
|
"Loaded {} | primitives: {} | materials: {} | animations: {} | skinned: {}",
|
||||||
|
label,
|
||||||
|
model.primitives.size(),
|
||||||
|
model.materials.size(),
|
||||||
|
model.animations.size(),
|
||||||
|
model.IsSkinned()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace ModelDocUtils
|
||||||
|
|
||||||
|
#endif // MODELDOCUTILS_H
|
||||||
@@ -768,6 +768,35 @@ static SkinnedModel LoadSkinnedModel(const std::string& path) {
|
|||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct LoadedMesh {
|
||||||
|
CPUMesh mesh;
|
||||||
|
std::vector<std::string> diffuseTexturePaths;
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::vector<std::string> LoadMaterialTexturePaths(
|
||||||
|
const aiScene* scene,
|
||||||
|
const aiMesh* mesh,
|
||||||
|
aiTextureType type,
|
||||||
|
const std::filesystem::path& modelDir
|
||||||
|
) {
|
||||||
|
std::vector<std::string> paths;
|
||||||
|
|
||||||
|
if (mesh->mMaterialIndex >= scene->mNumMaterials)
|
||||||
|
return paths;
|
||||||
|
|
||||||
|
aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < material->GetTextureCount(type); ++i) {
|
||||||
|
aiString texPath;
|
||||||
|
if (material->GetTexture(type, i, &texPath) == AI_SUCCESS) {
|
||||||
|
auto fullPath = modelDir / texPath.C_Str();
|
||||||
|
paths.push_back(fullPath.generic_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return paths;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ModelLoader
|
} // namespace ModelLoader
|
||||||
|
|
||||||
#endif // MODELLOADER_H
|
#endif // MODELLOADER_H
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <destrum/Scene/SceneManager.h>
|
#include <destrum/Scene/SceneManager.h>
|
||||||
|
|
||||||
#include "destrum/Graphics/Resources/Cubemap.h"
|
#include "destrum/Graphics/Resources/Cubemap.h"
|
||||||
|
#include "destrum/ObjectModel/GameObject.h"
|
||||||
|
|
||||||
class LightKeeper final : public App {
|
class LightKeeper final : public App {
|
||||||
public:
|
public:
|
||||||
@@ -28,6 +29,8 @@ private:
|
|||||||
MaterialID testMaterialID;
|
MaterialID testMaterialID;
|
||||||
|
|
||||||
std::unique_ptr<CubeMap> skyboxCubemap;
|
std::unique_ptr<CubeMap> skyboxCubemap;
|
||||||
|
|
||||||
|
GameObject* capybara = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //LIGHTKEEPER_H
|
#endif //LIGHTKEEPER_H
|
||||||
|
|||||||
+215
-34
@@ -10,10 +10,15 @@
|
|||||||
#include "destrum/Components/Spinner.h"
|
#include "destrum/Components/Spinner.h"
|
||||||
#include "destrum/Components/OrbitAndSpin.h"
|
#include "destrum/Components/OrbitAndSpin.h"
|
||||||
#include "destrum/ObjectModel/GameObject.h"
|
#include "destrum/ObjectModel/GameObject.h"
|
||||||
#include "destrum/Util/ModelLoader.h"
|
#include "destrum/Util/ModelDoc.h"
|
||||||
#include "destrum/Components/Animator.h"
|
#include "destrum/Components/Animator.h"
|
||||||
|
|
||||||
LightKeeper::LightKeeper(): App(), renderer(meshCache, materialCache) {
|
#include <filesystem>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "destrum/Util/ModelDocUtils.h"
|
||||||
|
|
||||||
|
LightKeeper::LightKeeper() : App(), renderer(meshCache, materialCache) {
|
||||||
}
|
}
|
||||||
|
|
||||||
LightKeeper::~LightKeeper() {
|
LightKeeper::~LightKeeper() {
|
||||||
@@ -26,23 +31,46 @@ void LightKeeper::customInit() {
|
|||||||
const float aspectRatio = static_cast<float>(m_params.renderSize.x) / static_cast<float>(m_params.renderSize.y);
|
const float aspectRatio = static_cast<float>(m_params.renderSize.x) / static_cast<float>(m_params.renderSize.y);
|
||||||
camera.setAspectRatio(aspectRatio);
|
camera.setAspectRatio(aspectRatio);
|
||||||
|
|
||||||
|
ModelDoc::LoadOptions staticModelOptions{};
|
||||||
|
staticModelOptions.meshImportMode = ModelDoc::MeshImportMode::MergedPerNode;
|
||||||
|
staticModelOptions.loadMaterials = true;
|
||||||
|
staticModelOptions.loadSkeleton = false;
|
||||||
|
staticModelOptions.loadAnimations = false;
|
||||||
|
|
||||||
|
auto kittyModel = ModelDoc::LoadModel(
|
||||||
|
AssetFS::GetInstance().GetFullPath("game://kitty.glb").generic_string(),
|
||||||
|
staticModelOptions
|
||||||
|
);
|
||||||
|
ModelDocUtils::LogModelDocSummary(kittyModel, "kitty.glb");
|
||||||
|
|
||||||
|
const auto &kittyPrimitive = ModelDocUtils::GetFirstPrimitiveOrThrow(kittyModel, "game://kitty.glb");
|
||||||
|
testMesh = kittyPrimitive.mesh;
|
||||||
testMesh.name = "Test Mesh";
|
testMesh.name = "Test Mesh";
|
||||||
auto list_of_models = ModelLoader::LoadGLTF_CPUMeshes_MergedPerMesh(AssetFS::GetInstance().GetFullPath("game://kitty.glb").generic_string());
|
|
||||||
testMesh = list_of_models[0];
|
|
||||||
testMeshID = meshCache.addMesh(gfxDevice, testMesh);
|
testMeshID = meshCache.addMesh(gfxDevice, testMesh);
|
||||||
spdlog::info("TestMesh uploaded with id: {}", testMeshID);
|
spdlog::info("TestMesh uploaded with id: {}", testMeshID);
|
||||||
|
|
||||||
auto testimgID = gfxDevice.loadImageFromFile(AssetFS::GetInstance().GetFullPath("game://kitty.png"));
|
const auto testTexturePath = ModelDocUtils::PickTexturePath(
|
||||||
spdlog::info("Test image loaded with id: {}", testimgID);
|
kittyModel,
|
||||||
|
kittyPrimitive,
|
||||||
|
AssetFS::GetInstance().GetFullPath("game://kitty.png")
|
||||||
|
);
|
||||||
|
|
||||||
|
const auto testimgID = gfxDevice.loadImageFromFile(testTexturePath);
|
||||||
|
spdlog::info("Test image loaded from '{}' with id: {}", testTexturePath.generic_string(), testimgID);
|
||||||
|
|
||||||
testMaterialID = materialCache.addMaterial(gfxDevice, {
|
testMaterialID = materialCache.addMaterial(gfxDevice, {
|
||||||
.baseColor = glm::vec3(1.f),
|
.baseColor = ModelDocUtils::GetImportedBaseColor(
|
||||||
|
kittyModel, kittyPrimitive),
|
||||||
.diffuseTexture = testimgID,
|
.diffuseTexture = testimgID,
|
||||||
|
.name = ModelDocUtils::GetImportedMaterialName(
|
||||||
|
kittyModel, kittyPrimitive, "TestMaterial"),
|
||||||
});
|
});
|
||||||
spdlog::info("Test material created with id: {}", testMaterialID);
|
spdlog::info("Test material created with id: {}", testMaterialID);
|
||||||
|
|
||||||
camera.SetRotation(glm::radians(glm::vec2(90.f, 0.f)));
|
camera.SetRotation(glm::radians(glm::vec2(90.f, 0.f)));
|
||||||
|
|
||||||
auto& scene = SceneManager::GetInstance().CreateScene("Main");
|
auto &scene = SceneManager::GetInstance().CreateScene("Main");
|
||||||
|
|
||||||
// auto testCube = std::make_shared<GameObject>("TestCube");
|
// auto testCube = std::make_shared<GameObject>("TestCube");
|
||||||
// auto meshComp = testCube->AddComponent<MeshRendererComponent>();
|
// auto meshComp = testCube->AddComponent<MeshRendererComponent>();
|
||||||
@@ -77,8 +105,6 @@ void LightKeeper::customInit() {
|
|||||||
globeRoot->AddComponent<Spinner>(glm::vec3(0, 1, 0), 1.0f); // spin around Y, rad/sec
|
globeRoot->AddComponent<Spinner>(glm::vec3(0, 1, 0), 1.0f); // spin around Y, rad/sec
|
||||||
scene.Add(globeRoot);
|
scene.Add(globeRoot);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// scene.Add(testCube);
|
// scene.Add(testCube);
|
||||||
|
|
||||||
// const auto skyboxID = AssetFS::GetInstance().GetFullPath("engine://textures/skybox.jpg");
|
// const auto skyboxID = AssetFS::GetInstance().GetFullPath("engine://textures/skybox.jpg");
|
||||||
@@ -97,55 +123,89 @@ void LightKeeper::customInit() {
|
|||||||
|
|
||||||
renderer.setSkyboxTexture(skyboxCubemap->GetCubeMapImageID());
|
renderer.setSkyboxTexture(skyboxCubemap->GetCubeMapImageID());
|
||||||
|
|
||||||
//
|
|
||||||
const auto planeObj = std::make_shared<GameObject>("GroundPlane");
|
const auto planeObj = std::make_shared<GameObject>("GroundPlane");
|
||||||
const auto planeMeshComp = planeObj->AddComponent<MeshRendererComponent>();
|
const auto planeMeshComp = planeObj->AddComponent<MeshRendererComponent>();
|
||||||
const auto planeModel = ModelLoader::LoadGLTF_CPUMeshes_MergedPerMesh(AssetFS::GetInstance().GetFullPath("game://plane.glb").generic_string());
|
|
||||||
const auto planeMeshID = meshCache.addMesh(gfxDevice, planeModel[0]);
|
|
||||||
|
|
||||||
const auto planeTextureID = gfxDevice.loadImageFromFile(AssetFS::GetInstance().GetFullPath("game://grass.png"));
|
auto planeModel = ModelDoc::LoadModel(
|
||||||
|
AssetFS::GetInstance().GetFullPath("game://plane.glb").generic_string(),
|
||||||
|
staticModelOptions
|
||||||
|
);
|
||||||
|
ModelDocUtils::LogModelDocSummary(planeModel, "plane.glb");
|
||||||
|
|
||||||
|
const auto &planePrimitive = ModelDocUtils::GetFirstPrimitiveOrThrow(planeModel, "game://plane.glb");
|
||||||
|
const auto planeMeshID = meshCache.addMesh(gfxDevice, planePrimitive.mesh);
|
||||||
|
|
||||||
|
const auto planeTexturePath = ModelDocUtils::PickTexturePath(
|
||||||
|
planeModel,
|
||||||
|
planePrimitive,
|
||||||
|
AssetFS::GetInstance().GetFullPath("game://grass.png")
|
||||||
|
);
|
||||||
|
|
||||||
|
const auto planeTextureID = gfxDevice.loadImageFromFile(planeTexturePath);
|
||||||
const auto planeMaterialID = materialCache.addMaterial(gfxDevice, {
|
const auto planeMaterialID = materialCache.addMaterial(gfxDevice, {
|
||||||
.baseColor = glm::vec3(1.f),
|
.baseColor = ModelDocUtils::GetImportedBaseColor(
|
||||||
.textureFilteringMode = TextureFilteringMode::Nearest,
|
planeModel, planePrimitive),
|
||||||
.diffuseTexture = planeTextureID,
|
.textureFilteringMode = TextureFilteringMode::Nearest,
|
||||||
.name = "GroundPlaneMaterial",
|
.diffuseTexture = planeTextureID,
|
||||||
});
|
.name = ModelDocUtils::GetImportedMaterialName(
|
||||||
|
planeModel, planePrimitive, "GroundPlaneMaterial"),
|
||||||
|
});
|
||||||
|
|
||||||
planeMeshComp->SetMeshID(planeMeshID);
|
planeMeshComp->SetMeshID(planeMeshID);
|
||||||
planeMeshComp->SetMaterialID(planeMaterialID);
|
planeMeshComp->SetMaterialID(planeMaterialID);
|
||||||
planeObj->GetTransform().SetWorldPosition(glm::vec3(0.f, -1.0f, 0.f));
|
planeObj->GetTransform().SetWorldPosition(glm::vec3(0.f, -1.0f, 0.f));
|
||||||
planeObj->GetTransform().SetWorldScale(glm::vec3(10.f, 1.f, 10.f));
|
planeObj->GetTransform().SetWorldScale(glm::vec3(10.f, 1.f, 10.f));
|
||||||
scene.Add(planeObj);
|
scene.Add(planeObj);
|
||||||
|
|
||||||
|
|
||||||
// At the bottom of customInit(), replace the incomplete CharObj block:
|
|
||||||
|
|
||||||
const auto CharObj = std::make_shared<GameObject>("Character");
|
const auto CharObj = std::make_shared<GameObject>("Character");
|
||||||
|
capybara = CharObj.get();
|
||||||
|
|
||||||
auto charModel = ModelLoader::LoadSkinnedModel(
|
ModelDoc::LoadOptions characterModelOptions{};
|
||||||
AssetFS::GetInstance().GetFullPath("engine://cotw-capybara-male/source/capybara.fbx").generic_string()
|
characterModelOptions.meshImportMode = ModelDoc::MeshImportMode::MergedPerNode;
|
||||||
|
characterModelOptions.loadMaterials = true;
|
||||||
|
characterModelOptions.loadSkeleton = true;
|
||||||
|
characterModelOptions.loadAnimations = true;
|
||||||
|
|
||||||
|
auto charModel = ModelDoc::LoadModel(
|
||||||
|
AssetFS::GetInstance().GetFullPath("engine://cotw-capybara-male/source/capybara.fbx").generic_string(),
|
||||||
|
characterModelOptions
|
||||||
|
);
|
||||||
|
ModelDocUtils::LogModelDocSummary(charModel, "capybara.fbx");
|
||||||
|
|
||||||
|
const auto &charPrimitive = ModelDocUtils::GetFirstSkinnedPrimitiveOrFirstOrThrow(
|
||||||
|
charModel,
|
||||||
|
"engine://cotw-capybara-male/source/capybara.fbx"
|
||||||
);
|
);
|
||||||
|
|
||||||
const auto charMeshID = meshCache.addMesh(gfxDevice, charModel.meshes[0]);
|
const auto charMeshID = meshCache.addMesh(gfxDevice, charPrimitive.mesh);
|
||||||
|
|
||||||
const auto charTextureID = gfxDevice.loadImageFromFile(AssetFS::GetInstance().GetFullPath("engine://cotw-capybara-male/textures/capybara_male_light_brown_dif.ddsc.DECA.RE.png"));
|
const auto charTexturePath = ModelDocUtils::PickTexturePath(
|
||||||
|
charModel,
|
||||||
|
charPrimitive,
|
||||||
|
AssetFS::GetInstance().GetFullPath(
|
||||||
|
"engine://cotw-capybara-male/textures/capybara_male_light_brown_dif.ddsc.DECA.RE.pngballs")
|
||||||
|
);
|
||||||
|
|
||||||
|
const auto charTextureID = gfxDevice.loadImageFromFile(charTexturePath);
|
||||||
// const auto charTextureID = gfxDevice.loadImageFromFile(AssetFS::GetInstance().GetFullPath("engine://char.jpg"));
|
// const auto charTextureID = gfxDevice.loadImageFromFile(AssetFS::GetInstance().GetFullPath("engine://char.jpg"));
|
||||||
const auto charMaterialID = materialCache.addMaterial(gfxDevice, {
|
const auto charMaterialID = materialCache.addMaterial(gfxDevice, {
|
||||||
.baseColor = glm::vec3(1.f),
|
.baseColor = ModelDocUtils::GetImportedBaseColor(
|
||||||
.diffuseTexture = charTextureID,
|
charModel, charPrimitive),
|
||||||
.name = "CharacterMaterial",
|
.diffuseTexture = charTextureID,
|
||||||
});
|
.name = ModelDocUtils::GetImportedMaterialName(
|
||||||
|
charModel, charPrimitive, "CharacterMaterial"),
|
||||||
|
});
|
||||||
|
|
||||||
const auto charMeshComp = CharObj->AddComponent<MeshRendererComponent>();
|
const auto charMeshComp = CharObj->AddComponent<MeshRendererComponent>();
|
||||||
charMeshComp->SetMeshID(charMeshID);
|
charMeshComp->SetMeshID(charMeshID);
|
||||||
charMeshComp->SetMaterialID(charMaterialID);
|
charMeshComp->SetMaterialID(charMaterialID);
|
||||||
|
|
||||||
|
|
||||||
const auto animator = CharObj->AddComponent<Animator>();
|
const auto animator = CharObj->AddComponent<Animator>();
|
||||||
animator->setSkeleton(std::move(charModel.skeleton));
|
animator->setSkeleton(std::move(charModel.skeleton));
|
||||||
|
|
||||||
std::string firstAnimationName;
|
std::string firstAnimationName;
|
||||||
|
|
||||||
for (auto& clip : charModel.animations) {
|
for (auto &clip: charModel.animations) {
|
||||||
spdlog::info("Loaded animation: '{}' ({:.2f}s)", clip.name, clip.duration);
|
spdlog::info("Loaded animation: '{}' ({:.2f}s)", clip.name, clip.duration);
|
||||||
|
|
||||||
if (firstAnimationName.empty())
|
if (firstAnimationName.empty())
|
||||||
@@ -159,11 +219,132 @@ void LightKeeper::customInit() {
|
|||||||
animator->play(firstAnimationName);
|
animator->play(firstAnimationName);
|
||||||
}
|
}
|
||||||
|
|
||||||
animator->play("capybara_canter_fwd_01|capybara_walk_fwd_01");
|
animator->play("capybara_canter_fwd_01|capybara_canter_fwd_01|run");
|
||||||
|
|
||||||
CharObj->GetTransform().SetWorldPosition(glm::vec3(0.f, 0.f, 0.f));
|
CharObj->GetTransform().SetWorldPosition(glm::vec3(0.f, 0.f, 0.f));
|
||||||
// CharObj->GetTransform().SetWorldScale(0.01f, 0.01f, 0.01f);
|
// CharObj->GetTransform().SetWorldScale(0.01f, 0.01f, 0.01f);
|
||||||
scene.Add(CharObj);
|
scene.Add(CharObj);
|
||||||
|
|
||||||
|
ModelDoc::LoadOptions options{};
|
||||||
|
options.meshImportMode = ModelDoc::MeshImportMode::MergedPerNode;
|
||||||
|
|
||||||
|
auto model = ModelDoc::LoadModel(
|
||||||
|
AssetFS::GetInstance()
|
||||||
|
.GetFullPath("engine://multiple_object_test.fbx")
|
||||||
|
.generic_string(),
|
||||||
|
options
|
||||||
|
);
|
||||||
|
|
||||||
|
ModelDocUtils::LogModelDocSummary(model, "engine://multiple_object_test.fbx");
|
||||||
|
|
||||||
|
auto root = std::make_shared<GameObject>("MultiMeshModel");
|
||||||
|
root->GetTransform().SetWorldPosition(glm::vec3(0.1f, 0.1f, 0.1f));
|
||||||
|
scene.Add(root);
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < model.primitives.size(); ++i) {
|
||||||
|
const auto &primitive = model.primitives[i];
|
||||||
|
|
||||||
|
const auto meshID = meshCache.addMesh(gfxDevice, primitive.mesh);
|
||||||
|
|
||||||
|
const auto texturePath = ModelDocUtils::PickTexturePath(
|
||||||
|
model,
|
||||||
|
primitive,
|
||||||
|
AssetFS::GetInstance().GetFullPath("engine://textures/white.png")
|
||||||
|
);
|
||||||
|
|
||||||
|
const auto textureID = gfxDevice.loadImageFromFile(texturePath);
|
||||||
|
|
||||||
|
const auto materialID = materialCache.addMaterial(gfxDevice, {
|
||||||
|
.baseColor = ModelDocUtils::GetImportedBaseColor(
|
||||||
|
model, primitive),
|
||||||
|
.diffuseTexture = textureID,
|
||||||
|
.name = ModelDocUtils::GetImportedMaterialName(
|
||||||
|
model,
|
||||||
|
primitive,
|
||||||
|
primitive.mesh.name + "_Material"
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
auto part = std::make_shared<GameObject>(
|
||||||
|
primitive.mesh.name.empty()
|
||||||
|
? "Primitive_" + std::to_string(i)
|
||||||
|
: primitive.mesh.name
|
||||||
|
);
|
||||||
|
|
||||||
|
auto meshComp = part->AddComponent<MeshRendererComponent>();
|
||||||
|
meshComp->SetMeshID(meshID);
|
||||||
|
meshComp->SetMaterialID(materialID);
|
||||||
|
|
||||||
|
scene.Add(part);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
const auto CharObj = std::make_shared<GameObject>("Character");
|
||||||
|
|
||||||
|
ModelDoc::LoadOptions characterOptions{};
|
||||||
|
characterOptions.meshImportMode = ModelDoc::MeshImportMode::MergedPerNode;
|
||||||
|
characterOptions.loadMaterials = true;
|
||||||
|
characterOptions.loadSkeleton = true;
|
||||||
|
characterOptions.loadAnimations = false;
|
||||||
|
|
||||||
|
auto charModel = ModelDoc::LoadModel(
|
||||||
|
AssetFS::GetInstance()
|
||||||
|
.GetFullPath("engine://characterMedium.fbx")
|
||||||
|
.generic_string(),
|
||||||
|
characterOptions
|
||||||
|
);
|
||||||
|
|
||||||
|
const auto &charPrimitive =
|
||||||
|
ModelDocUtils::GetFirstSkinnedPrimitiveOrFirstOrThrow(
|
||||||
|
charModel,
|
||||||
|
"engine://characterMedium.fbx"
|
||||||
|
);
|
||||||
|
|
||||||
|
const auto charMeshID = meshCache.addMesh(gfxDevice, charPrimitive.mesh);
|
||||||
|
|
||||||
|
const auto charTextureID = gfxDevice.loadImageFromFile(
|
||||||
|
AssetFS::GetInstance().GetFullPath("engine://textures/criminalMaleA.png")
|
||||||
|
);
|
||||||
|
|
||||||
|
const auto charMaterialID = materialCache.addMaterial(gfxDevice, {
|
||||||
|
.baseColor = ModelDocUtils::GetImportedBaseColor(
|
||||||
|
charModel, charPrimitive),
|
||||||
|
.diffuseTexture = charTextureID,
|
||||||
|
.name = ModelDocUtils::GetImportedMaterialName(
|
||||||
|
charModel,
|
||||||
|
charPrimitive,
|
||||||
|
"CharacterMaterial"
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
const auto charMeshComp = CharObj->AddComponent<MeshRendererComponent>();
|
||||||
|
charMeshComp->SetMeshID(charMeshID);
|
||||||
|
charMeshComp->SetMaterialID(charMaterialID);
|
||||||
|
|
||||||
|
const auto animator = CharObj->AddComponent<Animator>();
|
||||||
|
animator->setSkeleton(charModel.skeleton);
|
||||||
|
|
||||||
|
auto runClips = ModelDoc::LoadAnimationClips(
|
||||||
|
AssetFS::GetInstance()
|
||||||
|
.GetFullPath("engine://run.fbx")
|
||||||
|
.generic_string(),
|
||||||
|
charModel.skeleton
|
||||||
|
);
|
||||||
|
|
||||||
|
for (auto &clip: runClips) {
|
||||||
|
spdlog::info("Loaded animation: '{}' ({:.2f}s)", clip.name, clip.duration);
|
||||||
|
animator->addClip(std::make_shared<SkeletalAnimation>(std::move(clip)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!runClips.empty()) {
|
||||||
|
animator->play("Root|Run");
|
||||||
|
}
|
||||||
|
|
||||||
|
CharObj->GetTransform().SetWorldPosition(glm::vec3(0.f));
|
||||||
|
CharObj->GetTransform().SetWorldPosition(glm::vec3(5, 0, 0));
|
||||||
|
scene.Add(CharObj);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LightKeeper::customUpdate(float dt) {
|
void LightKeeper::customUpdate(float dt) {
|
||||||
@@ -194,7 +375,7 @@ void LightKeeper::customDraw() {
|
|||||||
renderer.endDrawing();
|
renderer.endDrawing();
|
||||||
|
|
||||||
const auto cmd = gfxDevice.beginFrame();
|
const auto cmd = gfxDevice.beginFrame();
|
||||||
const auto& drawImage = renderer.getDrawImage(gfxDevice);
|
const auto &drawImage = renderer.getDrawImage(gfxDevice);
|
||||||
|
|
||||||
renderer.draw(
|
renderer.draw(
|
||||||
cmd, gfxDevice, camera, GameRenderer::SceneData{
|
cmd, gfxDevice, camera, GameRenderer::SceneData{
|
||||||
|
|||||||
Reference in New Issue
Block a user