feat(editor): complete static cpp component workflow
This commit is contained in:
parent
695fc30d34
commit
78544ae5eb
@ -2588,6 +2588,93 @@ bool MetaCoreDeserializeComponentValue(
|
|||||||
return MetaCoreDeserializeFromBytes(payload, component, registry);
|
return MetaCoreDeserializeFromBytes(payload, component, registry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<std::vector<std::byte>> MetaCoreSerializeReflectedComponentValue(
|
||||||
|
const MetaCoreStructDescriptor& descriptor,
|
||||||
|
const void* component,
|
||||||
|
const MetaCoreTypeRegistry& registry
|
||||||
|
) {
|
||||||
|
if (component == nullptr) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
MetaCoreArchiveWriter writer;
|
||||||
|
writer.WriteBytes(&descriptor.Version, sizeof(descriptor.Version));
|
||||||
|
const std::uint32_t fieldCount = static_cast<std::uint32_t>(descriptor.Fields.size());
|
||||||
|
writer.WriteBytes(&fieldCount, sizeof(fieldCount));
|
||||||
|
|
||||||
|
for (const MetaCoreFieldDescriptor& field : descriptor.Fields) {
|
||||||
|
if (!MetaCoreSerializeValue(writer, field.Name, registry)) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
MetaCoreArchiveWriter fieldWriter;
|
||||||
|
if (!field.Serialize || !field.Serialize(component, fieldWriter, registry)) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::uint64_t fieldSize = static_cast<std::uint64_t>(fieldWriter.GetSize());
|
||||||
|
writer.WriteBytes(&fieldSize, sizeof(fieldSize));
|
||||||
|
writer.WriteSpan(fieldWriter.GetBuffer());
|
||||||
|
}
|
||||||
|
|
||||||
|
return writer.GetBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MetaCoreDeserializeReflectedComponentValue(
|
||||||
|
const MetaCoreStructDescriptor& descriptor,
|
||||||
|
std::span<const std::byte> payload,
|
||||||
|
void* component,
|
||||||
|
const MetaCoreTypeRegistry& registry
|
||||||
|
) {
|
||||||
|
if (component == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
MetaCoreArchiveReader reader(payload);
|
||||||
|
std::uint32_t serializedVersion = 0;
|
||||||
|
std::uint32_t fieldCount = 0;
|
||||||
|
if (!reader.ReadBytes(&serializedVersion, sizeof(serializedVersion)) ||
|
||||||
|
!reader.ReadBytes(&fieldCount, sizeof(fieldCount))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
(void)serializedVersion;
|
||||||
|
|
||||||
|
for (std::uint32_t index = 0; index < fieldCount; ++index) {
|
||||||
|
std::string fieldName;
|
||||||
|
std::uint64_t fieldSize = 0;
|
||||||
|
if (!MetaCoreDeserializeValue(reader, fieldName, registry) ||
|
||||||
|
!reader.ReadBytes(&fieldSize, sizeof(fieldSize))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto fieldBytes = reader.ReadSpan(static_cast<std::size_t>(fieldSize));
|
||||||
|
if (!fieldBytes.has_value()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto fieldIterator = std::find_if(
|
||||||
|
descriptor.Fields.begin(),
|
||||||
|
descriptor.Fields.end(),
|
||||||
|
[&](const MetaCoreFieldDescriptor& field) {
|
||||||
|
return field.Name == fieldName;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (fieldIterator == descriptor.Fields.end()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
MetaCoreArchiveReader fieldReader(*fieldBytes);
|
||||||
|
if (!fieldIterator->Deserialize ||
|
||||||
|
!fieldIterator->Deserialize(component, fieldReader, registry) ||
|
||||||
|
!fieldReader.IsEof()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return reader.IsEof();
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::string MetaCorePathToPortableString(const std::filesystem::path& path) {
|
[[nodiscard]] std::string MetaCorePathToPortableString(const std::filesystem::path& path) {
|
||||||
return path.lexically_normal().generic_string();
|
return path.lexically_normal().generic_string();
|
||||||
}
|
}
|
||||||
@ -5823,7 +5910,7 @@ public:
|
|||||||
: sceneDocument->Name;
|
: sceneDocument->Name;
|
||||||
SavedSnapshotBytes_ = MetaCoreSerializeSceneSnapshotBytes(
|
SavedSnapshotBytes_ = MetaCoreSerializeSceneSnapshotBytes(
|
||||||
ReflectionRegistry_->GetTypeRegistry(),
|
ReflectionRegistry_->GetTypeRegistry(),
|
||||||
editorContext.GetScene().CaptureSnapshot()
|
editorContext.CaptureStateSnapshot().SceneSnapshot
|
||||||
).value_or(std::vector<std::byte>{});
|
).value_or(std::vector<std::byte>{});
|
||||||
SetDirtyState(false);
|
SetDirtyState(false);
|
||||||
|
|
||||||
@ -5871,9 +5958,11 @@ public:
|
|||||||
sceneGuid = MetaCoreAssetGuid::Generate();
|
sceneGuid = MetaCoreAssetGuid::Generate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const MetaCoreEditorStateSnapshot savedState = editorContext.CaptureStateSnapshot();
|
||||||
|
|
||||||
MetaCoreSceneDocument sceneDocument;
|
MetaCoreSceneDocument sceneDocument;
|
||||||
sceneDocument.Name = CurrentSceneName_.empty() ? MetaCoreSceneDisplayNameFromPath(normalizedScenePath) : CurrentSceneName_;
|
sceneDocument.Name = CurrentSceneName_.empty() ? MetaCoreSceneDisplayNameFromPath(normalizedScenePath) : CurrentSceneName_;
|
||||||
sceneDocument.GameObjects = editorContext.GetScene().CaptureSnapshot().GameObjects;
|
sceneDocument.GameObjects = savedState.SceneSnapshot.GameObjects;
|
||||||
sceneDocument.Selection.SelectedObjectIds = editorContext.GetSelectedObjectIds();
|
sceneDocument.Selection.SelectedObjectIds = editorContext.GetSelectedObjectIds();
|
||||||
sceneDocument.Selection.ActiveObjectId = editorContext.GetActiveObjectId();
|
sceneDocument.Selection.ActiveObjectId = editorContext.GetActiveObjectId();
|
||||||
sceneDocument.Selection.SelectionAnchorId = editorContext.GetSelectionAnchorId();
|
sceneDocument.Selection.SelectionAnchorId = editorContext.GetSelectionAnchorId();
|
||||||
@ -5901,7 +5990,7 @@ public:
|
|||||||
CurrentSceneName_ = sceneDocument.Name;
|
CurrentSceneName_ = sceneDocument.Name;
|
||||||
SavedSnapshotBytes_ = MetaCoreSerializeSceneSnapshotBytes(
|
SavedSnapshotBytes_ = MetaCoreSerializeSceneSnapshotBytes(
|
||||||
ReflectionRegistry_->GetTypeRegistry(),
|
ReflectionRegistry_->GetTypeRegistry(),
|
||||||
editorContext.GetScene().CaptureSnapshot()
|
savedState.SceneSnapshot
|
||||||
).value_or(std::vector<std::byte>{});
|
).value_or(std::vector<std::byte>{});
|
||||||
SetDirtyState(false);
|
SetDirtyState(false);
|
||||||
|
|
||||||
@ -6167,7 +6256,7 @@ public:
|
|||||||
if (!gameObject.HasComponent<MetaCoreCameraComponent>()) {
|
if (!gameObject.HasComponent<MetaCoreCameraComponent>()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
gameObject.AddComponent<MetaCoreCameraComponent>(MetaCoreCameraComponent{});
|
gameObject.GetComponent<MetaCoreCameraComponent>() = MetaCoreCameraComponent{};
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
[](const MetaCoreGameObject& gameObject, const MetaCoreTypeRegistry& registry) -> std::optional<std::vector<std::byte>> {
|
[](const MetaCoreGameObject& gameObject, const MetaCoreTypeRegistry& registry) -> std::optional<std::vector<std::byte>> {
|
||||||
@ -6217,7 +6306,7 @@ public:
|
|||||||
if (!gameObject.HasComponent<MetaCoreLightComponent>()) {
|
if (!gameObject.HasComponent<MetaCoreLightComponent>()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
gameObject.AddComponent<MetaCoreLightComponent>(MetaCoreLightComponent{});
|
gameObject.GetComponent<MetaCoreLightComponent>() = MetaCoreLightComponent{};
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
[](const MetaCoreGameObject& gameObject, const MetaCoreTypeRegistry& registry) -> std::optional<std::vector<std::byte>> {
|
[](const MetaCoreGameObject& gameObject, const MetaCoreTypeRegistry& registry) -> std::optional<std::vector<std::byte>> {
|
||||||
@ -6267,7 +6356,7 @@ public:
|
|||||||
if (!gameObject.HasComponent<MetaCoreMeshRendererComponent>()) {
|
if (!gameObject.HasComponent<MetaCoreMeshRendererComponent>()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
gameObject.AddComponent<MetaCoreMeshRendererComponent>(MetaCoreMeshRendererComponent{});
|
gameObject.GetComponent<MetaCoreMeshRendererComponent>() = MetaCoreMeshRendererComponent{};
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
[](const MetaCoreGameObject& gameObject, const MetaCoreTypeRegistry& registry) -> std::optional<std::vector<std::byte>> {
|
[](const MetaCoreGameObject& gameObject, const MetaCoreTypeRegistry& registry) -> std::optional<std::vector<std::byte>> {
|
||||||
@ -6318,6 +6407,51 @@ public:
|
|||||||
if (descriptor.Category.empty()) {
|
if (descriptor.Category.empty()) {
|
||||||
descriptor.Category = "Scripts";
|
descriptor.Category = "Scripts";
|
||||||
}
|
}
|
||||||
|
if (!descriptor.CopyComponentPayload &&
|
||||||
|
descriptor.ReflectedType != nullptr &&
|
||||||
|
descriptor.ConstComponent &&
|
||||||
|
descriptor.HasComponent) {
|
||||||
|
const MetaCoreStructDescriptor* reflectedType = descriptor.ReflectedType;
|
||||||
|
auto constComponent = descriptor.ConstComponent;
|
||||||
|
auto hasComponent = descriptor.HasComponent;
|
||||||
|
descriptor.CopyComponentPayload =
|
||||||
|
[reflectedType, constComponent, hasComponent](
|
||||||
|
const MetaCoreGameObject& gameObject,
|
||||||
|
const MetaCoreTypeRegistry& registry
|
||||||
|
) -> std::optional<std::vector<std::byte>> {
|
||||||
|
if (!hasComponent(gameObject)) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
return MetaCoreSerializeReflectedComponentValue(*reflectedType, constComponent(gameObject), registry);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (!descriptor.PasteComponentPayload &&
|
||||||
|
descriptor.ReflectedType != nullptr &&
|
||||||
|
descriptor.MutableComponent &&
|
||||||
|
descriptor.HasComponent) {
|
||||||
|
const MetaCoreStructDescriptor* reflectedType = descriptor.ReflectedType;
|
||||||
|
auto mutableComponent = descriptor.MutableComponent;
|
||||||
|
auto hasComponent = descriptor.HasComponent;
|
||||||
|
auto addComponent = descriptor.AddComponent;
|
||||||
|
descriptor.PasteComponentPayload =
|
||||||
|
[reflectedType, mutableComponent, hasComponent, addComponent](
|
||||||
|
MetaCoreGameObject& gameObject,
|
||||||
|
std::span<const std::byte> payload,
|
||||||
|
const MetaCoreTypeRegistry& registry
|
||||||
|
) {
|
||||||
|
if (!hasComponent(gameObject)) {
|
||||||
|
if (!addComponent || !addComponent(gameObject)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return MetaCoreDeserializeReflectedComponentValue(
|
||||||
|
*reflectedType,
|
||||||
|
payload,
|
||||||
|
mutableComponent(gameObject),
|
||||||
|
registry
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
Descriptors_.push_back(std::move(descriptor));
|
Descriptors_.push_back(std::move(descriptor));
|
||||||
return true;
|
return true;
|
||||||
@ -6365,6 +6499,30 @@ public:
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DrawComponentGizmos(MetaCoreEditorContext& editorContext) override {
|
||||||
|
const std::vector<MetaCoreId> orderedObjectIds = editorContext.GetScene().BuildHierarchyPreorder();
|
||||||
|
for (const MetaCoreComponentDescriptor& descriptor : Descriptors_) {
|
||||||
|
if (!descriptor.HasComponent ||
|
||||||
|
(!descriptor.Lifecycle.OnDrawGizmos && !descriptor.Lifecycle.OnDrawGizmosSelected)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (MetaCoreId objectId : orderedObjectIds) {
|
||||||
|
MetaCoreGameObject gameObject = editorContext.GetScene().FindGameObject(objectId);
|
||||||
|
if (!gameObject || !descriptor.HasComponent(gameObject)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (descriptor.Lifecycle.OnDrawGizmos) {
|
||||||
|
descriptor.Lifecycle.OnDrawGizmos(editorContext, gameObject);
|
||||||
|
}
|
||||||
|
if (descriptor.Lifecycle.OnDrawGizmosSelected && editorContext.IsObjectSelected(objectId)) {
|
||||||
|
descriptor.Lifecycle.OnDrawGizmosSelected(editorContext, gameObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<MetaCoreComponentDescriptor> Descriptors_{};
|
std::vector<MetaCoreComponentDescriptor> Descriptors_{};
|
||||||
std::shared_ptr<MetaCoreIReflectionRegistry> ReflectionRegistry_{};
|
std::shared_ptr<MetaCoreIReflectionRegistry> ReflectionRegistry_{};
|
||||||
|
|||||||
@ -6059,7 +6059,7 @@ public:
|
|||||||
|
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
if (ImGui::Button("添加组件", ImVec2(-1, 26))) {
|
if (ImGui::Button("Add Component", ImVec2(-1, 26))) {
|
||||||
ImGui::OpenPopup("AddComponentPopup");
|
ImGui::OpenPopup("AddComponentPopup");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1225,6 +1225,11 @@ void MetaCoreEditorApp::DrawSceneViewWindow() {
|
|||||||
ImGui::EndDragDropTarget();
|
ImGui::EndDragDropTarget();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (const auto componentRegistry = ModuleRegistry_.ResolveService<MetaCoreIComponentTypeRegistry>();
|
||||||
|
componentRegistry != nullptr) {
|
||||||
|
componentRegistry->DrawComponentGizmos(*EditorContext_);
|
||||||
|
}
|
||||||
|
|
||||||
bool gizmoHovering = false;
|
bool gizmoHovering = false;
|
||||||
if (GMetaCoreEnableImGuizmo) {
|
if (GMetaCoreEnableImGuizmo) {
|
||||||
SceneInteractionService_.HandleGizmoManipulation(*EditorContext_);
|
SceneInteractionService_.HandleGizmoManipulation(*EditorContext_);
|
||||||
|
|||||||
@ -17,6 +17,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include <span>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
|
||||||
namespace MetaCore {
|
namespace MetaCore {
|
||||||
@ -124,9 +125,26 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lhs.SerializedComponents.size() != rhs.SerializedComponents.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (std::size_t index = 0; index < lhs.SerializedComponents.size(); ++index) {
|
||||||
|
if (lhs.SerializedComponents[index].TypeId != rhs.SerializedComponents[index].TypeId ||
|
||||||
|
lhs.SerializedComponents[index].Payload != rhs.SerializedComponents[index].Payload) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool MetaCoreIsNativeSceneComponentType(std::string_view typeId) {
|
||||||
|
return typeId == "Transform" ||
|
||||||
|
typeId == "Camera" ||
|
||||||
|
typeId == "Light" ||
|
||||||
|
typeId == "MeshRenderer";
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool MetaCoreStateSnapshotEqual(const MetaCoreEditorStateSnapshot& lhs, const MetaCoreEditorStateSnapshot& rhs) {
|
[[nodiscard]] bool MetaCoreStateSnapshotEqual(const MetaCoreEditorStateSnapshot& lhs, const MetaCoreEditorStateSnapshot& rhs) {
|
||||||
if (lhs.SceneSnapshot.GameObjects.size() != rhs.SceneSnapshot.GameObjects.size()) {
|
if (lhs.SceneSnapshot.GameObjects.size() != rhs.SceneSnapshot.GameObjects.size()) {
|
||||||
return false;
|
return false;
|
||||||
@ -616,6 +634,7 @@ bool MetaCoreEditorContext::RedoCommand() {
|
|||||||
MetaCoreEditorStateSnapshot MetaCoreEditorContext::CaptureStateSnapshot() const {
|
MetaCoreEditorStateSnapshot MetaCoreEditorContext::CaptureStateSnapshot() const {
|
||||||
MetaCoreEditorStateSnapshot snapshot;
|
MetaCoreEditorStateSnapshot snapshot;
|
||||||
snapshot.SceneSnapshot = Scene_.CaptureSnapshot();
|
snapshot.SceneSnapshot = Scene_.CaptureSnapshot();
|
||||||
|
CaptureSerializedComponents(snapshot.SceneSnapshot);
|
||||||
snapshot.SelectionSnapshot.SelectedObjectIds = SelectedObjectIds_;
|
snapshot.SelectionSnapshot.SelectedObjectIds = SelectedObjectIds_;
|
||||||
snapshot.SelectionSnapshot.ActiveObjectId = ActiveObjectId_;
|
snapshot.SelectionSnapshot.ActiveObjectId = ActiveObjectId_;
|
||||||
snapshot.SelectionSnapshot.SelectionAnchorId = SelectionAnchorId_;
|
snapshot.SelectionSnapshot.SelectionAnchorId = SelectionAnchorId_;
|
||||||
@ -624,6 +643,7 @@ MetaCoreEditorStateSnapshot MetaCoreEditorContext::CaptureStateSnapshot() const
|
|||||||
|
|
||||||
void MetaCoreEditorContext::RestoreStateSnapshot(const MetaCoreEditorStateSnapshot& snapshot) {
|
void MetaCoreEditorContext::RestoreStateSnapshot(const MetaCoreEditorStateSnapshot& snapshot) {
|
||||||
Scene_.RestoreSnapshot(snapshot.SceneSnapshot);
|
Scene_.RestoreSnapshot(snapshot.SceneSnapshot);
|
||||||
|
RestoreSerializedComponents(snapshot.SceneSnapshot);
|
||||||
SetSelection(snapshot.SelectionSnapshot.SelectedObjectIds, snapshot.SelectionSnapshot.ActiveObjectId);
|
SetSelection(snapshot.SelectionSnapshot.SelectedObjectIds, snapshot.SelectionSnapshot.ActiveObjectId);
|
||||||
if (snapshot.SelectionSnapshot.SelectionAnchorId != 0 &&
|
if (snapshot.SelectionSnapshot.SelectionAnchorId != 0 &&
|
||||||
(bool)Scene_.FindGameObject(snapshot.SelectionSnapshot.SelectionAnchorId)) {
|
(bool)Scene_.FindGameObject(snapshot.SelectionSnapshot.SelectionAnchorId)) {
|
||||||
@ -964,6 +984,97 @@ void MetaCoreEditorContext::ClearSelectedAssetSubId() {
|
|||||||
void MetaCoreEditorContext::SetDockLayoutBuilt(bool built) { DockLayoutBuilt_ = built; }
|
void MetaCoreEditorContext::SetDockLayoutBuilt(bool built) { DockLayoutBuilt_ = built; }
|
||||||
bool MetaCoreEditorContext::HasDockLayoutBuilt() const { return DockLayoutBuilt_; }
|
bool MetaCoreEditorContext::HasDockLayoutBuilt() const { return DockLayoutBuilt_; }
|
||||||
|
|
||||||
|
void MetaCoreEditorContext::CaptureSerializedComponents(MetaCoreSceneSnapshot& snapshot) const {
|
||||||
|
const auto componentRegistry = ModuleRegistry_.ResolveService<MetaCoreIComponentTypeRegistry>();
|
||||||
|
const auto reflectionRegistry = ModuleRegistry_.ResolveService<MetaCoreIReflectionRegistry>();
|
||||||
|
if (componentRegistry == nullptr || reflectionRegistry == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MetaCoreTypeRegistry& typeRegistry = reflectionRegistry->GetTypeRegistry();
|
||||||
|
for (MetaCoreGameObjectData& objectData : snapshot.GameObjects) {
|
||||||
|
objectData.SerializedComponents.clear();
|
||||||
|
const MetaCoreGameObject gameObject = Scene_.FindGameObject(objectData.Id);
|
||||||
|
if (!gameObject) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const MetaCoreIComponentTypeRegistry::MetaCoreComponentDescriptor& descriptor : componentRegistry->GetComponentDescriptors()) {
|
||||||
|
if (MetaCoreIsNativeSceneComponentType(descriptor.TypeId) ||
|
||||||
|
!descriptor.HasComponent ||
|
||||||
|
!descriptor.CopyComponentPayload ||
|
||||||
|
!descriptor.HasComponent(gameObject)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto payload = descriptor.CopyComponentPayload(gameObject, typeRegistry);
|
||||||
|
if (!payload.has_value() || payload->empty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
objectData.SerializedComponents.push_back(MetaCoreSerializedComponentData{
|
||||||
|
descriptor.TypeId,
|
||||||
|
*payload
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort(
|
||||||
|
objectData.SerializedComponents.begin(),
|
||||||
|
objectData.SerializedComponents.end(),
|
||||||
|
[](const MetaCoreSerializedComponentData& lhs, const MetaCoreSerializedComponentData& rhs) {
|
||||||
|
return lhs.TypeId < rhs.TypeId;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MetaCoreEditorContext::RestoreSerializedComponents(const MetaCoreSceneSnapshot& snapshot) {
|
||||||
|
const auto componentRegistry = ModuleRegistry_.ResolveService<MetaCoreIComponentTypeRegistry>();
|
||||||
|
const auto reflectionRegistry = ModuleRegistry_.ResolveService<MetaCoreIReflectionRegistry>();
|
||||||
|
if (componentRegistry == nullptr || reflectionRegistry == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MetaCoreTypeRegistry& typeRegistry = reflectionRegistry->GetTypeRegistry();
|
||||||
|
for (const MetaCoreGameObjectData& objectData : snapshot.GameObjects) {
|
||||||
|
if (objectData.SerializedComponents.empty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
MetaCoreGameObject gameObject = Scene_.FindGameObject(objectData.Id);
|
||||||
|
if (!gameObject) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const MetaCoreSerializedComponentData& componentData : objectData.SerializedComponents) {
|
||||||
|
if (MetaCoreIsNativeSceneComponentType(componentData.TypeId) || componentData.Payload.empty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto* descriptor = componentRegistry->FindDescriptor(componentData.TypeId);
|
||||||
|
if (descriptor == nullptr || !descriptor->PasteComponentPayload) {
|
||||||
|
AddConsoleMessage(
|
||||||
|
MetaCoreLogLevel::Warning,
|
||||||
|
"Scene",
|
||||||
|
"Skipped serialized component without registered C++ type: " + componentData.TypeId
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!descriptor->PasteComponentPayload(
|
||||||
|
gameObject,
|
||||||
|
std::span<const std::byte>(componentData.Payload.data(), componentData.Payload.size()),
|
||||||
|
typeRegistry)) {
|
||||||
|
AddConsoleMessage(
|
||||||
|
MetaCoreLogLevel::Warning,
|
||||||
|
"Scene",
|
||||||
|
"Failed to restore serialized component: " + componentData.TypeId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MetaCoreEditorContext::NormalizeSelection() {
|
void MetaCoreEditorContext::NormalizeSelection() {
|
||||||
std::vector<MetaCoreId> sanitizedSelection;
|
std::vector<MetaCoreId> sanitizedSelection;
|
||||||
sanitizedSelection.reserve(SelectedObjectIds_.size());
|
sanitizedSelection.reserve(SelectedObjectIds_.size());
|
||||||
@ -999,7 +1110,7 @@ void MetaCoreEditorContext::NotifySelectionChanged() {
|
|||||||
void MetaCoreEditorContext::RefreshSceneDirtyState() {
|
void MetaCoreEditorContext::RefreshSceneDirtyState() {
|
||||||
if (const auto scenePersistenceService = ModuleRegistry_.ResolveService<MetaCoreIScenePersistenceService>();
|
if (const auto scenePersistenceService = ModuleRegistry_.ResolveService<MetaCoreIScenePersistenceService>();
|
||||||
scenePersistenceService != nullptr) {
|
scenePersistenceService != nullptr) {
|
||||||
scenePersistenceService->UpdateDirtyState(Scene_.CaptureSnapshot());
|
scenePersistenceService->UpdateDirtyState(CaptureStateSnapshot().SceneSnapshot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -207,6 +207,8 @@ public:
|
|||||||
[[nodiscard]] bool HasDockLayoutBuilt() const;
|
[[nodiscard]] bool HasDockLayoutBuilt() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void CaptureSerializedComponents(MetaCoreSceneSnapshot& snapshot) const;
|
||||||
|
void RestoreSerializedComponents(const MetaCoreSceneSnapshot& snapshot);
|
||||||
void NormalizeSelection();
|
void NormalizeSelection();
|
||||||
void NotifySelectionChanged();
|
void NotifySelectionChanged();
|
||||||
void RefreshSceneDirtyState();
|
void RefreshSceneDirtyState();
|
||||||
|
|||||||
@ -351,6 +351,7 @@ public:
|
|||||||
[[nodiscard]] virtual bool CopyComponent(std::string_view typeId, const MetaCoreGameObject& gameObject) = 0;
|
[[nodiscard]] virtual bool CopyComponent(std::string_view typeId, const MetaCoreGameObject& gameObject) = 0;
|
||||||
[[nodiscard]] virtual bool CanPasteComponent(std::string_view typeId) const = 0;
|
[[nodiscard]] virtual bool CanPasteComponent(std::string_view typeId) const = 0;
|
||||||
[[nodiscard]] virtual bool PasteComponent(std::string_view typeId, MetaCoreGameObject& gameObject) const = 0;
|
[[nodiscard]] virtual bool PasteComponent(std::string_view typeId, MetaCoreGameObject& gameObject) const = 0;
|
||||||
|
virtual void DrawComponentGizmos(MetaCoreEditorContext& editorContext) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class MetaCorePlayModeState {
|
enum class MetaCorePlayModeState {
|
||||||
|
|||||||
@ -46,6 +46,7 @@ struct MetaCoreFieldEditorMetadata {
|
|||||||
std::string Tooltip{};
|
std::string Tooltip{};
|
||||||
std::string RawSpec{};
|
std::string RawSpec{};
|
||||||
std::string ResourceType{};
|
std::string ResourceType{};
|
||||||
|
std::string DefaultValue{};
|
||||||
std::optional<double> Min{};
|
std::optional<double> Min{};
|
||||||
std::optional<double> Max{};
|
std::optional<double> Max{};
|
||||||
std::optional<double> Step{};
|
std::optional<double> Step{};
|
||||||
|
|||||||
@ -32,6 +32,47 @@ static MetaCoreAssetGuid StringToGuid(const std::string& str) {
|
|||||||
return str.empty() ? MetaCoreAssetGuid{} : MetaCoreAssetGuid::Parse(str).value_or(MetaCoreAssetGuid{});
|
return str.empty() ? MetaCoreAssetGuid{} : MetaCoreAssetGuid::Parse(str).value_or(MetaCoreAssetGuid{});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char MetaCoreHexDigit(std::byte value) {
|
||||||
|
constexpr char digits[] = "0123456789abcdef";
|
||||||
|
return digits[static_cast<unsigned int>(value) & 0x0F];
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string BytesToHexString(const std::vector<std::byte>& bytes) {
|
||||||
|
std::string encoded;
|
||||||
|
encoded.reserve(bytes.size() * 2U);
|
||||||
|
for (std::byte byte : bytes) {
|
||||||
|
encoded.push_back(MetaCoreHexDigit(byte >> 4));
|
||||||
|
encoded.push_back(MetaCoreHexDigit(byte));
|
||||||
|
}
|
||||||
|
return encoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int HexValue(char character) {
|
||||||
|
if (character >= '0' && character <= '9') return character - '0';
|
||||||
|
if (character >= 'a' && character <= 'f') return 10 + character - 'a';
|
||||||
|
if (character >= 'A' && character <= 'F') return 10 + character - 'A';
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<std::byte> HexStringToBytes(const std::string& encoded) {
|
||||||
|
std::vector<std::byte> bytes;
|
||||||
|
if (encoded.size() % 2U != 0U) {
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes.reserve(encoded.size() / 2U);
|
||||||
|
for (std::size_t index = 0; index < encoded.size(); index += 2U) {
|
||||||
|
const int high = HexValue(encoded[index]);
|
||||||
|
const int low = HexValue(encoded[index + 1U]);
|
||||||
|
if (high < 0 || low < 0) {
|
||||||
|
bytes.clear();
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
bytes.push_back(static_cast<std::byte>((high << 4) | low));
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
// 辅助方法:将单个 MetaCoreGameObjectData 转换为 json 格式
|
// 辅助方法:将单个 MetaCoreGameObjectData 转换为 json 格式
|
||||||
static json GameObjectDataToJson(const MetaCoreGameObjectData& obj, const MetaCoreTypeRegistry& registry) {
|
static json GameObjectDataToJson(const MetaCoreGameObjectData& obj, const MetaCoreTypeRegistry& registry) {
|
||||||
json objJson = json::object();
|
json objJson = json::object();
|
||||||
@ -131,6 +172,20 @@ static json GameObjectDataToJson(const MetaCoreGameObjectData& obj, const MetaCo
|
|||||||
}
|
}
|
||||||
objJson["PrefabInstance"] = prefabJson;
|
objJson["PrefabInstance"] = prefabJson;
|
||||||
}
|
}
|
||||||
|
} else if (objField.Name == "SerializedComponents" && !obj.SerializedComponents.empty()) {
|
||||||
|
json componentsJson = json::array();
|
||||||
|
for (const MetaCoreSerializedComponentData& component : obj.SerializedComponents) {
|
||||||
|
if (component.TypeId.empty() || component.Payload.empty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
componentsJson.push_back(json{
|
||||||
|
{"TypeId", component.TypeId},
|
||||||
|
{"Payload", BytesToHexString(component.Payload)}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!componentsJson.empty()) {
|
||||||
|
objJson["Components"] = std::move(componentsJson);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return objJson;
|
return objJson;
|
||||||
@ -239,6 +294,18 @@ static MetaCoreGameObjectData JsonToGameObjectData(const json& objJson, const Me
|
|||||||
}
|
}
|
||||||
obj.PrefabInstance = prefab;
|
obj.PrefabInstance = prefab;
|
||||||
}
|
}
|
||||||
|
} else if (objField.Name == "SerializedComponents" && objJson.contains("Components") && objJson["Components"].is_array()) {
|
||||||
|
for (const auto& componentJson : objJson["Components"]) {
|
||||||
|
if (!componentJson.contains("TypeId") || !componentJson.contains("Payload")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
MetaCoreSerializedComponentData component{};
|
||||||
|
component.TypeId = componentJson["TypeId"].get<std::string>();
|
||||||
|
component.Payload = HexStringToBytes(componentJson["Payload"].get<std::string>());
|
||||||
|
if (!component.TypeId.empty() && !component.Payload.empty()) {
|
||||||
|
obj.SerializedComponents.push_back(std::move(component));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return obj;
|
return obj;
|
||||||
|
|||||||
@ -47,7 +47,7 @@ MC_STRUCT()
|
|||||||
struct MetaCoreCameraComponent {
|
struct MetaCoreCameraComponent {
|
||||||
MC_GENERATED_BODY()
|
MC_GENERATED_BODY()
|
||||||
|
|
||||||
MC_PROPERTY()
|
MC_PROPERTY(DisplayName = "Field Of View", Group = "Camera", Min = 1.0, Max = 179.0, Step = 1.0, DefaultValue = 60.0)
|
||||||
float FieldOfViewDegrees = 60.0F;
|
float FieldOfViewDegrees = 60.0F;
|
||||||
MC_PROPERTY()
|
MC_PROPERTY()
|
||||||
float NearClip = 0.1F;
|
float NearClip = 0.1F;
|
||||||
|
|||||||
@ -6,8 +6,10 @@
|
|||||||
#include "MetaCoreScene/MetaCoreComponents.h"
|
#include "MetaCoreScene/MetaCoreComponents.h"
|
||||||
|
|
||||||
#include <entt/entt.hpp>
|
#include <entt/entt.hpp>
|
||||||
|
#include <cstddef>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace MetaCore {
|
namespace MetaCore {
|
||||||
|
|
||||||
@ -49,6 +51,17 @@ struct MetaCoreNameComponent {
|
|||||||
std::string Name;
|
std::string Name;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
MC_STRUCT()
|
||||||
|
struct MetaCoreSerializedComponentData {
|
||||||
|
MC_GENERATED_BODY()
|
||||||
|
|
||||||
|
MC_PROPERTY()
|
||||||
|
std::string TypeId{};
|
||||||
|
|
||||||
|
MC_PROPERTY()
|
||||||
|
std::vector<std::byte> Payload{};
|
||||||
|
};
|
||||||
|
|
||||||
// The ECS handle
|
// The ECS handle
|
||||||
class MetaCoreGameObject {
|
class MetaCoreGameObject {
|
||||||
public:
|
public:
|
||||||
@ -129,6 +142,8 @@ struct MetaCoreGameObjectData {
|
|||||||
std::optional<MetaCorePrefabInstanceMetadata> PrefabInstance;
|
std::optional<MetaCorePrefabInstanceMetadata> PrefabInstance;
|
||||||
MC_PROPERTY()
|
MC_PROPERTY()
|
||||||
std::optional<MetaCoreModelRootTag> ModelRootTag;
|
std::optional<MetaCoreModelRootTag> ModelRootTag;
|
||||||
|
MC_PROPERTY()
|
||||||
|
std::vector<MetaCoreSerializedComponentData> SerializedComponents{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace MetaCore
|
} // namespace MetaCore
|
||||||
|
|||||||
@ -48,6 +48,90 @@
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
struct MetaCoreSmokeScriptComponent {
|
||||||
|
float Speed = 2.5F;
|
||||||
|
bool Enabled = true;
|
||||||
|
std::string Target = "Player";
|
||||||
|
MetaCore::MetaCoreMeshAlphaMode AlphaMode = MetaCore::MetaCoreMeshAlphaMode::Opaque;
|
||||||
|
};
|
||||||
|
|
||||||
|
void MetaCoreExpect(bool condition, const char* message);
|
||||||
|
|
||||||
|
const MetaCore::MetaCoreIComponentTypeRegistry::MetaCoreComponentDescriptor* MetaCoreRegisterSmokeScriptComponentForTesting(
|
||||||
|
MetaCore::MetaCoreIComponentTypeRegistry& componentRegistry,
|
||||||
|
MetaCore::MetaCoreIReflectionRegistry& reflectionRegistry,
|
||||||
|
std::string typeId = "SmokeScript"
|
||||||
|
) {
|
||||||
|
MetaCore::MetaCoreFieldEditorMetadata speedMetadata{};
|
||||||
|
speedMetadata.DisplayName = "Speed";
|
||||||
|
speedMetadata.Group = "Script";
|
||||||
|
speedMetadata.Min = 0.0;
|
||||||
|
speedMetadata.Max = 10.0;
|
||||||
|
speedMetadata.Step = 0.25;
|
||||||
|
speedMetadata.DefaultValue = "2.5";
|
||||||
|
|
||||||
|
MetaCore::MetaCoreFieldEditorMetadata targetMetadata{};
|
||||||
|
targetMetadata.DisplayName = "Target";
|
||||||
|
targetMetadata.Group = "Script";
|
||||||
|
targetMetadata.DefaultValue = "Player";
|
||||||
|
|
||||||
|
auto smokeScriptBuilder = MetaCore::MetaCoreRegisterGeneratedStruct<MetaCoreSmokeScriptComponent>(
|
||||||
|
reflectionRegistry.AccessTypeRegistry(),
|
||||||
|
"MetaCoreSmokeScriptComponent"
|
||||||
|
);
|
||||||
|
smokeScriptBuilder
|
||||||
|
.Field<&MetaCoreSmokeScriptComponent::Speed>("Speed", speedMetadata)
|
||||||
|
.Field<&MetaCoreSmokeScriptComponent::Enabled>("Enabled")
|
||||||
|
.Field<&MetaCoreSmokeScriptComponent::Target>("Target", targetMetadata)
|
||||||
|
.Field<&MetaCoreSmokeScriptComponent::AlphaMode>("AlphaMode");
|
||||||
|
|
||||||
|
const MetaCore::MetaCoreStructDescriptor* smokeScriptReflection =
|
||||||
|
reflectionRegistry.GetTypeRegistry().FindStruct<MetaCoreSmokeScriptComponent>();
|
||||||
|
MetaCoreExpect(smokeScriptReflection != nullptr, "Static C++ smoke script reflection should be registered");
|
||||||
|
|
||||||
|
MetaCore::MetaCoreIComponentTypeRegistry::MetaCoreComponentDescriptor smokeScriptDescriptor{};
|
||||||
|
smokeScriptDescriptor.TypeId = std::move(typeId);
|
||||||
|
smokeScriptDescriptor.DisplayName = "Smoke Script";
|
||||||
|
smokeScriptDescriptor.ReflectedType = smokeScriptReflection;
|
||||||
|
smokeScriptDescriptor.MutableComponent = [](MetaCore::MetaCoreGameObject& gameObject) -> void* {
|
||||||
|
return gameObject.HasComponent<MetaCoreSmokeScriptComponent>() ? &gameObject.GetComponent<MetaCoreSmokeScriptComponent>() : nullptr;
|
||||||
|
};
|
||||||
|
smokeScriptDescriptor.ConstComponent = [](const MetaCore::MetaCoreGameObject& gameObject) -> const void* {
|
||||||
|
return gameObject.HasComponent<MetaCoreSmokeScriptComponent>() ? &gameObject.GetComponent<MetaCoreSmokeScriptComponent>() : nullptr;
|
||||||
|
};
|
||||||
|
smokeScriptDescriptor.HasComponent = [](const MetaCore::MetaCoreGameObject& gameObject) {
|
||||||
|
return gameObject.HasComponent<MetaCoreSmokeScriptComponent>();
|
||||||
|
};
|
||||||
|
smokeScriptDescriptor.AddComponent = [](MetaCore::MetaCoreGameObject& gameObject) {
|
||||||
|
if (gameObject.HasComponent<MetaCoreSmokeScriptComponent>()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
gameObject.AddComponent<MetaCoreSmokeScriptComponent>();
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
smokeScriptDescriptor.RemoveComponent = [](MetaCore::MetaCoreGameObject& gameObject) {
|
||||||
|
if (!gameObject.HasComponent<MetaCoreSmokeScriptComponent>()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
gameObject.RemoveComponent<MetaCoreSmokeScriptComponent>();
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
smokeScriptDescriptor.ResetComponent = [](MetaCore::MetaCoreGameObject& gameObject) {
|
||||||
|
if (!gameObject.HasComponent<MetaCoreSmokeScriptComponent>()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
gameObject.GetComponent<MetaCoreSmokeScriptComponent>() = MetaCoreSmokeScriptComponent{};
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::string registeredTypeId = smokeScriptDescriptor.TypeId;
|
||||||
|
MetaCoreExpect(
|
||||||
|
componentRegistry.RegisterComponentDescriptor(std::move(smokeScriptDescriptor)),
|
||||||
|
"Component registry should accept a static C++ smoke script descriptor"
|
||||||
|
);
|
||||||
|
return componentRegistry.FindDescriptor(registeredTypeId);
|
||||||
|
}
|
||||||
|
|
||||||
void MetaCoreExpect(bool condition, const char* message) {
|
void MetaCoreExpect(bool condition, const char* message) {
|
||||||
if (!condition) {
|
if (!condition) {
|
||||||
std::cerr << "MetaCoreSmokeTests failed: " << message << '\n';
|
std::cerr << "MetaCoreSmokeTests failed: " << message << '\n';
|
||||||
@ -662,6 +746,121 @@ void MetaCoreTestComponentRegistryDescriptors() {
|
|||||||
"Registered component should support reflected field mutation"
|
"Registered component should support reflected field mutation"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
MetaCore::MetaCoreFieldEditorMetadata speedMetadata{};
|
||||||
|
speedMetadata.DisplayName = "Speed";
|
||||||
|
speedMetadata.Group = "Script";
|
||||||
|
speedMetadata.Min = 0.0;
|
||||||
|
speedMetadata.Max = 10.0;
|
||||||
|
speedMetadata.Step = 0.25;
|
||||||
|
speedMetadata.DefaultValue = "2.5";
|
||||||
|
|
||||||
|
MetaCore::MetaCoreFieldEditorMetadata targetMetadata{};
|
||||||
|
targetMetadata.DisplayName = "Target";
|
||||||
|
targetMetadata.Group = "Script";
|
||||||
|
targetMetadata.DefaultValue = "Player";
|
||||||
|
|
||||||
|
auto smokeScriptBuilder = MetaCore::MetaCoreRegisterGeneratedStruct<MetaCoreSmokeScriptComponent>(
|
||||||
|
reflectionRegistry->AccessTypeRegistry(),
|
||||||
|
"MetaCoreSmokeScriptComponent"
|
||||||
|
);
|
||||||
|
smokeScriptBuilder
|
||||||
|
.Field<&MetaCoreSmokeScriptComponent::Speed>("Speed", speedMetadata)
|
||||||
|
.Field<&MetaCoreSmokeScriptComponent::Enabled>("Enabled")
|
||||||
|
.Field<&MetaCoreSmokeScriptComponent::Target>("Target", targetMetadata)
|
||||||
|
.Field<&MetaCoreSmokeScriptComponent::AlphaMode>("AlphaMode");
|
||||||
|
|
||||||
|
const MetaCore::MetaCoreStructDescriptor* smokeScriptReflection =
|
||||||
|
reflectionRegistry->GetTypeRegistry().FindStruct<MetaCoreSmokeScriptComponent>();
|
||||||
|
MetaCoreExpect(smokeScriptReflection != nullptr, "Static C++ smoke script reflection should be registered");
|
||||||
|
|
||||||
|
MetaCore::MetaCoreIComponentTypeRegistry::MetaCoreComponentDescriptor smokeScriptDescriptor{};
|
||||||
|
smokeScriptDescriptor.TypeId = "SmokeScript";
|
||||||
|
smokeScriptDescriptor.DisplayName = "Smoke Script";
|
||||||
|
smokeScriptDescriptor.ReflectedType = smokeScriptReflection;
|
||||||
|
smokeScriptDescriptor.MutableComponent = [](MetaCore::MetaCoreGameObject& gameObject) -> void* {
|
||||||
|
return gameObject.HasComponent<MetaCoreSmokeScriptComponent>() ? &gameObject.GetComponent<MetaCoreSmokeScriptComponent>() : nullptr;
|
||||||
|
};
|
||||||
|
smokeScriptDescriptor.ConstComponent = [](const MetaCore::MetaCoreGameObject& gameObject) -> const void* {
|
||||||
|
return gameObject.HasComponent<MetaCoreSmokeScriptComponent>() ? &gameObject.GetComponent<MetaCoreSmokeScriptComponent>() : nullptr;
|
||||||
|
};
|
||||||
|
smokeScriptDescriptor.HasComponent = [](const MetaCore::MetaCoreGameObject& gameObject) {
|
||||||
|
return gameObject.HasComponent<MetaCoreSmokeScriptComponent>();
|
||||||
|
};
|
||||||
|
smokeScriptDescriptor.AddComponent = [](MetaCore::MetaCoreGameObject& gameObject) {
|
||||||
|
if (gameObject.HasComponent<MetaCoreSmokeScriptComponent>()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
gameObject.AddComponent<MetaCoreSmokeScriptComponent>();
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
smokeScriptDescriptor.RemoveComponent = [](MetaCore::MetaCoreGameObject& gameObject) {
|
||||||
|
if (!gameObject.HasComponent<MetaCoreSmokeScriptComponent>()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
gameObject.RemoveComponent<MetaCoreSmokeScriptComponent>();
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
smokeScriptDescriptor.ResetComponent = [](MetaCore::MetaCoreGameObject& gameObject) {
|
||||||
|
if (!gameObject.HasComponent<MetaCoreSmokeScriptComponent>()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
gameObject.GetComponent<MetaCoreSmokeScriptComponent>() = MetaCoreSmokeScriptComponent{};
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
MetaCoreExpect(
|
||||||
|
componentRegistry->RegisterComponentDescriptor(std::move(smokeScriptDescriptor)),
|
||||||
|
"Component registry should accept a static C++ smoke script descriptor"
|
||||||
|
);
|
||||||
|
const auto* registeredSmokeScriptDescriptor = componentRegistry->FindDescriptor("SmokeScript");
|
||||||
|
MetaCoreExpect(registeredSmokeScriptDescriptor != nullptr, "Static C++ smoke script should be discoverable");
|
||||||
|
MetaCoreExpect(registeredSmokeScriptDescriptor->Category == "Scripts", "Static C++ smoke script should default to Scripts category");
|
||||||
|
MetaCoreExpect(
|
||||||
|
static_cast<bool>(registeredSmokeScriptDescriptor->CopyComponentPayload),
|
||||||
|
"Static C++ smoke script should receive default reflected copy support"
|
||||||
|
);
|
||||||
|
MetaCoreExpect(
|
||||||
|
static_cast<bool>(registeredSmokeScriptDescriptor->PasteComponentPayload),
|
||||||
|
"Static C++ smoke script should receive default reflected paste support"
|
||||||
|
);
|
||||||
|
MetaCoreExpect(registeredSmokeScriptDescriptor->AddComponent(object), "Static C++ smoke script should be addable");
|
||||||
|
auto& smokeScript = object.GetComponent<MetaCoreSmokeScriptComponent>();
|
||||||
|
smokeScript.Speed = 6.75F;
|
||||||
|
smokeScript.Enabled = false;
|
||||||
|
smokeScript.Target = "Valve_A";
|
||||||
|
smokeScript.AlphaMode = MetaCore::MetaCoreMeshAlphaMode::Blend;
|
||||||
|
|
||||||
|
const auto speedFieldIterator = std::find_if(
|
||||||
|
registeredSmokeScriptDescriptor->ReflectedType->Fields.begin(),
|
||||||
|
registeredSmokeScriptDescriptor->ReflectedType->Fields.end(),
|
||||||
|
[](const MetaCore::MetaCoreFieldDescriptor& field) {
|
||||||
|
return field.Name == "Speed";
|
||||||
|
}
|
||||||
|
);
|
||||||
|
MetaCoreExpect(
|
||||||
|
speedFieldIterator != registeredSmokeScriptDescriptor->ReflectedType->Fields.end(),
|
||||||
|
"Static C++ smoke script should expose Speed field"
|
||||||
|
);
|
||||||
|
MetaCoreExpect(speedFieldIterator->Editor.DefaultValue == "2.5", "Static C++ smoke script Speed should preserve default metadata");
|
||||||
|
auto* reflectedSpeed = static_cast<float*>(speedFieldIterator->MutableValue(registeredSmokeScriptDescriptor->MutableComponent(object)));
|
||||||
|
*reflectedSpeed = 7.25F;
|
||||||
|
MetaCoreExpect(std::abs(object.GetComponent<MetaCoreSmokeScriptComponent>().Speed - 7.25F) <= 0.0001F, "Static C++ smoke script field mutation should work");
|
||||||
|
|
||||||
|
MetaCoreExpect(componentRegistry->CopyComponent("SmokeScript", object), "Static C++ smoke script should copy through default reflected payload");
|
||||||
|
MetaCore::MetaCoreGameObject scriptPasteTarget = scene.CreateGameObject("SmokeScriptPasteTarget");
|
||||||
|
MetaCoreExpect(!scriptPasteTarget.HasComponent<MetaCoreSmokeScriptComponent>(), "Paste target should not start with smoke script");
|
||||||
|
MetaCoreExpect(componentRegistry->PasteComponent("SmokeScript", scriptPasteTarget), "Static C++ smoke script should paste through default reflected payload");
|
||||||
|
MetaCoreExpect(scriptPasteTarget.HasComponent<MetaCoreSmokeScriptComponent>(), "Paste should add static C++ smoke script to target");
|
||||||
|
const auto& pastedSmokeScript = scriptPasteTarget.GetComponent<MetaCoreSmokeScriptComponent>();
|
||||||
|
MetaCoreExpect(std::abs(pastedSmokeScript.Speed - 7.25F) <= 0.0001F, "Pasted static C++ smoke script Speed should match");
|
||||||
|
MetaCoreExpect(!pastedSmokeScript.Enabled, "Pasted static C++ smoke script Enabled should match");
|
||||||
|
MetaCoreExpect(pastedSmokeScript.Target == "Valve_A", "Pasted static C++ smoke script Target should match");
|
||||||
|
MetaCoreExpect(pastedSmokeScript.AlphaMode == MetaCore::MetaCoreMeshAlphaMode::Blend, "Pasted static C++ smoke script enum should match");
|
||||||
|
|
||||||
|
MetaCoreExpect(registeredSmokeScriptDescriptor->ResetComponent(scriptPasteTarget), "Static C++ smoke script should reset");
|
||||||
|
MetaCoreExpect(std::abs(scriptPasteTarget.GetComponent<MetaCoreSmokeScriptComponent>().Speed - 2.5F) <= 0.0001F, "Reset static C++ smoke script Speed should return to default");
|
||||||
|
MetaCoreExpect(scriptPasteTarget.GetComponent<MetaCoreSmokeScriptComponent>().Target == "Player", "Reset static C++ smoke script Target should return to default");
|
||||||
|
|
||||||
coreServicesModule->Shutdown(moduleRegistry);
|
coreServicesModule->Shutdown(moduleRegistry);
|
||||||
moduleRegistry.ShutdownServices();
|
moduleRegistry.ShutdownServices();
|
||||||
}
|
}
|
||||||
@ -706,6 +905,27 @@ void MetaCoreTestReflectionFieldEditorDescriptors() {
|
|||||||
const auto* constPosition = static_cast<const glm::vec3*>(positionField->ConstValue(&transform));
|
const auto* constPosition = static_cast<const glm::vec3*>(positionField->ConstValue(&transform));
|
||||||
MetaCoreExpectVec3Near(*constPosition, glm::vec3(4.0F, 5.0F, 6.0F), "ConstValue should read the field");
|
MetaCoreExpectVec3Near(*constPosition, glm::vec3(4.0F, 5.0F, 6.0F), "ConstValue should read the field");
|
||||||
|
|
||||||
|
const MetaCore::MetaCoreStructDescriptor* cameraDescriptor =
|
||||||
|
registry.FindStruct<MetaCore::MetaCoreCameraComponent>();
|
||||||
|
MetaCoreExpect(cameraDescriptor != nullptr, "Camera reflection descriptor should exist");
|
||||||
|
const auto fovFieldIterator = std::find_if(
|
||||||
|
cameraDescriptor->Fields.begin(),
|
||||||
|
cameraDescriptor->Fields.end(),
|
||||||
|
[](const MetaCore::MetaCoreFieldDescriptor& field) {
|
||||||
|
return field.Name == "FieldOfViewDegrees";
|
||||||
|
}
|
||||||
|
);
|
||||||
|
MetaCoreExpect(
|
||||||
|
fovFieldIterator != cameraDescriptor->Fields.end(),
|
||||||
|
"Camera FOV field descriptor should exist"
|
||||||
|
);
|
||||||
|
MetaCoreExpect(fovFieldIterator->Editor.DisplayName == "Field Of View", "Camera FOV should preserve display metadata");
|
||||||
|
MetaCoreExpect(fovFieldIterator->Editor.Group == "Camera", "Camera FOV should preserve group metadata");
|
||||||
|
MetaCoreExpect(fovFieldIterator->Editor.Min.has_value() && std::abs(*fovFieldIterator->Editor.Min - 1.0) <= 0.0001, "Camera FOV should preserve min metadata");
|
||||||
|
MetaCoreExpect(fovFieldIterator->Editor.Max.has_value() && std::abs(*fovFieldIterator->Editor.Max - 179.0) <= 0.0001, "Camera FOV should preserve max metadata");
|
||||||
|
MetaCoreExpect(fovFieldIterator->Editor.Step.has_value() && std::abs(*fovFieldIterator->Editor.Step - 1.0) <= 0.0001, "Camera FOV should preserve step metadata");
|
||||||
|
MetaCoreExpect(fovFieldIterator->Editor.DefaultValue == "60.0", "Camera FOV should preserve default value metadata");
|
||||||
|
|
||||||
const MetaCore::MetaCoreEnumDescriptor* alphaModeEnum =
|
const MetaCore::MetaCoreEnumDescriptor* alphaModeEnum =
|
||||||
registry.FindEnum<MetaCore::MetaCoreMeshAlphaMode>();
|
registry.FindEnum<MetaCore::MetaCoreMeshAlphaMode>();
|
||||||
MetaCoreExpect(alphaModeEnum != nullptr, "Alpha mode enum reflection descriptor should exist");
|
MetaCoreExpect(alphaModeEnum != nullptr, "Alpha mode enum reflection descriptor should exist");
|
||||||
@ -782,6 +1002,8 @@ void MetaCoreTestPlayModeLifecycleAndSceneIsolation() {
|
|||||||
int startCount = 0;
|
int startCount = 0;
|
||||||
int updateCount = 0;
|
int updateCount = 0;
|
||||||
int destroyCount = 0;
|
int destroyCount = 0;
|
||||||
|
int drawGizmosCount = 0;
|
||||||
|
int drawSelectedGizmosCount = 0;
|
||||||
|
|
||||||
MetaCore::MetaCoreIComponentTypeRegistry::MetaCoreComponentDescriptor lifecycleDescriptor{};
|
MetaCore::MetaCoreIComponentTypeRegistry::MetaCoreComponentDescriptor lifecycleDescriptor{};
|
||||||
lifecycleDescriptor.TypeId = "LifecycleModelRootTag";
|
lifecycleDescriptor.TypeId = "LifecycleModelRootTag";
|
||||||
@ -822,6 +1044,12 @@ void MetaCoreTestPlayModeLifecycleAndSceneIsolation() {
|
|||||||
lifecycleDescriptor.Lifecycle.OnDestroy = [&](MetaCore::MetaCoreEditorContext&, MetaCore::MetaCoreGameObject&) {
|
lifecycleDescriptor.Lifecycle.OnDestroy = [&](MetaCore::MetaCoreEditorContext&, MetaCore::MetaCoreGameObject&) {
|
||||||
++destroyCount;
|
++destroyCount;
|
||||||
};
|
};
|
||||||
|
lifecycleDescriptor.Lifecycle.OnDrawGizmos = [&](MetaCore::MetaCoreEditorContext&, MetaCore::MetaCoreGameObject&) {
|
||||||
|
++drawGizmosCount;
|
||||||
|
};
|
||||||
|
lifecycleDescriptor.Lifecycle.OnDrawGizmosSelected = [&](MetaCore::MetaCoreEditorContext&, MetaCore::MetaCoreGameObject&) {
|
||||||
|
++drawSelectedGizmosCount;
|
||||||
|
};
|
||||||
|
|
||||||
MetaCoreExpect(
|
MetaCoreExpect(
|
||||||
componentRegistry->RegisterComponentDescriptor(std::move(lifecycleDescriptor)),
|
componentRegistry->RegisterComponentDescriptor(std::move(lifecycleDescriptor)),
|
||||||
@ -829,6 +1057,10 @@ void MetaCoreTestPlayModeLifecycleAndSceneIsolation() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
MetaCoreExpect(playModeService->GetState() == MetaCore::MetaCorePlayModeState::Edit, "Initial play mode state should be Edit");
|
MetaCoreExpect(playModeService->GetState() == MetaCore::MetaCorePlayModeState::Edit, "Initial play mode state should be Edit");
|
||||||
|
componentRegistry->DrawComponentGizmos(editorContext);
|
||||||
|
MetaCoreExpect(drawGizmosCount == 1, "OnDrawGizmos should run for registered static C++ components");
|
||||||
|
MetaCoreExpect(drawSelectedGizmosCount == 1, "OnDrawGizmosSelected should run for selected registered static C++ components");
|
||||||
|
|
||||||
MetaCoreExpect(playModeService->EnterPlayMode(editorContext), "EnterPlayMode should succeed");
|
MetaCoreExpect(playModeService->EnterPlayMode(editorContext), "EnterPlayMode should succeed");
|
||||||
MetaCoreExpect(playModeService->GetState() == MetaCore::MetaCorePlayModeState::Playing, "Play mode state should be Playing");
|
MetaCoreExpect(playModeService->GetState() == MetaCore::MetaCorePlayModeState::Playing, "Play mode state should be Playing");
|
||||||
MetaCoreExpect(startCount == 1, "OnStart should run once on enter play mode");
|
MetaCoreExpect(startCount == 1, "OnStart should run once on enter play mode");
|
||||||
@ -1172,6 +1404,135 @@ void MetaCoreTestJsonSceneSaveCurrentSceneUsesMcsceneJson() {
|
|||||||
std::filesystem::remove_all(tempProjectRoot);
|
std::filesystem::remove_all(tempProjectRoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MetaCoreTestStaticCppComponentScenePersistenceAndUndo() {
|
||||||
|
const std::filesystem::path tempProjectRoot =
|
||||||
|
std::filesystem::temp_directory_path() / "MetaCoreStaticCppComponentSceneProject";
|
||||||
|
std::filesystem::remove_all(tempProjectRoot);
|
||||||
|
std::filesystem::create_directories(tempProjectRoot / "Assets");
|
||||||
|
std::filesystem::create_directories(tempProjectRoot / "Scenes");
|
||||||
|
std::filesystem::create_directories(tempProjectRoot / "Library");
|
||||||
|
|
||||||
|
{
|
||||||
|
std::ofstream projectFile(tempProjectRoot / "MetaCore.project.json", std::ios::trunc);
|
||||||
|
projectFile << "{\n"
|
||||||
|
<< " \"name\": \"StaticCppComponentProject\",\n"
|
||||||
|
<< " \"version\": \"0.1.0\",\n"
|
||||||
|
<< " \"scenes\": [],\n"
|
||||||
|
<< " \"startup_scene\": \"\"\n"
|
||||||
|
<< "}\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
_putenv_s("METACORE_PROJECT_PATH", tempProjectRoot.string().c_str());
|
||||||
|
|
||||||
|
MetaCore::MetaCoreWindow window;
|
||||||
|
MetaCore::MetaCoreRenderDevice renderDevice;
|
||||||
|
MetaCore::MetaCoreEditorViewportRenderer viewportRenderer;
|
||||||
|
MetaCore::MetaCoreScene scene;
|
||||||
|
MetaCore::MetaCoreLogService logService;
|
||||||
|
MetaCore::MetaCoreEditorModuleRegistry moduleRegistry;
|
||||||
|
|
||||||
|
auto coreServicesModule = MetaCore::MetaCoreCreateBuiltinCoreServicesModule();
|
||||||
|
coreServicesModule->Startup(moduleRegistry);
|
||||||
|
|
||||||
|
const auto componentRegistry = moduleRegistry.ResolveService<MetaCore::MetaCoreIComponentTypeRegistry>();
|
||||||
|
const auto reflectionRegistry = moduleRegistry.ResolveService<MetaCore::MetaCoreIReflectionRegistry>();
|
||||||
|
const auto scenePersistenceService = moduleRegistry.ResolveService<MetaCore::MetaCoreIScenePersistenceService>();
|
||||||
|
MetaCoreExpect(componentRegistry != nullptr, "Component registry should be available for static C++ persistence");
|
||||||
|
MetaCoreExpect(reflectionRegistry != nullptr, "Reflection registry should be available for static C++ persistence");
|
||||||
|
MetaCoreExpect(scenePersistenceService != nullptr, "Scene persistence should be available for static C++ persistence");
|
||||||
|
|
||||||
|
const auto* smokeScriptDescriptor = MetaCoreRegisterSmokeScriptComponentForTesting(
|
||||||
|
*componentRegistry,
|
||||||
|
*reflectionRegistry,
|
||||||
|
"PersistentSmokeScript"
|
||||||
|
);
|
||||||
|
MetaCoreExpect(smokeScriptDescriptor != nullptr, "Persistent smoke script should be registered");
|
||||||
|
|
||||||
|
MetaCore::MetaCoreEditorContext editorContext(
|
||||||
|
window,
|
||||||
|
renderDevice,
|
||||||
|
viewportRenderer,
|
||||||
|
scene,
|
||||||
|
logService,
|
||||||
|
moduleRegistry
|
||||||
|
);
|
||||||
|
|
||||||
|
MetaCore::MetaCoreGameObject object = scene.CreateGameObject("ScriptedValve");
|
||||||
|
const MetaCore::MetaCoreId objectId = object.GetId();
|
||||||
|
MetaCoreExpect(smokeScriptDescriptor->AddComponent(object), "Static C++ component should be addable before save");
|
||||||
|
object.GetComponent<MetaCoreSmokeScriptComponent>().Speed = 4.5F;
|
||||||
|
object.GetComponent<MetaCoreSmokeScriptComponent>().Enabled = false;
|
||||||
|
object.GetComponent<MetaCoreSmokeScriptComponent>().Target = "Valve_A";
|
||||||
|
object.GetComponent<MetaCoreSmokeScriptComponent>().AlphaMode = MetaCore::MetaCoreMeshAlphaMode::Blend;
|
||||||
|
editorContext.SelectOnly(objectId);
|
||||||
|
|
||||||
|
const std::filesystem::path scenePath = std::filesystem::path("Scenes") / "StaticCppComponent.mcscene.json";
|
||||||
|
MetaCoreExpect(scenePersistenceService->SaveSceneAs(editorContext, scenePath), "Static C++ component scene should save");
|
||||||
|
MetaCoreExpect(!scenePersistenceService->IsSceneDirty(), "Static C++ component scene should be clean after save");
|
||||||
|
|
||||||
|
const auto loadedDocument = MetaCore::MetaCoreSceneSerializer::LoadSceneFromJson(
|
||||||
|
tempProjectRoot / scenePath,
|
||||||
|
reflectionRegistry->GetTypeRegistry()
|
||||||
|
);
|
||||||
|
MetaCoreExpect(loadedDocument.has_value(), "Saved static C++ component scene should parse");
|
||||||
|
const auto documentObjectIterator = std::find_if(
|
||||||
|
loadedDocument->GameObjects.begin(),
|
||||||
|
loadedDocument->GameObjects.end(),
|
||||||
|
[objectId](const MetaCore::MetaCoreGameObjectData& gameObjectData) {
|
||||||
|
return gameObjectData.Id == objectId;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
MetaCoreExpect(documentObjectIterator != loadedDocument->GameObjects.end(), "Saved scene document should include scripted object");
|
||||||
|
const bool hasSerializedSmokeScript = std::any_of(
|
||||||
|
documentObjectIterator->SerializedComponents.begin(),
|
||||||
|
documentObjectIterator->SerializedComponents.end(),
|
||||||
|
[](const MetaCore::MetaCoreSerializedComponentData& componentData) {
|
||||||
|
return componentData.TypeId == "PersistentSmokeScript" && !componentData.Payload.empty();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
MetaCoreExpect(hasSerializedSmokeScript, "Saved scene document should include serialized static C++ component payload");
|
||||||
|
|
||||||
|
scene.RestoreSnapshot(MetaCore::MetaCoreSceneSnapshot{});
|
||||||
|
editorContext.ClearSelection();
|
||||||
|
MetaCoreExpect(scene.GetGameObjects().empty(), "Scene should be empty before static C++ component reload");
|
||||||
|
|
||||||
|
MetaCoreExpect(scenePersistenceService->LoadScene(editorContext, scenePath), "Static C++ component scene should reload");
|
||||||
|
MetaCore::MetaCoreGameObject reloadedObject = scene.FindGameObject(objectId);
|
||||||
|
MetaCoreExpect(reloadedObject, "Reloaded scripted object should exist");
|
||||||
|
MetaCoreExpect(reloadedObject.HasComponent<MetaCoreSmokeScriptComponent>(), "Reloaded object should restore static C++ component");
|
||||||
|
MetaCoreExpect(std::abs(reloadedObject.GetComponent<MetaCoreSmokeScriptComponent>().Speed - 4.5F) <= 0.0001F, "Reloaded static C++ Speed should match");
|
||||||
|
MetaCoreExpect(!reloadedObject.GetComponent<MetaCoreSmokeScriptComponent>().Enabled, "Reloaded static C++ Enabled should match");
|
||||||
|
MetaCoreExpect(reloadedObject.GetComponent<MetaCoreSmokeScriptComponent>().Target == "Valve_A", "Reloaded static C++ Target should match");
|
||||||
|
MetaCoreExpect(reloadedObject.GetComponent<MetaCoreSmokeScriptComponent>().AlphaMode == MetaCore::MetaCoreMeshAlphaMode::Blend, "Reloaded static C++ enum should match");
|
||||||
|
|
||||||
|
const bool modified = editorContext.ExecuteSnapshotCommand("Modify static C++ component", [&]() {
|
||||||
|
MetaCore::MetaCoreGameObject editedObject = scene.FindGameObject(objectId);
|
||||||
|
if (!editedObject || !editedObject.HasComponent<MetaCoreSmokeScriptComponent>()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
editedObject.GetComponent<MetaCoreSmokeScriptComponent>().Speed = 9.0F;
|
||||||
|
editedObject.GetComponent<MetaCoreSmokeScriptComponent>().Target = "Valve_B";
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
MetaCoreExpect(modified, "Static C++ component field mutation should create an undoable command");
|
||||||
|
MetaCoreExpect(scenePersistenceService->IsSceneDirty(), "Static C++ component field mutation should mark scene dirty");
|
||||||
|
MetaCoreExpect(std::abs(scene.FindGameObject(objectId).GetComponent<MetaCoreSmokeScriptComponent>().Speed - 9.0F) <= 0.0001F, "Static C++ mutation should apply");
|
||||||
|
|
||||||
|
MetaCoreExpect(editorContext.UndoCommand(), "Undo static C++ component mutation should succeed");
|
||||||
|
MetaCoreExpect(scene.FindGameObject(objectId).HasComponent<MetaCoreSmokeScriptComponent>(), "Undo should keep static C++ component present");
|
||||||
|
MetaCoreExpect(std::abs(scene.FindGameObject(objectId).GetComponent<MetaCoreSmokeScriptComponent>().Speed - 4.5F) <= 0.0001F, "Undo should restore static C++ Speed");
|
||||||
|
MetaCoreExpect(scene.FindGameObject(objectId).GetComponent<MetaCoreSmokeScriptComponent>().Target == "Valve_A", "Undo should restore static C++ Target");
|
||||||
|
|
||||||
|
MetaCoreExpect(editorContext.RedoCommand(), "Redo static C++ component mutation should succeed");
|
||||||
|
MetaCoreExpect(std::abs(scene.FindGameObject(objectId).GetComponent<MetaCoreSmokeScriptComponent>().Speed - 9.0F) <= 0.0001F, "Redo should restore edited static C++ Speed");
|
||||||
|
MetaCoreExpect(scene.FindGameObject(objectId).GetComponent<MetaCoreSmokeScriptComponent>().Target == "Valve_B", "Redo should restore edited static C++ Target");
|
||||||
|
|
||||||
|
coreServicesModule->Shutdown(moduleRegistry);
|
||||||
|
moduleRegistry.ShutdownServices();
|
||||||
|
_putenv_s("METACORE_PROJECT_PATH", "");
|
||||||
|
std::filesystem::remove_all(tempProjectRoot);
|
||||||
|
}
|
||||||
|
|
||||||
void MetaCoreTestImportPipelineAndCook() {
|
void MetaCoreTestImportPipelineAndCook() {
|
||||||
const unsigned char ppmDataV1[] = {
|
const unsigned char ppmDataV1[] = {
|
||||||
0x50, 0x36, 0x0A, 0x31, 0x20, 0x31, 0x0A, 0x32,
|
0x50, 0x36, 0x0A, 0x31, 0x20, 0x31, 0x0A, 0x32,
|
||||||
@ -5169,6 +5530,8 @@ int main() {
|
|||||||
MetaCoreTestSceneRenderSyncBuildsRenderableCameraLightSnapshot();
|
MetaCoreTestSceneRenderSyncBuildsRenderableCameraLightSnapshot();
|
||||||
std::cout << "[RUN] MetaCoreTestJsonSceneSaveCurrentSceneUsesMcsceneJson..." << std::endl;
|
std::cout << "[RUN] MetaCoreTestJsonSceneSaveCurrentSceneUsesMcsceneJson..." << std::endl;
|
||||||
MetaCoreTestJsonSceneSaveCurrentSceneUsesMcsceneJson();
|
MetaCoreTestJsonSceneSaveCurrentSceneUsesMcsceneJson();
|
||||||
|
std::cout << "[RUN] MetaCoreTestStaticCppComponentScenePersistenceAndUndo..." << std::endl;
|
||||||
|
MetaCoreTestStaticCppComponentScenePersistenceAndUndo();
|
||||||
std::cout << "[RUN] MetaCoreTestImportPipelineAndCook..." << std::endl;
|
std::cout << "[RUN] MetaCoreTestImportPipelineAndCook..." << std::endl;
|
||||||
MetaCoreTestImportPipelineAndCook();
|
MetaCoreTestImportPipelineAndCook();
|
||||||
std::cout << "[RUN] MetaCoreTestProjectDescriptorAndGltfImporterSkeleton..." << std::endl;
|
std::cout << "[RUN] MetaCoreTestProjectDescriptorAndGltfImporterSkeleton..." << std::endl;
|
||||||
|
|||||||
@ -402,6 +402,8 @@ void MetaCoreEmitBoolMetadataAssignment(
|
|||||||
MetaCoreEmitStringMetadataAssignment(output, metadata, metadataVariable, "Tooltip", "Tooltip");
|
MetaCoreEmitStringMetadataAssignment(output, metadata, metadataVariable, "Tooltip", "Tooltip");
|
||||||
MetaCoreEmitStringMetadataAssignment(output, metadata, metadataVariable, "ResourceType", "ResourceType");
|
MetaCoreEmitStringMetadataAssignment(output, metadata, metadataVariable, "ResourceType", "ResourceType");
|
||||||
MetaCoreEmitStringMetadataAssignment(output, metadata, metadataVariable, "Resource", "ResourceType");
|
MetaCoreEmitStringMetadataAssignment(output, metadata, metadataVariable, "Resource", "ResourceType");
|
||||||
|
MetaCoreEmitStringMetadataAssignment(output, metadata, metadataVariable, "DefaultValue", "DefaultValue");
|
||||||
|
MetaCoreEmitStringMetadataAssignment(output, metadata, metadataVariable, "Default", "DefaultValue");
|
||||||
MetaCoreEmitOptionalDoubleMetadataAssignment(output, metadata, metadataVariable, "Min", "Min");
|
MetaCoreEmitOptionalDoubleMetadataAssignment(output, metadata, metadataVariable, "Min", "Min");
|
||||||
MetaCoreEmitOptionalDoubleMetadataAssignment(output, metadata, metadataVariable, "Max", "Max");
|
MetaCoreEmitOptionalDoubleMetadataAssignment(output, metadata, metadataVariable, "Max", "Max");
|
||||||
MetaCoreEmitOptionalDoubleMetadataAssignment(output, metadata, metadataVariable, "Step", "Step");
|
MetaCoreEmitOptionalDoubleMetadataAssignment(output, metadata, metadataVariable, "Step", "Step");
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user