diff --git a/editor/src/ComponentEditors/AudioListenerComponentEditor.h b/editor/src/ComponentEditors/AudioListenerComponentEditor.h new file mode 100644 index 00000000..f513ade5 --- /dev/null +++ b/editor/src/ComponentEditors/AudioListenerComponentEditor.h @@ -0,0 +1,100 @@ +#pragma once + +#include "IComponentEditor.h" +#include "Core/IUndoManager.h" +#include "UI/UI.h" + +#include + +namespace XCEngine { +namespace Editor { + +class AudioListenerComponentEditor : public IComponentEditor { +public: + const char* GetComponentTypeName() const override { + return "AudioListener"; + } + + const char* GetDisplayName() const override { + return "Audio Listener"; + } + + bool Render(::XCEngine::Components::Component* component, IUndoManager* undoManager) override { + auto* listener = dynamic_cast<::XCEngine::Components::AudioListenerComponent*>(component); + if (!listener) { + return false; + } + + constexpr const char* kUndoLabel = "Modify Audio Listener"; + + bool changed = false; + + float masterVolume = listener->GetMasterVolume(); + changed |= UI::ApplyPropertyChange( + UI::DrawPropertySliderFloat("Master Volume", masterVolume, 0.0f, 1.0f), + undoManager, + kUndoLabel, + [listener, masterVolume]() { + listener->SetMasterVolume(masterVolume); + }); + + bool mute = listener->IsMute(); + changed |= UI::ApplyPropertyChange( + UI::DrawPropertyBool("Mute", mute), + undoManager, + kUndoLabel, + [listener, mute]() { + listener->SetMute(mute); + }); + + float dopplerLevel = listener->GetDopplerLevel(); + changed |= UI::ApplyPropertyChange( + UI::DrawPropertyFloat("Doppler Level", dopplerLevel, 0.1f, 0.0f, 5.0f), + undoManager, + kUndoLabel, + [listener, dopplerLevel]() { + listener->SetDopplerLevel(dopplerLevel); + }); + + float speedOfSound = listener->GetSpeedOfSound(); + changed |= UI::ApplyPropertyChange( + UI::DrawPropertyFloat("Speed Of Sound", speedOfSound, 1.0f, 1.0f), + undoManager, + kUndoLabel, + [listener, speedOfSound]() { + listener->SetSpeedOfSound(speedOfSound); + }); + + float reverbLevel = listener->GetReverbLevel(); + changed |= UI::ApplyPropertyChange( + UI::DrawPropertySliderFloat("Reverb Level", reverbLevel, 0.0f, 1.0f), + undoManager, + kUndoLabel, + [listener, reverbLevel]() { + listener->SetReverbLevel(reverbLevel); + }); + + return changed; + } + + bool CanAddTo(::XCEngine::Components::GameObject* gameObject) const override { + return gameObject && !gameObject->GetComponent<::XCEngine::Components::AudioListenerComponent>(); + } + + const char* GetAddDisabledReason(::XCEngine::Components::GameObject* gameObject) const override { + if (!gameObject) { + return "Invalid"; + } + + return gameObject->GetComponent<::XCEngine::Components::AudioListenerComponent>() + ? "Already Added" + : nullptr; + } + + bool CanRemove(::XCEngine::Components::Component* component) const override { + return CanEdit(component); + } +}; + +} // namespace Editor +} // namespace XCEngine diff --git a/editor/src/ComponentEditors/AudioSourceComponentEditor.h b/editor/src/ComponentEditors/AudioSourceComponentEditor.h new file mode 100644 index 00000000..bc404b71 --- /dev/null +++ b/editor/src/ComponentEditors/AudioSourceComponentEditor.h @@ -0,0 +1,212 @@ +#pragma once + +#include "AssetReferenceEditorUtils.h" +#include "IComponentEditor.h" +#include "Core/IUndoManager.h" +#include "UI/UI.h" + +#include + +namespace XCEngine { +namespace Editor { + +class AudioSourceComponentEditor : public IComponentEditor { +public: + const char* GetComponentTypeName() const override { + return "AudioSource"; + } + + const char* GetDisplayName() const override { + return "Audio Source"; + } + + bool Render(::XCEngine::Components::Component* component, IUndoManager* undoManager) override { + auto* source = dynamic_cast<::XCEngine::Components::AudioSourceComponent*>(component); + if (!source) { + return false; + } + + constexpr const char* kUndoLabel = "Modify Audio Source"; + + bool changed = false; + const std::string clipPath = source->GetClipPath(); + + const ComponentEditorAssetUI::AssetReferenceInteraction clipInteraction = + ComponentEditorAssetUI::DrawAssetReferenceProperty( + "Clip", + clipPath, + "Drop WAV Asset", + { ".wav" }); + + changed |= UI::ApplyPropertyChange( + clipInteraction.clearRequested && !clipPath.empty(), + undoManager, + kUndoLabel, + [source]() { + source->ClearClip(); + }); + changed |= UI::ApplyPropertyChange( + !clipInteraction.assignedPath.empty() && + clipInteraction.assignedPath != clipPath, + undoManager, + kUndoLabel, + [source, assignedPath = clipInteraction.assignedPath]() { + source->SetClipPath(assignedPath); + }); + + float volume = source->GetVolume(); + changed |= UI::ApplyPropertyChange( + UI::DrawPropertySliderFloat("Volume", volume, 0.0f, 1.0f), + undoManager, + kUndoLabel, + [source, volume]() { + source->SetVolume(volume); + }); + + float pitch = source->GetPitch(); + changed |= UI::ApplyPropertyChange( + UI::DrawPropertyFloat("Pitch", pitch, 0.05f, 0.0f, 3.0f), + undoManager, + kUndoLabel, + [source, pitch]() { + source->SetPitch(pitch); + }); + + float pan = source->GetPan(); + changed |= UI::ApplyPropertyChange( + UI::DrawPropertySliderFloat("Pan", pan, -1.0f, 1.0f), + undoManager, + kUndoLabel, + [source, pan]() { + source->SetPan(pan); + }); + + bool looping = source->IsLooping(); + changed |= UI::ApplyPropertyChange( + UI::DrawPropertyBool("Looping", looping), + undoManager, + kUndoLabel, + [source, looping]() { + source->SetLooping(looping); + }); + + bool spatialize = source->IsSpatialize(); + changed |= UI::ApplyPropertyChange( + UI::DrawPropertyBool("Spatialize", spatialize), + undoManager, + kUndoLabel, + [source, spatialize]() { + source->SetSpatialize(spatialize); + }); + + if (spatialize) { + ::XCEngine::Audio::Audio3DParams params = source->Get3DParams(); + + float minDistance = params.minDistance; + changed |= UI::ApplyPropertyChange( + UI::DrawPropertyFloat("Min Distance", minDistance, 0.1f, 0.0f), + undoManager, + kUndoLabel, + [source, params, minDistance]() mutable { + params.minDistance = minDistance; + source->Set3DParams(params); + }); + + float maxDistance = params.maxDistance; + changed |= UI::ApplyPropertyChange( + UI::DrawPropertyFloat("Max Distance", maxDistance, 0.1f, minDistance), + undoManager, + kUndoLabel, + [source, params, maxDistance]() mutable { + params.maxDistance = maxDistance; + source->Set3DParams(params); + }); + + float panLevel = params.panLevel; + changed |= UI::ApplyPropertyChange( + UI::DrawPropertySliderFloat("Pan Level", panLevel, 0.0f, 1.0f), + undoManager, + kUndoLabel, + [source, params, panLevel]() mutable { + params.panLevel = panLevel; + source->Set3DParams(params); + }); + + float spread = params.spread; + changed |= UI::ApplyPropertyChange( + UI::DrawPropertySliderFloat("Spread", spread, 0.0f, 1.0f), + undoManager, + kUndoLabel, + [source, params, spread]() mutable { + params.spread = spread; + source->Set3DParams(params); + }); + + float reverbZoneMix = params.reverbZoneMix; + changed |= UI::ApplyPropertyChange( + UI::DrawPropertySliderFloat("Reverb Send", reverbZoneMix, 0.0f, 1.0f), + undoManager, + kUndoLabel, + [source, params, reverbZoneMix]() mutable { + params.reverbZoneMix = reverbZoneMix; + source->Set3DParams(params); + }); + + float dopplerLevel = params.dopplerLevel; + changed |= UI::ApplyPropertyChange( + UI::DrawPropertyFloat("Doppler Level", dopplerLevel, 0.1f, 0.0f), + undoManager, + kUndoLabel, + [source, params, dopplerLevel]() mutable { + params.dopplerLevel = dopplerLevel; + source->Set3DParams(params); + }); + + bool hrtfEnabled = source->IsHRTFEnabled(); + changed |= UI::ApplyPropertyChange( + UI::DrawPropertyBool("Use HRTF", hrtfEnabled), + undoManager, + kUndoLabel, + [source, hrtfEnabled]() { + source->SetHRTFEnabled(hrtfEnabled); + }); + + if (hrtfEnabled) { + float crossFeed = source->GetHRTFCrossFeed(); + changed |= UI::ApplyPropertyChange( + UI::DrawPropertySliderFloat("HRTF Crossfeed", crossFeed, 0.0f, 1.0f), + undoManager, + kUndoLabel, + [source, crossFeed]() { + source->SetHRTFCrossFeed(crossFeed); + }); + + int quality = static_cast(source->GetHRTFQuality()); + changed |= UI::ApplyPropertyChange( + UI::DrawPropertySliderInt("HRTF Quality", quality, 1, 3), + undoManager, + kUndoLabel, + [source, quality]() { + source->SetHRTFQuality(static_cast<::XCEngine::Audio::uint32>(quality)); + }); + } + } + + return changed; + } + + bool CanAddTo(::XCEngine::Components::GameObject* gameObject) const override { + return gameObject != nullptr; + } + + const char* GetAddDisabledReason(::XCEngine::Components::GameObject* gameObject) const override { + return gameObject == nullptr ? "Invalid" : nullptr; + } + + bool CanRemove(::XCEngine::Components::Component* component) const override { + return CanEdit(component); + } +}; + +} // namespace Editor +} // namespace XCEngine diff --git a/editor/src/ComponentEditors/BoxColliderComponentEditor.h b/editor/src/ComponentEditors/BoxColliderComponentEditor.h new file mode 100644 index 00000000..0e2b4501 --- /dev/null +++ b/editor/src/ComponentEditors/BoxColliderComponentEditor.h @@ -0,0 +1,107 @@ +#pragma once + +#include "ComponentEditors/IComponentEditor.h" +#include "Core/IUndoManager.h" +#include "UI/UI.h" + +#include + +namespace XCEngine { +namespace Editor { + +class BoxColliderComponentEditor : public IComponentEditor { +public: + const char* GetComponentTypeName() const override { + return "BoxCollider"; + } + + const char* GetDisplayName() const override { + return "Box Collider"; + } + + bool Render(::XCEngine::Components::Component* component, IUndoManager* undoManager) override { + auto* collider = dynamic_cast<::XCEngine::Components::BoxColliderComponent*>(component); + if (!collider) { + return false; + } + + constexpr const char* kUndoLabel = "Modify Box Collider"; + bool changed = false; + + bool isTrigger = collider->IsTrigger(); + changed |= UI::ApplyPropertyChange( + UI::DrawPropertyBool("Is Trigger", isTrigger), + undoManager, + kUndoLabel, + [&]() { + collider->SetTrigger(isTrigger); + }); + + ::XCEngine::Math::Vector3 center = collider->GetCenter(); + changed |= UI::ApplyPropertyChange( + UI::DrawPropertyVec3Input("Center", center, 0.1f), + undoManager, + kUndoLabel, + [&]() { + collider->SetCenter(center); + }); + + ::XCEngine::Math::Vector3 size = collider->GetSize(); + changed |= UI::ApplyPropertyChange( + UI::DrawPropertyVec3Input("Size", size, 0.1f), + undoManager, + kUndoLabel, + [&]() { + collider->SetSize(size); + }); + + float staticFriction = collider->GetStaticFriction(); + changed |= UI::ApplyPropertyChange( + UI::DrawPropertyFloat("Static Friction", staticFriction, 0.05f, 0.0f), + undoManager, + kUndoLabel, + [&]() { + collider->SetStaticFriction(staticFriction); + }); + + float dynamicFriction = collider->GetDynamicFriction(); + changed |= UI::ApplyPropertyChange( + UI::DrawPropertyFloat("Dynamic Friction", dynamicFriction, 0.05f, 0.0f), + undoManager, + kUndoLabel, + [&]() { + collider->SetDynamicFriction(dynamicFriction); + }); + + float restitution = collider->GetRestitution(); + changed |= UI::ApplyPropertyChange( + UI::DrawPropertySliderFloat("Restitution", restitution, 0.0f, 1.0f, "%.2f"), + undoManager, + kUndoLabel, + [&]() { + collider->SetRestitution(restitution); + }); + + return changed; + } + + bool CanAddTo(::XCEngine::Components::GameObject* gameObject) const override { + return gameObject && !gameObject->GetComponent<::XCEngine::Components::ColliderComponent>(); + } + + const char* GetAddDisabledReason(::XCEngine::Components::GameObject* gameObject) const override { + if (!gameObject) { + return "Invalid"; + } + return gameObject->GetComponent<::XCEngine::Components::ColliderComponent>() + ? "Only One Collider Supported" + : nullptr; + } + + bool CanRemove(::XCEngine::Components::Component* component) const override { + return CanEdit(component); + } +}; + +} // namespace Editor +} // namespace XCEngine diff --git a/editor/src/ComponentEditors/CapsuleColliderComponentEditor.h b/editor/src/ComponentEditors/CapsuleColliderComponentEditor.h new file mode 100644 index 00000000..dd796962 --- /dev/null +++ b/editor/src/ComponentEditors/CapsuleColliderComponentEditor.h @@ -0,0 +1,127 @@ +#pragma once + +#include "ComponentEditors/IComponentEditor.h" +#include "Core/IUndoManager.h" +#include "UI/UI.h" + +#include + +namespace XCEngine { +namespace Editor { + +class CapsuleColliderComponentEditor : public IComponentEditor { +public: + const char* GetComponentTypeName() const override { + return "CapsuleCollider"; + } + + const char* GetDisplayName() const override { + return "Capsule Collider"; + } + + bool Render(::XCEngine::Components::Component* component, IUndoManager* undoManager) override { + auto* collider = dynamic_cast<::XCEngine::Components::CapsuleColliderComponent*>(component); + if (!collider) { + return false; + } + + constexpr const char* kUndoLabel = "Modify Capsule Collider"; + bool changed = false; + + bool isTrigger = collider->IsTrigger(); + changed |= UI::ApplyPropertyChange( + UI::DrawPropertyBool("Is Trigger", isTrigger), + undoManager, + kUndoLabel, + [&]() { + collider->SetTrigger(isTrigger); + }); + + ::XCEngine::Math::Vector3 center = collider->GetCenter(); + changed |= UI::ApplyPropertyChange( + UI::DrawPropertyVec3Input("Center", center, 0.1f), + undoManager, + kUndoLabel, + [&]() { + collider->SetCenter(center); + }); + + float radius = collider->GetRadius(); + changed |= UI::ApplyPropertyChange( + UI::DrawPropertyFloat("Radius", radius, 0.05f, 0.0001f), + undoManager, + kUndoLabel, + [&]() { + collider->SetRadius(radius); + }); + + float height = collider->GetHeight(); + changed |= UI::ApplyPropertyChange( + UI::DrawPropertyFloat("Height", height, 0.05f, 0.0001f), + undoManager, + kUndoLabel, + [&]() { + collider->SetHeight(height); + }); + + int axis = static_cast(collider->GetAxis()); + const char* axisLabels[] = { "X", "Y", "Z" }; + const int newAxis = UI::DrawPropertyCombo("Axis", axis, axisLabels, 3); + changed |= UI::ApplyPropertyChange( + newAxis != axis, + undoManager, + kUndoLabel, + [&]() { + collider->SetAxis(static_cast<::XCEngine::Components::ColliderAxis>(newAxis)); + }); + + float staticFriction = collider->GetStaticFriction(); + changed |= UI::ApplyPropertyChange( + UI::DrawPropertyFloat("Static Friction", staticFriction, 0.05f, 0.0f), + undoManager, + kUndoLabel, + [&]() { + collider->SetStaticFriction(staticFriction); + }); + + float dynamicFriction = collider->GetDynamicFriction(); + changed |= UI::ApplyPropertyChange( + UI::DrawPropertyFloat("Dynamic Friction", dynamicFriction, 0.05f, 0.0f), + undoManager, + kUndoLabel, + [&]() { + collider->SetDynamicFriction(dynamicFriction); + }); + + float restitution = collider->GetRestitution(); + changed |= UI::ApplyPropertyChange( + UI::DrawPropertySliderFloat("Restitution", restitution, 0.0f, 1.0f, "%.2f"), + undoManager, + kUndoLabel, + [&]() { + collider->SetRestitution(restitution); + }); + + return changed; + } + + bool CanAddTo(::XCEngine::Components::GameObject* gameObject) const override { + return gameObject && !gameObject->GetComponent<::XCEngine::Components::ColliderComponent>(); + } + + const char* GetAddDisabledReason(::XCEngine::Components::GameObject* gameObject) const override { + if (!gameObject) { + return "Invalid"; + } + return gameObject->GetComponent<::XCEngine::Components::ColliderComponent>() + ? "Only One Collider Supported" + : nullptr; + } + + bool CanRemove(::XCEngine::Components::Component* component) const override { + return CanEdit(component); + } +}; + +} // namespace Editor +} // namespace XCEngine diff --git a/editor/src/ComponentEditors/ComponentEditorRegistry.cpp b/editor/src/ComponentEditors/ComponentEditorRegistry.cpp index 37a5cf48..194d275d 100644 --- a/editor/src/ComponentEditors/ComponentEditorRegistry.cpp +++ b/editor/src/ComponentEditors/ComponentEditorRegistry.cpp @@ -1,10 +1,16 @@ #include "ComponentEditors/ComponentEditorRegistry.h" +#include "ComponentEditors/AudioListenerComponentEditor.h" +#include "ComponentEditors/AudioSourceComponentEditor.h" +#include "ComponentEditors/BoxColliderComponentEditor.h" #include "ComponentEditors/CameraComponentEditor.h" +#include "ComponentEditors/CapsuleColliderComponentEditor.h" #include "ComponentEditors/LightComponentEditor.h" #include "ComponentEditors/MeshFilterComponentEditor.h" #include "ComponentEditors/MeshRendererComponentEditor.h" +#include "ComponentEditors/RigidbodyComponentEditor.h" #include "ComponentEditors/ScriptComponentEditor.h" +#include "ComponentEditors/SphereColliderComponentEditor.h" #include "ComponentEditors/TransformComponentEditor.h" #include "ComponentEditors/VolumeRendererComponentEditor.h" @@ -18,8 +24,14 @@ ComponentEditorRegistry& ComponentEditorRegistry::Get() { ComponentEditorRegistry::ComponentEditorRegistry() { RegisterEditor(std::make_unique()); + RegisterEditor(std::make_unique()); + RegisterEditor(std::make_unique()); RegisterEditor(std::make_unique()); RegisterEditor(std::make_unique()); + RegisterEditor(std::make_unique()); + RegisterEditor(std::make_unique()); + RegisterEditor(std::make_unique()); + RegisterEditor(std::make_unique()); RegisterEditor(std::make_unique()); RegisterEditor(std::make_unique()); RegisterEditor(std::make_unique()); diff --git a/editor/src/ComponentEditors/RigidbodyComponentEditor.h b/editor/src/ComponentEditors/RigidbodyComponentEditor.h new file mode 100644 index 00000000..c370300e --- /dev/null +++ b/editor/src/ComponentEditors/RigidbodyComponentEditor.h @@ -0,0 +1,107 @@ +#pragma once + +#include "ComponentEditors/IComponentEditor.h" +#include "Core/IUndoManager.h" +#include "UI/UI.h" + +#include + +namespace XCEngine { +namespace Editor { + +class RigidbodyComponentEditor : public IComponentEditor { +public: + const char* GetComponentTypeName() const override { + return "Rigidbody"; + } + + const char* GetDisplayName() const override { + return "Rigidbody"; + } + + bool Render(::XCEngine::Components::Component* component, IUndoManager* undoManager) override { + auto* rigidbody = dynamic_cast<::XCEngine::Components::RigidbodyComponent*>(component); + if (!rigidbody) { + return false; + } + + constexpr const char* kUndoLabel = "Modify Rigidbody"; + bool changed = false; + + int bodyType = static_cast(rigidbody->GetBodyType()); + const char* bodyTypeLabels[] = { "Static", "Dynamic", "Kinematic" }; + const int newBodyType = UI::DrawPropertyCombo("Body Type", bodyType, bodyTypeLabels, 3); + changed |= UI::ApplyPropertyChange( + newBodyType != bodyType, + undoManager, + kUndoLabel, + [&]() { + rigidbody->SetBodyType(static_cast<::XCEngine::Physics::PhysicsBodyType>(newBodyType)); + }); + + float mass = rigidbody->GetMass(); + changed |= UI::ApplyPropertyChange( + UI::DrawPropertyFloat("Mass", mass, 0.1f, 0.0001f), + undoManager, + kUndoLabel, + [&]() { + rigidbody->SetMass(mass); + }); + + float linearDamping = rigidbody->GetLinearDamping(); + changed |= UI::ApplyPropertyChange( + UI::DrawPropertyFloat("Linear Damping", linearDamping, 0.05f, 0.0f), + undoManager, + kUndoLabel, + [&]() { + rigidbody->SetLinearDamping(linearDamping); + }); + + float angularDamping = rigidbody->GetAngularDamping(); + changed |= UI::ApplyPropertyChange( + UI::DrawPropertyFloat("Angular Damping", angularDamping, 0.05f, 0.0f), + undoManager, + kUndoLabel, + [&]() { + rigidbody->SetAngularDamping(angularDamping); + }); + + bool useGravity = rigidbody->GetUseGravity(); + changed |= UI::ApplyPropertyChange( + UI::DrawPropertyBool("Use Gravity", useGravity), + undoManager, + kUndoLabel, + [&]() { + rigidbody->SetUseGravity(useGravity); + }); + + bool enableCCD = rigidbody->GetEnableCCD(); + changed |= UI::ApplyPropertyChange( + UI::DrawPropertyBool("Enable CCD", enableCCD), + undoManager, + kUndoLabel, + [&]() { + rigidbody->SetEnableCCD(enableCCD); + }); + + return changed; + } + + bool CanAddTo(::XCEngine::Components::GameObject* gameObject) const override { + return gameObject && !gameObject->GetComponent<::XCEngine::Components::RigidbodyComponent>(); + } + + const char* GetAddDisabledReason(::XCEngine::Components::GameObject* gameObject) const override { + if (!gameObject) { + return "Invalid"; + } + return gameObject->GetComponent<::XCEngine::Components::RigidbodyComponent>() ? "Already Added" : nullptr; + } + + bool CanRemove(::XCEngine::Components::Component* component) const override { + return CanEdit(component); + } +}; + +} // namespace Editor +} // namespace XCEngine diff --git a/editor/src/ComponentEditors/SphereColliderComponentEditor.h b/editor/src/ComponentEditors/SphereColliderComponentEditor.h new file mode 100644 index 00000000..ed10716d --- /dev/null +++ b/editor/src/ComponentEditors/SphereColliderComponentEditor.h @@ -0,0 +1,107 @@ +#pragma once + +#include "ComponentEditors/IComponentEditor.h" +#include "Core/IUndoManager.h" +#include "UI/UI.h" + +#include + +namespace XCEngine { +namespace Editor { + +class SphereColliderComponentEditor : public IComponentEditor { +public: + const char* GetComponentTypeName() const override { + return "SphereCollider"; + } + + const char* GetDisplayName() const override { + return "Sphere Collider"; + } + + bool Render(::XCEngine::Components::Component* component, IUndoManager* undoManager) override { + auto* collider = dynamic_cast<::XCEngine::Components::SphereColliderComponent*>(component); + if (!collider) { + return false; + } + + constexpr const char* kUndoLabel = "Modify Sphere Collider"; + bool changed = false; + + bool isTrigger = collider->IsTrigger(); + changed |= UI::ApplyPropertyChange( + UI::DrawPropertyBool("Is Trigger", isTrigger), + undoManager, + kUndoLabel, + [&]() { + collider->SetTrigger(isTrigger); + }); + + ::XCEngine::Math::Vector3 center = collider->GetCenter(); + changed |= UI::ApplyPropertyChange( + UI::DrawPropertyVec3Input("Center", center, 0.1f), + undoManager, + kUndoLabel, + [&]() { + collider->SetCenter(center); + }); + + float radius = collider->GetRadius(); + changed |= UI::ApplyPropertyChange( + UI::DrawPropertyFloat("Radius", radius, 0.05f, 0.0001f), + undoManager, + kUndoLabel, + [&]() { + collider->SetRadius(radius); + }); + + float staticFriction = collider->GetStaticFriction(); + changed |= UI::ApplyPropertyChange( + UI::DrawPropertyFloat("Static Friction", staticFriction, 0.05f, 0.0f), + undoManager, + kUndoLabel, + [&]() { + collider->SetStaticFriction(staticFriction); + }); + + float dynamicFriction = collider->GetDynamicFriction(); + changed |= UI::ApplyPropertyChange( + UI::DrawPropertyFloat("Dynamic Friction", dynamicFriction, 0.05f, 0.0f), + undoManager, + kUndoLabel, + [&]() { + collider->SetDynamicFriction(dynamicFriction); + }); + + float restitution = collider->GetRestitution(); + changed |= UI::ApplyPropertyChange( + UI::DrawPropertySliderFloat("Restitution", restitution, 0.0f, 1.0f, "%.2f"), + undoManager, + kUndoLabel, + [&]() { + collider->SetRestitution(restitution); + }); + + return changed; + } + + bool CanAddTo(::XCEngine::Components::GameObject* gameObject) const override { + return gameObject && !gameObject->GetComponent<::XCEngine::Components::ColliderComponent>(); + } + + const char* GetAddDisabledReason(::XCEngine::Components::GameObject* gameObject) const override { + if (!gameObject) { + return "Invalid"; + } + return gameObject->GetComponent<::XCEngine::Components::ColliderComponent>() + ? "Only One Collider Supported" + : nullptr; + } + + bool CanRemove(::XCEngine::Components::Component* component) const override { + return CanEdit(component); + } +}; + +} // namespace Editor +} // namespace XCEngine diff --git a/tests/Components/CMakeLists.txt b/tests/Components/CMakeLists.txt index 87e4a1ce..1060e723 100644 --- a/tests/Components/CMakeLists.txt +++ b/tests/Components/CMakeLists.txt @@ -8,6 +8,7 @@ set(COMPONENTS_TEST_SOURCES test_transform_component.cpp test_game_object.cpp test_camera_light_component.cpp + test_physics_components.cpp test_gaussian_splat_renderer_component.cpp test_mesh_render_components.cpp test_volume_renderer_component.cpp diff --git a/tests/Components/test_physics_components.cpp b/tests/Components/test_physics_components.cpp new file mode 100644 index 00000000..0a72a61a --- /dev/null +++ b/tests/Components/test_physics_components.cpp @@ -0,0 +1,79 @@ +#include + +#include +#include +#include +#include +#include + +#include + +using namespace XCEngine::Components; + +namespace { + +TEST(PhysicsComponents_Test, Rigidbody_SerializeRoundTripPreservesFields) { + RigidbodyComponent source; + source.SetBodyType(XCEngine::Physics::PhysicsBodyType::Kinematic); + source.SetMass(3.5f); + source.SetLinearDamping(0.2f); + source.SetAngularDamping(0.8f); + source.SetUseGravity(false); + source.SetEnableCCD(true); + + std::stringstream stream; + source.Serialize(stream); + + RigidbodyComponent target; + target.Deserialize(stream); + + EXPECT_EQ(target.GetBodyType(), XCEngine::Physics::PhysicsBodyType::Kinematic); + EXPECT_FLOAT_EQ(target.GetMass(), 3.5f); + EXPECT_FLOAT_EQ(target.GetLinearDamping(), 0.2f); + EXPECT_FLOAT_EQ(target.GetAngularDamping(), 0.8f); + EXPECT_FALSE(target.GetUseGravity()); + EXPECT_TRUE(target.GetEnableCCD()); +} + +TEST(PhysicsComponents_Test, BoxCollider_SerializeRoundTripPreservesFields) { + BoxColliderComponent source; + source.SetTrigger(true); + source.SetCenter(XCEngine::Math::Vector3(1.0f, 2.0f, 3.0f)); + source.SetStaticFriction(0.7f); + source.SetDynamicFriction(0.2f); + source.SetRestitution(0.4f); + source.SetSize(XCEngine::Math::Vector3(2.0f, 4.0f, 6.0f)); + + std::stringstream stream; + source.Serialize(stream); + + BoxColliderComponent target; + target.Deserialize(stream); + + EXPECT_TRUE(target.IsTrigger()); + EXPECT_EQ(target.GetCenter(), XCEngine::Math::Vector3(1.0f, 2.0f, 3.0f)); + EXPECT_FLOAT_EQ(target.GetStaticFriction(), 0.7f); + EXPECT_FLOAT_EQ(target.GetDynamicFriction(), 0.2f); + EXPECT_FLOAT_EQ(target.GetRestitution(), 0.4f); + EXPECT_EQ(target.GetSize(), XCEngine::Math::Vector3(2.0f, 4.0f, 6.0f)); +} + +TEST(PhysicsComponents_Test, SphereCollider_InvalidRadiusIsSanitized) { + SphereColliderComponent sphere; + sphere.SetRadius(-10.0f); + + EXPECT_GT(sphere.GetRadius(), 0.0f); +} + +TEST(PhysicsComponents_Test, CapsuleCollider_HeightStaysAtLeastDiameter) { + CapsuleColliderComponent capsule; + capsule.SetRadius(1.25f); + capsule.SetHeight(1.0f); + capsule.SetAxis(ColliderAxis::Z); + + EXPECT_FLOAT_EQ(capsule.GetRadius(), 1.25f); + EXPECT_FLOAT_EQ(capsule.GetHeight(), 2.5f); + EXPECT_EQ(capsule.GetAxis(), ColliderAxis::Z); +} + +} // namespace diff --git a/tests/editor/CMakeLists.txt b/tests/editor/CMakeLists.txt index 6a14b386..a6b02ac2 100644 --- a/tests/editor/CMakeLists.txt +++ b/tests/editor/CMakeLists.txt @@ -47,6 +47,7 @@ set(EDITOR_TEST_SOURCES test_xcui_draw_data.cpp test_xcui_imgui_transition_backend.cpp test_editor_console_sink.cpp + test_component_editor_registry.cpp test_editor_script_assembly_builder.cpp test_editor_script_assembly_builder_utils.cpp test_material_inspector_material_state_io.cpp diff --git a/tests/editor/test_component_editor_registry.cpp b/tests/editor/test_component_editor_registry.cpp new file mode 100644 index 00000000..4078ad54 --- /dev/null +++ b/tests/editor/test_component_editor_registry.cpp @@ -0,0 +1,38 @@ +#include + +#include "ComponentEditors/AudioListenerComponentEditor.h" +#include "ComponentEditors/AudioSourceComponentEditor.h" +#include "ComponentEditors/BoxColliderComponentEditor.h" +#include "ComponentEditors/CapsuleColliderComponentEditor.h" +#include "ComponentEditors/RigidbodyComponentEditor.h" +#include "ComponentEditors/SphereColliderComponentEditor.h" + +namespace { + +TEST(ComponentEditorRegistry_Test, AudioEditorsExposeExpectedTypeNames) { + XCEngine::Editor::AudioSourceComponentEditor sourceEditor; + XCEngine::Editor::AudioListenerComponentEditor listenerEditor; + + EXPECT_STREQ(sourceEditor.GetComponentTypeName(), "AudioSource"); + EXPECT_STREQ(listenerEditor.GetComponentTypeName(), "AudioListener"); + EXPECT_STREQ(sourceEditor.GetDisplayName(), "Audio Source"); + EXPECT_STREQ(listenerEditor.GetDisplayName(), "Audio Listener"); +} + +TEST(ComponentEditorRegistry_Test, PhysicsEditorsExposeExpectedTypeNames) { + XCEngine::Editor::RigidbodyComponentEditor rigidbodyEditor; + XCEngine::Editor::BoxColliderComponentEditor boxEditor; + XCEngine::Editor::SphereColliderComponentEditor sphereEditor; + XCEngine::Editor::CapsuleColliderComponentEditor capsuleEditor; + + EXPECT_STREQ(rigidbodyEditor.GetComponentTypeName(), "Rigidbody"); + EXPECT_STREQ(boxEditor.GetComponentTypeName(), "BoxCollider"); + EXPECT_STREQ(sphereEditor.GetComponentTypeName(), "SphereCollider"); + EXPECT_STREQ(capsuleEditor.GetComponentTypeName(), "CapsuleCollider"); + EXPECT_STREQ(rigidbodyEditor.GetDisplayName(), "Rigidbody"); + EXPECT_STREQ(boxEditor.GetDisplayName(), "Box Collider"); + EXPECT_STREQ(sphereEditor.GetDisplayName(), "Sphere Collider"); + EXPECT_STREQ(capsuleEditor.GetDisplayName(), "Capsule Collider"); +} + +} // namespace