2026-04-11 22:14:02 +08:00
|
|
|
#include "Commands/ProjectCommands.h"
|
|
|
|
|
#include "Core/EditorContext.h"
|
|
|
|
|
|
|
|
|
|
#include <XCEngine/Components/CameraComponent.h>
|
|
|
|
|
#include <XCEngine/Components/GameObject.h>
|
|
|
|
|
#include <XCEngine/Components/LightComponent.h>
|
|
|
|
|
#include <XCEngine/Components/MeshFilterComponent.h>
|
|
|
|
|
#include <XCEngine/Components/MeshRendererComponent.h>
|
|
|
|
|
#include <XCEngine/Core/Asset/ResourceManager.h>
|
|
|
|
|
#include <XCEngine/Core/Math/Color.h>
|
|
|
|
|
#include <XCEngine/Core/Math/Quaternion.h>
|
|
|
|
|
#include <XCEngine/Core/Math/Rect.h>
|
|
|
|
|
#include <XCEngine/Core/Math/Vector3.h>
|
|
|
|
|
|
|
|
|
|
#include <filesystem>
|
|
|
|
|
#include <iostream>
|
|
|
|
|
#include <memory>
|
|
|
|
|
#include <string>
|
|
|
|
|
|
|
|
|
|
namespace fs = std::filesystem;
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
constexpr const char* kDefaultModelAssetPath = "Assets/Models/nahida/Avatar_Loli_Catalyst_Nahida.fbx";
|
|
|
|
|
constexpr const char* kDefaultSceneAssetPath = "Assets/Scenes/NahidaPreview.xc";
|
|
|
|
|
constexpr const char* kDefaultSceneName = "Nahida Preview";
|
|
|
|
|
|
|
|
|
|
std::shared_ptr<XCEngine::Editor::AssetItem> MakeModelAssetItem(
|
|
|
|
|
const fs::path& projectRoot,
|
|
|
|
|
const std::string& modelAssetPath) {
|
|
|
|
|
auto item = std::make_shared<XCEngine::Editor::AssetItem>();
|
|
|
|
|
item->name = fs::path(modelAssetPath).filename().string();
|
|
|
|
|
item->type = "Model";
|
|
|
|
|
item->isFolder = false;
|
|
|
|
|
item->fullPath = (projectRoot / fs::path(modelAssetPath)).string();
|
|
|
|
|
return item;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConfigurePreviewCamera(XCEngine::Components::GameObject& gameObject) {
|
|
|
|
|
using namespace XCEngine;
|
|
|
|
|
|
2026-04-12 22:50:50 +08:00
|
|
|
gameObject.GetTransform()->SetLocalPosition(Math::Vector3(0.0f, 1.2f, -4.25f));
|
2026-04-11 22:14:02 +08:00
|
|
|
gameObject.GetTransform()->SetLocalRotation(Math::Quaternion(0.104528f, 0.0f, 0.0f, 0.994522f));
|
|
|
|
|
|
|
|
|
|
auto* camera = gameObject.AddComponent<Components::CameraComponent>();
|
|
|
|
|
camera->SetProjectionType(Components::CameraProjectionType::Perspective);
|
|
|
|
|
camera->SetFieldOfView(35.0f);
|
|
|
|
|
camera->SetNearClipPlane(0.01f);
|
|
|
|
|
camera->SetFarClipPlane(100.0f);
|
|
|
|
|
camera->SetDepth(0.0f);
|
|
|
|
|
camera->SetPrimary(true);
|
|
|
|
|
camera->SetClearMode(Components::CameraClearMode::Auto);
|
|
|
|
|
camera->SetStackType(Components::CameraStackType::Base);
|
|
|
|
|
camera->SetCullingMask(0xFFFFFFFFu);
|
|
|
|
|
camera->SetViewportRect(Math::Rect(0.0f, 0.0f, 1.0f, 1.0f));
|
|
|
|
|
camera->SetClearColor(Math::Color(0.04f, 0.05f, 0.07f, 1.0f));
|
|
|
|
|
camera->SetSkyboxEnabled(false);
|
|
|
|
|
camera->SetSkyboxTopColor(Math::Color(0.18f, 0.36f, 0.74f, 1.0f));
|
|
|
|
|
camera->SetSkyboxHorizonColor(Math::Color(0.78f, 0.84f, 0.92f, 1.0f));
|
|
|
|
|
camera->SetSkyboxBottomColor(Math::Color(0.92f, 0.93f, 0.95f, 1.0f));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConfigureKeyLight(XCEngine::Components::GameObject& gameObject) {
|
|
|
|
|
using namespace XCEngine;
|
|
|
|
|
|
|
|
|
|
gameObject.GetTransform()->SetLocalPosition(Math::Vector3(2.5f, 3.0f, -2.0f));
|
|
|
|
|
gameObject.GetTransform()->SetLocalRotation(Math::Quaternion(0.21644f, -0.39404f, 0.09198f, 0.88755f));
|
|
|
|
|
|
|
|
|
|
auto* light = gameObject.AddComponent<Components::LightComponent>();
|
|
|
|
|
light->SetLightType(Components::LightType::Directional);
|
|
|
|
|
light->SetColor(Math::Color(1.0f, 0.976f, 0.94f, 1.0f));
|
|
|
|
|
light->SetIntensity(1.4f);
|
|
|
|
|
light->SetRange(10.0f);
|
|
|
|
|
light->SetSpotAngle(30.0f);
|
|
|
|
|
light->SetCastsShadows(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConfigureFillLight(XCEngine::Components::GameObject& gameObject) {
|
|
|
|
|
using namespace XCEngine;
|
|
|
|
|
|
|
|
|
|
gameObject.GetTransform()->SetLocalPosition(Math::Vector3(-1.75f, 1.5f, -1.25f));
|
|
|
|
|
gameObject.GetTransform()->SetLocalRotation(Math::Quaternion::Identity());
|
|
|
|
|
|
|
|
|
|
auto* light = gameObject.AddComponent<Components::LightComponent>();
|
|
|
|
|
light->SetLightType(Components::LightType::Point);
|
|
|
|
|
light->SetColor(Math::Color(0.24f, 0.32f, 0.5f, 1.0f));
|
|
|
|
|
light->SetIntensity(0.35f);
|
|
|
|
|
light->SetRange(10.0f);
|
|
|
|
|
light->SetSpotAngle(30.0f);
|
|
|
|
|
light->SetCastsShadows(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConfigureGround(XCEngine::Components::GameObject& gameObject) {
|
|
|
|
|
using namespace XCEngine;
|
|
|
|
|
|
|
|
|
|
gameObject.GetTransform()->SetLocalPosition(Math::Vector3::Zero());
|
|
|
|
|
gameObject.GetTransform()->SetLocalRotation(Math::Quaternion::Identity());
|
|
|
|
|
gameObject.GetTransform()->SetLocalScale(Math::Vector3(8.0f, 1.0f, 8.0f));
|
|
|
|
|
|
|
|
|
|
auto* meshFilter = gameObject.AddComponent<Components::MeshFilterComponent>();
|
|
|
|
|
meshFilter->SetMeshPath("builtin://meshes/plane");
|
|
|
|
|
|
|
|
|
|
auto* meshRenderer = gameObject.AddComponent<Components::MeshRendererComponent>();
|
|
|
|
|
meshRenderer->SetMaterialPath(0, "builtin://materials/default-primitive");
|
|
|
|
|
meshRenderer->SetCastShadows(true);
|
|
|
|
|
meshRenderer->SetReceiveShadows(true);
|
|
|
|
|
meshRenderer->SetRenderLayer(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string ParseArgValue(int argc, char** argv, const char* name, const char* fallback) {
|
|
|
|
|
const std::string optionPrefix = std::string(name) + "=";
|
|
|
|
|
for (int index = 1; index < argc; ++index) {
|
|
|
|
|
const std::string argument = argv[index];
|
|
|
|
|
if (argument.rfind(optionPrefix, 0) == 0) {
|
|
|
|
|
return argument.substr(optionPrefix.size());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return fallback != nullptr ? std::string(fallback) : std::string();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
int main(int argc, char** argv) {
|
|
|
|
|
using namespace XCEngine;
|
|
|
|
|
|
|
|
|
|
const fs::path repoRoot(XCENGINE_EDITOR_REPO_ROOT);
|
|
|
|
|
const std::string projectArg = ParseArgValue(argc, argv, "--project-root", nullptr);
|
|
|
|
|
const fs::path projectRoot = projectArg.empty() ? (repoRoot / "project") : fs::path(projectArg);
|
|
|
|
|
const std::string modelAssetPath =
|
|
|
|
|
ParseArgValue(argc, argv, "--model-asset", kDefaultModelAssetPath);
|
|
|
|
|
const std::string sceneAssetPath =
|
|
|
|
|
ParseArgValue(argc, argv, "--scene-asset", kDefaultSceneAssetPath);
|
|
|
|
|
const fs::path sceneFilePath = projectRoot / fs::path(sceneAssetPath);
|
|
|
|
|
|
|
|
|
|
if (!fs::exists(projectRoot) || !fs::is_directory(projectRoot)) {
|
|
|
|
|
std::cerr << "Project root does not exist: " << projectRoot << std::endl;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Editor::EditorContext context;
|
|
|
|
|
context.SetProjectPath(projectRoot.string());
|
|
|
|
|
context.GetProjectManager().Initialize(projectRoot.string());
|
|
|
|
|
context.GetSceneManager().NewScene(kDefaultSceneName);
|
|
|
|
|
|
|
|
|
|
auto& resourceManager = Resources::ResourceManager::Get();
|
|
|
|
|
resourceManager.Initialize();
|
|
|
|
|
resourceManager.SetResourceRoot(projectRoot.string().c_str());
|
|
|
|
|
|
|
|
|
|
auto* camera = context.GetSceneManager().CreateEntity("Preview Camera");
|
|
|
|
|
auto* keyLight = context.GetSceneManager().CreateEntity("Key Light");
|
|
|
|
|
auto* fillLight = context.GetSceneManager().CreateEntity("Fill Light");
|
|
|
|
|
auto* ground = context.GetSceneManager().CreateEntity("Ground");
|
|
|
|
|
auto* avatarRoot = context.GetSceneManager().CreateEntity("AvatarRoot");
|
|
|
|
|
if (camera == nullptr || keyLight == nullptr || fillLight == nullptr || ground == nullptr || avatarRoot == nullptr) {
|
|
|
|
|
std::cerr << "Failed to create preview scene entities." << std::endl;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ConfigurePreviewCamera(*camera);
|
|
|
|
|
ConfigureKeyLight(*keyLight);
|
|
|
|
|
ConfigureFillLight(*fillLight);
|
|
|
|
|
ConfigureGround(*ground);
|
|
|
|
|
avatarRoot->GetTransform()->SetLocalPosition(Math::Vector3::Zero());
|
|
|
|
|
avatarRoot->GetTransform()->SetLocalRotation(Math::Quaternion::Identity());
|
|
|
|
|
avatarRoot->GetTransform()->SetLocalScale(Math::Vector3::One());
|
|
|
|
|
|
|
|
|
|
const auto modelItem = MakeModelAssetItem(projectRoot, modelAssetPath);
|
|
|
|
|
auto* createdRoot = Editor::Commands::InstantiateModelAsset(
|
|
|
|
|
context,
|
|
|
|
|
modelItem,
|
|
|
|
|
avatarRoot,
|
|
|
|
|
"Instantiate Nahida Preview Model");
|
|
|
|
|
if (createdRoot == nullptr) {
|
|
|
|
|
std::cerr << "Failed to instantiate model asset: " << modelAssetPath << std::endl;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
createdRoot->SetName("NahidaUnityModel");
|
|
|
|
|
|
|
|
|
|
if (!context.GetSceneManager().SaveSceneAs(sceneFilePath.string())) {
|
|
|
|
|
std::cerr << "Failed to save scene: " << sceneFilePath << std::endl;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
resourceManager.UnloadAll();
|
|
|
|
|
resourceManager.SetResourceRoot("");
|
|
|
|
|
resourceManager.Shutdown();
|
|
|
|
|
|
|
|
|
|
std::cout << "Generated scene: " << sceneFilePath << std::endl;
|
|
|
|
|
std::cout << "Model asset: " << modelAssetPath << std::endl;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|