From 50f1e1ca84ae8ddc78ff0ed4598348d2a58b1521 Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Wed, 13 Mar 2024 20:26:30 +0000 Subject: [PATCH 01/49] Ignore perf.data files --- .gitignore | 3 +++ {radiantcore/entity => libs/scene}/AttachmentData.cpp | 0 {radiantcore/entity => libs/scene}/AttachmentData.h | 0 3 files changed, 3 insertions(+) rename {radiantcore/entity => libs/scene}/AttachmentData.cpp (100%) rename {radiantcore/entity => libs/scene}/AttachmentData.h (100%) diff --git a/.gitignore b/.gitignore index 560aa19487..c929ac4859 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,9 @@ cmake_install.cmake *.la .deps +# Linux profiling +perf.data* + # Editor temporary files *.swp tags diff --git a/radiantcore/entity/AttachmentData.cpp b/libs/scene/AttachmentData.cpp similarity index 100% rename from radiantcore/entity/AttachmentData.cpp rename to libs/scene/AttachmentData.cpp diff --git a/radiantcore/entity/AttachmentData.h b/libs/scene/AttachmentData.h similarity index 100% rename from radiantcore/entity/AttachmentData.h rename to libs/scene/AttachmentData.h From 0108265d1e69501dd38ab2a94e09ffc8998311c2 Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Wed, 13 Mar 2024 20:26:48 +0000 Subject: [PATCH 02/49] Devirtualise EntityKeyValue Instead of a virtual interface EntityKeyValue implemented by a concrete KeyValue class, there is now only a concrete EntityKeyValue class defined in the scene library and used directly by downstream code. --- include/ientity.h | 37 +-------- libs/scene/CMakeLists.txt | 3 + .../scene/EntityKeyValue.cpp | 35 ++++----- libs/scene/EntityKeyValue.h | 76 +++++++++++++++++++ .../entity => libs/scene}/SpawnArgs.cpp | 26 +++---- .../entity => libs/scene}/SpawnArgs.h | 10 +-- libs/selection/CollectiveSpawnargs.h | 17 +++-- plugins/dm.gameconnection/MapObserver.cpp | 3 +- radiantcore/CMakeLists.txt | 5 +- radiantcore/entity/EntityModule.cpp | 4 +- radiantcore/entity/KeyObserverMap.h | 3 +- radiantcore/entity/KeyValue.h | 61 --------------- radiantcore/entity/KeyValueObserver.cpp | 1 + radiantcore/entity/NameKey.h | 4 +- radiantcore/entity/NameKeyObserver.cpp | 1 + radiantcore/entity/NamespaceManager.h | 4 +- .../entity/generic/GenericEntityNode.h | 4 +- radiantcore/entity/target/TargetKey.cpp | 1 + radiantcore/entity/target/TargetableNode.h | 3 +- test/Entity.cpp | 3 +- 20 files changed, 141 insertions(+), 160 deletions(-) rename radiantcore/entity/KeyValue.cpp => libs/scene/EntityKeyValue.cpp (62%) create mode 100644 libs/scene/EntityKeyValue.h rename {radiantcore/entity => libs/scene}/SpawnArgs.cpp (94%) rename {radiantcore/entity => libs/scene}/SpawnArgs.h (92%) delete mode 100644 radiantcore/entity/KeyValue.h diff --git a/include/ientity.h b/include/ientity.h index 2dc8e913cc..4d629d4efc 100644 --- a/include/ientity.h +++ b/include/ientity.h @@ -29,42 +29,7 @@ class KeyObserver: public sigc::trackable virtual void onKeyValueChanged(const std::string& newValue) = 0; }; -/** - * @brief Object representing a single keyvalue (spawnarg) on an entity. - * - * This class exists so that each spawnarg can have its own independent set of - * KeyObservers responding to changes in its value. For most purposes it is - * simpler to use Entity::Observer::onKeyChange, Entity::setKeyValue and - * Entity::getKeyValue to interact with key values. - */ -class EntityKeyValue: public NameObserver -{ -public: - virtual ~EntityKeyValue() {} - - /// Retrieves the actual value of this key - virtual const std::string& get() const = 0; - - /// Sets the value of this key - virtual void assign(const std::string& other) = 0; - - /// Attaches a callback to get notified about the key change. - virtual void attach(KeyObserver& observer) = 0; - - /** - * @brief Detach the given observer from this key value. - * - * @param observer - * Observer to detach. No action will be taken if this observer is not - * already attached. - * - * @param sendEmptyValue - * If true (the default), the observer will be invoked with an empty value - * before being detached. If false, no final value will be sent. - */ - virtual void detach(KeyObserver& observer, bool sendEmptyValue = true) = 0; -}; -typedef std::shared_ptr EntityKeyValuePtr; +class EntityKeyValue; /** * Interface for a map entity. The Entity is the main building block of a diff --git a/libs/scene/CMakeLists.txt b/libs/scene/CMakeLists.txt index b60db5718f..f3383549b2 100644 --- a/libs/scene/CMakeLists.txt +++ b/libs/scene/CMakeLists.txt @@ -1,6 +1,8 @@ add_library(scenegraph + AttachmentData.cpp ChildPrimitives.cpp InstanceWalkers.cpp + EntityKeyValue.cpp LayerUsageBreakdown.cpp ModelFinder.cpp Node.cpp @@ -11,6 +13,7 @@ add_library(scenegraph merge/ThreeWayMergeOperation.cpp SelectableNode.cpp SelectionIndex.cpp + SpawnArgs.cpp TraversableNodeSet.cpp Traverse.cpp) target_compile_options(scenegraph PUBLIC ${SIGC_CFLAGS}) diff --git a/radiantcore/entity/KeyValue.cpp b/libs/scene/EntityKeyValue.cpp similarity index 62% rename from radiantcore/entity/KeyValue.cpp rename to libs/scene/EntityKeyValue.cpp index dff3bf3b98..ad60081d01 100644 --- a/radiantcore/entity/KeyValue.cpp +++ b/libs/scene/EntityKeyValue.cpp @@ -1,36 +1,33 @@ -#include "KeyValue.h" +#include "EntityKeyValue.h" #include #include "iundo.h" -namespace entity -{ - -KeyValue::KeyValue(const std::string& value, const std::string& empty, +EntityKeyValue::EntityKeyValue(const std::string& value, const std::string& empty, const std::function& valueChanged) : _value(value), _emptyValue(empty), - _undo(_value, std::bind(&KeyValue::importState, this, std::placeholders::_1), - std::bind(&KeyValue::onUndoRedoOperationFinished, this), "KeyValue"), + _undo(_value, std::bind(&EntityKeyValue::importState, this, std::placeholders::_1), + std::bind(&EntityKeyValue::onUndoRedoOperationFinished, this), "KeyValue"), _valueChanged(valueChanged) {} -KeyValue::~KeyValue() +EntityKeyValue::~EntityKeyValue() { assert(_observers.empty()); } -void KeyValue::connectUndoSystem(IUndoSystem& undoSystem) +void EntityKeyValue::connectUndoSystem(IUndoSystem& undoSystem) { _undo.connectUndoSystem(undoSystem); } -void KeyValue::disconnectUndoSystem(IUndoSystem& undoSystem) +void EntityKeyValue::disconnectUndoSystem(IUndoSystem& undoSystem) { _undo.disconnectUndoSystem(undoSystem); } -void KeyValue::attach(KeyObserver& observer) +void EntityKeyValue::attach(KeyObserver& observer) { // Store the observer _observers.push_back(&observer); @@ -39,7 +36,7 @@ void KeyValue::attach(KeyObserver& observer) observer.onKeyValueChanged(get()); } -void KeyValue::detach(KeyObserver& observer, bool sendEmptyValue) +void EntityKeyValue::detach(KeyObserver& observer, bool sendEmptyValue) { // Send final empty value if requested if (sendEmptyValue) @@ -51,13 +48,13 @@ void KeyValue::detach(KeyObserver& observer, bool sendEmptyValue) _observers.erase(found); } -const std::string& KeyValue::get() const +const std::string& EntityKeyValue::get() const { // Return the string if the actual value is "" return (_value.empty()) ? _emptyValue : _value; } -void KeyValue::assign(const std::string& other) +void EntityKeyValue::assign(const std::string& other) { if (_value != other) { @@ -67,7 +64,7 @@ void KeyValue::assign(const std::string& other) } } -void KeyValue::notify() +void EntityKeyValue::notify() { // Store the name locally, to avoid string-copy operations in the loop below const std::string& value = get(); @@ -81,23 +78,21 @@ void KeyValue::notify() } } -void KeyValue::importState(const std::string& string) +void EntityKeyValue::importState(const std::string& string) { // We notify our observers after the entire undo rollback is done _value = string; } -void KeyValue::onUndoRedoOperationFinished() +void EntityKeyValue::onUndoRedoOperationFinished() { notify(); } -void KeyValue::onNameChange(const std::string& oldName, const std::string& newName) +void EntityKeyValue::onNameChange(const std::string& oldName, const std::string& newName) { assert(oldName == _value); // The old name should match // Just assign the new name to this keyvalue assign(newName); } - -} // namespace entity diff --git a/libs/scene/EntityKeyValue.h b/libs/scene/EntityKeyValue.h new file mode 100644 index 0000000000..f0758150c9 --- /dev/null +++ b/libs/scene/EntityKeyValue.h @@ -0,0 +1,76 @@ +#pragma once + +#include "ientity.h" +#include "ObservedUndoable.h" +#include "string/string.h" +#include + +class SpawnArgs; + +/** + * @brief Object representing a single keyvalue (spawnarg) on an entity. + * + * This class exists so that each spawnarg can have its own independent set of + * KeyObservers responding to changes in its value. For most purposes it is + * simpler to use Entity::Observer::onKeyChange, Entity::setKeyValue and + * Entity::getKeyValue to interact with key values. + */ +class EntityKeyValue final: public NameObserver +{ + typedef std::vector KeyObservers; + KeyObservers _observers; + + std::string _value; + std::string _emptyValue; + undo::ObservedUndoable _undo; + + // This is a specialised callback pointing to the owning SpawnArgs + std::function _valueChanged; + +public: + EntityKeyValue(const std::string& value, const std::string& empty, const std::function& valueChanged); + + EntityKeyValue(const EntityKeyValue& other) = delete; + EntityKeyValue& operator=(const EntityKeyValue& other) = delete; + + ~EntityKeyValue(); + + void connectUndoSystem(IUndoSystem& undoSystem); + void disconnectUndoSystem(IUndoSystem& undoSystem); + + /// Attaches a callback to get notified about the key change. + void attach(KeyObserver& observer); + + /** + * @brief Detach the given observer from this key value. + * + * @param observer + * Observer to detach. No action will be taken if this observer is not + * already attached. + * + * @param sendEmptyValue + * If true (the default), the observer will be invoked with an empty value + * before being detached. If false, no final value will be sent. + */ + void detach(KeyObserver& observer, bool sendEmptyValue = true); + + /// Retrieves the actual value of this key + const std::string& get() const; + + /// Sets the value of this key + void assign(const std::string& other); + + void notify(); + + void importState(const std::string& string); + + // NameObserver implementation + void onNameChange(const std::string& oldName, const std::string& newName) override; + +private: + // Gets called after a undo/redo operation is fully completed. + // This triggers a keyobserver refresh, to allow for reconnection to Namespaces and such. + void onUndoRedoOperationFinished(); +}; + +using EntityKeyValuePtr = std::shared_ptr; diff --git a/radiantcore/entity/SpawnArgs.cpp b/libs/scene/SpawnArgs.cpp similarity index 94% rename from radiantcore/entity/SpawnArgs.cpp rename to libs/scene/SpawnArgs.cpp index 3478c88ec1..2b425e28f0 100644 --- a/radiantcore/entity/SpawnArgs.cpp +++ b/libs/scene/SpawnArgs.cpp @@ -10,7 +10,7 @@ namespace entity SpawnArgs::SpawnArgs(const IEntityClassPtr& eclass) : _eclass(eclass), - _undo(_keyValues, std::bind(&SpawnArgs::importState, this, std::placeholders::_1), + _undo(_keyValues, std::bind(&SpawnArgs::importState, this, std::placeholders::_1), std::function(), "EntityKeyValues"), _observerMutex(false), _isContainer(!eclass->isFixedSize()), @@ -23,7 +23,7 @@ SpawnArgs::SpawnArgs(const IEntityClassPtr& eclass) : SpawnArgs::SpawnArgs(const SpawnArgs& other) : Entity(other), _eclass(other.getEntityClass()), - _undo(_keyValues, std::bind(&SpawnArgs::importState, this, std::placeholders::_1), + _undo(_keyValues, std::bind(&SpawnArgs::importState, this, std::placeholders::_1), std::function(), "EntityKeyValues"), _observerMutex(false), _isContainer(other._isContainer), @@ -232,7 +232,7 @@ void SpawnArgs::setIsContainer(bool isContainer) _isContainer = isContainer; } -void SpawnArgs::notifyInsert(const std::string& key, KeyValue& value) +void SpawnArgs::notifyInsert(const std::string& key, EntityKeyValue& value) { // Block the addition/removal of new Observers during this process _observerMutex = true; @@ -260,7 +260,7 @@ void SpawnArgs::notifyChange(const std::string& k, const std::string& v) _observerMutex = false; } -void SpawnArgs::notifyErase(const std::string& key, KeyValue& value) +void SpawnArgs::notifyErase(const std::string& key, EntityKeyValue& value) { // Block the addition/removal of new Observers during this process _observerMutex = true; @@ -289,26 +289,26 @@ void SpawnArgs::insert(const std::string& key, const KeyValuePtr& keyValue) void SpawnArgs::insert(const std::string& key, const std::string& value) { - // Try to lookup the key in the map + // Try to lookup the key in the map auto i = find(key); if (i != _keyValues.end()) { // Key has been found, assign the value i->second->assign(value); - // Observer notification happens through the lambda callback we passed + // Observer notification happens through the lambda callback we passed // to the KeyValue constructor } - else - { - // No key with that name found, create a new one - _undo.save(); + else + { + // No key with that name found, create a new one + _undo.save(); - // Allocate a new KeyValue object and insert it into the map + // Allocate a new KeyValue object and insert it into the map // Capture the key by value in the lambda - insert(key, std::make_shared(value, _eclass->getAttributeValue(key), + insert(key, std::make_shared(value, _eclass->getAttributeValue(key), [key, this](const std::string& value) { notifyChange(key, value); })); - } + } } void SpawnArgs::erase(const KeyValues::iterator& i) diff --git a/radiantcore/entity/SpawnArgs.h b/libs/scene/SpawnArgs.h similarity index 92% rename from radiantcore/entity/SpawnArgs.h rename to libs/scene/SpawnArgs.h index e534769e69..a2443505d4 100644 --- a/radiantcore/entity/SpawnArgs.h +++ b/libs/scene/SpawnArgs.h @@ -1,9 +1,9 @@ #pragma once -#include "AttachmentData.h" +#include "scene/AttachmentData.h" +#include "scene/EntityKeyValue.h" #include -#include "KeyValue.h" #include class IUndoSystem; @@ -24,7 +24,7 @@ class SpawnArgs: public Entity { IEntityClassPtr _eclass; - typedef std::shared_ptr KeyValuePtr; + typedef std::shared_ptr KeyValuePtr; // A key value pair using a dynamically allocated value typedef std::pair KeyValuePair; @@ -88,9 +88,9 @@ class SpawnArgs: public Entity void parseAttachments(); // Notification functions - void notifyInsert(const std::string& key, KeyValue& value); + void notifyInsert(const std::string& key, EntityKeyValue& value); void notifyChange(const std::string& k, const std::string& v); - void notifyErase(const std::string& key, KeyValue& value); + void notifyErase(const std::string& key, EntityKeyValue& value); void insert(const std::string& key, const KeyValuePtr& keyValue); void insert(const std::string& key, const std::string& value); diff --git a/libs/selection/CollectiveSpawnargs.h b/libs/selection/CollectiveSpawnargs.h index 3bc2184e5d..615f23a2cd 100644 --- a/libs/selection/CollectiveSpawnargs.h +++ b/libs/selection/CollectiveSpawnargs.h @@ -7,20 +7,21 @@ #include #include "ientity.h" #include "i18n.h" +#include "scene/EntityKeyValue.h" namespace selection { /** - * Helper class to keep the Entity Inspector key/value list view up to date + * Helper class to keep the Entity Inspector key/value list view up to date * when one or more entities are selected in the scene. - * + * * It not only knows which entity contributes which key value pair, * it also tracks the uniqueness of all the values for a given key. - * + * * The signals emitted by this class help the client code (i.e. Entity Inspector) * to update only those rows that actually changed their meaning. - * + * * Keys that are now shared but were not listed before => signal_KeyAdded * Values that are no longer shared will disappear from the list => signal_KeyRemoved * Keys changing their value (shared or not) => signal_KeyValueSetChanged @@ -149,7 +150,7 @@ class CollectiveSpawnargs _sigKeyValueSetChanged.emit(key, ""); } } - // The value was not equal for all entities up till now, + // The value was not equal for all entities up till now, // but may be this entity is completing the set // Only bother checking if the entity count is the same as the value count else if (keyValueSet.entities.size() == _keyValuesByEntity.size()) @@ -187,7 +188,7 @@ class CollectiveSpawnargs // We have more than one entity check the set checkKeyValueSetAfterChange(entity, key, value, e->second); } - // We only have one entity with this key, + // We only have one entity with this key, // fire the signal only if this is the only entity we know about else if (_keyValuesByEntity.size() == 1) { @@ -282,7 +283,7 @@ class CollectiveSpawnargs auto sharedValue = getKeySharedByAllEntities(key); keyValueSet.valueIsEqualOnAllEntities = !sharedValue.empty(); - + // Fire the signal to make that value re-appear _sigKeyAdded.emit(key, sharedValue); @@ -431,7 +432,7 @@ class CollectiveSpawnargs if (!keyValueSet.valueIsEqualOnAllEntities) { auto sharedValue = getKeySharedByAllEntities(key); - + if (!sharedValue.empty()) { keyValueSet.valueIsEqualOnAllEntities = true; diff --git a/plugins/dm.gameconnection/MapObserver.cpp b/plugins/dm.gameconnection/MapObserver.cpp index 25306642e8..9c63949453 100644 --- a/plugins/dm.gameconnection/MapObserver.cpp +++ b/plugins/dm.gameconnection/MapObserver.cpp @@ -2,6 +2,7 @@ #include "inode.h" #include "ientity.h" +#include "scene/EntityKeyValue.h" namespace gameconn { @@ -10,7 +11,7 @@ class EntityNodeCollector : public scene::NodeVisitor { public: std::vector foundEntities; - + bool pre(const scene::INodePtr& node) override { if (auto ptr = std::dynamic_pointer_cast(node)) { diff --git a/radiantcore/CMakeLists.txt b/radiantcore/CMakeLists.txt index b2ccd1432f..d177294873 100644 --- a/radiantcore/CMakeLists.txt +++ b/radiantcore/CMakeLists.txt @@ -30,19 +30,16 @@ add_library(radiantcore MODULE eclass/EClassColourManager.cpp eclass/EClassManager.cpp entity/AngleKey.cpp - entity/AttachmentData.cpp entity/curve/CurveCatmullRom.cpp entity/curve/Curve.cpp entity/curve/CurveEditInstance.cpp entity/curve/CurveNURBS.cpp - entity/SpawnArgs.cpp entity/doom3group/StaticGeometryNode.cpp entity/eclassmodel/EclassModelNode.cpp entity/EntityModule.cpp entity/EntityNode.cpp entity/EntitySettings.cpp entity/generic/GenericEntityNode.cpp - entity/KeyValue.cpp entity/KeyValueObserver.cpp entity/light/LightNode.cpp entity/light/Renderables.cpp @@ -329,4 +326,4 @@ target_link_libraries(radiantcore PUBLIC if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.16.0") target_precompile_headers(radiantcore PRIVATE "$<$:precompiled.h>") -endif() \ No newline at end of file +endif() diff --git a/radiantcore/entity/EntityModule.cpp b/radiantcore/entity/EntityModule.cpp index dd96dca656..24b914177b 100644 --- a/radiantcore/entity/EntityModule.cpp +++ b/radiantcore/entity/EntityModule.cpp @@ -12,7 +12,7 @@ #include "string/replace.h" -#include "SpawnArgs.h" +#include "scene/SpawnArgs.h" #include "light/LightNode.h" #include "doom3group/StaticGeometryNode.h" @@ -83,7 +83,7 @@ IEntityNodePtr createNodeForEntity(const IEntityClassPtr& eclass) return GenericEntityNode::Create(eclass); // Fixed size, no model path default: - throw std::invalid_argument("Entity class type " + + throw std::invalid_argument("Entity class type " + string::to_string(static_cast(eclass->getClassType())) + " is not supported"); } } diff --git a/radiantcore/entity/KeyObserverMap.h b/radiantcore/entity/KeyObserverMap.h index 2ce14e3c1a..9781ca6f83 100644 --- a/radiantcore/entity/KeyObserverMap.h +++ b/radiantcore/entity/KeyObserverMap.h @@ -27,7 +27,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include #include -#include "SpawnArgs.h" +#include "scene/SpawnArgs.h" +#include "KeyObserverDelegate.h" namespace entity { diff --git a/radiantcore/entity/KeyValue.h b/radiantcore/entity/KeyValue.h deleted file mode 100644 index 2014610149..0000000000 --- a/radiantcore/entity/KeyValue.h +++ /dev/null @@ -1,61 +0,0 @@ -#pragma once - -#include "ientity.h" -#include "ObservedUndoable.h" -#include "string/string.h" -#include - -namespace entity -{ - -class SpawnArgs; - -/// \brief -/// Represents the string value of an entity key. -/// -/// - Notifies observers when value changes - value changes to "" on destruction. -/// - Provides undo support through the map's undo system. -class KeyValue final: public EntityKeyValue -{ -private: - typedef std::vector KeyObservers; - KeyObservers _observers; - - std::string _value; - std::string _emptyValue; - undo::ObservedUndoable _undo; - - // This is a specialised callback pointing to the owning SpawnArgs - std::function _valueChanged; - -public: - KeyValue(const std::string& value, const std::string& empty, const std::function& valueChanged); - - KeyValue(const KeyValue& other) = delete; - KeyValue& operator=(const KeyValue& other) = delete; - - ~KeyValue(); - - void connectUndoSystem(IUndoSystem& undoSystem); - void disconnectUndoSystem(IUndoSystem& undoSystem); - - // EntityKeyValue implementation - void attach(KeyObserver& observer) override; - void detach(KeyObserver& observer, bool sendEmptyValue) override; - const std::string& get() const override; - void assign(const std::string& other) override; - - void notify(); - - void importState(const std::string& string); - - // NameObserver implementation - void onNameChange(const std::string& oldName, const std::string& newName) override; - -private: - // Gets called after a undo/redo operation is fully completed. - // This triggers a keyobserver refresh, to allow for reconnection to Namespaces and such. - void onUndoRedoOperationFinished(); -}; - -} // namespace entity diff --git a/radiantcore/entity/KeyValueObserver.cpp b/radiantcore/entity/KeyValueObserver.cpp index bc270342ae..10b5b82ec3 100644 --- a/radiantcore/entity/KeyValueObserver.cpp +++ b/radiantcore/entity/KeyValueObserver.cpp @@ -2,6 +2,7 @@ #include "inamespace.h" #include "ientity.h" +#include "scene/EntityKeyValue.h" namespace entity { diff --git a/radiantcore/entity/NameKey.h b/radiantcore/entity/NameKey.h index 00287ed5ed..ffc68272c0 100644 --- a/radiantcore/entity/NameKey.h +++ b/radiantcore/entity/NameKey.h @@ -2,9 +2,9 @@ #include "ieclass.h" #include "ientity.h" -#include "SpawnArgs.h" +#include "scene/SpawnArgs.h" -namespace entity +namespace entity { class NameKey : diff --git a/radiantcore/entity/NameKeyObserver.cpp b/radiantcore/entity/NameKeyObserver.cpp index 79cdf9dda3..04d8567703 100644 --- a/radiantcore/entity/NameKeyObserver.cpp +++ b/radiantcore/entity/NameKeyObserver.cpp @@ -2,6 +2,7 @@ #include "inamespace.h" #include "ientity.h" +#include "scene/EntityKeyValue.h" namespace entity { diff --git a/radiantcore/entity/NamespaceManager.h b/radiantcore/entity/NamespaceManager.h index dd89b78970..d44417a479 100644 --- a/radiantcore/entity/NamespaceManager.h +++ b/radiantcore/entity/NamespaceManager.h @@ -4,7 +4,7 @@ #include "inamespace.h" #include -#include "SpawnArgs.h" +#include "scene/SpawnArgs.h" #include "KeyValueObserver.h" #include "NameKeyObserver.h" #include "util/Noncopyable.h" @@ -86,7 +86,7 @@ class NamespaceManager : */ bool keyIsName(const std::string& key); - // Some keyvalues are not referring to names, but to entityDefs, those should not + // Some keyvalues are not referring to names, but to entityDefs, those should not // change themselves if an incidentially matching name is changed bool keyIsReferringToEntityDef(const std::string& key); diff --git a/radiantcore/entity/generic/GenericEntityNode.h b/radiantcore/entity/generic/GenericEntityNode.h index 5345a04439..1eefba689e 100644 --- a/radiantcore/entity/generic/GenericEntityNode.h +++ b/radiantcore/entity/generic/GenericEntityNode.h @@ -12,7 +12,7 @@ #include "../OriginKey.h" #include "../AngleKey.h" #include "../RotationKey.h" -#include "../SpawnArgs.h" +#include "scene/SpawnArgs.h" #include "../RenderableArrow.h" #include "../RenderableEntityBox.h" @@ -56,7 +56,7 @@ class GenericEntityNode final : public EntityNode, public Snappable // TRUE if this entity's arrow can be rotated in all directions, // FALSE if the arrow is caught in the xy plane bool _allow3Drotations; - + // TRUE if direction uses up after 3D rotation (only true for func_emitter/func_splat) // FALSE if direction uses forward after 3D rotation (default) bool _3DdirectionUsesUp; diff --git a/radiantcore/entity/target/TargetKey.cpp b/radiantcore/entity/target/TargetKey.cpp index 035669f1cb..f3b25393a9 100644 --- a/radiantcore/entity/target/TargetKey.cpp +++ b/radiantcore/entity/target/TargetKey.cpp @@ -2,6 +2,7 @@ #include "TargetManager.h" #include "TargetKeyCollection.h" +#include "scene/EntityKeyValue.h" namespace entity { diff --git a/radiantcore/entity/target/TargetableNode.h b/radiantcore/entity/target/TargetableNode.h index 52c6044330..8a0b21534d 100644 --- a/radiantcore/entity/target/TargetableNode.h +++ b/radiantcore/entity/target/TargetableNode.h @@ -2,10 +2,9 @@ #include "selectionlib.h" #include "scene/Node.h" +#include "scene/SpawnArgs.h" #include "entitylib.h" -#include "../SpawnArgs.h" - #include "TargetKeyCollection.h" #include "RenderableTargetLines.h" diff --git a/test/Entity.cpp b/test/Entity.cpp index 38427d12df..3e79ef397e 100644 --- a/test/Entity.cpp +++ b/test/Entity.cpp @@ -18,6 +18,7 @@ #include "scenelib.h" #include "algorithm/Entity.h" #include "algorithm/Scene.h" +#include "scene/EntityKeyValue.h" namespace test { @@ -1838,4 +1839,4 @@ TEST_F(EntityTest, MovingSpeakerNotRemovingDistanceArgs) EXPECT_NE(speaker->getEntity().getKeyValue("s_maxdistance"), "") << "Key value has been lost in translation"; } -} \ No newline at end of file +} From ebbea3176e218cb33d0727398370fc5eb89da87f Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Tue, 19 Mar 2024 19:42:07 +0000 Subject: [PATCH 03/49] Rename scenegraph library to scene --- CMakeLists.txt | 2 +- libs/scene/CMakeLists.txt | 6 +++--- libs/wxutil/CMakeLists.txt | 2 +- radiant/CMakeLists.txt | 2 +- radiantcore/CMakeLists.txt | 2 +- test/CMakeLists.txt | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 30a1d7153a..c2b31a0b3f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -135,7 +135,7 @@ else() endif() # Install main targets -install(TARGETS darkradiant math xmlutil scenegraph wxutil +install(TARGETS darkradiant math xmlutil scene wxutil RUNTIME DESTINATION bin LIBRARY DESTINATION ${PKGLIBDIR}) install(TARGETS radiantcore script sound diff --git a/libs/scene/CMakeLists.txt b/libs/scene/CMakeLists.txt index f3383549b2..af6d2e6087 100644 --- a/libs/scene/CMakeLists.txt +++ b/libs/scene/CMakeLists.txt @@ -1,4 +1,4 @@ -add_library(scenegraph +add_library(scene AttachmentData.cpp ChildPrimitives.cpp InstanceWalkers.cpp @@ -16,5 +16,5 @@ add_library(scenegraph SpawnArgs.cpp TraversableNodeSet.cpp Traverse.cpp) -target_compile_options(scenegraph PUBLIC ${SIGC_CFLAGS}) -target_link_libraries(scenegraph PUBLIC ${SIGC_LIBRARIES} math) +target_compile_options(scene PUBLIC ${SIGC_CFLAGS}) +target_link_libraries(scene PUBLIC ${SIGC_LIBRARIES} math) diff --git a/libs/wxutil/CMakeLists.txt b/libs/wxutil/CMakeLists.txt index 203fb00659..27985a8573 100644 --- a/libs/wxutil/CMakeLists.txt +++ b/libs/wxutil/CMakeLists.txt @@ -43,7 +43,7 @@ add_library(wxutil target_compile_options(wxutil PUBLIC ${FTGL_CFLAGS}) target_link_libraries(wxutil PUBLIC math - scenegraph + scene xmlutil ${wxWidgets_LIBRARIES} ${SIGC_LIBRARIES} diff --git a/radiant/CMakeLists.txt b/radiant/CMakeLists.txt index bbf981c2d6..2b63eda7c7 100644 --- a/radiant/CMakeLists.txt +++ b/radiant/CMakeLists.txt @@ -176,7 +176,7 @@ add_executable(darkradiant target_compile_options(darkradiant PRIVATE ${FREETYPE_CFLAGS} ${GLIB_CFLAGS}) target_include_directories(darkradiant PRIVATE .) target_link_libraries(darkradiant PRIVATE - math xmlutil scenegraph wxutil module + math xmlutil scene wxutil module ${GLIB_LIBRARIES}) # Enable precompiled header for radiant diff --git a/radiantcore/CMakeLists.txt b/radiantcore/CMakeLists.txt index d177294873..e82cc4c11b 100644 --- a/radiantcore/CMakeLists.txt +++ b/radiantcore/CMakeLists.txt @@ -319,7 +319,7 @@ add_library(radiantcore MODULE xmlregistry/XMLRegistry.cpp) target_include_directories(radiantcore PRIVATE .) target_link_libraries(radiantcore PUBLIC - math xmlutil scenegraph wxutil module + math xmlutil scene wxutil module ${JPEG_LIBRARIES} ${PNG_LIBRARIES} ${ZLIB_LIBRARIES}) # Enable precompiled header for radiantcore diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 599ae5ad37..f6f97133fd 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -70,10 +70,10 @@ get_filename_component(TEST_BASE_PATH "./" ABSOLUTE) add_compile_definitions(TEST_BASE_PATH="${TEST_BASE_PATH}") target_link_libraries(drtest PUBLIC - math xmlutil scenegraph module + math xmlutil scene module ${GTEST_LIBRARIES} ${GTEST_MAIN_LIBRARIES} ${SIGC_LIBRARIES} ${GLEW_LIBRARIES} ${X11_LIBRARIES} PRIVATE Threads::Threads) install(TARGETS drtest) -gtest_discover_tests(drtest) \ No newline at end of file +gtest_discover_tests(drtest) From 36ebf11df56b596ba4ad9a08e71370d023cd6a1f Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Tue, 19 Mar 2024 20:26:15 +0000 Subject: [PATCH 04/49] Devirtualise Entity class Entity is now defined in scene/Entity.h, and includes the concrete functionality of the old SpawnArgs class as well as the interface of Entity. Since most downstream users of the ientity.h header also need the full definition of Entity, most ientity.h includes have been replaced with scene/Entity.h. --- include/ientity.h | 222 +--------------- libs/entitylib.h | 11 +- libs/maplib.h | 2 +- libs/scene/AttachmentData.cpp | 3 +- libs/scene/AttachmentData.h | 6 +- libs/scene/CMakeLists.txt | 2 +- libs/scene/ChildPrimitives.cpp | 1 + libs/scene/{SpawnArgs.cpp => Entity.cpp} | 73 +++--- libs/scene/Entity.h | 247 ++++++++++++++++++ libs/scene/EntityAttachment.h | 20 ++ libs/scene/EntityBreakdown.h | 3 +- libs/scene/ModelFinder.cpp | 1 + libs/scene/SpawnArgs.h | 105 -------- libs/scene/merge/MergeAction.h | 21 +- libs/scene/merge/NodeUtils.h | 3 +- libs/selection/CollectiveSpawnargs.h | 2 +- libs/selection/EntitiesFirstSelector.h | 4 +- libs/selection/EntitySelection.h | 18 +- libs/selectionlib.h | 5 +- libs/wxutil/preview/EntityPreview.cpp | 1 + libs/wxutil/preview/EntityPreview.h | 4 +- libs/wxutil/preview/ParticlePreview.cpp | 18 +- plugins/dm.conversation/ActorNodeFinder.h | 2 +- plugins/dm.conversation/ConversationDialog.h | 4 +- .../dm.conversation/ConversationEntity.cpp | 18 +- plugins/dm.conversation/ConversationEntity.h | 2 +- .../ConversationEntityFinder.h | 1 + .../ConversationKeyExtractor.h | 4 +- plugins/dm.difficulty/DifficultyDialog.h | 2 +- plugins/dm.difficulty/DifficultyEntity.cpp | 2 +- plugins/dm.difficulty/DifficultyEntity.h | 2 +- .../dm.difficulty/DifficultyEntityFinder.h | 2 +- plugins/dm.editing/AIEditingPanel.h | 6 +- plugins/dm.editing/AIHeadPropertyEditor.cpp | 2 +- .../dm.editing/AIVocalSetPropertyEditor.cpp | 2 +- plugins/dm.editing/SpawnargLinkedCheckbox.h | 12 +- plugins/dm.editing/SpawnargLinkedSpinButton.h | 18 +- .../dm.gameconnection/DiffDoom3MapWriter.cpp | 2 +- plugins/dm.gameconnection/GameConnection.cpp | 4 +- plugins/dm.gameconnection/MapObserver.cpp | 2 +- plugins/dm.gameconnection/MapObserver.h | 2 +- plugins/dm.gui/ReadableEditorDialog.cpp | 14 +- plugins/dm.gui/plugin.cpp | 2 +- plugins/dm.objectives/ObjectiveEntity.cpp | 19 +- .../dm.objectives/ObjectiveEntityFinder.cpp | 6 +- plugins/dm.objectives/ObjectiveKeyExtractor.h | 2 +- plugins/dm.objectives/ObjectivesEditor.cpp | 32 +-- plugins/dm.objectives/TargetList.h | 2 +- .../ce/specpanel/EntityNameSpecifierPanel.cpp | 3 +- plugins/dm.objectives/objectives.cpp | 2 +- plugins/dm.stimresponse/SRPropertyLoader.h | 2 +- plugins/dm.stimresponse/SRPropertyRemover.h | 2 +- plugins/dm.stimresponse/StimResponseEditor.h | 2 +- plugins/dm.stimresponse/StimTypes.h | 2 +- plugins/script/CMakeLists.txt | 4 +- plugins/script/interfaces/EntityInterface.cpp | 6 +- plugins/script/interfaces/EntityInterface.h | 5 +- radiant/ui/UserInterfaceModule.cpp | 2 +- .../ui/animationpreview/AnimationPreview.cpp | 4 +- .../ui/animationpreview/AnimationPreview.h | 2 +- radiant/ui/common/EntityChooser.cpp | 2 +- radiant/ui/eclasstree/EClassTree.cpp | 2 +- radiant/ui/einspector/AddPropertyDialog.cpp | 2 +- radiant/ui/einspector/AddPropertyDialog.h | 2 +- radiant/ui/einspector/AnglePropertyEditor.cpp | 4 +- .../ui/einspector/BooleanPropertyEditor.cpp | 2 +- .../ui/einspector/ClassnamePropertyEditor.cpp | 2 +- .../ui/einspector/ColourPropertyEditor.cpp | 9 +- radiant/ui/einspector/EntityInspector.cpp | 4 +- .../ui/einspector/EntityPropertyEditor.cpp | 2 +- radiant/ui/einspector/FloatPropertyEditor.cpp | 4 +- radiant/ui/einspector/FxPropertyEditor.cpp | 2 +- radiant/ui/einspector/ModelPropertyEditor.cpp | 2 +- radiant/ui/einspector/PropertyEditor.cpp | 2 +- radiant/ui/einspector/SkinPropertyEditor.cpp | 2 +- radiant/ui/einspector/SoundPropertyEditor.cpp | 2 +- .../ui/einspector/TexturePropertyEditor.cpp | 2 +- .../ui/einspector/Vector3PropertyEditor.cpp | 4 +- radiant/ui/lightinspector/LightInspector.cpp | 2 +- .../ui/materials/editor/MaterialPreview.cpp | 10 +- radiant/ui/ortho/OrthoContextMenu.cpp | 2 +- radiant/xyview/OrthoView.cpp | 2 +- radiantcore/brush/csg/CSG.cpp | 4 +- radiantcore/brush/export/CollisionModel.cpp | 2 +- radiantcore/entity/AngleKey.cpp | 3 +- radiantcore/entity/ColourKey.h | 2 +- radiantcore/entity/EntityModule.cpp | 2 +- radiantcore/entity/EntityModule.h | 2 +- radiantcore/entity/EntityNode.cpp | 2 +- radiantcore/entity/EntityNode.h | 4 +- radiantcore/entity/EntitySettings.h | 2 +- radiantcore/entity/KeyObserverDelegate.h | 4 +- radiantcore/entity/KeyObserverMap.h | 8 +- radiantcore/entity/KeyValueObserver.cpp | 2 +- radiantcore/entity/KeyValueObserver.h | 2 +- radiantcore/entity/NameKey.h | 8 +- radiantcore/entity/NameKeyObserver.cpp | 2 +- radiantcore/entity/NameKeyObserver.h | 2 +- radiantcore/entity/NamespaceManager.cpp | 38 +-- radiantcore/entity/NamespaceManager.h | 8 +- radiantcore/entity/OriginKey.h | 2 +- radiantcore/entity/RotationKey.h | 2 +- radiantcore/entity/RotationMatrix.cpp | 2 +- radiantcore/entity/curve/Curve.h | 4 +- .../entity/eclassmodel/EclassModelNode.h | 4 +- .../entity/generic/GenericEntityNode.h | 2 +- .../entity/speaker/SpeakerRenderables.cpp | 2 +- .../entity/target/RenderableTargetLines.h | 4 +- radiantcore/entity/target/Target.h | 4 +- radiantcore/entity/target/TargetKey.h | 2 +- radiantcore/entity/target/TargetManager.h | 2 +- radiantcore/entity/target/TargetableNode.cpp | 6 +- radiantcore/entity/target/TargetableNode.h | 6 +- radiantcore/filters/InstanceUpdateWalker.h | 4 +- .../SetObjectSelectionByFilterWalker.h | 4 +- radiantcore/filters/XMLFilter.cpp | 4 +- radiantcore/layers/AddToLayerWalker.h | 2 +- radiantcore/layers/LayerInfoFileModule.cpp | 8 +- radiantcore/map/MapPosition.cpp | 10 +- radiantcore/map/MapPositionManager.cpp | 4 +- radiantcore/map/MapResource.cpp | 18 +- radiantcore/map/NodeCounter.h | 4 +- radiantcore/map/RegionManager.cpp | 2 +- radiantcore/map/RegionManager.h | 6 +- radiantcore/map/RootNode.h | 8 +- radiantcore/map/algorithm/Export.cpp | 4 +- radiantcore/map/algorithm/Import.cpp | 10 +- radiantcore/map/algorithm/MapExporter.cpp | 8 +- radiantcore/map/algorithm/MapImporter.cpp | 2 +- radiantcore/map/algorithm/Models.cpp | 2 +- radiantcore/map/format/Doom3MapReader.cpp | 10 +- radiantcore/map/format/Doom3MapWriter.cpp | 2 +- radiantcore/map/format/Quake3MapReader.cpp | 10 +- .../map/format/portable/PortableMapReader.cpp | 14 +- .../map/format/portable/PortableMapWriter.cpp | 2 +- .../model/export/ModelScalePreserver.cpp | 2 +- .../model/export/ScaledModelExporter.cpp | 10 +- radiantcore/patch/PatchNode.cpp | 6 +- .../selection/SceneManipulationPivot.cpp | 2 +- radiantcore/selection/SceneWalkers.h | 2 +- radiantcore/selection/algorithm/Curves.cpp | 2 +- radiantcore/selection/algorithm/General.h | 6 +- radiantcore/selection/algorithm/Group.cpp | 2 +- .../selection/algorithm/Primitives.cpp | 2 +- .../group/SelectionGroupInfoFileModule.cpp | 8 +- .../manipulators/ManipulatorComponents.cpp | 2 +- .../ClosestTexturableFinder.cpp | 4 +- test/Curves.cpp | 2 +- test/Entity.cpp | 10 +- test/ModelExport.cpp | 2 +- test/Renderer.cpp | 6 +- test/Selection.cpp | 6 +- test/Transformation.cpp | 2 +- test/UndoRedo.cpp | 10 +- test/WorldspawnColour.cpp | 2 +- test/algorithm/Entity.h | 2 +- test/algorithm/Scene.h | 4 +- 157 files changed, 677 insertions(+), 735 deletions(-) rename libs/scene/{SpawnArgs.cpp => Entity.cpp} (77%) create mode 100644 libs/scene/Entity.h create mode 100644 libs/scene/EntityAttachment.h delete mode 100644 libs/scene/SpawnArgs.h diff --git a/include/ientity.h b/include/ientity.h index 4d629d4efc..090cbfccbc 100644 --- a/include/ientity.h +++ b/include/ientity.h @@ -30,227 +30,7 @@ class KeyObserver: public sigc::trackable }; class EntityKeyValue; - -/** - * Interface for a map entity. The Entity is the main building block of a - * map, and the uppermost layer in the scenegraph under the root node. Each - * entity contains a arbitrary dictionary of strings ("properties" or - * "spawnargs") containing information about this entity which is used by the - * game engine to modify its behaviour, and may additionally contain child - * primitives (brushes and patches) depending on its type. - * - * At the minimum, each Entity must contain three properties: "name" which - * contains a map-unique string identifier, "classname" which identifies the - * entity class to the game, and "origin" which stores the location of the - * entity in 3-dimensional world space. - * - * A valid Id Tech 4 map must contain at least one entity: the - * "worldspawn" which is the parent of all map geometry primitives. - * - * greebo: Note that keys are treated case-insensitively in Doom 3, so - * the Entity class will return the same result for "MYKeY" as for "mykey". - */ -class Entity -{ -public: - // A container with key => value pairs - typedef std::vector< std::pair > KeyValuePairs; - - /** - * \brief - * Abstract base class for entity observers. - * - * An entity observer receives notifications when keyvalues are inserted or - * deleted on the entity it is observing. - */ - class Observer - { - public: - virtual ~Observer() {} - - /** - * \brief - * Notification that a new key value has been inserted on the entity. - */ - virtual void onKeyInsert(const std::string& key, EntityKeyValue& value) - { } - - /** - * \brief - * Notification that a key value has changed on the entity. - */ - virtual void onKeyChange(const std::string& key, const std::string& val) - { } - - /** - * \brief - * Notification that a key value has been removed from the entity. - */ - virtual void onKeyErase(const std::string& key, EntityKeyValue& value) - { } - }; - - // Function typedef to visit actual EntityKeyValue objects, not just the string values - typedef std::function EntityKeyValueVisitFunctor; - - virtual ~Entity() {} - - /** - * Return the entity class object for this entity. - */ - virtual IEntityClassPtr getEntityClass() const = 0; - - /// Functor to receive keys and values as strings - using KeyValueVisitFunc = std::function< - void(const std::string&, const std::string&) - >; - - /** - * \brief Enumerate all keys and values on this entity, optionally including - * inherited spawnargs. - * - * \param func - * Functor to receive each key and its associated value. - * - * \param includeInherited - * true if the functor should be invoked for each inherited spawnarg (from - * the entity class), false if only explicit spawnargs on this particular - * entity should be visited. - */ - virtual void forEachKeyValue(KeyValueVisitFunc func, - bool includeInherited = false) const = 0; - - // Similar to above, visiting the EntityKeyValue objects itself, not just the string value. - virtual void forEachEntityKeyValue(const EntityKeyValueVisitFunctor& visitor) = 0; - - /** Set a key value on this entity. Setting the value to "" will - * remove the key. - * - * @param key - * The key to set. - * - * @param value - * Value to give the key, or the empty string to remove the key. - */ - virtual void setKeyValue(const std::string& key, - const std::string& value) = 0; - - /* Retrieve a key value from the entity. - * - * @param key - * The key to retrieve. - * - * @returns - * The current value for this key, or the empty string if it does not - * exist. - */ - virtual std::string getKeyValue(const std::string& key) const = 0; - - /** - * greebo: Checks whether the given key is inherited or not. - * - * @returns: TRUE if the value is inherited, - * FALSE when it is not or when the key doesn't exist at all. - */ - virtual bool isInherited(const std::string& key) const = 0; - - /** - * \brief Return the list of keyvalues matching the given prefix. - * - * This method performs a search for all spawnargs whose key matches the - * given prefix, with a suffix consisting of zero or more arbitrary - * characters. For example, if "target" were specified as the prefix, the - * list would include "target", "target0", "target127" etc. - * - * This operation may not have high performance, due to the need to scan - * for matching names, therefore should not be used in performance-critical - * code. - * - * @param prefix - * The prefix to search for, interpreted case-insensitively. - * - * @return - * A list of KeyValue pairs matching the provided prefix. This list will be - * empty if there were no matches. - */ - KeyValuePairs getKeyValuePairs(const std::string& prefix) const - { - KeyValuePairs list; - - forEachKeyValue([&](const std::string& k, const std::string& v) { - if (string::istarts_with(k, prefix)) - list.push_back(std::make_pair(k, v)); - }); - - return list; - } - - /** greebo: Returns true if the entity is a model. For Doom3, this is - * usually true when the classname == "func_static" and - * the non-empty spawnarg "model" != "name". - */ - virtual bool isModel() const = 0; - - /** - * Returns true if this entity is the worldspawn, which can be game-specific, - * but is usually true if this entity's classname equals "worldspawn" - */ - virtual bool isWorldspawn() const = 0; - - virtual bool isContainer() const = 0; - - /** - * \brief - * Attach an Entity::Observer to this Entity. - */ - virtual void attachObserver(Observer* observer) = 0; - - /** - * \brief - * Detach an Entity::Observer from this Entity. - */ - virtual void detachObserver(Observer* observer) = 0; - - /** - * Returns true if this entity is of type or inherits from the - * given entity class name. className is treated case-sensitively. - */ - virtual bool isOfType(const std::string& className) = 0; - - /* ENTITY ATTACHMENTS */ - - /// Details of an attached entity - struct Attachment - { - /// Entity class of the attached entity - std::string eclass; - - // Optional attachment name (arbitrarily chosen by the user) - std::string name; - - /// Vector offset where the attached entity should appear - Vector3 offset; - - /// Optional model joint to use as origin - std::string joint; - }; - - /// A functor which can receive Attachment objects - using AttachmentFunc = std::function; - - /** - * \brief Iterate over attached entities, if any. - * - * Each entity can define one or more attached entities, which should - * appear at specific offsets relative to the parent entity. Such attached - * entities are for visualisation only, and should not be saved into the - * map as genuine map entities. - * - * \param func - * Functor to receive attachment information. - */ - virtual void forEachAttachment(AttachmentFunc func) const = 0; -}; +class Entity; /// Callback for an entity key value change using KeyObserverFunc = sigc::slot; diff --git a/libs/entitylib.h b/libs/entitylib.h index abe74fc01a..1e14cb46cc 100644 --- a/libs/entitylib.h +++ b/libs/entitylib.h @@ -1,12 +1,13 @@ #pragma once -#include "ientity.h" +#include "scene/Entity.h" #include "ieclass.h" #include "iselection.h" #include "iselectiontest.h" #include "math/AABB.h" #include "scenelib.h" +#include "scene/Entity.h" #include "imd5anim.h" #include "imd5model.h" @@ -107,7 +108,7 @@ inline Entity* Scene_FindEntityByClass(const std::string& className) /* Check if a node is the worldspawn. */ -inline bool Node_isWorldspawn(const scene::INodePtr& node) +inline bool Node_isWorldspawn(const scene::INodePtr& node) { Entity* entity = Node_getEntity(node); @@ -151,7 +152,7 @@ inline scene::INodePtr changeEntityClassname(const scene::INodePtr& node, // Copy all keyvalues except classname oldEntity->forEachKeyValue([&](const std::string& key, const std::string& value) { - if (key != "classname") + if (key != "classname") { newEntity.setKeyValue(key, value); } @@ -166,8 +167,8 @@ inline scene::INodePtr changeEntityClassname(const scene::INodePtr& node, // Traverse the child and reparent all primitives to the new entity node scene::parentPrimitives(oldNode, newNode); - // Remove the old entity node from the parent. This will disconnect - // oldNode from the scene and the UndoSystem, so it's important to do + // Remove the old entity node from the parent. This will disconnect + // oldNode from the scene and the UndoSystem, so it's important to do // this step last, after the primitives have been moved. (#4718) scene::removeNodeFromParent(oldNode); diff --git a/libs/maplib.h b/libs/maplib.h index f7cea63006..c082e00b5f 100644 --- a/libs/maplib.h +++ b/libs/maplib.h @@ -1,7 +1,7 @@ #pragma once #include "imap.h" -#include "ientity.h" +#include "scene/Entity.h" namespace map { diff --git a/libs/scene/AttachmentData.cpp b/libs/scene/AttachmentData.cpp index 42bf7d63e5..39d3f42d0c 100644 --- a/libs/scene/AttachmentData.cpp +++ b/libs/scene/AttachmentData.cpp @@ -1,4 +1,5 @@ #include "AttachmentData.h" +#include "itextstream.h" #include #include @@ -102,4 +103,4 @@ void AttachmentData::validateAttachments() } } -} \ No newline at end of file +} diff --git a/libs/scene/AttachmentData.h b/libs/scene/AttachmentData.h index f4f0551bb2..fc57822c70 100644 --- a/libs/scene/AttachmentData.h +++ b/libs/scene/AttachmentData.h @@ -4,7 +4,7 @@ #include #include "math/Vector3.h" -#include +#include "scene/EntityAttachment.h" namespace entity { @@ -111,7 +111,7 @@ class AttachmentData const AttachPos& pos = _positions.at(i->second.posName); // Construct the functor argument - Entity::Attachment a; + EntityAttachment a; a.eclass = i->second.className; a.name = i->second.name; a.offset = pos.origin; @@ -123,4 +123,4 @@ class AttachmentData } }; -} \ No newline at end of file +} diff --git a/libs/scene/CMakeLists.txt b/libs/scene/CMakeLists.txt index af6d2e6087..8c4a874cc4 100644 --- a/libs/scene/CMakeLists.txt +++ b/libs/scene/CMakeLists.txt @@ -2,6 +2,7 @@ add_library(scene AttachmentData.cpp ChildPrimitives.cpp InstanceWalkers.cpp + Entity.cpp EntityKeyValue.cpp LayerUsageBreakdown.cpp ModelFinder.cpp @@ -13,7 +14,6 @@ add_library(scene merge/ThreeWayMergeOperation.cpp SelectableNode.cpp SelectionIndex.cpp - SpawnArgs.cpp TraversableNodeSet.cpp Traverse.cpp) target_compile_options(scene PUBLIC ${SIGC_CFLAGS}) diff --git a/libs/scene/ChildPrimitives.cpp b/libs/scene/ChildPrimitives.cpp index 5c2debeedd..8beb59990a 100644 --- a/libs/scene/ChildPrimitives.cpp +++ b/libs/scene/ChildPrimitives.cpp @@ -4,6 +4,7 @@ #include "ientity.h" #include "igroupnode.h" +#include "scene/Entity.h" #include "registry/registry.h" namespace scene diff --git a/libs/scene/SpawnArgs.cpp b/libs/scene/Entity.cpp similarity index 77% rename from libs/scene/SpawnArgs.cpp rename to libs/scene/Entity.cpp index 2b425e28f0..855fca58b6 100644 --- a/libs/scene/SpawnArgs.cpp +++ b/libs/scene/Entity.cpp @@ -1,16 +1,13 @@ -#include "SpawnArgs.h" +#include "Entity.h" #include "ieclass.h" #include "debugging/debugging.h" #include "string/predicate.h" #include -namespace entity -{ - -SpawnArgs::SpawnArgs(const IEntityClassPtr& eclass) : +Entity::Entity(const IEntityClassPtr& eclass) : _eclass(eclass), - _undo(_keyValues, std::bind(&SpawnArgs::importState, this, std::placeholders::_1), + _undo(_keyValues, std::bind(&Entity::importState, this, std::placeholders::_1), std::function(), "EntityKeyValues"), _observerMutex(false), _isContainer(!eclass->isFixedSize()), @@ -20,10 +17,9 @@ SpawnArgs::SpawnArgs(const IEntityClassPtr& eclass) : parseAttachments(); } -SpawnArgs::SpawnArgs(const SpawnArgs& other) : - Entity(other), +Entity::Entity(const Entity& other) : _eclass(other.getEntityClass()), - _undo(_keyValues, std::bind(&SpawnArgs::importState, this, std::placeholders::_1), + _undo(_keyValues, std::bind(&Entity::importState, this, std::placeholders::_1), std::function(), "EntityKeyValues"), _observerMutex(false), _isContainer(other._isContainer), @@ -36,7 +32,7 @@ SpawnArgs::SpawnArgs(const SpawnArgs& other) : } } -void SpawnArgs::parseAttachments() +void Entity::parseAttachments() { // Parse the keys forEachKeyValue( @@ -49,7 +45,7 @@ void SpawnArgs::parseAttachments() _attachments.validateAttachments(); } -bool SpawnArgs::isModel() const +bool Entity::isModel() const { std::string name = getKeyValue("name"); std::string model = getKeyValue("model"); @@ -58,12 +54,12 @@ bool SpawnArgs::isModel() const return (classname == "func_static" && !name.empty() && name != model); } -bool SpawnArgs::isOfType(const std::string& className) +bool Entity::isOfType(const std::string& className) { return _eclass->isOfType(className); } -void SpawnArgs::importState(const KeyValues& keyValues) +void Entity::importState(const KeyValues& keyValues) { // Remove the entity key values, one by one while (_keyValues.size() > 0) @@ -77,7 +73,7 @@ void SpawnArgs::importState(const KeyValues& keyValues) } } -void SpawnArgs::attachObserver(Observer* observer) +void Entity::attachObserver(Observer* observer) { ASSERT_MESSAGE(!_observerMutex, "observer cannot be attached during iteration"); @@ -91,7 +87,7 @@ void SpawnArgs::attachObserver(Observer* observer) } } -void SpawnArgs::detachObserver(Observer* observer) +void Entity::detachObserver(Observer* observer) { ASSERT_MESSAGE(!_observerMutex, "observer cannot be detached during iteration"); @@ -114,7 +110,7 @@ void SpawnArgs::detachObserver(Observer* observer) } } -void SpawnArgs::connectUndoSystem(IUndoSystem& undoSystem) +void Entity::connectUndoSystem(IUndoSystem& undoSystem) { for (const auto& keyValue : _keyValues) { @@ -124,7 +120,7 @@ void SpawnArgs::connectUndoSystem(IUndoSystem& undoSystem) _undo.connectUndoSystem(undoSystem); } -void SpawnArgs::disconnectUndoSystem(IUndoSystem& undoSystem) +void Entity::disconnectUndoSystem(IUndoSystem& undoSystem) { _undo.disconnectUndoSystem(undoSystem); @@ -134,13 +130,12 @@ void SpawnArgs::disconnectUndoSystem(IUndoSystem& undoSystem) } } -IEntityClassPtr SpawnArgs::getEntityClass() const +IEntityClassPtr Entity::getEntityClass() const { return _eclass; } -void SpawnArgs::forEachKeyValue(KeyValueVisitFunc func, - bool includeInherited) const +void Entity::forEachKeyValue(KeyValueVisitFunc func, bool includeInherited) const { // Visit explicit spawnargs for (const KeyValuePair& pair : _keyValues) @@ -157,7 +152,7 @@ void SpawnArgs::forEachKeyValue(KeyValueVisitFunc func, } } -void SpawnArgs::forEachEntityKeyValue(const EntityKeyValueVisitFunctor& func) +void Entity::forEachEntityKeyValue(const EntityKeyValueVisitFunctor& func) { for (const KeyValuePair& pair : _keyValues) { @@ -165,7 +160,7 @@ void SpawnArgs::forEachEntityKeyValue(const EntityKeyValueVisitFunctor& func) } } -void SpawnArgs::setKeyValue(const std::string& key, const std::string& value) +void Entity::setKeyValue(const std::string& key, const std::string& value) { if (value.empty()) { @@ -179,7 +174,7 @@ void SpawnArgs::setKeyValue(const std::string& key, const std::string& value) } } -std::string SpawnArgs::getKeyValue(const std::string& key) const +std::string Entity::getKeyValue(const std::string& key) const { // Lookup the key in the map KeyValues::const_iterator i = find(key); @@ -196,7 +191,7 @@ std::string SpawnArgs::getKeyValue(const std::string& key) const } } -bool SpawnArgs::isInherited(const std::string& key) const +bool Entity::isInherited(const std::string& key) const { // Check if we have the key in the local keyvalue map bool definedLocally = (find(key) != _keyValues.end()); @@ -205,34 +200,34 @@ bool SpawnArgs::isInherited(const std::string& key) const return (!definedLocally && !_eclass->getAttributeValue(key).empty()); } -void SpawnArgs::forEachAttachment(AttachmentFunc func) const +void Entity::forEachAttachment(AttachmentFunc func) const { _attachments.forEachAttachment(func); } -EntityKeyValuePtr SpawnArgs::getEntityKeyValue(const std::string& key) +EntityKeyValuePtr Entity::getEntityKeyValue(const std::string& key) { KeyValues::const_iterator found = find(key); return (found != _keyValues.end()) ? found->second : EntityKeyValuePtr(); } -bool SpawnArgs::isWorldspawn() const +bool Entity::isWorldspawn() const { return getKeyValue("classname") == "worldspawn"; } -bool SpawnArgs::isContainer() const +bool Entity::isContainer() const { return _isContainer; } -void SpawnArgs::setIsContainer(bool isContainer) +void Entity::setIsContainer(bool isContainer) { _isContainer = isContainer; } -void SpawnArgs::notifyInsert(const std::string& key, EntityKeyValue& value) +void Entity::notifyInsert(const std::string& key, EntityKeyValue& value) { // Block the addition/removal of new Observers during this process _observerMutex = true; @@ -246,7 +241,7 @@ void SpawnArgs::notifyInsert(const std::string& key, EntityKeyValue& value) _observerMutex = false; } -void SpawnArgs::notifyChange(const std::string& k, const std::string& v) +void Entity::notifyChange(const std::string& k, const std::string& v) { _observerMutex = true; @@ -260,7 +255,7 @@ void SpawnArgs::notifyChange(const std::string& k, const std::string& v) _observerMutex = false; } -void SpawnArgs::notifyErase(const std::string& key, EntityKeyValue& value) +void Entity::notifyErase(const std::string& key, EntityKeyValue& value) { // Block the addition/removal of new Observers during this process _observerMutex = true; @@ -273,7 +268,7 @@ void SpawnArgs::notifyErase(const std::string& key, EntityKeyValue& value) _observerMutex = false; } -void SpawnArgs::insert(const std::string& key, const KeyValuePtr& keyValue) +void Entity::insert(const std::string& key, const KeyValuePtr& keyValue) { // Insert the new key at the end of the list auto& pair = _keyValues.emplace_back(key, keyValue); @@ -287,7 +282,7 @@ void SpawnArgs::insert(const std::string& key, const KeyValuePtr& keyValue) } } -void SpawnArgs::insert(const std::string& key, const std::string& value) +void Entity::insert(const std::string& key, const std::string& value) { // Try to lookup the key in the map auto i = find(key); @@ -311,7 +306,7 @@ void SpawnArgs::insert(const std::string& key, const std::string& value) } } -void SpawnArgs::erase(const KeyValues::iterator& i) +void Entity::erase(const KeyValues::iterator& i) { if (_undo.isConnected()) { @@ -332,7 +327,7 @@ void SpawnArgs::erase(const KeyValues::iterator& i) // as the std::shared_ptr useCount will reach zero. } -void SpawnArgs::erase(const std::string& key) +void Entity::erase(const std::string& key) { // Try to lookup the key KeyValues::iterator i = find(key); @@ -344,7 +339,7 @@ void SpawnArgs::erase(const std::string& key) } } -SpawnArgs::KeyValues::const_iterator SpawnArgs::find(const std::string& key) const +Entity::KeyValues::const_iterator Entity::find(const std::string& key) const { for (KeyValues::const_iterator i = _keyValues.begin(); i != _keyValues.end(); @@ -360,7 +355,7 @@ SpawnArgs::KeyValues::const_iterator SpawnArgs::find(const std::string& key) con return _keyValues.end(); } -SpawnArgs::KeyValues::iterator SpawnArgs::find(const std::string& key) +Entity::KeyValues::iterator Entity::find(const std::string& key) { for (KeyValues::iterator i = _keyValues.begin(); i != _keyValues.end(); @@ -375,5 +370,3 @@ SpawnArgs::KeyValues::iterator SpawnArgs::find(const std::string& key) // Not found return _keyValues.end(); } - -} // namespace entity diff --git a/libs/scene/Entity.h b/libs/scene/Entity.h new file mode 100644 index 0000000000..2c5d3f2551 --- /dev/null +++ b/libs/scene/Entity.h @@ -0,0 +1,247 @@ +#pragma once + +#include "scene/AttachmentData.h" +#include "scene/EntityKeyValue.h" + +#include +#include + +class IUndoSystem; + +/** + * The Entity is the main building block of a map, and the uppermost layer in the scenegraph + * under the root node. Each entity contains a arbitrary dictionary of strings ("properties" + * or "spawnargs") containing information about this entity which is used by the game engine + * to modify its behaviour, and may additionally contain child primitives (brushes and + * patches) depending on its type. + * + * At the minimum, each Entity must contain three properties: "name" which contains a + * map-unique string identifier, "classname" which identifies the entity class to the game, + * and "origin" which stores the location of the entity in 3-dimensional world space. + * + * A valid Id Tech 4 map must contain at least one entity: the "worldspawn" which is + * the parent of all map geometry primitives. + * + * greebo: Note that keys are treated case-insensitively in Doom 3, so the Entity class will + * return the same result for "MYKeY" as for "mykey". + */ +class Entity +{ +public: + + /** + * \brief + * Abstract base class for entity observers. + * + * An entity observer receives notifications when keyvalues are inserted or + * deleted on the entity it is observing. + */ + class Observer + { + public: + virtual ~Observer() {} + + /// Notification that a new key value has been inserted on the entity. + virtual void onKeyInsert(const std::string& key, EntityKeyValue& value) {} + + /// Notification that a key value has changed on the entity. + virtual void onKeyChange(const std::string& key, const std::string& val) {} + + /// Notification that a key value has been removed from the entity. + virtual void onKeyErase(const std::string& key, EntityKeyValue& value) {} + }; + +private: + IEntityClassPtr _eclass; + + typedef std::shared_ptr KeyValuePtr; + + // A key value pair using a dynamically allocated value + typedef std::pair KeyValuePair; + + // The unsorted list of KeyValue pairs + typedef std::vector KeyValues; + KeyValues _keyValues; + + typedef std::set Observers; + Observers _observers; + + undo::ObservedUndoable _undo; + + bool _observerMutex; + + bool _isContainer; + + // Store attachment information + entity::AttachmentData _attachments; + +public: + // A container with key => value pairs + typedef std::vector< std::pair > KeyValuePairs; + + // Function typedef to visit actual EntityKeyValue objects, not just the string values + typedef std::function EntityKeyValueVisitFunctor; + + /// Functor to receive keys and values as strings + using KeyValueVisitFunc = std::function; + + // Constructor, pass the according entity class + Entity(const IEntityClassPtr& eclass); + + // Copy constructor + Entity(const Entity& other); + + void importState(const KeyValues& keyValues); + + /// Attach an Entity::Observer to this Entity. + void attachObserver(Observer* observer); + + /// Detach an Entity::Observer from this Entity. + void detachObserver(Observer* observer); + + void connectUndoSystem(IUndoSystem& undoSystem); + void disconnectUndoSystem(IUndoSystem& undoSystem); + + /// Return the entity class object for this entity. + IEntityClassPtr getEntityClass() const; + + /** + * \brief Enumerate all keys and values on this entity, optionally including + * inherited spawnargs. + * + * \param func + * Functor to receive each key and its associated value. + * + * \param includeInherited + * true if the functor should be invoked for each inherited spawnarg (from + * the entity class), false if only explicit spawnargs on this particular + * entity should be visited. + */ + void forEachKeyValue(KeyValueVisitFunc func, bool includeInherited = false) const; + + void forEachEntityKeyValue(const EntityKeyValueVisitFunctor& visitor); + + /** Set a key value on this entity. Setting the value to "" will + * remove the key. + * + * @param key + * The key to set. + * + * @param value + * Value to give the key, or the empty string to remove the key. + */ + void setKeyValue(const std::string& key, const std::string& value); + + /* Retrieve a key value from the entity. + * + * @param key + * The key to retrieve. + * + * @returns + * The current value for this key, or the empty string if it does not + * exist. + */ + std::string getKeyValue(const std::string& key) const; + + /** + * \brief Return the list of keyvalues matching the given prefix. + * + * This method performs a search for all spawnargs whose key matches the + * given prefix, with a suffix consisting of zero or more arbitrary + * characters. For example, if "target" were specified as the prefix, the + * list would include "target", "target0", "target127" etc. + * + * This operation may not have high performance, due to the need to scan + * for matching names, therefore should not be used in performance-critical + * code. + * + * @param prefix + * The prefix to search for, interpreted case-insensitively. + * + * @return + * A list of KeyValue pairs matching the provided prefix. This list will be + * empty if there were no matches. + */ + KeyValuePairs getKeyValuePairs(const std::string& prefix) const + { + KeyValuePairs list; + + forEachKeyValue([&](const std::string& k, const std::string& v) { + if (string::istarts_with(k, prefix)) + list.push_back(std::make_pair(k, v)); + }); + + return list; + } + + /** + * greebo: Checks whether the given key is inherited or not. + * + * @returns: TRUE if the value is inherited, + * FALSE when it is not or when the key doesn't exist at all. + */ + bool isInherited(const std::string& key) const; + + /** + * Returns true if this entity is the worldspawn, which can be game-specific, + * but is usually true if this entity's classname equals "worldspawn" + */ + bool isWorldspawn() const; + bool isContainer() const; + void setIsContainer(bool isContainer); + + /** greebo: Returns true if the entity is a model. For Doom3, this is + * usually true when the classname == "func_static" and + * the non-empty spawnarg "model" != "name". + */ + bool isModel() const; + + // Returns the actual pointer to a KeyValue (or NULL if not found), + // not just the string like getKeyValue() does. + // Only returns non-NULL for non-inherited keyvalues. + EntityKeyValuePtr getEntityKeyValue(const std::string& key); + + /** + * Returns true if this entity is of type or inherits from the + * given entity class name. className is treated case-sensitively. + */ + bool isOfType(const std::string& className); + + /* ENTITY ATTACHMENTS */ + + /// A functor which can receive Attachment objects + using AttachmentFunc = std::function; + + /** + * \brief Iterate over attached entities, if any. + * + * Each entity can define one or more attached entities, which should + * appear at specific offsets relative to the parent entity. Such attached + * entities are for visualisation only, and should not be saved into the + * map as genuine map entities. + * + * \param func + * Functor to receive attachment information. + */ + void forEachAttachment(AttachmentFunc func) const; + +private: + + // Parse attachment information from def_attach and related keys (which are + // most likely on the entity class, not the entity itself) + void parseAttachments(); + + // Notification functions + void notifyInsert(const std::string& key, EntityKeyValue& value); + void notifyChange(const std::string& k, const std::string& v); + void notifyErase(const std::string& key, EntityKeyValue& value); + + void insert(const std::string& key, const KeyValuePtr& keyValue); + void insert(const std::string& key, const std::string& value); + + void erase(const KeyValues::iterator& i); + void erase(const std::string& key); + + KeyValues::iterator find(const std::string& key); + KeyValues::const_iterator find(const std::string& key) const; +}; diff --git a/libs/scene/EntityAttachment.h b/libs/scene/EntityAttachment.h new file mode 100644 index 0000000000..84fa93a109 --- /dev/null +++ b/libs/scene/EntityAttachment.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include "math/Vector3.h" + +/// Details of an attached entity +struct EntityAttachment +{ + /// Entity class of the attached entity + std::string eclass; + + // Optional attachment name (arbitrarily chosen by the user) + std::string name; + + /// Vector offset where the attached entity should appear + Vector3 offset; + + /// Optional model joint to use as origin + std::string joint; +}; diff --git a/libs/scene/EntityBreakdown.h b/libs/scene/EntityBreakdown.h index 1d56a7bf06..d335f93f01 100644 --- a/libs/scene/EntityBreakdown.h +++ b/libs/scene/EntityBreakdown.h @@ -3,9 +3,10 @@ #include #include #include "iscenegraph.h" -#include "ientity.h" #include "ieclass.h" +#include "scene/Entity.h" + namespace scene { diff --git a/libs/scene/ModelFinder.cpp b/libs/scene/ModelFinder.cpp index 0903230f0c..7040a8a516 100644 --- a/libs/scene/ModelFinder.cpp +++ b/libs/scene/ModelFinder.cpp @@ -1,6 +1,7 @@ #include "ModelFinder.h" #include "ientity.h" +#include "scene/Entity.h" namespace scene { diff --git a/libs/scene/SpawnArgs.h b/libs/scene/SpawnArgs.h deleted file mode 100644 index a2443505d4..0000000000 --- a/libs/scene/SpawnArgs.h +++ /dev/null @@ -1,105 +0,0 @@ -#pragma once - -#include "scene/AttachmentData.h" -#include "scene/EntityKeyValue.h" - -#include -#include - -class IUndoSystem; - -namespace entity { - -/** - * \brief Implementation of the class Entity. - * - * A SpawnArgs basically just keeps track of all the spawnargs and delivers - * them on request, taking the inheritance tree (EntityClasses) into account. - * The actual rendering and entity behaviour is handled by the EntityNode. - * - * It's possible to attach observers to this entity to get notified upon - * key/value changes. - */ -class SpawnArgs: public Entity -{ - IEntityClassPtr _eclass; - - typedef std::shared_ptr KeyValuePtr; - - // A key value pair using a dynamically allocated value - typedef std::pair KeyValuePair; - - // The unsorted list of KeyValue pairs - typedef std::vector KeyValues; - KeyValues _keyValues; - - typedef std::set Observers; - Observers _observers; - - undo::ObservedUndoable _undo; - - bool _observerMutex; - - bool _isContainer; - - // Store attachment information - AttachmentData _attachments; - -public: - // Constructor, pass the according entity class - SpawnArgs(const IEntityClassPtr& eclass); - - // Copy constructor - SpawnArgs(const SpawnArgs& other); - - void importState(const KeyValues& keyValues); - - /* Entity implementation */ - void attachObserver(Observer* observer) override; - void detachObserver(Observer* observer) override; - void connectUndoSystem(IUndoSystem& undoSystem); - void disconnectUndoSystem(IUndoSystem& undoSystem); - IEntityClassPtr getEntityClass() const override; - void forEachKeyValue(KeyValueVisitFunc func, - bool includeInherited) const override; - void forEachEntityKeyValue(const EntityKeyValueVisitFunctor& visitor) override; - void setKeyValue(const std::string& key, const std::string& value) override; - std::string getKeyValue(const std::string& key) const override; - bool isInherited(const std::string& key) const override; - void forEachAttachment(AttachmentFunc func) const override; - - bool isWorldspawn() const override; - bool isContainer() const override; - void setIsContainer(bool isContainer); - - bool isModel() const override; - - // Returns the actual pointer to a KeyValue (or NULL if not found), - // not just the string like getKeyValue() does. - // Only returns non-NULL for non-inherited keyvalues. - EntityKeyValuePtr getEntityKeyValue(const std::string& key); - - bool isOfType(const std::string& className) override; - -private: - - // Parse attachment information from def_attach and related keys (which are - // most likely on the entity class, not the entity itself) - void parseAttachments(); - - // Notification functions - void notifyInsert(const std::string& key, EntityKeyValue& value); - void notifyChange(const std::string& k, const std::string& v); - void notifyErase(const std::string& key, EntityKeyValue& value); - - void insert(const std::string& key, const KeyValuePtr& keyValue); - void insert(const std::string& key, const std::string& value); - - void erase(const KeyValues::iterator& i); - void erase(const std::string& key); - - KeyValues::iterator find(const std::string& key); - KeyValues::const_iterator find(const std::string& key) const; -}; - -} // namespace entity diff --git a/libs/scene/merge/MergeAction.h b/libs/scene/merge/MergeAction.h index f8aa278575..e051d371da 100644 --- a/libs/scene/merge/MergeAction.h +++ b/libs/scene/merge/MergeAction.h @@ -5,16 +5,17 @@ #include "imapmerge.h" #include "../Clone.h" #include "../scenelib.h" +#include "scene/Entity.h" namespace scene -{ +{ namespace merge { // Represents a single step of a merge process, like adding a brush, // removing an entity, setting a keyvalue, etc. -class MergeAction : +class MergeAction : public virtual IMergeAction { private: @@ -142,9 +143,9 @@ class AddCloneToParentAction : auto activeLayer = parent->getRootNode()->getLayerManager().getActiveLayer(); _cloneToBeInserted->moveToLayer(activeLayer); - _cloneToBeInserted->foreachNode([=](const INodePtr& child) - { - child->moveToLayer(activeLayer); return true; + _cloneToBeInserted->foreachNode([=](const INodePtr& child) + { + child->moveToLayer(activeLayer); return true; }); } @@ -238,7 +239,7 @@ class SetEntityKeyValueAction : assert(_node); assert(Node_isEntity(_node)); assert(!_key.empty()); - + // Store the existing value, it's reverted when deactivating this action _unchangedValue = Node_getEntity(node)->getKeyValue(key); } @@ -335,7 +336,7 @@ class ChangeEntityKeyValueAction : /** * A ConflictResolutionAction encapsulates a source change that has a * encountered a conflicting target change. - * + * * The source change will only be applied if this action has been * told to do so by calling setResolvedByUsingSource(true), * otherwise nothing happens and the target change stays in effect. @@ -448,10 +449,10 @@ class EntityConflictResolutionAction : EntityConflictResolutionAction(conflictType, conflictingSourceEntity, conflictingTargetEntity, sourceAction, MergeAction::Ptr()) {} - EntityConflictResolutionAction(ConflictType conflictType, + EntityConflictResolutionAction(ConflictType conflictType, const INodePtr& conflictingSourceEntity, const INodePtr& conflictingTargetEntity, - const MergeAction::Ptr& sourceAction, + const MergeAction::Ptr& sourceAction, const MergeAction::Ptr& targetAction) : ConflictResolutionAction(conflictType, conflictingSourceEntity, conflictingTargetEntity, sourceAction, targetAction) {} @@ -465,7 +466,7 @@ class EntityKeyValueConflictResolutionAction : EntityKeyValueConflictResolutionAction(ConflictType conflictType, const INodePtr& conflictingSourceEntity, const INodePtr& conflictingTargetEntity, - const MergeAction::Ptr& sourceAction, + const MergeAction::Ptr& sourceAction, const MergeAction::Ptr& targetAction) : ConflictResolutionAction(conflictType, conflictingSourceEntity, conflictingTargetEntity, sourceAction, targetAction) {} diff --git a/libs/scene/merge/NodeUtils.h b/libs/scene/merge/NodeUtils.h index cbe8f278e9..3d65af1d89 100644 --- a/libs/scene/merge/NodeUtils.h +++ b/libs/scene/merge/NodeUtils.h @@ -5,6 +5,7 @@ #include "icomparablenode.h" #include "ientity.h" #include "itextstream.h" +#include "scene/Entity.h" namespace scene { @@ -102,4 +103,4 @@ class NodeUtils } -} \ No newline at end of file +} diff --git a/libs/selection/CollectiveSpawnargs.h b/libs/selection/CollectiveSpawnargs.h index 615f23a2cd..dacb13d299 100644 --- a/libs/selection/CollectiveSpawnargs.h +++ b/libs/selection/CollectiveSpawnargs.h @@ -5,7 +5,7 @@ #include #include #include -#include "ientity.h" +#include "scene/Entity.h" #include "i18n.h" #include "scene/EntityKeyValue.h" diff --git a/libs/selection/EntitiesFirstSelector.h b/libs/selection/EntitiesFirstSelector.h index 344a2418e0..c06d4f761b 100644 --- a/libs/selection/EntitiesFirstSelector.h +++ b/libs/selection/EntitiesFirstSelector.h @@ -4,7 +4,7 @@ #include "iselectable.h" #include "iselectiontest.h" -#include "ientity.h" +#include "scene/Entity.h" namespace selection { @@ -96,7 +96,7 @@ class EntitiesFirstSelector : // Visit each selectable, entities first, then primitives void foreachSelectable(const std::function& functor) override - { + { for (const auto& [_, selectable] : _entityPool) { functor(selectable); diff --git a/libs/selection/EntitySelection.h b/libs/selection/EntitySelection.h index b40564d715..cbc1947b0f 100644 --- a/libs/selection/EntitySelection.h +++ b/libs/selection/EntitySelection.h @@ -3,7 +3,7 @@ #include #include "inode.h" #include "ieclass.h" -#include "ientity.h" +#include "scene/Entity.h" #include "iselection.h" #include "iselectable.h" #include "CollectiveSpawnargs.h" @@ -14,18 +14,18 @@ namespace selection /** * Helper object used by the Entity Inspector to track all selected entities * in the scene and keep the list of their spawnargs up to date. - * + * * On update() it rescans the entity selection in the scene, triggering * the contained CollectiveSpawnargs object to emit its signals. The signals - * are emitted such that the listening EntityInspector will show the + * are emitted such that the listening EntityInspector will show the * correct set of keys: the ones with shared values will show just that, * the keys with differing values will show a placeholder text instead. - * - * This instance will monitor the selected entities to dispatch all + * + * This instance will monitor the selected entities to dispatch all * key value changes to the CollectiveSpawnargs. - * Selection updates will not take effect until update() is called, which + * Selection updates will not take effect until update() is called, which * ideally should happen in an idle processing loop after a selection change. - * + * * This class keeps weak references to the scene::Nodes to not interfere with * node destruction. */ @@ -196,12 +196,12 @@ class EntitySelection final : for (auto& tracked : _trackedEntities) { auto node = tracked.getNode(); - + if (!node) continue; auto entityNode = scene::node_cast(node); assert(entityNode); - + if (entityNode) { functor(entityNode); diff --git a/libs/selectionlib.h b/libs/selectionlib.h index 52e55bdd3f..914db7d2a2 100644 --- a/libs/selectionlib.h +++ b/libs/selectionlib.h @@ -6,12 +6,13 @@ #include "igroupnode.h" #include "iselectiongroup.h" #include "iscenegraph.h" -#include "ientity.h" +#include "scene/Entity.h" #include "ipatch.h" #include "math/Vector3.h" #include "math/AABB.h" +#include "scene/Entity.h" -/** +/** * @brief A structure containing information about the current Selection. * * An instance of this is maintained by the RadiantSelectionSystem, and a const reference can be diff --git a/libs/wxutil/preview/EntityPreview.cpp b/libs/wxutil/preview/EntityPreview.cpp index 734ee743d7..65f739d280 100644 --- a/libs/wxutil/preview/EntityPreview.cpp +++ b/libs/wxutil/preview/EntityPreview.cpp @@ -4,6 +4,7 @@ #include "ieclass.h" #include "ifilter.h" #include "scene/BasicRootNode.h" +#include "scene/Entity.h" #include "../dialog/MessageBox.h" #include "scene/PrefabBoundsAccumulator.h" diff --git a/libs/wxutil/preview/EntityPreview.h b/libs/wxutil/preview/EntityPreview.h index 3899630bac..5fae27722e 100644 --- a/libs/wxutil/preview/EntityPreview.h +++ b/libs/wxutil/preview/EntityPreview.h @@ -2,7 +2,7 @@ #include "RenderPreview.h" -#include "ientity.h" +#include "scene/Entity.h" #include "inode.h" #include "imap.h" @@ -60,4 +60,4 @@ class EntityPreview : void setEntity(const IEntityNodePtr& entity); }; -} \ No newline at end of file +} diff --git a/libs/wxutil/preview/ParticlePreview.cpp b/libs/wxutil/preview/ParticlePreview.cpp index 8ac60c7ea9..34c3ca00ea 100644 --- a/libs/wxutil/preview/ParticlePreview.cpp +++ b/libs/wxutil/preview/ParticlePreview.cpp @@ -1,7 +1,7 @@ #include "ParticlePreview.h" #include "ui/ieventmanager.h" -#include "ientity.h" +#include "scene/Entity.h" #include "ieclass.h" #include "iparticles.h" #include "iparticlestage.h" @@ -45,23 +45,23 @@ ParticlePreview::ParticlePreview(wxWindow* parent) : wxToolBar* toolbar = new wxToolBar(_mainPanel, wxID_ANY); toolbar->SetToolBitmapSize(wxSize(24, 24)); - _showAxesButton = toolbar->AddCheckTool(TOOL_SHOW_AXES, "", + _showAxesButton = toolbar->AddCheckTool(TOOL_SHOW_AXES, "", wxutil::GetLocalBitmap("axes.png", wxART_TOOLBAR)); _showAxesButton->SetShortHelp(_("Show coordinate axes")); - toolbar->Connect(_showAxesButton->GetId(), wxEVT_TOOL, + toolbar->Connect(_showAxesButton->GetId(), wxEVT_TOOL, wxCommandEventHandler(ParticlePreview::onToolItemClickRefresh), NULL, this); - _showWireFrameButton = toolbar->AddCheckTool(TOOL_SHOW_WIREFRAME, "", + _showWireFrameButton = toolbar->AddCheckTool(TOOL_SHOW_WIREFRAME, "", wxutil::GetLocalBitmap("wireframe.png", wxART_TOOLBAR)); _showWireFrameButton->SetShortHelp(_("Show wireframe")); - toolbar->Connect(_showWireFrameButton->GetId(), wxEVT_TOOL, + toolbar->Connect(_showWireFrameButton->GetId(), wxEVT_TOOL, wxCommandEventHandler(ParticlePreview::onToolItemClickRefresh), NULL, this); - _automaticLoopButton = toolbar->AddCheckTool(TOOL_AUTO_LOOP, _("Auto Loop"), + _automaticLoopButton = toolbar->AddCheckTool(TOOL_AUTO_LOOP, _("Auto Loop"), wxutil::GetLocalBitmap("loop.png", wxART_TOOLBAR)); _automaticLoopButton->SetShortHelp(_("Auto Loop")); - _reloadButton = toolbar->AddTool(TOOL_REFRESH, "", + _reloadButton = toolbar->AddTool(TOOL_REFRESH, "", wxutil::GetLocalBitmap("refresh.png", wxART_TOOLBAR)); _reloadButton->SetShortHelp(_("Reload Particle Defs")); IEventPtr ev = GlobalEventManager().findEvent("ReloadDecls"); @@ -117,7 +117,7 @@ void ParticlePreview::SetPreviewDeclName(const std::string& declName) setupSceneGraph(); } - if (!_entity) return; // FUNC_EMITTER_CLASS not found + if (!_entity) return; // FUNC_EMITTER_CLASS not found if (_particleNode) { @@ -175,7 +175,7 @@ void ParticlePreview::setupSceneGraph() try { _rootNode = std::make_shared(); - + _entity = GlobalEntityModule().createEntity( GlobalEntityClassManager().findClass(FUNC_EMITTER_CLASS)); diff --git a/plugins/dm.conversation/ActorNodeFinder.h b/plugins/dm.conversation/ActorNodeFinder.h index a5a594da08..7c767893b6 100644 --- a/plugins/dm.conversation/ActorNodeFinder.h +++ b/plugins/dm.conversation/ActorNodeFinder.h @@ -1,7 +1,7 @@ #pragma once #include "iscenegraph.h" -#include "ientity.h" +#include "scene/Entity.h" namespace scene { diff --git a/plugins/dm.conversation/ConversationDialog.h b/plugins/dm.conversation/ConversationDialog.h index eb7e715d33..87f0b3d77d 100644 --- a/plugins/dm.conversation/ConversationDialog.h +++ b/plugins/dm.conversation/ConversationDialog.h @@ -2,7 +2,7 @@ #include -#include "ientity.h" +#include "scene/Entity.h" #include "icommandsystem.h" #include "iradiant.h" #include "wxutil/dialog/DialogBase.h" @@ -30,7 +30,7 @@ class ConversationDialog : conversation::ConvEntityColumns _convEntityColumns; wxutil::TreeModel::Ptr _entityList; wxutil::TreeView* _entityView; - + // List of conversations on the selected entity conversation::ConversationColumns _convColumns; wxutil::TreeModel::Ptr _convList; diff --git a/plugins/dm.conversation/ConversationEntity.cpp b/plugins/dm.conversation/ConversationEntity.cpp index 9e806524a6..4c4fa2b797 100644 --- a/plugins/dm.conversation/ConversationEntity.cpp +++ b/plugins/dm.conversation/ConversationEntity.cpp @@ -4,8 +4,8 @@ #include "i18n.h" #include "itextstream.h" -#include "ientity.h" - +#include "scene/Entity.h" +#include "scene/Entity.h" #include "string/convert.h" #include "ConversationKeyExtractor.h" @@ -28,7 +28,7 @@ ConversationEntity::ConversationEntity(const scene::INodePtr& node) : } // Delete the entity's world node -void ConversationEntity::deleteWorldNode() +void ConversationEntity::deleteWorldNode() { // Try to convert the weak_ptr reference to a shared_ptr scene::INodePtr node = _entityNode.lock(); @@ -50,11 +50,11 @@ int ConversationEntity::getHighestIndex() } // Add a new conversation -int ConversationEntity::addConversation() +int ConversationEntity::addConversation() { // Locate the first unused id int index = 1; - while (_conversations.find(index) != _conversations.end()) + while (_conversations.find(index) != _conversations.end()) { if (index == std::numeric_limits::max()) { @@ -73,12 +73,12 @@ int ConversationEntity::addConversation() return index; } -void ConversationEntity::deleteConversation(int index) +void ConversationEntity::deleteConversation(int index) { // Look up the conversation with the given index auto i = _conversations.find(index); - if (i == _conversations.end()) + if (i == _conversations.end()) { // not found, nothing to do return; @@ -88,7 +88,7 @@ void ConversationEntity::deleteConversation(int index) _conversations.erase(i++); // Then iterate all the way to the highest index - while (i != _conversations.end()) + while (i != _conversations.end()) { // Decrease the index of this conversation int newIndex = i->first - 1; @@ -189,7 +189,7 @@ void ConversationEntity::writeToEntity() { std::string cmdPrefix = prefix + "cmd_" + string::to_string(cmd.first) + "_"; - try + try { const auto& cmdInfo = ConversationCommandLibrary::Instance().findCommandInfo(cmd.second->type); diff --git a/plugins/dm.conversation/ConversationEntity.h b/plugins/dm.conversation/ConversationEntity.h index e9d23d35e7..f8ae9ff497 100644 --- a/plugins/dm.conversation/ConversationEntity.h +++ b/plugins/dm.conversation/ConversationEntity.h @@ -1,7 +1,7 @@ #pragma once #include "inode.h" -#include "ientity.h" +#include "scene/Entity.h" #include #include "wxutil/dataview/TreeModel.h" diff --git a/plugins/dm.conversation/ConversationEntityFinder.h b/plugins/dm.conversation/ConversationEntityFinder.h index 0a03f3de68..b44fe17db3 100644 --- a/plugins/dm.conversation/ConversationEntityFinder.h +++ b/plugins/dm.conversation/ConversationEntityFinder.h @@ -1,6 +1,7 @@ #pragma once #include "i18n.h" +#include "scene/Entity.h" #include #include "ConversationEntity.h" diff --git a/plugins/dm.conversation/ConversationKeyExtractor.h b/plugins/dm.conversation/ConversationKeyExtractor.h index 7d28c9402a..4d99fc7ba3 100644 --- a/plugins/dm.conversation/ConversationKeyExtractor.h +++ b/plugins/dm.conversation/ConversationKeyExtractor.h @@ -3,9 +3,9 @@ #include #include "Conversation.h" -#include "ientity.h" +#include "scene/Entity.h" -namespace conversation +namespace conversation { /** diff --git a/plugins/dm.difficulty/DifficultyDialog.h b/plugins/dm.difficulty/DifficultyDialog.h index 1990078dc2..e821879e44 100644 --- a/plugins/dm.difficulty/DifficultyDialog.h +++ b/plugins/dm.difficulty/DifficultyDialog.h @@ -1,6 +1,6 @@ #pragma once -#include "ientity.h" +#include "scene/Entity.h" #include "iradiant.h" #include "icommandsystem.h" #include "wxutil/dialog/DialogBase.h" diff --git a/plugins/dm.difficulty/DifficultyEntity.cpp b/plugins/dm.difficulty/DifficultyEntity.cpp index 1a83323be9..1469fb2494 100644 --- a/plugins/dm.difficulty/DifficultyEntity.cpp +++ b/plugins/dm.difficulty/DifficultyEntity.cpp @@ -1,6 +1,6 @@ #include "DifficultyEntity.h" -#include "ientity.h" +#include "scene/Entity.h" #include "string/convert.h" namespace difficulty { diff --git a/plugins/dm.difficulty/DifficultyEntity.h b/plugins/dm.difficulty/DifficultyEntity.h index b9af14fdd8..ab93f108ef 100644 --- a/plugins/dm.difficulty/DifficultyEntity.h +++ b/plugins/dm.difficulty/DifficultyEntity.h @@ -1,7 +1,7 @@ #ifndef DIFFICULTY_ENTITY_H_ #define DIFFICULTY_ENTITY_H_ -#include "ientity.h" +#include "scene/Entity.h" #include "Setting.h" namespace difficulty { diff --git a/plugins/dm.difficulty/DifficultyEntityFinder.h b/plugins/dm.difficulty/DifficultyEntityFinder.h index 6be14662e1..60495ebee3 100644 --- a/plugins/dm.difficulty/DifficultyEntityFinder.h +++ b/plugins/dm.difficulty/DifficultyEntityFinder.h @@ -1,6 +1,6 @@ #pragma once -#include "ientity.h" +#include "scene/Entity.h" #include "gamelib.h" #include "DifficultySettings.h" diff --git a/plugins/dm.editing/AIEditingPanel.h b/plugins/dm.editing/AIEditingPanel.h index fcf6c74cbb..48ef03e831 100644 --- a/plugins/dm.editing/AIEditingPanel.h +++ b/plugins/dm.editing/AIEditingPanel.h @@ -1,7 +1,7 @@ #pragma once #include -#include "ientity.h" +#include "scene/Entity.h" #include #include @@ -25,7 +25,7 @@ typedef std::shared_ptr AIEditingPanelPtr; class SpawnargLinkedCheckbox; class SpawnargLinkedSpinButton; -class AIEditingPanel : +class AIEditingPanel : public wxutil::DockablePanel, public Entity::Observer, public sigc::trackable, @@ -77,7 +77,7 @@ class AIEditingPanel : void constructWidgets(); wxSizer* createSpinButtonHbox(SpawnargLinkedSpinButton* spinButton); wxStaticText* createSectionLabel(const std::string& text); - void createChooserRow(wxSizer* table, const std::string& rowLabel, + void createChooserRow(wxSizer* table, const std::string& rowLabel, const std::string& buttonLabel, const std::string& buttonIcon, const std::string& key); diff --git a/plugins/dm.editing/AIHeadPropertyEditor.cpp b/plugins/dm.editing/AIHeadPropertyEditor.cpp index 2cd6d43a0d..3232731b5e 100644 --- a/plugins/dm.editing/AIHeadPropertyEditor.cpp +++ b/plugins/dm.editing/AIHeadPropertyEditor.cpp @@ -2,7 +2,7 @@ #include "i18n.h" #include "ieclass.h" -#include "ientity.h" +#include "scene/Entity.h" #include #include diff --git a/plugins/dm.editing/AIVocalSetPropertyEditor.cpp b/plugins/dm.editing/AIVocalSetPropertyEditor.cpp index 366a065300..822658b7f9 100644 --- a/plugins/dm.editing/AIVocalSetPropertyEditor.cpp +++ b/plugins/dm.editing/AIVocalSetPropertyEditor.cpp @@ -2,7 +2,7 @@ #include "i18n.h" #include "ieclass.h" -#include "ientity.h" +#include "scene/Entity.h" #include #include diff --git a/plugins/dm.editing/SpawnargLinkedCheckbox.h b/plugins/dm.editing/SpawnargLinkedCheckbox.h index afcf1d2af0..c84e0d315c 100644 --- a/plugins/dm.editing/SpawnargLinkedCheckbox.h +++ b/plugins/dm.editing/SpawnargLinkedCheckbox.h @@ -2,7 +2,7 @@ #include "iundo.h" #include "ieclass.h" -#include "ientity.h" +#include "scene/Entity.h" #include namespace ui @@ -12,10 +12,10 @@ namespace ui * An enhanced checkbox that is updating the named * entity property (spawnarg) when toggled. * - * The logic toggled = "1" can be optionally inversed such that + * The logic toggled = "1" can be optionally inversed such that * an unchecked box reflects a property value of "1". */ -class SpawnargLinkedCheckbox : +class SpawnargLinkedCheckbox : public wxCheckBox { private: @@ -30,8 +30,8 @@ class SpawnargLinkedCheckbox : bool _defaultValueForMissingKeyValue; public: - SpawnargLinkedCheckbox(wxWindow* parent, const std::string& label, - const std::string& propertyName, + SpawnargLinkedCheckbox(wxWindow* parent, const std::string& label, + const std::string& propertyName, bool inverseLogic = false) : wxCheckBox(parent, wxID_ANY, label), _inverseLogic(inverseLogic), @@ -53,7 +53,7 @@ class SpawnargLinkedCheckbox : { _entity = entity; - if (_entity == NULL) + if (_entity == NULL) { SetToolTip(""); return; diff --git a/plugins/dm.editing/SpawnargLinkedSpinButton.h b/plugins/dm.editing/SpawnargLinkedSpinButton.h index a4f4d7d4fa..0686560431 100644 --- a/plugins/dm.editing/SpawnargLinkedSpinButton.h +++ b/plugins/dm.editing/SpawnargLinkedSpinButton.h @@ -2,7 +2,7 @@ #include "iundo.h" #include "ieclass.h" -#include "ientity.h" +#include "scene/Entity.h" #include "string/convert.h" #include "util/ScopedBoolLock.h" #include @@ -22,7 +22,7 @@ namespace ui * I had to work around it by putting the wxSpinCtrlDouble into * a parent wxPanel first. */ -class SpawnargLinkedSpinButton : +class SpawnargLinkedSpinButton : public wxPanel { private: @@ -35,14 +35,14 @@ class SpawnargLinkedSpinButton : Entity* _entity; bool _updateLock; - + public: SpawnargLinkedSpinButton(wxWindow* parent, - const std::string& label, - const std::string& propertyName, + const std::string& label, + const std::string& propertyName, double min, double max, - double increment = 1, + double increment = 1, unsigned int digits = 0) : wxPanel(parent, wxID_ANY), _spinCtrl(new wxSpinCtrlDouble(this, wxID_ANY)), @@ -61,7 +61,7 @@ class SpawnargLinkedSpinButton : // 6 chars wide _spinCtrl->SetMaxSize(wxSize(GetCharWidth() * 9, -1)); - _spinCtrl->Connect(wxEVT_SPINCTRLDOUBLE, + _spinCtrl->Connect(wxEVT_SPINCTRLDOUBLE, wxSpinDoubleEventHandler(SpawnargLinkedSpinButton::onSpinButtonChanged), NULL, this); } @@ -75,7 +75,7 @@ class SpawnargLinkedSpinButton : { _entity = entity; - if (_entity == NULL) + if (_entity == NULL) { SetToolTip(""); return; @@ -93,7 +93,7 @@ class SpawnargLinkedSpinButton : } protected: - + void onSpinButtonChanged(wxSpinDoubleEvent& ev) { ev.Skip(); diff --git a/plugins/dm.gameconnection/DiffDoom3MapWriter.cpp b/plugins/dm.gameconnection/DiffDoom3MapWriter.cpp index 8c9b2bb291..0c79b04ee3 100644 --- a/plugins/dm.gameconnection/DiffDoom3MapWriter.cpp +++ b/plugins/dm.gameconnection/DiffDoom3MapWriter.cpp @@ -1,7 +1,7 @@ #include "DiffDoom3MapWriter.h" #include "DiffStatus.h" -#include "ientity.h" +#include "scene/Entity.h" namespace gameconn { diff --git a/plugins/dm.gameconnection/GameConnection.cpp b/plugins/dm.gameconnection/GameConnection.cpp index f6ae2494f3..53afa7c1b7 100644 --- a/plugins/dm.gameconnection/GameConnection.cpp +++ b/plugins/dm.gameconnection/GameConnection.cpp @@ -8,7 +8,7 @@ #include "icameraview.h" #include "inode.h" #include "imap.h" -#include "ientity.h" +#include "scene/Entity.h" #include "iselection.h" #include "ui/iuserinterface.h" #include "ui/imenumanager.h" @@ -102,7 +102,7 @@ void GameConnection::think() } void GameConnection::onTimerEvent(wxTimerEvent& ev) -{ +{ if (_timerInProgress) return; // avoid double-entering util::ScopedBoolLock guard(_timerInProgress); diff --git a/plugins/dm.gameconnection/MapObserver.cpp b/plugins/dm.gameconnection/MapObserver.cpp index 9c63949453..4cdaca0a16 100644 --- a/plugins/dm.gameconnection/MapObserver.cpp +++ b/plugins/dm.gameconnection/MapObserver.cpp @@ -1,6 +1,6 @@ #include "MapObserver.h" #include "inode.h" -#include "ientity.h" +#include "scene/Entity.h" #include "scene/EntityKeyValue.h" diff --git a/plugins/dm.gameconnection/MapObserver.h b/plugins/dm.gameconnection/MapObserver.h index 5ed404a79b..ccf34980ab 100644 --- a/plugins/dm.gameconnection/MapObserver.h +++ b/plugins/dm.gameconnection/MapObserver.h @@ -2,7 +2,7 @@ #include "icommandsystem.h" #include "iscenegraph.h" -#include "ientity.h" +#include "scene/Entity.h" #include "DiffStatus.h" namespace gameconn diff --git a/plugins/dm.gui/ReadableEditorDialog.cpp b/plugins/dm.gui/ReadableEditorDialog.cpp index c36f8e21ab..9d679cd464 100644 --- a/plugins/dm.gui/ReadableEditorDialog.cpp +++ b/plugins/dm.gui/ReadableEditorDialog.cpp @@ -22,7 +22,7 @@ // Modules #include "iundo.h" #include "ui/imainframe.h" -#include "ientity.h" +#include "scene/Entity.h" #include "imap.h" #include "igame.h" #include "ui/idialogmanager.h" @@ -181,7 +181,7 @@ void ReadableEditorDialog::setupPageRelatedInterface() // Delete Button findNamedObject(this, "ReadableEditorDeletePage")->Connect(wxEVT_BUTTON, wxCommandEventHandler(ReadableEditorDialog::onDelete), NULL, this); - + // Page Switcher findNamedObject(this, "ReadableEditorGotoFirstPage")->Connect(wxEVT_BUTTON, wxCommandEventHandler(ReadableEditorDialog::onFirstPage), NULL, this); @@ -227,7 +227,7 @@ void ReadableEditorDialog::createMenus() _insertMenu->Append(InsertWholePage, _("Insert whole Page"), ""); _insertMenu->Append(InsertLeft, _("Insert on left Side"), ""); _insertMenu->Append(InsertRight, _("Insert on right Side"), ""); - + _insertMenu->Connect(wxEVT_MENU, wxCommandEventHandler(ReadableEditorDialog::onMenuItemClick), NULL, this); // Delete Menu @@ -309,7 +309,7 @@ bool ReadableEditorDialog::initControlsFromEntity() refreshWindowTitle(); return true; } - + return false; // cancelled } catch (XdFileChooserDialog::ImportFailedException&) @@ -491,7 +491,7 @@ std::string ReadableEditorDialog::constructStoragePath() std::string newFileName = _mapBasedFilename; if (!compPath.empty()) // VFS-path does exist, now check whether it is in another absolute folder. - { + { int fileIdx = 2; compPath += XData::XDATA_DIR + _mapBasedFilename; @@ -519,7 +519,7 @@ std::string ReadableEditorDialog::constructStoragePath() } else { - // We are exporting a previously imported XData definition. + // We are exporting a previously imported XData definition. // The _xdFilename itself can be a VFS path or an absolute path, depending on the user's settings if (path_is_absolute(_xdFilename.c_str())) { @@ -1550,7 +1550,7 @@ void ReadableEditorDialog::showDuplicateDefinitions() { wxutil::Messagebox::Show( _("Duplicated XData definitions"), - _("There are no duplicated definitions!"), + _("There are no duplicated definitions!"), ui::IDialog::MESSAGE_CONFIRM, this ); return; diff --git a/plugins/dm.gui/plugin.cpp b/plugins/dm.gui/plugin.cpp index c44784840f..81467c9663 100644 --- a/plugins/dm.gui/plugin.cpp +++ b/plugins/dm.gui/plugin.cpp @@ -9,7 +9,7 @@ // Modules #include "icommandsystem.h" -#include "ientity.h" +#include "scene/Entity.h" #include "ifilesystem.h" #include "ifonts.h" #include "igame.h" diff --git a/plugins/dm.objectives/ObjectiveEntity.cpp b/plugins/dm.objectives/ObjectiveEntity.cpp index 0c88e4f0a3..3a82a67448 100644 --- a/plugins/dm.objectives/ObjectiveEntity.cpp +++ b/plugins/dm.objectives/ObjectiveEntity.cpp @@ -6,8 +6,9 @@ #include "itextstream.h" #include "iscenegraph.h" #include "iundo.h" -#include "ientity.h" +#include "scene/Entity.h" +#include "scene/Entity.h" #include "string/convert.h" #include "string/predicate.h" #include "string/split.h" @@ -66,7 +67,7 @@ void ObjectiveEntity::readObjectiveConditions(Entity& ent) int index = string::convert(results[1].str()); // Valid indices are [1..infinity) - if (index < 1) + if (index < 1) { continue; // invalid index, continue } @@ -93,7 +94,7 @@ void ObjectiveEntity::readObjectiveConditions(Entity& ent) } else { - rWarning() << "Unsupported objective condition source state encountered: " + rWarning() << "Unsupported objective condition source state encountered: " << kv->second << std::endl; } } @@ -117,7 +118,7 @@ void ObjectiveEntity::readObjectiveConditions(Entity& ent) } else { - rWarning() << "Unsupported objective condition type encountered: " + rWarning() << "Unsupported objective condition type encountered: " << kv->second << std::endl; } } @@ -130,7 +131,7 @@ void ObjectiveEntity::readObjectiveConditions(Entity& ent) void ObjectiveEntity::writeObjectiveConditions(Entity& ent) { - // No need to clear previous set of obj_condition_ spawnargs, + // No need to clear previous set of obj_condition_ spawnargs, // as they've been removed by clearEntity() already // Spawnargs are numbered starting with 1 as first index @@ -138,7 +139,7 @@ void ObjectiveEntity::writeObjectiveConditions(Entity& ent) // Go through all the conditions and save them. Skip invalid ones such that the // set of conditions will be "compressed" in terms of their indices. - for (ObjectiveEntity::ConditionMap::const_iterator i = _objConditions.begin(); + for (ObjectiveEntity::ConditionMap::const_iterator i = _objConditions.begin(); i != _objConditions.end(); ++i) { const ObjectiveCondition& cond = *i->second; @@ -293,7 +294,7 @@ int ObjectiveEntity::moveObjective(int index, int delta) } // Constrain the obj index to sane values - if (targetIndex < 0) + if (targetIndex < 0) { targetIndex = 0; } @@ -311,7 +312,7 @@ int ObjectiveEntity::moveObjective(int index, int delta) if (oldObj == _objectives.end()) return -1; // invalid source objective - if (newObj == _objectives.end()) + if (newObj == _objectives.end()) { // no objective at the target index, just re-locate the source objective Objective temp(oldObj->second); @@ -320,7 +321,7 @@ int ObjectiveEntity::moveObjective(int index, int delta) _objectives[targetIndex] = temp; } - else + else { // Both source and target indices exist, swap them Objective temp(oldObj->second); diff --git a/plugins/dm.objectives/ObjectiveEntityFinder.cpp b/plugins/dm.objectives/ObjectiveEntityFinder.cpp index 4aa20bd5e1..62c2763ad8 100644 --- a/plugins/dm.objectives/ObjectiveEntityFinder.cpp +++ b/plugins/dm.objectives/ObjectiveEntityFinder.cpp @@ -1,6 +1,7 @@ #include "ObjectiveEntityFinder.h" -#include "ientity.h" +#include "scene/Entity.h" +#include "scene/Entity.h" namespace objectives { @@ -51,6 +52,3 @@ bool ObjectiveEntityFinder::pre(const scene::INodePtr& node) } } - - - diff --git a/plugins/dm.objectives/ObjectiveKeyExtractor.h b/plugins/dm.objectives/ObjectiveKeyExtractor.h index 3e32cd30a6..e6e2356fc3 100644 --- a/plugins/dm.objectives/ObjectiveKeyExtractor.h +++ b/plugins/dm.objectives/ObjectiveKeyExtractor.h @@ -3,7 +3,7 @@ #include "Objective.h" #include "Specifier.h" -#include "ientity.h" +#include "scene/Entity.h" namespace objectives { diff --git a/plugins/dm.objectives/ObjectivesEditor.cpp b/plugins/dm.objectives/ObjectivesEditor.cpp index 7914a20c09..9a6cf3a5ae 100644 --- a/plugins/dm.objectives/ObjectivesEditor.cpp +++ b/plugins/dm.objectives/ObjectivesEditor.cpp @@ -14,7 +14,7 @@ #include "iregistry.h" #include "ieclass.h" #include "igame.h" -#include "ientity.h" +#include "scene/Entity.h" #include "wxutil/dialog/MessageBox.h" @@ -52,7 +52,7 @@ ObjectivesEditor::ObjectivesEditor() : wxButton* successLogicButton = findNamedObject(this, "ObjDialogSuccessLogicButton"); successLogicButton->Connect(wxEVT_BUTTON, wxCommandEventHandler(ObjectivesEditor::_onEditLogic), NULL, this); successLogicButton->Enable(false); - + wxButton* objCondButton = findNamedObject(this, "ObjDialogObjConditionsButton"); objCondButton->Connect(wxEVT_BUTTON, wxCommandEventHandler(ObjectivesEditor::_onEditObjConditions), NULL, this); objCondButton->Enable(false); @@ -98,13 +98,13 @@ void ObjectivesEditor::setupEntitiesPanel() _objectiveEntityView->AppendTextColumn("", _objEntityColumns.displayName.getColumnIndex(), wxDATAVIEW_CELL_INERT, wxCOL_WIDTH_AUTOSIZE, wxALIGN_NOT, wxDATAVIEW_COL_SORTABLE); - _objectiveEntityView->Connect(wxEVT_DATAVIEW_SELECTION_CHANGED, + _objectiveEntityView->Connect(wxEVT_DATAVIEW_SELECTION_CHANGED, wxDataViewEventHandler(ObjectivesEditor::_onEntitySelectionChanged), NULL, this); // Active-at-start column (checkbox) - _objectiveEntityView->Connect(wxEVT_DATAVIEW_ITEM_EDITING_DONE, + _objectiveEntityView->Connect(wxEVT_DATAVIEW_ITEM_EDITING_DONE, wxDataViewEventHandler(ObjectivesEditor::_onStartActiveCellToggled), NULL, this); - + // Connect button signals findNamedObject(this, "ObjDialogAddEntityButton")->Connect( wxEVT_BUTTON, wxCommandEventHandler(ObjectivesEditor::_onAddEntity), NULL, this); @@ -136,7 +136,7 @@ void ObjectivesEditor::setupObjectivesPanel() _objectiveView->Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, &ObjectivesEditor::_onObjectiveSelectionChanged, this); _objectiveView->Bind(wxEVT_DATAVIEW_ITEM_ACTIVATED, &ObjectivesEditor::_onObjectiveActivated, this); - + wxButton* addButton = findNamedObject(this, "ObjDialogAddObjectiveButton"); addButton->Connect(wxEVT_BUTTON, wxCommandEventHandler(ObjectivesEditor::_onAddObjective), NULL, this); @@ -194,7 +194,7 @@ void ObjectivesEditor::populateWidgets() // Select the first entity in the list for convenience wxDataViewItemArray children; _objectiveEntityList->GetChildren(_objectiveEntityList->GetRoot(), children); - + if (!children.empty()) { _objectiveEntityView->Select(children.front()); @@ -325,7 +325,7 @@ void ObjectivesEditor::selectObjectiveByIndex(int index) if (index == -1) return; // Select the new objective - wxDataViewItem newObjLoc = _objectiveList->FindInteger(index, + wxDataViewItem newObjLoc = _objectiveList->FindInteger(index, _objectiveColumns.objNumber); _objectiveView->Select(newObjLoc); @@ -348,7 +348,7 @@ void ObjectivesEditor::handleEntitySelectionChange() { // Clear the objectives list _objectiveList->Clear(); - + updateEditorButtonPanel(); } @@ -417,7 +417,7 @@ void ObjectivesEditor::updateEditorButtonPanel() // Get the selection wxDataViewItem item = _objectiveEntityView->GetSelection(); - if (item.IsOk()) + if (item.IsOk()) { // Get name of the entity and find the corresponding ObjectiveEntity in // the map @@ -465,8 +465,8 @@ void ObjectivesEditor::_onAddEntity(wxCommandEvent& ev) // Obtain the entity class object IEntityClassPtr eclass = GlobalEntityClassManager().findClass(objEClass); - - if (eclass) + + if (eclass) { // Construct a Node of this entity type IEntityNodePtr node(GlobalEntityModule().createEntity(eclass)); @@ -495,8 +495,8 @@ void ObjectivesEditor::_onDeleteEntity(wxCommandEvent& ev) { // Get the selection wxDataViewItem item = _objectiveEntityView->GetSelection(); - - if (item.IsOk()) + + if (item.IsOk()) { // Get the name of the selected entity wxutil::TreeModel::Row row(item, *_objectiveEntityList); @@ -591,7 +591,7 @@ void ObjectivesEditor::_onClearObjectives(wxCommandEvent& ev) void ObjectivesEditor::_onEditLogic(wxCommandEvent& ev) { MissionLogicDialog* dialog = new MissionLogicDialog(this, *_curEntity->second); - + dialog->ShowModal(); dialog->Destroy(); @@ -601,7 +601,7 @@ void ObjectivesEditor::_onEditLogic(wxCommandEvent& ev) void ObjectivesEditor::_onEditObjConditions(wxCommandEvent& ev) { ObjectiveConditionsDialog* dialog = new ObjectiveConditionsDialog(this, *_curEntity->second); - + dialog->ShowModal(); dialog->Destroy(); diff --git a/plugins/dm.objectives/TargetList.h b/plugins/dm.objectives/TargetList.h index 0b3a52d52e..837890688b 100644 --- a/plugins/dm.objectives/TargetList.h +++ b/plugins/dm.objectives/TargetList.h @@ -1,6 +1,6 @@ #pragma once -#include "ientity.h" +#include "scene/Entity.h" #include #include diff --git a/plugins/dm.objectives/ce/specpanel/EntityNameSpecifierPanel.cpp b/plugins/dm.objectives/ce/specpanel/EntityNameSpecifierPanel.cpp index dedd3dd539..1be821d2a5 100644 --- a/plugins/dm.objectives/ce/specpanel/EntityNameSpecifierPanel.cpp +++ b/plugins/dm.objectives/ce/specpanel/EntityNameSpecifierPanel.cpp @@ -3,7 +3,7 @@ #include #include "inode.h" #include "imap.h" -#include "ientity.h" +#include "scene/Entity.h" namespace objectives { @@ -75,4 +75,3 @@ EntityNameSpecifierPanel::RegHelper EntityNameSpecifierPanel::_regHelper; } } - diff --git a/plugins/dm.objectives/objectives.cpp b/plugins/dm.objectives/objectives.cpp index 643c0bd3db..7dd78f29f7 100644 --- a/plugins/dm.objectives/objectives.cpp +++ b/plugins/dm.objectives/objectives.cpp @@ -6,7 +6,7 @@ #include "iradiant.h" #include "iscenegraph.h" #include "ieclass.h" -#include "ientity.h" +#include "scene/Entity.h" #include "itextstream.h" #include "ce/ComponentEditorFactory.h" diff --git a/plugins/dm.stimresponse/SRPropertyLoader.h b/plugins/dm.stimresponse/SRPropertyLoader.h index e50efda9a3..ac1802d53c 100644 --- a/plugins/dm.stimresponse/SRPropertyLoader.h +++ b/plugins/dm.stimresponse/SRPropertyLoader.h @@ -1,7 +1,7 @@ #pragma once #include "ieclass.h" -#include "ientity.h" +#include "scene/Entity.h" #include "SREntity.h" #include "StimTypes.h" diff --git a/plugins/dm.stimresponse/SRPropertyRemover.h b/plugins/dm.stimresponse/SRPropertyRemover.h index ff393a2719..580df11cd9 100644 --- a/plugins/dm.stimresponse/SRPropertyRemover.h +++ b/plugins/dm.stimresponse/SRPropertyRemover.h @@ -1,6 +1,6 @@ #pragma once -#include "ientity.h" +#include "scene/Entity.h" #include "SREntity.h" #include "StimTypes.h" diff --git a/plugins/dm.stimresponse/StimResponseEditor.h b/plugins/dm.stimresponse/StimResponseEditor.h index ac5fc0988b..a8917552cd 100644 --- a/plugins/dm.stimresponse/StimResponseEditor.h +++ b/plugins/dm.stimresponse/StimResponseEditor.h @@ -1,7 +1,7 @@ #pragma once #include -#include "ientity.h" +#include "scene/Entity.h" #include "iradiant.h" #include "icommandsystem.h" #include "iscenegraph.h" diff --git a/plugins/dm.stimresponse/StimTypes.h b/plugins/dm.stimresponse/StimTypes.h index 7980aba8f1..63f2332e78 100644 --- a/plugins/dm.stimresponse/StimTypes.h +++ b/plugins/dm.stimresponse/StimTypes.h @@ -2,7 +2,7 @@ #include #include -#include "ientity.h" +#include "scene/Entity.h" #include "wxutil/dataview/TreeModel.h" class wxBitmapComboBox; diff --git a/plugins/script/CMakeLists.txt b/plugins/script/CMakeLists.txt index be1cb6d4a8..ca436c7ef9 100644 --- a/plugins/script/CMakeLists.txt +++ b/plugins/script/CMakeLists.txt @@ -33,10 +33,10 @@ target_include_directories(script SYSTEM PUBLIC ${CMAKE_SOURCE_DIR}/libs/pybind ${Python_INCLUDE_DIRS}) target_link_libraries(script PUBLIC - math + math scene ${Python_LIBRARIES} ${SIGC_LIBRARIES}) # Enable precompiled header for script plugin if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.16.0") target_precompile_headers(script PRIVATE "precompiled.h") -endif() \ No newline at end of file +endif() diff --git a/plugins/script/interfaces/EntityInterface.cpp b/plugins/script/interfaces/EntityInterface.cpp index 5ee3f3d92b..0305595c71 100644 --- a/plugins/script/interfaces/EntityInterface.cpp +++ b/plugins/script/interfaces/EntityInterface.cpp @@ -2,13 +2,13 @@ #include -#include "ientity.h" +#include "scene/Entity.h" #include "ieclass.h" #include "itextstream.h" #include "../SceneNodeBuffer.h" -namespace script +namespace script { // Constructor, checks if the passed node is actually an entity @@ -135,7 +135,7 @@ struct EntityKeyValuePair : } }; -void EntityInterface::registerInterface(py::module& scope, py::dict& globals) +void EntityInterface::registerInterface(py::module& scope, py::dict& globals) { // Add the EntityNode interface py::class_ entityNode(scope, "EntityNode"); diff --git a/plugins/script/interfaces/EntityInterface.h b/plugins/script/interfaces/EntityInterface.h index 6cca50f880..f729751bed 100644 --- a/plugins/script/interfaces/EntityInterface.h +++ b/plugins/script/interfaces/EntityInterface.h @@ -3,12 +3,13 @@ #include "iscript.h" #include "iscriptinterface.h" -#include "ientity.h" +#include "scene/Entity.h" +#include "scene/Entity.h" #include "EClassInterface.h" #include "SceneGraphInterface.h" -namespace script +namespace script { // Visitor object diff --git a/radiant/ui/UserInterfaceModule.cpp b/radiant/ui/UserInterfaceModule.cpp index ab1ea948b5..dea80b295c 100644 --- a/radiant/ui/UserInterfaceModule.cpp +++ b/radiant/ui/UserInterfaceModule.cpp @@ -6,7 +6,7 @@ #include "i18n.h" #include "ilayer.h" #include "ifilter.h" -#include "ientity.h" +#include "scene/Entity.h" #include "imru.h" #include "imap.h" #include "ibrush.h" diff --git a/radiant/ui/animationpreview/AnimationPreview.cpp b/radiant/ui/animationpreview/AnimationPreview.cpp index 22113cd1be..2e68656004 100644 --- a/radiant/ui/animationpreview/AnimationPreview.cpp +++ b/radiant/ui/animationpreview/AnimationPreview.cpp @@ -2,7 +2,7 @@ #include "i18n.h" #include "imodel.h" -#include "ientity.h" +#include "scene/Entity.h" #include "ieclass.h" #include "imd5anim.h" #include "itextstream.h" @@ -45,7 +45,7 @@ void AnimationPreview::setModelNode(const scene::INodePtr& node) // Ensure that this is an MD5 model node model::ModelNodePtr model = Node_getModel(node); - + if (!model) { rError() << "AnimationPreview::setModelNode: node is not a model." << std::endl; diff --git a/radiant/ui/animationpreview/AnimationPreview.h b/radiant/ui/animationpreview/AnimationPreview.h index c096b64b92..85a40158b1 100644 --- a/radiant/ui/animationpreview/AnimationPreview.h +++ b/radiant/ui/animationpreview/AnimationPreview.h @@ -6,7 +6,7 @@ #include "imd5model.h" #include "inode.h" #include "ieclass.h" -#include "ientity.h" +#include "scene/Entity.h" #include "imap.h" namespace ui diff --git a/radiant/ui/common/EntityChooser.cpp b/radiant/ui/common/EntityChooser.cpp index e1257943f9..9977225b2f 100644 --- a/radiant/ui/common/EntityChooser.cpp +++ b/radiant/ui/common/EntityChooser.cpp @@ -2,7 +2,7 @@ #include "i18n.h" #include "inode.h" -#include "ientity.h" +#include "scene/Entity.h" #include "ui/imainframe.h" #include "iscenegraph.h" diff --git a/radiant/ui/eclasstree/EClassTree.cpp b/radiant/ui/eclasstree/EClassTree.cpp index 4346ff734c..27959216d9 100644 --- a/radiant/ui/eclasstree/EClassTree.cpp +++ b/radiant/ui/eclasstree/EClassTree.cpp @@ -1,7 +1,7 @@ #include "EClassTree.h" #include "ieclass.h" -#include "ientity.h" +#include "scene/Entity.h" #include "iselection.h" #include "i18n.h" diff --git a/radiant/ui/einspector/AddPropertyDialog.cpp b/radiant/ui/einspector/AddPropertyDialog.cpp index 52d1493b83..30e45373f0 100644 --- a/radiant/ui/einspector/AddPropertyDialog.cpp +++ b/radiant/ui/einspector/AddPropertyDialog.cpp @@ -5,7 +5,7 @@ #include "ui/imainframe.h" #include "ieclass.h" #include "igame.h" -#include "ientity.h" +#include "scene/Entity.h" #include "wxutil/dataview/TreeView.h" #include diff --git a/radiant/ui/einspector/AddPropertyDialog.h b/radiant/ui/einspector/AddPropertyDialog.h index fb8bb4a991..62c81d994f 100644 --- a/radiant/ui/einspector/AddPropertyDialog.h +++ b/radiant/ui/einspector/AddPropertyDialog.h @@ -7,7 +7,7 @@ #include "wxutil/XmlResourceBasedWidget.h" #include -#include "ientity.h" +#include "scene/Entity.h" class wxDataViewCtrl; diff --git a/radiant/ui/einspector/AnglePropertyEditor.cpp b/radiant/ui/einspector/AnglePropertyEditor.cpp index 3a59d8379a..358461edb6 100644 --- a/radiant/ui/einspector/AnglePropertyEditor.cpp +++ b/radiant/ui/einspector/AnglePropertyEditor.cpp @@ -1,6 +1,6 @@ #include "AnglePropertyEditor.h" -#include "ientity.h" +#include "scene/Entity.h" #include "string/convert.h" #include @@ -34,7 +34,7 @@ AnglePropertyEditor::AnglePropertyEditor(wxWindow* parent, IEntitySelection& ent wxBitmapButton* AnglePropertyEditor::constructAngleButton(wxPanel* parent, const std::string& icon, int angleValue) { - wxBitmapButton* button = new wxBitmapButton(parent, wxID_ANY, + wxBitmapButton* button = new wxBitmapButton(parent, wxID_ANY, wxutil::GetLocalBitmap(icon)); button->Bind(wxEVT_BUTTON, &AnglePropertyEditor::_onButtonClick, this); diff --git a/radiant/ui/einspector/BooleanPropertyEditor.cpp b/radiant/ui/einspector/BooleanPropertyEditor.cpp index bcfc445adc..fb277b71cf 100644 --- a/radiant/ui/einspector/BooleanPropertyEditor.cpp +++ b/radiant/ui/einspector/BooleanPropertyEditor.cpp @@ -1,6 +1,6 @@ #include "BooleanPropertyEditor.h" -#include "ientity.h" +#include "scene/Entity.h" #include #include diff --git a/radiant/ui/einspector/ClassnamePropertyEditor.cpp b/radiant/ui/einspector/ClassnamePropertyEditor.cpp index dcfecb1eff..1e4d77d6e4 100644 --- a/radiant/ui/einspector/ClassnamePropertyEditor.cpp +++ b/radiant/ui/einspector/ClassnamePropertyEditor.cpp @@ -2,7 +2,7 @@ #include "PropertyEditorFactory.h" #include "i18n.h" -#include "ientity.h" +#include "scene/Entity.h" #include "ieclass.h" #include "icommandsystem.h" diff --git a/radiant/ui/einspector/ColourPropertyEditor.cpp b/radiant/ui/einspector/ColourPropertyEditor.cpp index 4dc36f159e..c7bfb70cd5 100644 --- a/radiant/ui/einspector/ColourPropertyEditor.cpp +++ b/radiant/ui/einspector/ColourPropertyEditor.cpp @@ -1,6 +1,6 @@ #include "ColourPropertyEditor.h" -#include "ientity.h" +#include "scene/Entity.h" #include #include @@ -25,11 +25,11 @@ ColourPropertyEditor::ColourPropertyEditor(wxWindow* parent, IEntitySelection& e setMainWidget(mainVBox); // Create the colour button - _colorButton = new wxColourPickerCtrl(mainVBox, wxID_ANY, - wxColour(0, 0, 0), wxDefaultPosition, + _colorButton = new wxColourPickerCtrl(mainVBox, wxID_ANY, + wxColour(0, 0, 0), wxDefaultPosition, wxDefaultSize, wxCLRP_USE_TEXTCTRL); - _colorButton->Connect(wxEVT_COLOURPICKER_CHANGED, + _colorButton->Connect(wxEVT_COLOURPICKER_CHANGED, wxColourPickerEventHandler(ColourPropertyEditor::_onColorSet), NULL, this); mainVBox->GetSizer()->Add(_colorButton, 1, wxEXPAND | wxALL, 15); @@ -78,4 +78,3 @@ void ColourPropertyEditor::_onColorSet(wxColourPickerEvent& ev) } // namespace ui - diff --git a/radiant/ui/einspector/EntityInspector.cpp b/radiant/ui/einspector/EntityInspector.cpp index a57d34a824..5f7fb0571f 100644 --- a/radiant/ui/einspector/EntityInspector.cpp +++ b/radiant/ui/einspector/EntityInspector.cpp @@ -4,7 +4,7 @@ #include "AddPropertyDialog.h" #include "i18n.h" -#include "ientity.h" +#include "scene/Entity.h" #include "ideclmanager.h" #include "ieclass.h" #include "iregistry.h" @@ -1533,7 +1533,7 @@ std::string EntityInspector::getPropertyTypeForAttachmentKey(const std::string& _entitySelection->foreachEntity([&](const IEntityNodePtr& entity) { - entity->getEntity().forEachAttachment([&](const Entity::Attachment& attachment) + entity->getEntity().forEachAttachment([&](const EntityAttachment& attachment) { if (attachment.name != attachmentName) return; diff --git a/radiant/ui/einspector/EntityPropertyEditor.cpp b/radiant/ui/einspector/EntityPropertyEditor.cpp index 05dde63839..84309c9cf3 100644 --- a/radiant/ui/einspector/EntityPropertyEditor.cpp +++ b/radiant/ui/einspector/EntityPropertyEditor.cpp @@ -3,7 +3,7 @@ #include "i18n.h" #include "iscenegraph.h" #include "iundo.h" -#include "ientity.h" +#include "scene/Entity.h" #include "PropertyEditorFactory.h" diff --git a/radiant/ui/einspector/FloatPropertyEditor.cpp b/radiant/ui/einspector/FloatPropertyEditor.cpp index 982ef30154..ce48417443 100644 --- a/radiant/ui/einspector/FloatPropertyEditor.cpp +++ b/radiant/ui/einspector/FloatPropertyEditor.cpp @@ -1,6 +1,6 @@ #include "FloatPropertyEditor.h" -#include "ientity.h" +#include "scene/Entity.h" #include "i18n.h" #include "itextstream.h" @@ -51,7 +51,7 @@ void FloatPropertyEditor::updateFromEntity() if (_spinCtrl == nullptr) return; auto value = string::convert(_entities.getSharedKeyValue(_key->getFullKey(), false), 0); - + _spinCtrl->SetValue(value); } diff --git a/radiant/ui/einspector/FxPropertyEditor.cpp b/radiant/ui/einspector/FxPropertyEditor.cpp index 36a495cd95..06c0d7623f 100644 --- a/radiant/ui/einspector/FxPropertyEditor.cpp +++ b/radiant/ui/einspector/FxPropertyEditor.cpp @@ -2,7 +2,7 @@ #include "i18n.h" #include "icommandsystem.h" -#include "ientity.h" +#include "scene/Entity.h" #include "ClassnamePropertyEditor.h" #include "PropertyEditorFactory.h" diff --git a/radiant/ui/einspector/ModelPropertyEditor.cpp b/radiant/ui/einspector/ModelPropertyEditor.cpp index 62988fc479..ee02c13a42 100644 --- a/radiant/ui/einspector/ModelPropertyEditor.cpp +++ b/radiant/ui/einspector/ModelPropertyEditor.cpp @@ -5,7 +5,7 @@ #include "ui/particles/ParticleChooserDialog.h" #include "i18n.h" -#include "ientity.h" +#include "scene/Entity.h" #include "scenelib.h" #include "wxutil/dialog/MessageBox.h" diff --git a/radiant/ui/einspector/PropertyEditor.cpp b/radiant/ui/einspector/PropertyEditor.cpp index 14325fb7f6..e6cbca897c 100644 --- a/radiant/ui/einspector/PropertyEditor.cpp +++ b/radiant/ui/einspector/PropertyEditor.cpp @@ -1,6 +1,6 @@ #include "PropertyEditor.h" -#include "ientity.h" +#include "scene/Entity.h" #include "iundo.h" #include #include diff --git a/radiant/ui/einspector/SkinPropertyEditor.cpp b/radiant/ui/einspector/SkinPropertyEditor.cpp index 7fe64233e9..67149d9634 100644 --- a/radiant/ui/einspector/SkinPropertyEditor.cpp +++ b/radiant/ui/einspector/SkinPropertyEditor.cpp @@ -9,7 +9,7 @@ #include #include -#include "ientity.h" +#include "scene/Entity.h" namespace ui { diff --git a/radiant/ui/einspector/SoundPropertyEditor.cpp b/radiant/ui/einspector/SoundPropertyEditor.cpp index 304c4f0ff2..96cf7bd2cc 100644 --- a/radiant/ui/einspector/SoundPropertyEditor.cpp +++ b/radiant/ui/einspector/SoundPropertyEditor.cpp @@ -4,7 +4,7 @@ #include "ui/iresourcechooser.h" #include "ui/idialogmanager.h" #include "i18n.h" -#include "ientity.h" +#include "scene/Entity.h" #include "isound.h" #include diff --git a/radiant/ui/einspector/TexturePropertyEditor.cpp b/radiant/ui/einspector/TexturePropertyEditor.cpp index 9923758178..7a47bc1b83 100644 --- a/radiant/ui/einspector/TexturePropertyEditor.cpp +++ b/radiant/ui/einspector/TexturePropertyEditor.cpp @@ -3,7 +3,7 @@ #include "PropertyEditorFactory.h" #include "i18n.h" -#include "ientity.h" +#include "scene/Entity.h" #include #include diff --git a/radiant/ui/einspector/Vector3PropertyEditor.cpp b/radiant/ui/einspector/Vector3PropertyEditor.cpp index 1ef3b737b6..6ed1c10863 100644 --- a/radiant/ui/einspector/Vector3PropertyEditor.cpp +++ b/radiant/ui/einspector/Vector3PropertyEditor.cpp @@ -1,7 +1,7 @@ #include "Vector3PropertyEditor.h" #include "i18n.h" -#include "ientity.h" +#include "scene/Entity.h" #include #include @@ -48,7 +48,7 @@ Vector3PropertyEditor::Vector3PropertyEditor(wxWindow* parent, IEntitySelection& // Register the main widget in the base class setMainWidget(mainVBox); - // Create the spin buttons + // Create the spin buttons _xValue = makeSpinCtrl(mainVBox); _yValue = makeSpinCtrl(mainVBox); _zValue = makeSpinCtrl(mainVBox); diff --git a/radiant/ui/lightinspector/LightInspector.cpp b/radiant/ui/lightinspector/LightInspector.cpp index 2a8c07f656..fc6b5f69f2 100644 --- a/radiant/ui/lightinspector/LightInspector.cpp +++ b/radiant/ui/lightinspector/LightInspector.cpp @@ -1,7 +1,7 @@ #include "LightInspector.h" #include "icameraview.h" -#include "ientity.h" +#include "scene/Entity.h" #include "ieclass.h" #include "ishaders.h" #include "iselection.h" diff --git a/radiant/ui/materials/editor/MaterialPreview.cpp b/radiant/ui/materials/editor/MaterialPreview.cpp index e9fd42db56..ac4d53b8f4 100644 --- a/radiant/ui/materials/editor/MaterialPreview.cpp +++ b/radiant/ui/materials/editor/MaterialPreview.cpp @@ -2,7 +2,7 @@ #include "i18n.h" #include "ibrush.h" -#include "ientity.h" +#include "scene/Entity.h" #include "imodelcache.h" #include "ieclass.h" #include "ishaders.h" @@ -77,7 +77,7 @@ void MaterialPreview::setupToolbar() _testModelCubeButton = toolbar->AddRadioTool(wxID_ANY, "", wxutil::GetLocalBitmap("cube.png", wxART_TOOLBAR)); _testModelCubeButton->SetShortHelp(_("Show Cube")); toolbar->ToggleTool(_testModelCubeButton->GetId(), true); - + _testModelSphereButton = toolbar->AddRadioTool(wxID_ANY, "", wxutil::GetLocalBitmap("sphere.png", wxART_TOOLBAR)); _testModelSphereButton->SetShortHelp(_("Show Sphere")); @@ -369,7 +369,7 @@ void MaterialPreview::setupRoom() _rootNode->addChildNode(roomEntity); roomEntity->addChildNode(_room); - + updateRoomSkin(getRoomMaterial()); } @@ -452,9 +452,9 @@ void MaterialPreview::setLightClassname(const std::string& className) Vector3 MaterialPreview::getLightColour() { if (!_light) return Vector3(0,0,0); - + auto value = Node_getEntity(_light)->getKeyValue("_color"); - + if (value.empty()) { value = "1 1 1"; diff --git a/radiant/ui/ortho/OrthoContextMenu.cpp b/radiant/ui/ortho/OrthoContextMenu.cpp index bf85f2d3f9..0505d5f53d 100644 --- a/radiant/ui/ortho/OrthoContextMenu.cpp +++ b/radiant/ui/ortho/OrthoContextMenu.cpp @@ -6,7 +6,7 @@ #include "ui/iresourcechooser.h" #include "ui/idialogmanager.h" #include "entitylib.h" // EntityFindByClassnameWalker -#include "ientity.h" // Node_getEntity() +#include "scene/Entity.h" // Node_getEntity() #include "iregistry.h" #include "ui/imainframe.h" diff --git a/radiant/xyview/OrthoView.cpp b/radiant/xyview/OrthoView.cpp index b261bfda4d..37ad57600f 100644 --- a/radiant/xyview/OrthoView.cpp +++ b/radiant/xyview/OrthoView.cpp @@ -7,7 +7,7 @@ #include "ui/ieventmanager.h" #include "ui/imainframe.h" #include "icolourscheme.h" -#include "ientity.h" +#include "scene/Entity.h" #include "igrid.h" #include "iregion.h" #include "ui/istatusbarmanager.h" diff --git a/radiantcore/brush/csg/CSG.cpp b/radiantcore/brush/csg/CSG.cpp index c9c25a4a66..56af18f3a3 100644 --- a/radiantcore/brush/csg/CSG.cpp +++ b/radiantcore/brush/csg/CSG.cpp @@ -7,7 +7,7 @@ #include "iundo.h" #include "igrid.h" #include "iselection.h" -#include "ientity.h" +#include "scene/Entity.h" #include "scenelib.h" #include "shaderlib.h" @@ -280,7 +280,7 @@ void subtractBrushesFromUnselected(const cmd::ArgumentList& args) radiant::NotificationMessage::SendInformation( _("Note: be careful when using the CSG tool, as you might end up\n" "with an unnecessary number of tiny brushes and/or leaks.\n" - "This popup will not be shown again."), + "This popup will not be shown again."), _("This Is Not Dromed Warning")); // Disable this warning diff --git a/radiantcore/brush/export/CollisionModel.cpp b/radiantcore/brush/export/CollisionModel.cpp index 4ab8f0a1aa..f8ed9f50a5 100644 --- a/radiantcore/brush/export/CollisionModel.cpp +++ b/radiantcore/brush/export/CollisionModel.cpp @@ -2,7 +2,7 @@ #include "itextstream.h" #include "iselection.h" -#include "ientity.h" +#include "scene/Entity.h" #include "selectionlib.h" #include "gamelib.h" #include "brush/Brush.h" diff --git a/radiantcore/entity/AngleKey.cpp b/radiantcore/entity/AngleKey.cpp index cde7b70e7c..57e7a5481e 100644 --- a/radiantcore/entity/AngleKey.cpp +++ b/radiantcore/entity/AngleKey.cpp @@ -1,6 +1,6 @@ #include "AngleKey.h" -#include "ientity.h" +#include "scene/Entity.h" #include "string/convert.h" #include "math/Matrix4.h" @@ -35,4 +35,3 @@ float AngleKey::getRotatedValue(float angle, const Quaternion& rotation) } } // namespace - diff --git a/radiantcore/entity/ColourKey.h b/radiantcore/entity/ColourKey.h index ece377555b..c8bae0734a 100644 --- a/radiantcore/entity/ColourKey.h +++ b/radiantcore/entity/ColourKey.h @@ -1,7 +1,7 @@ #pragma once #include -#include "ientity.h" +#include "scene/Entity.h" #include "irender.h" namespace entity diff --git a/radiantcore/entity/EntityModule.cpp b/radiantcore/entity/EntityModule.cpp index 24b914177b..50662193ec 100644 --- a/radiantcore/entity/EntityModule.cpp +++ b/radiantcore/entity/EntityModule.cpp @@ -12,7 +12,7 @@ #include "string/replace.h" -#include "scene/SpawnArgs.h" +#include "scene/Entity.h" #include "light/LightNode.h" #include "doom3group/StaticGeometryNode.h" diff --git a/radiantcore/entity/EntityModule.h b/radiantcore/entity/EntityModule.h index 907f8c6f97..93e72ecae3 100644 --- a/radiantcore/entity/EntityModule.h +++ b/radiantcore/entity/EntityModule.h @@ -1,6 +1,6 @@ #pragma once -#include "ientity.h" +#include "scene/Entity.h" #include "ieclass.h" #include diff --git a/radiantcore/entity/EntityNode.cpp b/radiantcore/entity/EntityNode.cpp index 6742e7b07d..69a665f31d 100644 --- a/radiantcore/entity/EntityNode.cpp +++ b/radiantcore/entity/EntityNode.cpp @@ -135,7 +135,7 @@ void EntityNode::destruct() void EntityNode::createAttachedEntities() { _spawnArgs.forEachAttachment( - [this](const Entity::Attachment& a) + [this](const EntityAttachment& a) { // Since we can't yet handle joint positions, ignore this attachment // if it is attached to a joint diff --git a/radiantcore/entity/EntityNode.h b/radiantcore/entity/EntityNode.h index 3f4d44987f..83bf03609e 100644 --- a/radiantcore/entity/EntityNode.h +++ b/radiantcore/entity/EntityNode.h @@ -1,7 +1,7 @@ #pragma once #include -#include "ientity.h" +#include "scene/Entity.h" #include "inamespace.h" #include "icomparablenode.h" #include "Bounded.h" @@ -46,7 +46,7 @@ class EntityNode : IEntityClassPtr _eclass; // The actual entity (which contains the key/value pairs) - SpawnArgs _spawnArgs; + Entity _spawnArgs; // Transformation applied to this node and its children Matrix4 _localToParent = Matrix4::getIdentity(); diff --git a/radiantcore/entity/EntitySettings.h b/radiantcore/entity/EntitySettings.h index 989318506e..8141fcfd75 100644 --- a/radiantcore/entity/EntitySettings.h +++ b/radiantcore/entity/EntitySettings.h @@ -1,6 +1,6 @@ #pragma once -#include "ientity.h" +#include "scene/Entity.h" #include "iregistry.h" #include #include diff --git a/radiantcore/entity/KeyObserverDelegate.h b/radiantcore/entity/KeyObserverDelegate.h index 3ac57e9f5c..3b49641832 100644 --- a/radiantcore/entity/KeyObserverDelegate.h +++ b/radiantcore/entity/KeyObserverDelegate.h @@ -1,6 +1,6 @@ #pragma once -#include "ientity.h" +#include "scene/Entity.h" #include namespace entity @@ -41,4 +41,4 @@ class KeyObserverDelegate : {} }; -} // namespace entity \ No newline at end of file +} // namespace entity diff --git a/radiantcore/entity/KeyObserverMap.h b/radiantcore/entity/KeyObserverMap.h index 9781ca6f83..4896dd6e26 100644 --- a/radiantcore/entity/KeyObserverMap.h +++ b/radiantcore/entity/KeyObserverMap.h @@ -22,12 +22,12 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #if !defined(INCLUDED_KEYOBSERVERS_H) #define INCLUDED_KEYOBSERVERS_H -#include "ientity.h" +#include "scene/Entity.h" #include #include #include -#include "scene/SpawnArgs.h" +#include "scene/Entity.h" #include "KeyObserverDelegate.h" namespace entity @@ -60,7 +60,7 @@ class KeyObserverMap : std::multimap _connectionsByObserver; // The observed entity - SpawnArgs& _entity; + Entity& _entity; void attachObserver(const std::string& key, KeyObserver& observer) { @@ -83,7 +83,7 @@ class KeyObserverMap : } public: - KeyObserverMap(SpawnArgs& entity) : + KeyObserverMap(Entity& entity) : _entity(entity) { // Start observing the entity diff --git a/radiantcore/entity/KeyValueObserver.cpp b/radiantcore/entity/KeyValueObserver.cpp index 10b5b82ec3..ef60fd6f71 100644 --- a/radiantcore/entity/KeyValueObserver.cpp +++ b/radiantcore/entity/KeyValueObserver.cpp @@ -1,7 +1,7 @@ #include "KeyValueObserver.h" #include "inamespace.h" -#include "ientity.h" +#include "scene/Entity.h" #include "scene/EntityKeyValue.h" namespace entity { diff --git a/radiantcore/entity/KeyValueObserver.h b/radiantcore/entity/KeyValueObserver.h index c21cdf376b..07d8cdfca5 100644 --- a/radiantcore/entity/KeyValueObserver.h +++ b/radiantcore/entity/KeyValueObserver.h @@ -1,7 +1,7 @@ #ifndef _KEY_VALUE_OBSERVER_H_ #define _KEY_VALUE_OBSERVER_H_ -#include "ientity.h" +#include "scene/Entity.h" #include class INamespace; diff --git a/radiantcore/entity/NameKey.h b/radiantcore/entity/NameKey.h index ffc68272c0..d4bb5a7e58 100644 --- a/radiantcore/entity/NameKey.h +++ b/radiantcore/entity/NameKey.h @@ -1,8 +1,8 @@ #pragma once #include "ieclass.h" -#include "ientity.h" -#include "scene/SpawnArgs.h" +#include "scene/Entity.h" +#include "scene/Entity.h" namespace entity { @@ -11,7 +11,7 @@ class NameKey : public KeyObserver { // The reference to the spawnarg structure - SpawnArgs& _entity; + Entity& _entity; // Cached "name" keyvalue std::string _name; @@ -19,7 +19,7 @@ class NameKey : sigc::signal _sigNameChanged; public: - NameKey(SpawnArgs& entity) : + NameKey(Entity& entity) : _entity(entity) {} diff --git a/radiantcore/entity/NameKeyObserver.cpp b/radiantcore/entity/NameKeyObserver.cpp index 04d8567703..0fed7e9c04 100644 --- a/radiantcore/entity/NameKeyObserver.cpp +++ b/radiantcore/entity/NameKeyObserver.cpp @@ -1,7 +1,7 @@ #include "NameKeyObserver.h" #include "inamespace.h" -#include "ientity.h" +#include "scene/Entity.h" #include "scene/EntityKeyValue.h" namespace entity { diff --git a/radiantcore/entity/NameKeyObserver.h b/radiantcore/entity/NameKeyObserver.h index 5322c2bf11..7387f39f0c 100644 --- a/radiantcore/entity/NameKeyObserver.h +++ b/radiantcore/entity/NameKeyObserver.h @@ -1,7 +1,7 @@ #ifndef _NAME_KEY_OBSERVER_H_ #define _NAME_KEY_OBSERVER_H_ -#include "ientity.h" +#include "scene/Entity.h" class INamespace; diff --git a/radiantcore/entity/NamespaceManager.cpp b/radiantcore/entity/NamespaceManager.cpp index 45493bb5b9..e6f468de26 100644 --- a/radiantcore/entity/NamespaceManager.cpp +++ b/radiantcore/entity/NamespaceManager.cpp @@ -5,7 +5,7 @@ #include "string/predicate.h" #include "gamelib.h" -namespace entity +namespace entity { namespace @@ -13,7 +13,7 @@ namespace const char* const NAME_KEY("name"); } -NamespaceManager::NamespaceManager(SpawnArgs& entity) : +NamespaceManager::NamespaceManager(Entity& entity) : _namespace(nullptr), _entity(entity), _updateMutex(false), @@ -23,12 +23,12 @@ NamespaceManager::NamespaceManager(SpawnArgs& entity) : _entity.attachObserver(this); } -NamespaceManager::~NamespaceManager() +NamespaceManager::~NamespaceManager() { // Detach from the observed Entity _entity.detachObserver(this); - if (_namespace != nullptr) + if (_namespace != nullptr) { // We're still attached to a namespace, break the connection disconnectNameObservers(); @@ -39,17 +39,17 @@ NamespaceManager::~NamespaceManager() } // Gets/sets the namespace of this named object -void NamespaceManager::setNamespace(INamespace* space) +void NamespaceManager::setNamespace(INamespace* space) { _namespace = space; } -INamespace* NamespaceManager::getNamespace() const +INamespace* NamespaceManager::getNamespace() const { return _namespace; } -void NamespaceManager::detachNames() +void NamespaceManager::detachNames() { if (_namespace == nullptr) return; @@ -57,7 +57,7 @@ void NamespaceManager::detachNames() detachNameKeys(); } -void NamespaceManager::connectNameObservers() +void NamespaceManager::connectNameObservers() { if (_namespace == nullptr) return; @@ -65,7 +65,7 @@ void NamespaceManager::connectNameObservers() attachKeyObservers(); } -void NamespaceManager::disconnectNameObservers() +void NamespaceManager::disconnectNameObservers() { if (_namespace == nullptr) return; @@ -81,19 +81,19 @@ std::string NamespaceManager::getName() const return _entity.getKeyValue(_nameKey); } -void NamespaceManager::changeName(const std::string& newName) +void NamespaceManager::changeName(const std::string& newName) { // Set the value, this should trigger the nameChanged() event on all observers _entity.setKeyValue(_nameKey, newName); } -void NamespaceManager::onKeyInsert(const std::string& key, EntityKeyValue& value) +void NamespaceManager::onKeyInsert(const std::string& key, EntityKeyValue& value) { // avoid double-updates when the keyvalue gets updated during this process if (_updateMutex) return; // Check if the key is relevant - if (keyIsName(key)) + if (keyIsName(key)) { // Key is a "name", remember that one _nameKeys.insert(std::make_pair(key, &value)); @@ -106,13 +106,13 @@ void NamespaceManager::onKeyInsert(const std::string& key, EntityKeyValue& value attachKeyObserver(key, value); } -void NamespaceManager::onKeyErase(const std::string& key, EntityKeyValue& value) +void NamespaceManager::onKeyErase(const std::string& key, EntityKeyValue& value) { // avoid double-updates when the keyvalue gets updated during this process if (_updateMutex) return; // Check if the key is relevant - if (keyIsName(key)) + if (keyIsName(key)) { // Remove the key from the namespace detachKeyFromNamespace(key, value); @@ -125,7 +125,7 @@ void NamespaceManager::onKeyErase(const std::string& key, EntityKeyValue& value) detachKeyObserver(key, value); } -bool NamespaceManager::keyIsName(const std::string& key) +bool NamespaceManager::keyIsName(const std::string& key) { // In D3, only "name" spawnargs are actual names return key == _nameKey; @@ -147,7 +147,7 @@ void NamespaceManager::attachNames() } } -void NamespaceManager::detachNameKeys() +void NamespaceManager::detachNameKeys() { for (const auto& pair : _nameKeys) { @@ -182,7 +182,7 @@ void NamespaceManager::attachKeyToNamespace(const std::string& key, EntityKeyVal } } -void NamespaceManager::detachKeyFromNamespace(const std::string& key, EntityKeyValue& keyValue) +void NamespaceManager::detachKeyFromNamespace(const std::string& key, EntityKeyValue& keyValue) { if (_namespace == nullptr) return; @@ -224,7 +224,7 @@ void NamespaceManager::attachKeyObservers() }); } -void NamespaceManager::detachKeyObserver(const std::string& key, EntityKeyValue& keyValue) +void NamespaceManager::detachKeyObserver(const std::string& key, EntityKeyValue& keyValue) { if (_namespace == nullptr) return; @@ -240,7 +240,7 @@ void NamespaceManager::detachKeyObserver(const std::string& key, EntityKeyValue& } } -void NamespaceManager::detachKeyObservers() +void NamespaceManager::detachKeyObservers() { // May not be called with empty namespace assert(_namespace); diff --git a/radiantcore/entity/NamespaceManager.h b/radiantcore/entity/NamespaceManager.h index d44417a479..65a214fbe5 100644 --- a/radiantcore/entity/NamespaceManager.h +++ b/radiantcore/entity/NamespaceManager.h @@ -1,10 +1,10 @@ #pragma once -#include "ientity.h" +#include "scene/Entity.h" #include "inamespace.h" #include -#include "scene/SpawnArgs.h" +#include "scene/Entity.h" #include "KeyValueObserver.h" #include "NameKeyObserver.h" #include "util/Noncopyable.h" @@ -20,7 +20,7 @@ class NamespaceManager : INamespace* _namespace; // The attached entity - SpawnArgs& _entity; + Entity& _entity; // All the observed key values of the entity get remembered // This prevents having to traverse all the keyvalues again when changing namespaces @@ -40,7 +40,7 @@ class NamespaceManager : std::string _nameKey; public: - NamespaceManager(SpawnArgs& entity); + NamespaceManager(Entity& entity); ~NamespaceManager(); diff --git a/radiantcore/entity/OriginKey.h b/radiantcore/entity/OriginKey.h index 346c0f57a2..19ca59d7aa 100644 --- a/radiantcore/entity/OriginKey.h +++ b/radiantcore/entity/OriginKey.h @@ -1,6 +1,6 @@ #pragma once -#include "ientity.h" +#include "scene/Entity.h" #include "math/Vector3.h" #include diff --git a/radiantcore/entity/RotationKey.h b/radiantcore/entity/RotationKey.h index 1dea76926c..26ca5c3a3b 100644 --- a/radiantcore/entity/RotationKey.h +++ b/radiantcore/entity/RotationKey.h @@ -1,6 +1,6 @@ #pragma once -#include "ientity.h" +#include "scene/Entity.h" #include "math/Quaternion.h" #include diff --git a/radiantcore/entity/RotationMatrix.cpp b/radiantcore/entity/RotationMatrix.cpp index 77eb5628d0..b9ec25189b 100644 --- a/radiantcore/entity/RotationMatrix.cpp +++ b/radiantcore/entity/RotationMatrix.cpp @@ -1,6 +1,6 @@ #include "RotationMatrix.h" -#include "ientity.h" +#include "scene/Entity.h" #include "math/Matrix4.h" void RotationMatrix::setIdentity() diff --git a/radiantcore/entity/curve/Curve.h b/radiantcore/entity/curve/Curve.h index afb9e56312..3cf7eb5887 100644 --- a/radiantcore/entity/curve/Curve.h +++ b/radiantcore/entity/curve/Curve.h @@ -2,12 +2,12 @@ #include "generic/callback.h" #include "iselectiontest.h" -#include "ientity.h" +#include "scene/Entity.h" #include "math/curve.h" #include "math/AABB.h" #include "RenderableCurve.h" -namespace entity +namespace entity { /** greebo: This is the base class for the two Doom3-supported curve diff --git a/radiantcore/entity/eclassmodel/EclassModelNode.h b/radiantcore/entity/eclassmodel/EclassModelNode.h index f2aa39aaf0..2b9e099a30 100644 --- a/radiantcore/entity/eclassmodel/EclassModelNode.h +++ b/radiantcore/entity/eclassmodel/EclassModelNode.h @@ -2,7 +2,7 @@ #include "inamespace.h" #include "modelskin.h" -#include "ientity.h" +#include "scene/Entity.h" #include "iselection.h" #include "editable.h" @@ -74,7 +74,7 @@ class EclassModelNode : // Returns the original "origin" value const Vector3& getUntransformedOrigin() override; - + const Vector3& getWorldPosition() const override; virtual bool isShadowCasting() const override; diff --git a/radiantcore/entity/generic/GenericEntityNode.h b/radiantcore/entity/generic/GenericEntityNode.h index 1eefba689e..dcd6e8a665 100644 --- a/radiantcore/entity/generic/GenericEntityNode.h +++ b/radiantcore/entity/generic/GenericEntityNode.h @@ -12,7 +12,7 @@ #include "../OriginKey.h" #include "../AngleKey.h" #include "../RotationKey.h" -#include "scene/SpawnArgs.h" +#include "scene/Entity.h" #include "../RenderableArrow.h" #include "../RenderableEntityBox.h" diff --git a/radiantcore/entity/speaker/SpeakerRenderables.cpp b/radiantcore/entity/speaker/SpeakerRenderables.cpp index 0ae65d643a..c2a864ddbf 100644 --- a/radiantcore/entity/speaker/SpeakerRenderables.cpp +++ b/radiantcore/entity/speaker/SpeakerRenderables.cpp @@ -1,6 +1,6 @@ #include "SpeakerRenderables.h" -#include "ientity.h" +#include "scene/Entity.h" #include "render.h" #include "entity/EntityNode.h" diff --git a/radiantcore/entity/target/RenderableTargetLines.h b/radiantcore/entity/target/RenderableTargetLines.h index 10fd10b714..db5d1a1b96 100644 --- a/radiantcore/entity/target/RenderableTargetLines.h +++ b/radiantcore/entity/target/RenderableTargetLines.h @@ -2,7 +2,7 @@ #include "TargetKeyCollection.h" #include "render.h" -#include "ientity.h" +#include "scene/Entity.h" #include "irenderable.h" #include "ivolumetest.h" #include "math/Segment.h" @@ -59,7 +59,7 @@ class RenderableTargetLines : _updateNeeded |= worldPosition != _worldPosition; if (!_updateNeeded) return; - + _updateNeeded = false; // Store the new world position for use in updateGeometry() diff --git a/radiantcore/entity/target/Target.h b/radiantcore/entity/target/Target.h index 4d093646ed..22e923c8ab 100644 --- a/radiantcore/entity/target/Target.h +++ b/radiantcore/entity/target/Target.h @@ -1,7 +1,7 @@ #pragma once #include "inode.h" -#include "ientity.h" +#include "scene/Entity.h" #include "ilightnode.h" #include "math/Vector3.h" #include "math/AABB.h" @@ -84,7 +84,7 @@ class Target : { return lightNode->getSelectAABB().getOrigin(); } - + return node->worldAABB().getOrigin(); } diff --git a/radiantcore/entity/target/TargetKey.h b/radiantcore/entity/target/TargetKey.h index 2d6af9fee5..71603696ca 100644 --- a/radiantcore/entity/target/TargetKey.h +++ b/radiantcore/entity/target/TargetKey.h @@ -1,6 +1,6 @@ #pragma once -#include "ientity.h" +#include "scene/Entity.h" #include #include "Target.h" diff --git a/radiantcore/entity/target/TargetManager.h b/radiantcore/entity/target/TargetManager.h index 7470441946..eeb3e717ff 100644 --- a/radiantcore/entity/target/TargetManager.h +++ b/radiantcore/entity/target/TargetManager.h @@ -2,7 +2,7 @@ #include #include -#include "ientity.h" +#include "scene/Entity.h" #include "Target.h" namespace entity diff --git a/radiantcore/entity/target/TargetableNode.cpp b/radiantcore/entity/target/TargetableNode.cpp index 5f83282cfe..6975efa957 100644 --- a/radiantcore/entity/target/TargetableNode.cpp +++ b/radiantcore/entity/target/TargetableNode.cpp @@ -6,7 +6,7 @@ namespace entity { -TargetableNode::TargetableNode(SpawnArgs& entity, EntityNode& node) : +TargetableNode::TargetableNode(Entity& entity, EntityNode& node) : _d3entity(entity), _targetKeys(*this), _node(node), @@ -68,7 +68,7 @@ void TargetableNode::onKeyValueChanged(const std::string& name) } // Entity::Observer implementation, gets called on key insert -void TargetableNode::onKeyInsert(const std::string& key, EntityKeyValue& value) +void TargetableNode::onKeyInsert(const std::string& key, EntityKeyValue& value) { if (key == "name") { @@ -149,7 +149,7 @@ void TargetableNode::onTargetKeyCollectionChanged() if (!_targetLineNode) { _targetLineNode.reset(new TargetLineNode(_node)); - + // Fix #4373: Move the target lines to the same layers as the owning node _targetLineNode->assignToLayers(_node.getLayers()); diff --git a/radiantcore/entity/target/TargetableNode.h b/radiantcore/entity/target/TargetableNode.h index 8a0b21534d..f6b7ef61e8 100644 --- a/radiantcore/entity/target/TargetableNode.h +++ b/radiantcore/entity/target/TargetableNode.h @@ -2,7 +2,7 @@ #include "selectionlib.h" #include "scene/Node.h" -#include "scene/SpawnArgs.h" +#include "scene/Entity.h" #include "entitylib.h" #include "TargetKeyCollection.h" @@ -28,7 +28,7 @@ class TargetableNode : public Entity::Observer, public KeyObserver { - SpawnArgs& _d3entity; + Entity& _d3entity; TargetKeyCollection _targetKeys; // The current name of this entity (used for comparison in "onKeyValueChanged") @@ -44,7 +44,7 @@ class TargetableNode : TargetLineNodePtr _targetLineNode; public: - TargetableNode(SpawnArgs& entity, EntityNode& node); + TargetableNode(Entity& entity, EntityNode& node); // This might return nullptr if the node is not inserted in a scene ITargetManager* getTargetManager(); diff --git a/radiantcore/filters/InstanceUpdateWalker.h b/radiantcore/filters/InstanceUpdateWalker.h index 7883b0a3eb..708ab68c0f 100644 --- a/radiantcore/filters/InstanceUpdateWalker.h +++ b/radiantcore/filters/InstanceUpdateWalker.h @@ -1,12 +1,12 @@ #pragma once #include "inode.h" -#include "ientity.h" +#include "scene/Entity.h" #include "iselectable.h" #include "ipatch.h" #include "ibrush.h" -namespace filters +namespace filters { // Walker: de-selects a complete subgraph diff --git a/radiantcore/filters/SetObjectSelectionByFilterWalker.h b/radiantcore/filters/SetObjectSelectionByFilterWalker.h index ab1deeee1e..3595f39fd7 100644 --- a/radiantcore/filters/SetObjectSelectionByFilterWalker.h +++ b/radiantcore/filters/SetObjectSelectionByFilterWalker.h @@ -4,7 +4,7 @@ #include "ifilter.h" #include "ipatch.h" #include "ibrush.h" -#include "ientity.h" +#include "scene/Entity.h" #include "iselectable.h" #include "XMLFilter.h" @@ -49,7 +49,7 @@ class SetObjectSelectionByFilterWalker : // If the entity is affected, don't traverse its child nodes return isVisible; } - + // greebo: Check visibility of Patches if (Node_isPatch(node)) { diff --git a/radiantcore/filters/XMLFilter.cpp b/radiantcore/filters/XMLFilter.cpp index 411ddeda51..e554b724eb 100644 --- a/radiantcore/filters/XMLFilter.cpp +++ b/radiantcore/filters/XMLFilter.cpp @@ -1,6 +1,6 @@ #include "XMLFilter.h" -#include "ientity.h" +#include "scene/Entity.h" #include "ieclass.h" #include "ifilter.h" #include @@ -55,7 +55,7 @@ bool XMLFilter::isEntityVisible(const FilterRule::Type type, const Entity& entit bool visible = true; // default if unmodified by rules IEntityClassConstPtr eclass = entity.getEntityClass(); - + for (FilterRules::const_iterator ruleIter = _rules.begin(); ruleIter != _rules.end(); ++ruleIter) diff --git a/radiantcore/layers/AddToLayerWalker.h b/radiantcore/layers/AddToLayerWalker.h index b6642649b9..5a73abae82 100644 --- a/radiantcore/layers/AddToLayerWalker.h +++ b/radiantcore/layers/AddToLayerWalker.h @@ -2,7 +2,7 @@ #define ADDTOLAYERWALKER_H_ #include "iselection.h" -#include "ientity.h" +#include "scene/Entity.h" #include "ilayer.h" namespace scene { diff --git a/radiantcore/layers/LayerInfoFileModule.cpp b/radiantcore/layers/LayerInfoFileModule.cpp index dd60eb7267..30ddd3cc00 100644 --- a/radiantcore/layers/LayerInfoFileModule.cpp +++ b/radiantcore/layers/LayerInfoFileModule.cpp @@ -1,7 +1,7 @@ #include "LayerInfoFileModule.h" #include "ilayer.h" -#include "ientity.h" +#include "scene/Entity.h" #include "itextstream.h" #include "scenelib.h" #include "scene/LayerValidityCheckWalker.h" @@ -174,7 +174,7 @@ void LayerInfoFileModule::onInfoFileLoadStart() bool LayerInfoFileModule::canParseBlock(const std::string& blockName) { - return blockName == LAYERS || blockName == NODE_TO_LAYER_MAPPING || + return blockName == LAYERS || blockName == NODE_TO_LAYER_MAPPING || blockName == LAYER_HIERARCHY || blockName == LAYER_PROPERTIES; } @@ -205,7 +205,7 @@ void LayerInfoFileModule::parseLayerNames(parser::DefTokeniser& tok) // The opening brace tok.assertNextToken("{"); - while (tok.hasMoreTokens()) + while (tok.hasMoreTokens()) { std::string token = tok.nextToken(); @@ -361,7 +361,7 @@ void LayerInfoFileModule::parseLayerProperties(parser::DefTokeniser& tok) // Add the ID to the list _hiddenLayerIds.push_back(string::convert(nodeToken)); } - + continue; } diff --git a/radiantcore/map/MapPosition.cpp b/radiantcore/map/MapPosition.cpp index b89bc1409b..e6b69c9033 100644 --- a/radiantcore/map/MapPosition.cpp +++ b/radiantcore/map/MapPosition.cpp @@ -1,6 +1,6 @@ #include "MapPosition.h" -#include "ientity.h" +#include "scene/Entity.h" #include "icameraview.h" #include "icommandsystem.h" #include "itextstream.h" @@ -11,7 +11,7 @@ namespace map { -namespace +namespace { const char* const GKEY_MAP_POSROOT = "/mapFormat/mapPositionPosKey"; const char* const GKEY_MAP_ANGLEROOT = "/mapFormat/mapPositionAngleKey"; @@ -97,7 +97,7 @@ void MapPosition::clear() _angle = Vector3(0,0,0); } -bool MapPosition::empty() const +bool MapPosition::empty() const { return _position == Vector3(0,0,0) && _angle == Vector3(0,0,0); } @@ -113,7 +113,7 @@ void MapPosition::store(const cmd::ArgumentList& args) } rMessage() << "Storing map position #" << _index << std::endl; - + try { auto& cameraView = GlobalCameraManager().getActiveView(); @@ -132,7 +132,7 @@ void MapPosition::store(const cmd::ArgumentList& args) } } -void MapPosition::recall(const cmd::ArgumentList& args) +void MapPosition::recall(const cmd::ArgumentList& args) { auto mapRoot = GlobalMapModule().getRoot(); diff --git a/radiantcore/map/MapPositionManager.cpp b/radiantcore/map/MapPositionManager.cpp index 504956b5a4..640ca658e1 100644 --- a/radiantcore/map/MapPositionManager.cpp +++ b/radiantcore/map/MapPositionManager.cpp @@ -1,7 +1,7 @@ #include "MapPositionManager.h" #include "maplib.h" -#include "ientity.h" +#include "scene/Entity.h" #include "icameraview.h" #include "gamelib.h" #include "iregistry.h" @@ -142,7 +142,7 @@ void MapPositionManager::convertLegacyPositions() { return; // no worldspawn or root } - + for (unsigned int i = 1; i <= MAX_POSITIONS; i++) { MapPosition pos(i); diff --git a/radiantcore/map/MapResource.cpp b/radiantcore/map/MapResource.cpp index ba0497dda0..61c5cba2a1 100644 --- a/radiantcore/map/MapResource.cpp +++ b/radiantcore/map/MapResource.cpp @@ -5,7 +5,7 @@ #include #include #include "ifiletypes.h" -#include "ientity.h" +#include "scene/Entity.h" #include "iarchive.h" #include "igroupnode.h" #include "ifilesystem.h" @@ -73,7 +73,7 @@ void MapResource::rename(const std::string& fullPath) void MapResource::constructPaths(const std::string& resourcePath) { - // Since the resource path can contain dots like this ".." + // Since the resource path can contain dots like this ".." // pass the filename part only to getExtension(). _extension = os::getExtension(os::getFilename(resourcePath)); @@ -82,7 +82,7 @@ void MapResource::constructPaths(const std::string& resourcePath) _path = rootPath(resourcePath); // Try to create a relative path, based on the VFS directories - // If no relative path can be deducted, use the absolute resourcePath + // If no relative path can be deducted, use the absolute resourcePath // in unmodified form. _name = os::getRelativePath(resourcePath, _path); } @@ -129,7 +129,7 @@ void MapResource::save(const MapFormatPtr& mapFormat) } rMessage() << "Using " << format->getMapFormatName() << " format to save the resource." << std::endl; - + std::string fullpath = getAbsoluteResourcePath(); // Save a backup of the existing file (rename it to .bak) if it exists in the first place @@ -173,7 +173,7 @@ bool MapResource::saveBackup() fs::path backup = fullpath; backup.replace_extension(".bak"); - + // replace_extension() doesn't accept something like ".darkradiant.bak", so roll our own fs::path auxFileBackup = auxFile.string() + ".bak"; @@ -192,7 +192,7 @@ bool MapResource::saveBackup() } catch (fs::filesystem_error& ex) { - rWarning() << "Error while creating backups: " << ex.what() << + rWarning() << "Error while creating backups: " << ex.what() << ", the file is possibly opened by the game." << std::endl; errorOccurred = true; } @@ -328,7 +328,7 @@ RootNodePtr MapResource::loadMapNode() catch (const OperationException& ex) { // Re-throw the exception, prepending the map file path to the message (if not cancelled) - throw ex.operationCancelled() ? ex : + throw ex.operationCancelled() ? ex : OperationException(fmt::format(_("Failure reading map file:\n{0}\n\n{1}"), getAbsoluteResourcePath(), ex.what())); } @@ -447,8 +447,8 @@ void MapResource::saveFile(const MapFormat& format, const scene::IMapRootNodePtr // Check the total count of nodes to traverse NodeCounter counter; traverse(root, counter); - - // Create our main MapExporter walker, and pass the desired + + // Create our main MapExporter walker, and pass the desired // format to it. The constructor will prepare the scene // and the destructor will clean it up afterwards. That way // we ensure a nice and tidy scene when exceptions are thrown. diff --git a/radiantcore/map/NodeCounter.h b/radiantcore/map/NodeCounter.h index da50e1a60f..6bcf39376d 100644 --- a/radiantcore/map/NodeCounter.h +++ b/radiantcore/map/NodeCounter.h @@ -1,7 +1,7 @@ #pragma once #include "inode.h" -#include "ientity.h" +#include "scene/Entity.h" #include "scenelib.h" namespace map @@ -33,4 +33,4 @@ class NodeCounter : } }; -} \ No newline at end of file +} diff --git a/radiantcore/map/RegionManager.cpp b/radiantcore/map/RegionManager.cpp index a0b1671057..4f73373523 100644 --- a/radiantcore/map/RegionManager.cpp +++ b/radiantcore/map/RegionManager.cpp @@ -4,7 +4,7 @@ #include "ibrush.h" #include "icameraview.h" #include "iorthoview.h" -#include "ientity.h" +#include "scene/Entity.h" #include "ieclass.h" #include "imru.h" #include "ifiletypes.h" diff --git a/radiantcore/map/RegionManager.h b/radiantcore/map/RegionManager.h index 8885029d2b..7a5b30e38e 100644 --- a/radiantcore/map/RegionManager.h +++ b/radiantcore/map/RegionManager.h @@ -6,10 +6,10 @@ #include "math/AABB.h" #include "math/Vector2.h" #include "iscenegraph.h" -#include "ientity.h" +#include "scene/Entity.h" #include "imap.h" -/** +/** * greebo: The RegionManager provides methods to enable/disable * the regioning for map editing as well as functions * to set the region bounds from brushes/xyview/current selection. @@ -149,7 +149,7 @@ class RegionManager : void onMapEvent(IMap::MapEvent ev); // Helper to create the actual brushes bounding the region - static void constructRegionBrushes(scene::INodePtr brushes[6], + static void constructRegionBrushes(scene::INodePtr brushes[6], const Vector3& region_mins, const Vector3& region_maxs); AABB getVisibleBounds(); diff --git a/radiantcore/map/RootNode.h b/radiantcore/map/RootNode.h index c73fca61a3..7c78e646b8 100644 --- a/radiantcore/map/RootNode.h +++ b/radiantcore/map/RootNode.h @@ -3,7 +3,7 @@ #include "inamespace.h" #include "imap.h" #include "ilayer.h" -#include "ientity.h" +#include "scene/Entity.h" #include "iselectiongroup.h" #include "iselectionset.h" #include "scene/Node.h" @@ -13,17 +13,17 @@ #include "undo/UndoSystem.h" #include -namespace map +namespace map { -/** +/** * greebo: This is the root node of the map, it gets inserted as * the top node into the scenegraph. Each entity node is * inserted as child node to this. * * Note: Inserting a child node to this MapRoot automatically * triggers an "instantiation" of this child node, which can be - * seen as "activation" of this node. In contrast to nodes on the + * seen as "activation" of this node. In contrast to nodes on the * undo stack which are "not instantiated"/inactive. */ class RootNode : diff --git a/radiantcore/map/algorithm/Export.cpp b/radiantcore/map/algorithm/Export.cpp index c6b79d7fa9..e26f9ca9f5 100644 --- a/radiantcore/map/algorithm/Export.cpp +++ b/radiantcore/map/algorithm/Export.cpp @@ -5,7 +5,7 @@ #include "ieclass.h" #include "ifilesystem.h" #include "imodelcache.h" -#include "ientity.h" +#include "scene/Entity.h" #include "iundo.h" #include "itextstream.h" @@ -182,7 +182,7 @@ void exportSelectedAsModel(const model::ModelExportOptions& options) Node_setSelected(modelNode, true); } - + // It's possible that the export overwrote a model we're already using in this map, refresh it refreshModelsByPath(relativeModelPath); } diff --git a/radiantcore/map/algorithm/Import.cpp b/radiantcore/map/algorithm/Import.cpp index 67de8589d5..44c00a2f8b 100644 --- a/radiantcore/map/algorithm/Import.cpp +++ b/radiantcore/map/algorithm/Import.cpp @@ -10,7 +10,7 @@ #include "imapformat.h" #include "inamespace.h" #include "iselectiongroup.h" -#include "ientity.h" +#include "scene/Entity.h" #include "iscenegraph.h" #include "scene/BasicRootNode.h" #include "scene/ChildPrimitives.h" @@ -27,7 +27,7 @@ namespace map namespace algorithm { -// Will rewrite the group memberships of visited nodes to not be +// Will rewrite the group memberships of visited nodes to not be // in conflict with any of the groups present in the target scene class SelectionGroupRemapper : public scene::NodeVisitor @@ -108,7 +108,7 @@ class SelectionGroupRemapper : return found.first->second; } - + std::size_t generateNonConflictingGroupId() { while (++_nextNewGroupId < std::numeric_limits::max()) @@ -330,7 +330,7 @@ class SimpleMapImportFilter : entity->addChildNode(primitive); return true; } - + return false; } }; @@ -369,7 +369,7 @@ void importFromStream(std::istream& stream) // Clear out the root node, otherwise we end up with half a map scene::NodeRemover remover; importFilter.getRootNode()->traverseChildren(remover); - + throw cmd::ExecutionFailure(fmt::format(_("Failure reading map from clipboard:\n{0}"), ex.what())); } } diff --git a/radiantcore/map/algorithm/MapExporter.cpp b/radiantcore/map/algorithm/MapExporter.cpp index fe0aebe44d..eb88ba29cd 100644 --- a/radiantcore/map/algorithm/MapExporter.cpp +++ b/radiantcore/map/algorithm/MapExporter.cpp @@ -5,7 +5,7 @@ #include "itextstream.h" #include "ibrush.h" #include "ipatch.h" -#include "ientity.h" +#include "scene/Entity.h" #include "imapresource.h" #include "imap.h" #include "igroupnode.h" @@ -60,7 +60,7 @@ MapExporter::~MapExporter() // Close any info file stream _infoFileExporter.reset(); - // The finish() call is placed in the destructor to make sure that + // The finish() call is placed in the destructor to make sure that // even on unhandled exceptions the map is left in a working state finishScene(); } @@ -147,7 +147,7 @@ bool MapExporter::pre(const scene::INodePtr& node) { // Progress dialog handling onNodeProgress(); - + _writer.beginWriteEntity(entity, _mapStream); if (_infoFileExporter) _infoFileExporter->visitEntity(node, _entityNum); @@ -237,7 +237,7 @@ void MapExporter::onNodeProgress() // button is clicked, which we must catch and handle. if (_dialogEventLimiter.readyForEvent()) { - float progressFraction = _totalNodeCount > 0 ? + float progressFraction = _totalNodeCount > 0 ? static_cast(_curNodeCount) / static_cast(_totalNodeCount) : 0.0f; if (_sendProgressMessages) diff --git a/radiantcore/map/algorithm/MapImporter.cpp b/radiantcore/map/algorithm/MapImporter.cpp index 3e40ed3e58..4904ff3126 100644 --- a/radiantcore/map/algorithm/MapImporter.cpp +++ b/radiantcore/map/algorithm/MapImporter.cpp @@ -1,7 +1,7 @@ #include "MapImporter.h" #include "i18n.h" -#include "ientity.h" +#include "scene/Entity.h" #include "imap.h" #include "iradiant.h" diff --git a/radiantcore/map/algorithm/Models.cpp b/radiantcore/map/algorithm/Models.cpp index aad9f96c26..c5d819da2d 100644 --- a/radiantcore/map/algorithm/Models.cpp +++ b/radiantcore/map/algorithm/Models.cpp @@ -5,7 +5,7 @@ #include "i18n.h" #include "inode.h" #include "iselection.h" -#include "ientity.h" +#include "scene/Entity.h" #include "imodel.h" #include "imodelcache.h" #include "iscenegraph.h" diff --git a/radiantcore/map/format/Doom3MapReader.cpp b/radiantcore/map/format/Doom3MapReader.cpp index 1d33cd5e90..ae3f13691c 100644 --- a/radiantcore/map/format/Doom3MapReader.cpp +++ b/radiantcore/map/format/Doom3MapReader.cpp @@ -3,7 +3,7 @@ #include "itextstream.h" #include "ieclass.h" #include "igame.h" -#include "ientity.h" +#include "scene/Entity.h" #include "string/string.h" #include "Doom3MapFormat.h" @@ -18,7 +18,7 @@ namespace map { -Doom3MapReader::Doom3MapReader(IMapImportFilter& importFilter) : +Doom3MapReader::Doom3MapReader(IMapImportFilter& importFilter) : _importFilter(importFilter), _entityCount(0), _primitiveCount(0) @@ -145,7 +145,7 @@ void Doom3MapReader::parsePrimitive(parser::DefTokeniser& tok, const scene::INod } // Now add the primitive as a child of the entity - _importFilter.addPrimitiveToEntity(primitive, parentEntity); + _importFilter.addPrimitiveToEntity(primitive, parentEntity); } catch (parser::ParseException& e) { @@ -214,7 +214,7 @@ void Doom3MapReader::parseEntity(parser::DefTokeniser& tok) // primitive, or a "}" to indicate the end of the entity if (token == "{") // PRIMITIVE - { + { // Create the entity right now, if not yet done if (entity == NULL) { @@ -235,7 +235,7 @@ void Doom3MapReader::parseEntity(parser::DefTokeniser& tok) break; } else // KEY - { + { std::string value = tok.nextToken(); // Sanity check (invalid number of tokens will get us out of sync) diff --git a/radiantcore/map/format/Doom3MapWriter.cpp b/radiantcore/map/format/Doom3MapWriter.cpp index f44a2f2d9a..9788961c7c 100644 --- a/radiantcore/map/format/Doom3MapWriter.cpp +++ b/radiantcore/map/format/Doom3MapWriter.cpp @@ -1,7 +1,7 @@ #include "Doom3MapWriter.h" #include "igame.h" -#include "ientity.h" +#include "scene/Entity.h" #include "primitivewriters/BrushDef3Exporter.h" #include "primitivewriters/PatchDefExporter.h" diff --git a/radiantcore/map/format/Quake3MapReader.cpp b/radiantcore/map/format/Quake3MapReader.cpp index 6336856829..7aa426f317 100644 --- a/radiantcore/map/format/Quake3MapReader.cpp +++ b/radiantcore/map/format/Quake3MapReader.cpp @@ -3,7 +3,7 @@ #include "itextstream.h" #include "ieclass.h" #include "igame.h" -#include "ientity.h" +#include "scene/Entity.h" #include "string/string.h" #include "i18n.h" @@ -16,7 +16,7 @@ namespace map { -Quake3MapReader::Quake3MapReader(IMapImportFilter& importFilter) : +Quake3MapReader::Quake3MapReader(IMapImportFilter& importFilter) : _importFilter(importFilter), _entityCount(0), _primitiveCount(0) @@ -103,7 +103,7 @@ void Quake3MapReader::parsePrimitive(parser::DefTokeniser& tok, const scene::INo } // Now add the primitive as a child of the entity - _importFilter.addPrimitiveToEntity(primitive, parentEntity); + _importFilter.addPrimitiveToEntity(primitive, parentEntity); } catch (parser::ParseException& e) { @@ -172,7 +172,7 @@ void Quake3MapReader::parseEntity(parser::DefTokeniser& tok) // primitive, or a "}" to indicate the end of the entity if (token == "{") // PRIMITIVE - { + { // Create the entity right now, if not yet done if (entity == NULL) { @@ -193,7 +193,7 @@ void Quake3MapReader::parseEntity(parser::DefTokeniser& tok) break; } else // KEY - { + { std::string value = tok.nextToken(); // Sanity check (invalid number of tokens will get us out of sync) diff --git a/radiantcore/map/format/portable/PortableMapReader.cpp b/radiantcore/map/format/portable/PortableMapReader.cpp index 9e5ea6ecd8..ceb6367dcd 100644 --- a/radiantcore/map/format/portable/PortableMapReader.cpp +++ b/radiantcore/map/format/portable/PortableMapReader.cpp @@ -7,7 +7,7 @@ #include "ilayer.h" #include "ibrush.h" #include "ieclass.h" -#include "ientity.h" +#include "scene/Entity.h" #include "PortableMapFormat.h" #include "Constants.h" @@ -24,7 +24,7 @@ namespace format namespace { - class BadDocumentFormatException : + class BadDocumentFormatException : public std::runtime_error { public: @@ -218,7 +218,7 @@ void PortableMapReader::readEntities(const xml::Node& mapNode) void PortableMapReader::readPrimitives(const xml::Node& primitivesNode, const scene::INodePtr& entity) { auto childNodes = primitivesNode.getChildren(); - + for (const auto childNode : childNodes) { const std::string name = childNode.getName(); @@ -290,7 +290,7 @@ void PortableMapReader::readBrush(const xml::Node& brushTag, const scene::INodeP } catch (const BadDocumentFormatException& ex) { - rError() << "PortableMapReader: Entity " << entity->name() << ", Brush " << + rError() << "PortableMapReader: Entity " << entity->name() << ", Brush " << brushTag.getAttributeValue(ATTR_BRUSH_NUMBER) << ": " << ex.what() << std::endl; } } @@ -484,7 +484,7 @@ void PortableMapReader::readSelectionSetInformation(const xml::Node& tag, const ); auto setIter = _selectionSets.find(id); - + if (setIter != _selectionSets.end()) { setIter->second->addNode(sceneNode); @@ -495,11 +495,11 @@ void PortableMapReader::readSelectionSetInformation(const xml::Node& tag, const bool PortableMapReader::CanLoad(std::istream& stream) { // Instead of instantiating an XML parser and buffering the whole - // file into memory, read in some lines and look for + // file into memory, read in some lines and look for // certain signature parts. // This will fail if the XML is oddly formatted with e.g. line-breaks std::string buffer(512, '\0'); - + for (int i = 0; i < 25; ++i) { std::getline(stream, buffer); diff --git a/radiantcore/map/format/portable/PortableMapWriter.cpp b/radiantcore/map/format/portable/PortableMapWriter.cpp index e71f4eac79..4ae75f17ee 100644 --- a/radiantcore/map/format/portable/PortableMapWriter.cpp +++ b/radiantcore/map/format/portable/PortableMapWriter.cpp @@ -1,7 +1,7 @@ #include "PortableMapWriter.h" #include "igame.h" -#include "ientity.h" +#include "scene/Entity.h" #include "ipatch.h" #include "imap.h" #include "ibrush.h" diff --git a/radiantcore/model/export/ModelScalePreserver.cpp b/radiantcore/model/export/ModelScalePreserver.cpp index 799d9aee72..7aef027eaa 100644 --- a/radiantcore/model/export/ModelScalePreserver.cpp +++ b/radiantcore/model/export/ModelScalePreserver.cpp @@ -1,6 +1,6 @@ #include "ModelScalePreserver.h" -#include "ientity.h" +#include "scene/Entity.h" #include "itransformable.h" #include "imapresource.h" #include "itextstream.h" diff --git a/radiantcore/model/export/ScaledModelExporter.cpp b/radiantcore/model/export/ScaledModelExporter.cpp index 090298c67f..95df4b6dd4 100644 --- a/radiantcore/model/export/ScaledModelExporter.cpp +++ b/radiantcore/model/export/ScaledModelExporter.cpp @@ -6,7 +6,7 @@ #include "iundo.h" #include "itextstream.h" #include "igame.h" -#include "ientity.h" +#include "scene/Entity.h" #include "iscenegraph.h" #include "os/fs.h" #include "os/path.h" @@ -85,7 +85,7 @@ void ScaledModelExporter::saveScaledModel(const scene::INodePtr& entityNode, con std::string outputExtension = registry::getValue(RKEY_DEFAULT_MODEL_EXPORT_FORMAT); string::to_lower(outputExtension); - rMessage() << "Model format used for export: " << outputExtension << + rMessage() << "Model format used for export: " << outputExtension << " (this can be changed in the preferences)" << std::endl; // Save the scaled model in the configured format @@ -121,10 +121,10 @@ void ScaledModelExporter::saveScaledModel(const scene::INodePtr& entityNode, con fs::path modelKeyValue = entity->getKeyValue("model"); - rMessage() << "Exporting scaled model for entity " << entity->getKeyValue("name") << + rMessage() << "Exporting scaled model for entity " << entity->getKeyValue("name") << ": " << modelKeyValue.string() << std::endl; - // Generate a new model name, à la "haystack_scaled3.ase" + // Generate a new model name, � la "haystack_scaled3.ase" std::string modelFilename = generateUniqueModelFilename(targetPath, modelKeyValue, outputExtension); // assemble the new model spawnarg @@ -169,7 +169,7 @@ std::string ScaledModelExporter::generateUniqueModelFilename( return generatedFilename; // break the loop } } - + throw new std::runtime_error("Could not generate a unique model filename."); } diff --git a/radiantcore/patch/PatchNode.cpp b/radiantcore/patch/PatchNode.cpp index 1c86dc518f..91016be14e 100644 --- a/radiantcore/patch/PatchNode.cpp +++ b/radiantcore/patch/PatchNode.cpp @@ -1,7 +1,7 @@ #include "PatchNode.h" #include "ifilter.h" -#include "ientity.h" +#include "scene/Entity.h" #include "iradiant.h" #include "icounter.h" #include "math/Frustum.h" @@ -97,7 +97,7 @@ void PatchNode::updateSelectableControls() // The passed callback points back to this class (the member method selectedChangedComponent() is called). for(auto& ctrl : ctrlPoints) { - m_ctrl_instances.emplace_back(ctrl, + m_ctrl_instances.emplace_back(ctrl, std::bind(&PatchNode::selectedChangedComponent, this, std::placeholders::_1)); } } @@ -355,7 +355,7 @@ void PatchNode::onPreRender(const VolumeTest& volume) if (m_patch.getWidth() > 0 && m_patch.getHeight() > 0) { _renderableSurfaceSolid.update(m_patch._shader.getGLShader()); - _renderableSurfaceWireframe.update(getRenderState() == RenderState::Active ? + _renderableSurfaceWireframe.update(getRenderState() == RenderState::Active ? _renderEntity->getWireShader() : _inactiveShader); _renderableSurfaceSolid.attachToEntity(_renderEntity); } diff --git a/radiantcore/selection/SceneManipulationPivot.cpp b/radiantcore/selection/SceneManipulationPivot.cpp index 4e17cffdb5..e5b6d6e32b 100644 --- a/radiantcore/selection/SceneManipulationPivot.cpp +++ b/radiantcore/selection/SceneManipulationPivot.cpp @@ -2,7 +2,7 @@ #include "iselection.h" #include "ilightnode.h" -#include "ientity.h" +#include "scene/Entity.h" #include "igrid.h" #include "selectionlib.h" #include "registry/registry.h" diff --git a/radiantcore/selection/SceneWalkers.h b/radiantcore/selection/SceneWalkers.h index 07d2a3e968..d5ff18c0ea 100644 --- a/radiantcore/selection/SceneWalkers.h +++ b/radiantcore/selection/SceneWalkers.h @@ -1,6 +1,6 @@ #pragma once -#include "ientity.h" +#include "scene/Entity.h" #include "ieclass.h" #include "itransformnode.h" #include "itextstream.h" diff --git a/radiantcore/selection/algorithm/Curves.cpp b/radiantcore/selection/algorithm/Curves.cpp index 7e3f36c7ff..1ef4a49e09 100644 --- a/radiantcore/selection/algorithm/Curves.cpp +++ b/radiantcore/selection/algorithm/Curves.cpp @@ -5,7 +5,7 @@ #include "iundo.h" #include "ieclass.h" #include "itransformable.h" -#include "ientity.h" +#include "scene/Entity.h" #include "iorthoview.h" #include "scenelib.h" diff --git a/radiantcore/selection/algorithm/General.h b/radiantcore/selection/algorithm/General.h index 4fedb2abf5..f63ff489ce 100644 --- a/radiantcore/selection/algorithm/General.h +++ b/radiantcore/selection/algorithm/General.h @@ -4,13 +4,13 @@ #include #include "icommandsystem.h" #include "iscenegraph.h" -#include "ientity.h" +#include "scene/Entity.h" #include "math/Vector3.h" #include "math/AABB.h" namespace selection { - namespace algorithm + namespace algorithm { const char* const RKEY_FREE_OBJECT_ROTATION = "user/ui/rotateObjectsIndependently"; @@ -97,7 +97,7 @@ namespace selection Vector3 getCurrentSelectionCenter(); // Returns the AABB of the current selection (invalid bounds if nothing is selected). - // Use the bool to specify whether you want to have the light volumes calculated in + // Use the bool to specify whether you want to have the light volumes calculated in // in their entirety. AABB getCurrentSelectionBounds(bool considerLightVolumes); diff --git a/radiantcore/selection/algorithm/Group.cpp b/radiantcore/selection/algorithm/Group.cpp index f55cbc2080..dbdf7b32d2 100644 --- a/radiantcore/selection/algorithm/Group.cpp +++ b/radiantcore/selection/algorithm/Group.cpp @@ -3,7 +3,7 @@ #include #include "i18n.h" #include "igroupnode.h" -#include "ientity.h" +#include "scene/Entity.h" #include "itextstream.h" #include "iselectiongroup.h" #include "imap.h" diff --git a/radiantcore/selection/algorithm/Primitives.cpp b/radiantcore/selection/algorithm/Primitives.cpp index f99553f1d2..c3a02856ab 100644 --- a/radiantcore/selection/algorithm/Primitives.cpp +++ b/radiantcore/selection/algorithm/Primitives.cpp @@ -4,7 +4,7 @@ #include "i18n.h" #include "igroupnode.h" -#include "ientity.h" +#include "scene/Entity.h" #include "itextstream.h" #include "iundo.h" #include "ibrush.h" diff --git a/radiantcore/selection/group/SelectionGroupInfoFileModule.cpp b/radiantcore/selection/group/SelectionGroupInfoFileModule.cpp index 4023b25b31..b9f0221f96 100644 --- a/radiantcore/selection/group/SelectionGroupInfoFileModule.cpp +++ b/radiantcore/selection/group/SelectionGroupInfoFileModule.cpp @@ -2,7 +2,7 @@ #include #include "iselectiongroup.h" -#include "ientity.h" +#include "scene/Entity.h" #include "string/convert.h" #include "string/replace.h" #include "parser/DefTokeniser.h" @@ -116,7 +116,7 @@ void SelectionGroupInfoFileModule::saveNode(const scene::INodePtr& node, std::si { _output << " " << primitiveNum; } - + _output << " )"; _output << " ( "; @@ -330,7 +330,7 @@ void SelectionGroupInfoFileModule::applyInfoToScene(const scene::IMapRootNodePtr if (found == groups.end()) { - rWarning() << "Invalid group ID " << id << " encountered for node (" << + rWarning() << "Invalid group ID " << id << " encountered for node (" << mapping.first.first << "," << mapping.first.second << ")" << std::endl; continue; } @@ -343,7 +343,7 @@ void SelectionGroupInfoFileModule::applyInfoToScene(const scene::IMapRootNodePtr failedNodes++; } } - + if (failedNodes > 0) { rWarning() << "Couldn't resolve " << failedNodes << " nodes in group mapping " << std::endl; diff --git a/radiantcore/selection/manipulators/ManipulatorComponents.cpp b/radiantcore/selection/manipulators/ManipulatorComponents.cpp index 98cbd5eada..75c692c277 100644 --- a/radiantcore/selection/manipulators/ManipulatorComponents.cpp +++ b/radiantcore/selection/manipulators/ManipulatorComponents.cpp @@ -1,6 +1,6 @@ #include "ManipulatorComponents.h" -#include "ientity.h" +#include "scene/Entity.h" #include "itransformable.h" #include "igrid.h" #include "math/FloatTools.h" diff --git a/radiantcore/selection/shaderclipboard/ClosestTexturableFinder.cpp b/radiantcore/selection/shaderclipboard/ClosestTexturableFinder.cpp index 911c770b4f..c9c765f574 100644 --- a/radiantcore/selection/shaderclipboard/ClosestTexturableFinder.cpp +++ b/radiantcore/selection/shaderclipboard/ClosestTexturableFinder.cpp @@ -1,7 +1,7 @@ #include "ClosestTexturableFinder.h" #include "ifilter.h" -#include "ientity.h" +#include "scene/Entity.h" #include "brush/Brush.h" #include "patch/Patch.h" @@ -13,7 +13,7 @@ namespace selection { -namespace algorithm +namespace algorithm { ClosestTexturableFinder::ClosestTexturableFinder(SelectionTest& test, Texturable& texturable) : diff --git a/test/Curves.cpp b/test/Curves.cpp index 244c77a7a9..168bf7c1a9 100644 --- a/test/Curves.cpp +++ b/test/Curves.cpp @@ -1,6 +1,6 @@ #include "RadiantTest.h" -#include "ientity.h" +#include "scene/Entity.h" #include "iselection.h" #include "imap.h" #include "algorithm/Scene.h" diff --git a/test/Entity.cpp b/test/Entity.cpp index 3e79ef397e..66a38fe6ce 100644 --- a/test/Entity.cpp +++ b/test/Entity.cpp @@ -1,7 +1,7 @@ #include "RadiantTest.h" #include "ieclass.h" -#include "ientity.h" +#include "scene/Entity.h" #include "irendersystemfactory.h" #include "iselectable.h" #include "iselection.h" @@ -53,13 +53,13 @@ struct TestEntity }; // Obtain entity attachments as a simple std::list -std::list getAttachments(const IEntityNodePtr& node) +std::list getAttachments(const IEntityNodePtr& node) { - std::list attachments; + std::list attachments; if (node) { node->getEntity().forEachAttachment( - [&](const Entity::Attachment& a) { attachments.push_back(a); } + [&](const EntityAttachment& a) { attachments.push_back(a); } ); } return attachments; @@ -908,7 +908,7 @@ TEST_F(EntityTest, CreateAttachedLightEntity) EXPECT_EQ(attachments.size(), 1); // Examine the properties of the single attachment - Entity::Attachment attachment = attachments.front(); + EntityAttachment attachment = attachments.front(); EXPECT_EQ(attachment.eclass, spawnArgs.getKeyValue("def_attach")); EXPECT_EQ(attachment.offset, Vector3(0, 0, 10)); EXPECT_EQ(attachment.name, spawnArgs.getKeyValue("name_attach")); diff --git a/test/ModelExport.cpp b/test/ModelExport.cpp index 2d23a5e7c3..45c135d380 100644 --- a/test/ModelExport.cpp +++ b/test/ModelExport.cpp @@ -4,7 +4,7 @@ #include "imodelcache.h" #include "imap.h" #include "ieclass.h" -#include "ientity.h" +#include "scene/Entity.h" #include "ModelExportOptions.h" #include "algorithm/Primitives.h" #include "algorithm/Scene.h" diff --git a/test/Renderer.cpp b/test/Renderer.cpp index 11ba7c8f23..8defe4cd03 100644 --- a/test/Renderer.cpp +++ b/test/Renderer.cpp @@ -1,7 +1,7 @@ #include "RadiantTest.h" #include "ieclass.h" -#include "ientity.h" +#include "scene/Entity.h" #include "irender.h" #include "ilightnode.h" #include "math/Matrix4.h" @@ -381,7 +381,7 @@ TEST_F(RenderSystemTest, EntityRegistration) scene::addNodeToContainer(entity2, rootNode); EXPECT_EQ(getEntityCount(renderSystem), 2) << "Rendersystem should contain two entities now"; - + scene::removeNodeFromParent(entity); EXPECT_EQ(getEntityCount(renderSystem), 1) << "Rendersystem should contain one entity now"; @@ -428,7 +428,7 @@ TEST_F(RenderSystemTest, EntityEnumeration) { auto rootNode = GlobalMapModule().getRoot(); auto renderSystem = rootNode->getRenderSystem(); - + auto entity = createByClassName("func_static"); scene::addNodeToContainer(entity, rootNode); diff --git a/test/Selection.cpp b/test/Selection.cpp index 9a0b50f311..afdfc01b0d 100644 --- a/test/Selection.cpp +++ b/test/Selection.cpp @@ -8,7 +8,7 @@ #include "ilightnode.h" #include "ibrush.h" #include "ipatch.h" -#include "ientity.h" +#include "scene/Entity.h" #include "ieclass.h" #include "algorithm/Scene.h" #include "algorithm/Primitives.h" @@ -700,7 +700,7 @@ TEST_F(OrthoViewSelectionTest, ToggleSelectPointPrimitiveModeFavoursEntities) // Assume that the entity is located below the brush Vector3 funcStaticOrigin = funcStatic->worldAABB().getOrigin(); Vector3 originalBrushPosition = brush->worldAABB().getOrigin(); - EXPECT_LT(funcStaticOrigin.z() + funcStatic->worldAABB().extents.z(), + EXPECT_LT(funcStaticOrigin.z() + funcStatic->worldAABB().extents.z(), originalBrushPosition.z() + brush->worldAABB().extents.z()) << "Entity should be located below the brush"; expectNodeSelectionStatus({}, { brush, funcStatic }); @@ -1344,7 +1344,7 @@ TEST_F(OrthoViewSelectionTest, DragManipulateUnselectedBrushVerticesComponentMod auto topFace = algorithm::findBrushFaceWithNormal(brush, { 0, 0, 1 }); auto bottomFace = algorithm::findBrushFaceWithNormal(brush, { 0, 0, -1 }); - EXPECT_TRUE(algorithm::faceHasVertex(topFace, + EXPECT_TRUE(algorithm::faceHasVertex(topFace, [&](const WindingVertex& v) { return v.vertex.x() == vertexPosition.x() && v.vertex.y() == vertexPosition.y(); })); EXPECT_TRUE(algorithm::faceHasVertex(bottomFace, [&](const WindingVertex& v) { return v.vertex.x() == vertexPosition.x() && v.vertex.y() == vertexPosition.y(); })); diff --git a/test/Transformation.cpp b/test/Transformation.cpp index 2a2a7ad10c..cd32b6a73f 100644 --- a/test/Transformation.cpp +++ b/test/Transformation.cpp @@ -1,7 +1,7 @@ #include "RadiantTest.h" #include "ieclass.h" -#include "ientity.h" +#include "scene/Entity.h" #include "itransformable.h" #include "icommandsystem.h" #include "scenelib.h" diff --git a/test/UndoRedo.cpp b/test/UndoRedo.cpp index 4e18a979b5..078b992698 100644 --- a/test/UndoRedo.cpp +++ b/test/UndoRedo.cpp @@ -4,7 +4,7 @@ #include "iundo.h" #include "ibrush.h" #include "ieclass.h" -#include "ientity.h" +#include "scene/Entity.h" #include "iscenegraphfactory.h" #include "imap.h" #include "icommandsystem.h" @@ -59,7 +59,7 @@ TEST_F(UndoTest, BrushCreation) brush.setShader("textures/numbers/19"); brush.evaluateBRep(); - + EXPECT_FALSE(GlobalMapModule().isModified()) << "Map already modified before the change"; { @@ -204,7 +204,7 @@ TEST_F(UndoTest, MultipleChangesInSingleOperation) TEST_F(UndoTest, NodeOutsideScene) { auto entity = setupTestEntity(); - + scene::removeNodeFromParent(entity); EXPECT_FALSE(GlobalMapModule().isModified()) << "Map already modified before the change"; @@ -455,7 +455,7 @@ TEST_F(UndoTest, SceneNodeRemoval) { UndoableCommand cmd("testOperation"); - + // Remove one of the entities scene::removeNodeFromParent(speaker_1); @@ -563,7 +563,7 @@ TEST_F(UndoTest, CreateBrushBasedEntity) // The brush should be back at worldspawn EXPECT_TRUE(Node_getEntity(brush->getParent())->isWorldspawn()); EXPECT_TRUE(algorithm::findFirstBrushWithMaterial(worldspawn, "textures/numbers/1")) << "Worldspawn should have this brush again"; - + // The entity should be gone EXPECT_FALSE(algorithm::getEntityByName(GlobalMapModule().getRoot(), entityName)) << "Could look up the entity by name"; EXPECT_FALSE(algorithm::findFirstBrushWithMaterial(entity, "textures/numbers/1")) << "The door should have lost this brush"; diff --git a/test/WorldspawnColour.cpp b/test/WorldspawnColour.cpp index ada2e83b32..1865aba333 100644 --- a/test/WorldspawnColour.cpp +++ b/test/WorldspawnColour.cpp @@ -3,7 +3,7 @@ #include #include "ieclass.h" #include "icolourscheme.h" -#include "ientity.h" +#include "scene/Entity.h" #include "imap.h" #include "ibrush.h" #include "ifilesystem.h" diff --git a/test/algorithm/Entity.h b/test/algorithm/Entity.h index 56e2a26d50..a1c61b222c 100644 --- a/test/algorithm/Entity.h +++ b/test/algorithm/Entity.h @@ -2,7 +2,7 @@ #include #include "ieclass.h" -#include "ientity.h" +#include "scene/Entity.h" namespace test { diff --git a/test/algorithm/Scene.h b/test/algorithm/Scene.h index cd36b1cdfe..195d0553c7 100644 --- a/test/algorithm/Scene.h +++ b/test/algorithm/Scene.h @@ -3,7 +3,7 @@ #include #include "inode.h" #include "iundo.h" -#include "ientity.h" +#include "scene/Entity.h" #include "ibrush.h" #include "ipatch.h" #include "imodel.h" @@ -177,7 +177,7 @@ inline model::ModelNodePtr findChildModel(const scene::INodePtr& parent) } // Returns the number of children of the given parent node matching the given predicate -inline std::size_t getChildCount(const scene::INodePtr& parent, +inline std::size_t getChildCount(const scene::INodePtr& parent, const std::function& predicate) { std::size_t count = 0; From f171b8a565581a231dc1d891d0b9ae3be061bee6 Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Tue, 2 Apr 2024 20:29:52 +0100 Subject: [PATCH 05/49] Updated debian/changelog for 3.9.0 release --- debian/changelog | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/debian/changelog b/debian/changelog index 47767da47e..50ce100d77 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,15 @@ +darkradiant (3.9.0~jammy1) jammy; urgency=medium + + * Add "Show definition" button for the "inherit" spawnarg. + * Preserve patch tesselation fixed subdivisions when creating caps. + * Add Filters for Location Entities and Player Start. + * Support saving entity key/value pairs containing double quotes. + * Allow a way to easily see all properties of attached entities. + * Ortho views preserve their orientation between sessions. + * Various bug fixes and minor improvements. + + -- Matthew Mott Tue, 02 Apr 2024 20:26:39 +0100 + darkradiant (3.8.0~jammy1) jammy; urgency=medium * New feature: Support new frob-related material keywords. From 40da36f77c67593412573412ddb26a5e5cc3424b Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Wed, 20 Mar 2024 20:27:14 +0000 Subject: [PATCH 06/49] Merge EntityNode and IEntityNode Pure virtual IEntityNode interface removed; instead all downstream code uses EntityNode directly which has been made available in the scene library. This required moving a fairly large number of dependency classes from radiantcore/entity into libs/scene. --- include/ientity.h | 74 +------- include/imapformat.h | 29 ++- libs/entitylib.h | 66 +------ libs/maplib.h | 2 +- libs/scene/CMakeLists.txt | 14 ++ libs/scene/ChildPrimitives.cpp | 2 +- .../entity => libs/scene}/ColourKey.h | 5 - libs/scene/EntityBreakdown.h | 2 +- .../entity => libs/scene}/EntityNode.cpp | 13 +- .../entity => libs/scene}/EntityNode.h | 171 +++++++++++++++--- .../entity => libs/scene}/EntitySettings.cpp | 5 - .../entity => libs/scene}/EntitySettings.h | 4 - .../scene}/KeyObserverDelegate.h | 5 - .../entity => libs/scene}/KeyObserverMap.h | 5 - .../scene}/KeyValueObserver.cpp | 4 - .../entity => libs/scene}/KeyValueObserver.h | 4 - libs/scene/ModelFinder.cpp | 2 +- .../entity => libs/scene}/ModelKey.cpp | 0 {radiantcore/entity => libs/scene}/ModelKey.h | 0 {radiantcore/entity => libs/scene}/NameKey.h | 5 - .../entity => libs/scene}/NameKeyObserver.cpp | 4 - .../entity => libs/scene}/NameKeyObserver.h | 4 - .../scene}/NamespaceManager.cpp | 5 - .../entity => libs/scene}/NamespaceManager.h | 4 - .../entity => libs/scene}/OriginKey.h | 0 .../scene}/RenderableEntityName.cpp | 5 - .../scene}/RenderableEntityName.h | 5 - .../scene}/RenderableObjectCollection.h | 11 +- libs/scene/RenderableTargetLines.cpp | 60 ++++++ .../scene}/RenderableTargetLines.h | 66 +------ .../entity => libs/scene}/ShaderParms.cpp | 5 - .../entity => libs/scene}/ShaderParms.h | 5 - .../entity/target => libs/scene}/Target.h | 4 - .../target => libs/scene}/TargetKey.cpp | 4 - .../entity/target => libs/scene}/TargetKey.h | 5 - .../scene}/TargetKeyCollection.cpp | 6 +- .../scene}/TargetKeyCollection.h | 5 - .../target => libs/scene}/TargetLineNode.cpp | 7 +- .../target => libs/scene}/TargetLineNode.h | 7 +- .../target => libs/scene}/TargetManager.cpp | 0 .../target => libs/scene}/TargetManager.h | 0 .../target => libs/scene}/TargetableNode.cpp | 7 +- .../target => libs/scene}/TargetableNode.h | 6 - libs/scene/merge/GraphComparer.cpp | 2 +- libs/scene/merge/MergeAction.h | 2 +- libs/scene/scene_fwd.h | 9 + libs/selection/EntitiesFirstSelector.h | 4 +- libs/selection/EntitySelection.h | 10 +- libs/selectionlib.h | 34 ---- libs/wxutil/preview/EntityPreview.cpp | 6 +- libs/wxutil/preview/EntityPreview.h | 6 +- plugins/dm.conversation/ActorNodeFinder.h | 2 +- .../dm.conversation/ConversationDialog.cpp | 7 +- .../dm.conversation/ConversationEntity.cpp | 3 +- .../DifficultySettingsManager.cpp | 2 +- plugins/dm.editing/AIEditingPanel.cpp | 15 +- plugins/dm.editing/AIHeadPropertyEditor.cpp | 4 +- .../dm.editing/AIVocalSetPropertyEditor.cpp | 4 +- .../dm.gameconnection/DiffDoom3MapWriter.cpp | 6 +- .../dm.gameconnection/DiffDoom3MapWriter.h | 5 +- plugins/dm.gameconnection/GameConnection.cpp | 2 +- plugins/dm.gameconnection/MapObserver.cpp | 17 +- plugins/dm.gameconnection/MapObserver.h | 6 +- plugins/dm.gui/ReadableEditorDialog.cpp | 2 +- plugins/dm.objectives/ObjectiveEntity.cpp | 3 +- .../dm.objectives/ObjectiveEntityFinder.cpp | 3 +- plugins/dm.objectives/ObjectivesEditor.cpp | 4 +- .../ce/specpanel/EntityNameSpecifierPanel.cpp | 2 +- .../dm.stimresponse/StimResponseEditor.cpp | 13 +- plugins/script/interfaces/EntityInterface.cpp | 4 +- .../ui/animationpreview/AnimationPreview.cpp | 2 +- .../ui/animationpreview/AnimationPreview.h | 2 +- radiant/ui/common/EntityChooser.cpp | 2 +- radiant/ui/eclasstree/EClassTree.cpp | 2 +- radiant/ui/einspector/EntityInspector.cpp | 4 +- radiant/ui/einspector/ModelPropertyEditor.cpp | 4 +- radiant/ui/einspector/PropertyEditor.cpp | 4 +- radiant/ui/lightinspector/LightInspector.cpp | 2 +- .../ui/modelexport/ExportAsModelDialog.cpp | 19 +- radiant/ui/ortho/OrthoContextMenu.cpp | 2 +- radiantcore/CMakeLists.txt | 13 -- radiantcore/entity/EntityModule.cpp | 14 +- radiantcore/entity/EntityModule.h | 4 +- radiantcore/entity/RenderableArrow.cpp | 6 +- radiantcore/entity/RenderableArrow.h | 4 +- radiantcore/entity/RenderableEntityBox.cpp | 4 +- radiantcore/entity/RenderableEntityBox.h | 6 +- radiantcore/entity/VertexInstance.h | 6 +- radiantcore/entity/curve/Curve.cpp | 4 +- radiantcore/entity/curve/Curve.h | 2 +- radiantcore/entity/curve/CurveCatmullRom.cpp | 2 +- radiantcore/entity/curve/CurveCatmullRom.h | 2 +- radiantcore/entity/curve/CurveNURBS.cpp | 2 +- radiantcore/entity/curve/CurveNURBS.h | 2 +- radiantcore/entity/curve/RenderableCurve.h | 5 +- .../entity/doom3group/RenderableVertex.h | 2 +- .../entity/doom3group/StaticGeometryNode.h | 10 +- .../entity/eclassmodel/EclassModelNode.h | 8 +- .../entity/generic/GenericEntityNode.cpp | 3 +- .../entity/generic/GenericEntityNode.h | 6 +- radiantcore/entity/light/LightNode.cpp | 7 +- radiantcore/entity/light/LightNode.h | 4 +- radiantcore/entity/light/Renderables.cpp | 2 +- radiantcore/entity/speaker/SpeakerNode.cpp | 3 +- radiantcore/entity/speaker/SpeakerNode.h | 6 +- .../entity/speaker/SpeakerRenderables.cpp | 2 +- .../entity/speaker/SpeakerRenderables.h | 10 +- radiantcore/filters/InstanceUpdateWalker.h | 3 +- .../SetObjectSelectionByFilterWalker.h | 2 +- radiantcore/map/RegionManager.h | 2 +- radiantcore/map/algorithm/Export.cpp | 2 +- radiantcore/map/algorithm/MapExporter.cpp | 6 +- radiantcore/map/algorithm/MapImporter.cpp | 2 +- radiantcore/map/algorithm/Models.cpp | 12 +- radiantcore/map/format/Doom3MapReader.cpp | 4 +- radiantcore/map/format/Doom3MapWriter.cpp | 8 +- radiantcore/map/format/Doom3MapWriter.h | 7 +- radiantcore/map/format/Quake3MapReader.cpp | 4 +- .../map/format/portable/PortableMapReader.cpp | 2 +- .../map/format/portable/PortableMapWriter.cpp | 6 +- .../map/format/portable/PortableMapWriter.h | 4 +- .../model/export/ModelScalePreserver.cpp | 2 +- .../model/export/ScaledModelExporter.cpp | 2 +- .../selection/SceneManipulationPivot.cpp | 2 +- radiantcore/selection/algorithm/Curves.cpp | 4 +- .../manipulators/ManipulatorComponents.cpp | 2 +- test/Entity.cpp | 20 +- test/EntityInspector.cpp | 2 +- test/MapMerging.cpp | 64 +++---- test/Renderer.cpp | 6 +- test/SceneNode.cpp | 11 +- test/Transformation.cpp | 2 +- test/UndoRedo.cpp | 2 +- test/WorldspawnColour.cpp | 2 +- test/algorithm/Entity.h | 2 +- test/algorithm/Scene.h | 12 +- 136 files changed, 529 insertions(+), 666 deletions(-) rename {radiantcore/entity => libs/scene}/ColourKey.h (97%) rename {radiantcore/entity => libs/scene}/EntityNode.cpp (98%) rename {radiantcore/entity => libs/scene}/EntityNode.h (68%) rename {radiantcore/entity => libs/scene}/EntitySettings.cpp (98%) rename {radiantcore/entity => libs/scene}/EntitySettings.h (98%) rename {radiantcore/entity => libs/scene}/KeyObserverDelegate.h (94%) rename {radiantcore/entity => libs/scene}/KeyObserverMap.h (99%) rename {radiantcore/entity => libs/scene}/KeyValueObserver.cpp (96%) rename {radiantcore/entity => libs/scene}/KeyValueObserver.h (96%) rename {radiantcore/entity => libs/scene}/ModelKey.cpp (100%) rename {radiantcore/entity => libs/scene}/ModelKey.h (100%) rename {radiantcore/entity => libs/scene}/NameKey.h (94%) rename {radiantcore/entity => libs/scene}/NameKeyObserver.cpp (94%) rename {radiantcore/entity => libs/scene}/NameKeyObserver.h (95%) rename {radiantcore/entity => libs/scene}/NamespaceManager.cpp (99%) rename {radiantcore/entity => libs/scene}/NamespaceManager.h (98%) rename {radiantcore/entity => libs/scene}/OriginKey.h (100%) rename {radiantcore/entity => libs/scene}/RenderableEntityName.cpp (95%) rename {radiantcore/entity => libs/scene}/RenderableEntityName.h (96%) rename {radiantcore/entity => libs/scene}/RenderableObjectCollection.h (98%) create mode 100644 libs/scene/RenderableTargetLines.cpp rename {radiantcore/entity/target => libs/scene}/RenderableTargetLines.h (54%) rename {radiantcore/entity => libs/scene}/ShaderParms.cpp (97%) rename {radiantcore/entity => libs/scene}/ShaderParms.h (96%) rename {radiantcore/entity/target => libs/scene}/Target.h (98%) rename {radiantcore/entity/target => libs/scene}/TargetKey.cpp (97%) rename {radiantcore/entity/target => libs/scene}/TargetKey.h (97%) rename {radiantcore/entity/target => libs/scene}/TargetKeyCollection.cpp (95%) rename {radiantcore/entity/target => libs/scene}/TargetKeyCollection.h (97%) rename {radiantcore/entity/target => libs/scene}/TargetLineNode.cpp (98%) rename {radiantcore/entity/target => libs/scene}/TargetLineNode.h (94%) rename {radiantcore/entity/target => libs/scene}/TargetManager.cpp (100%) rename {radiantcore/entity/target => libs/scene}/TargetManager.h (100%) rename {radiantcore/entity/target => libs/scene}/TargetableNode.cpp (98%) rename {radiantcore/entity/target => libs/scene}/TargetableNode.h (97%) create mode 100644 libs/scene/scene_fwd.h diff --git a/include/ientity.h b/include/ientity.h index 090cbfccbc..e18561fa50 100644 --- a/include/ientity.h +++ b/include/ientity.h @@ -8,6 +8,7 @@ #include "itransformnode.h" #include +#include "scene/scene_fwd.h" #include "string/predicate.h" class IEntityClass; @@ -29,75 +30,12 @@ class KeyObserver: public sigc::trackable virtual void onKeyValueChanged(const std::string& newValue) = 0; }; -class EntityKeyValue; -class Entity; - /// Callback for an entity key value change using KeyObserverFunc = sigc::slot; -/** - * \brief Interface for a node which represents an entity. - * - * As well as providing access to the entity data with getEntity(), every - * IEntityNode can clone itself and apply a transformation matrix to its - * children (which might be brushes, patches or other entities). - */ -class IEntityNode : public IRenderEntity, - public virtual scene::INode, - public scene::Cloneable, - public IMatrixTransform -{ -public: - virtual ~IEntityNode() {} - - /// Get a modifiable reference to the contained Entity - virtual Entity& getEntity() = 0; - - /** - * @brief Observe key value changes using a callback function. - * - * This method provides a simpler interface for observing key value changes - * via the use of a callback function, rather than requiring a full - * KeyObserver object to be constructed and maintained by the calling code. - * - * @param key - * The key to observe. - * - * @param func - * Function to call when the key value changes. - */ - virtual void observeKey(const std::string& key, KeyObserverFunc func) = 0; - - /** - * greebo: Tells the entity to reload the child model. This usually - * includes removal of the child model node and triggering - * a "skin changed" event. - */ - virtual void refreshModel() = 0; - - /** - * Invokes the given function object for each attached entity. - * At this point attachment entities are not accessible through the node's children, - * they have to be accessed through this method instead. - */ - virtual void foreachAttachment(const std::function& functor) = 0; -}; -typedef std::shared_ptr IEntityNodePtr; - -inline Entity* Node_getEntity(const scene::INodePtr& node) -{ - IEntityNodePtr entityNode = std::dynamic_pointer_cast(node); - - if (entityNode != NULL) { - return &(entityNode->getEntity()); - } - - return NULL; -} - inline bool Node_isEntity(const scene::INodePtr& node) { - //assert(!((std::dynamic_pointer_cast(node) != nullptr) ^ (node->getNodeType() == scene::INode::Type::Entity))); + //assert(!((std::dynamic_pointer_cast(node) != nullptr) ^ (node->getNodeType() == scene::INode::Type::Entity))); return node->getNodeType() == scene::INode::Type::Entity; } @@ -114,7 +52,7 @@ class IEntitySelection virtual std::size_t size() const = 0; // Iterates over each selected entity node, invoking the given functor - virtual void foreachEntity(const std::function& functor) = 0; + virtual void foreachEntity(const std::function& functor) = 0; // Returns the key value shared by all entities in this set, or an empty string // if there is no such value. @@ -262,7 +200,7 @@ class IEntityModule : virtual ~IEntityModule() {} /// Create an entity node with the given entity class. - virtual IEntityNodePtr createEntity(const IEntityClassPtr& eclass) = 0; + virtual EntityNodePtr createEntity(const IEntityClassPtr& eclass) = 0; // Constructs a new targetmanager instance (used by root nodes) virtual ITargetManagerPtr createTargetManager() = 0; @@ -274,10 +212,10 @@ class IEntityModule : * Create an instance of the given entity at the given position, and return * the Node containing the new entity. * - * @returns: the scene::IEntityNodePtr referring to the new entity. + * @returns: the scene::EntityNodePtr referring to the new entity. * @throws: cmd::ExecutionFailure if anything goes wrong or the selection is not suitable. */ - virtual IEntityNodePtr createEntityFromSelection(const std::string& name, const Vector3& origin) = 0; + virtual EntityNodePtr createEntityFromSelection(const std::string& name, const Vector3& origin) = 0; }; inline IEntityModule& GlobalEntityModule() diff --git a/include/imapformat.h b/include/imapformat.h index ec9e67ea62..d318919fd9 100644 --- a/include/imapformat.h +++ b/include/imapformat.h @@ -1,6 +1,7 @@ -#pragma once +#pragma once #include "imodule.h" +#include "scene/scene_fwd.h" namespace scene { @@ -17,8 +18,6 @@ class IPatchNode; typedef std::shared_ptr IPatchNodePtr; class IBrushNode; typedef std::shared_ptr IBrushNodePtr; -class IEntityNode; -typedef std::shared_ptr IEntityNodePtr; /** Callback function to control how the Walker traverses the scene graph. This function * will be provided to the map export module by the Radiant map code. @@ -59,11 +58,11 @@ typedef std::shared_ptr PrimitiveParserPtr; /** * An abstract map writer class used to write any map elements - * as string to the given output stream. + * as string to the given output stream. * - * The IMapWriter interface defines beginWrite/endWrite pairs for - * each scene element (Entity, primitives and the Map itself). - * These are called by the map saving algorithm when traversing + * The IMapWriter interface defines beginWrite/endWrite pairs for + * each scene element (Entity, primitives and the Map itself). + * These are called by the map saving algorithm when traversing * the scene depth-first. The usual call order will look like this: * * beginWriteMap @@ -82,7 +81,7 @@ typedef std::shared_ptr PrimitiveParserPtr; * IMapWriter::FailureException will be thrown. The calling code * is designed to catch this exception. */ -class IMapWriter +class IMapWriter { public: // The generic exception type which is thrown by the IMapWriter methods @@ -97,7 +96,7 @@ class IMapWriter // Destructor virtual ~IMapWriter() {} - + /** * This is called before writing any nodes, to give an opportunity * to write a map header and version info. @@ -111,8 +110,8 @@ class IMapWriter virtual void endWriteMap(const scene::IMapRootNodePtr& root, std::ostream& stream) = 0; // Entity export methods - virtual void beginWriteEntity(const IEntityNodePtr& entity, std::ostream& stream) = 0; - virtual void endWriteEntity(const IEntityNodePtr& entity, std::ostream& stream) = 0; + virtual void beginWriteEntity(const std::shared_ptr& entity, std::ostream& stream) = 0; + virtual void endWriteEntity(const std::shared_ptr& entity, std::ostream& stream) = 0; // Brush export methods virtual void beginWriteBrush(const IBrushNodePtr& brush, std::ostream& stream) = 0; @@ -145,7 +144,7 @@ class IMapReader /** * Read the contents of the given stream and send them through the given MapImportFilter. - * Whether the nodes are actually added to the map or not is something the + * Whether the nodes are actually added to the map or not is something the * ImportFilter can decide. * * throws: FailureException on any error. @@ -202,10 +201,10 @@ class MapFormat : virtual const std::string& getGameType() const = 0; /** - * Instantiate a new map reader, using the given ImportFilter + * Instantiate a new map reader, using the given ImportFilter * which will be fed with nodes during the import. */ - virtual IMapReaderPtr getMapReader(IMapImportFilter& filter) const = 0; + virtual IMapReaderPtr getMapReader(IMapImportFilter& filter) const = 0; /** * Acquire a map writer instance, for exporting nodes to a stream. @@ -256,7 +255,7 @@ class IMapFormatManager : * Tries to look up the default map format for the given game type (e.g. "doom3") * associated with the given file extension. */ - virtual MapFormatPtr getMapFormatForGameType(const std::string& gameType, + virtual MapFormatPtr getMapFormatForGameType(const std::string& gameType, const std::string& extension) = 0; /** diff --git a/libs/entitylib.h b/libs/entitylib.h index 1e14cb46cc..d5fc54818c 100644 --- a/libs/entitylib.h +++ b/libs/entitylib.h @@ -1,6 +1,7 @@ #pragma once #include "scene/Entity.h" +#include "scene/EntityNode.h" #include "ieclass.h" #include "iselection.h" #include "iselectiontest.h" @@ -43,69 +44,6 @@ inline std::ostream& operator<< (std::ostream& os, const Entity& entity) { return os; } -class EntityNodeFindByClassnameWalker : - public scene::NodeVisitor -{ -protected: - // Name to search for - std::string _name; - - // The search result - scene::INodePtr _entityNode; - -public: - // Constructor - EntityNodeFindByClassnameWalker(const std::string& name) : - _name(name) - {} - - scene::INodePtr getEntityNode() { - return _entityNode; - } - - Entity* getEntity() { - return _entityNode != NULL ? Node_getEntity(_entityNode) : NULL; - } - - // Pre-descent callback - bool pre(const scene::INodePtr& node) { - if (_entityNode == NULL) { - // Entity not found yet - Entity* entity = Node_getEntity(node); - - if (entity != NULL) { - // Got an entity, let's see if the name matches - if (entity->getKeyValue("classname") == _name) { - _entityNode = node; - } - - return false; // don't traverse entities - } - else { - // Not an entity, traverse - return true; - } - } - else { - // Entity already found, don't traverse any further - return false; - } - } -}; - -/* greebo: Finds an entity with the given classname - */ -inline Entity* Scene_FindEntityByClass(const std::string& className) -{ - // Instantiate a walker to find the entity - EntityNodeFindByClassnameWalker walker(className); - - // Walk the scenegraph - GlobalSceneGraph().root()->traverse(walker); - - return walker.getEntity(); -} - /* Check if a node is the worldspawn. */ inline bool Node_isWorldspawn(const scene::INodePtr& node) @@ -142,7 +80,7 @@ inline scene::INodePtr changeEntityClassname(const scene::INodePtr& node, assert(eclass); // Create a new entity with the given class - IEntityNodePtr newNode(GlobalEntityModule().createEntity(eclass)); + EntityNodePtr newNode(GlobalEntityModule().createEntity(eclass)); Entity* oldEntity = Node_getEntity(oldNode); diff --git a/libs/maplib.h b/libs/maplib.h index c082e00b5f..1914286947 100644 --- a/libs/maplib.h +++ b/libs/maplib.h @@ -1,7 +1,7 @@ #pragma once #include "imap.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" namespace map { diff --git a/libs/scene/CMakeLists.txt b/libs/scene/CMakeLists.txt index 8c4a874cc4..4fc85939a0 100644 --- a/libs/scene/CMakeLists.txt +++ b/libs/scene/CMakeLists.txt @@ -4,16 +4,30 @@ add_library(scene InstanceWalkers.cpp Entity.cpp EntityKeyValue.cpp + EntityNode.cpp + EntitySettings.cpp + KeyValueObserver.cpp LayerUsageBreakdown.cpp ModelFinder.cpp + ModelKey.cpp + NameKeyObserver.cpp + NamespaceManager.cpp Node.cpp merge/MergeOperation.cpp merge/MergeOperationBase.cpp merge/MergeActionNode.cpp merge/GraphComparer.cpp merge/ThreeWayMergeOperation.cpp + RenderableEntityName.cpp + RenderableTargetLines.cpp SelectableNode.cpp SelectionIndex.cpp + ShaderParms.cpp + TargetableNode.cpp + TargetKey.cpp + TargetKeyCollection.cpp + TargetLineNode.cpp + TargetManager.cpp TraversableNodeSet.cpp Traverse.cpp) target_compile_options(scene PUBLIC ${SIGC_CFLAGS}) diff --git a/libs/scene/ChildPrimitives.cpp b/libs/scene/ChildPrimitives.cpp index 8beb59990a..e292cbf205 100644 --- a/libs/scene/ChildPrimitives.cpp +++ b/libs/scene/ChildPrimitives.cpp @@ -4,7 +4,7 @@ #include "ientity.h" #include "igroupnode.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" #include "registry/registry.h" namespace scene diff --git a/radiantcore/entity/ColourKey.h b/libs/scene/ColourKey.h similarity index 97% rename from radiantcore/entity/ColourKey.h rename to libs/scene/ColourKey.h index c8bae0734a..578275a628 100644 --- a/radiantcore/entity/ColourKey.h +++ b/libs/scene/ColourKey.h @@ -4,9 +4,6 @@ #include "scene/Entity.h" #include "irender.h" -namespace entity -{ - /** * greebo: this is a class encapsulating the "_color" spawnarg * of entity, observing it and maintaining the corresponding shader. @@ -87,5 +84,3 @@ class ColourKey : } } }; - -} // namespace entity diff --git a/libs/scene/EntityBreakdown.h b/libs/scene/EntityBreakdown.h index d335f93f01..d50e309a81 100644 --- a/libs/scene/EntityBreakdown.h +++ b/libs/scene/EntityBreakdown.h @@ -5,7 +5,7 @@ #include "iscenegraph.h" #include "ieclass.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" namespace scene { diff --git a/radiantcore/entity/EntityNode.cpp b/libs/scene/EntityNode.cpp similarity index 98% rename from radiantcore/entity/EntityNode.cpp rename to libs/scene/EntityNode.cpp index 69a665f31d..0a01ad3ef3 100644 --- a/radiantcore/entity/EntityNode.cpp +++ b/libs/scene/EntityNode.cpp @@ -7,13 +7,11 @@ #include "imap.h" #include "itransformable.h" #include "math/Hash.h" +#include "scenelib.h" #include "string/case_conv.h" #include "EntitySettings.h" -namespace entity -{ - EntityNode::EntityNode(const IEntityClassPtr& eclass) : TargetableNode(_spawnArgs, *this), _eclass(eclass), @@ -33,7 +31,6 @@ EntityNode::EntityNode(const IEntityClassPtr& eclass) : } EntityNode::EntityNode(const EntityNode& other) : - IEntityNode(other), SelectableNode(other), SelectionTestable(other), Namespaced(other), @@ -193,7 +190,7 @@ void EntityNode::observeKey(const std::string& key, KeyObserverFunc func) _keyObservers.observeKey(key, func); } -void EntityNode::foreachAttachment(const std::function& functor) +void EntityNode::foreachAttachment(const std::function& functor) { for (const auto& [node, _] : _attachedEnts) { @@ -605,7 +602,7 @@ void EntityNode::onEntitySettingsChanged() } // Notify all attached entities - foreachAttachment([](const IEntityNodePtr& node) + foreachAttachment([](const EntityNodePtr& node) { if (auto attachedEntity = std::dynamic_pointer_cast(node); attachedEntity) { @@ -645,10 +642,8 @@ void EntityNode::setRenderState(RenderState state) SelectableNode::setRenderState(state); // Propagate to attachments - foreachAttachment([=](const IEntityNodePtr& attachment) + foreachAttachment([=](const EntityNodePtr& attachment) { attachment->setRenderState(state); }); } - -} // namespace entity diff --git a/radiantcore/entity/EntityNode.h b/libs/scene/EntityNode.h similarity index 68% rename from radiantcore/entity/EntityNode.h rename to libs/scene/EntityNode.h index 83bf03609e..750b6bf074 100644 --- a/radiantcore/entity/EntityNode.h +++ b/libs/scene/EntityNode.h @@ -4,13 +4,14 @@ #include "scene/Entity.h" #include "inamespace.h" #include "icomparablenode.h" +#include "iselectiontest.h" #include "Bounded.h" #include "scene/SelectableNode.h" #include "transformlib.h" #include "NamespaceManager.h" -#include "target/TargetableNode.h" +#include "TargetableNode.h" #include "NameKey.h" #include "ColourKey.h" #include "ModelKey.h" @@ -21,25 +22,27 @@ #include "RenderableEntityName.h" #include "RenderableObjectCollection.h" -namespace entity -{ - const Vector4 INACTIVE_ENTITY_COLOUR(0.73, 0.73, 0.73, 1); class EntityNode; typedef std::shared_ptr EntityNodePtr; /** - * greebo: This is the common base class of all map entities. + * \brief Interface for a node which represents an entity. + * + * As well as providing access to the entity data with getEntity(), every + * EntityNode can clone itself and apply a transformation matrix to its + * children (which might be brushes, patches or other entities). */ -class EntityNode : - public IEntityNode, - public scene::SelectableNode, // derives from scene::Node - public SelectionTestable, - public Namespaced, - public TargetableNode, - public Transformable, - public scene::IComparableNode +class EntityNode: public IRenderEntity, + public scene::Cloneable, + public IMatrixTransform, + public scene::SelectableNode, // derives from scene::Node + public SelectionTestable, + public Namespaced, + public TargetableNode, + public Transformable, + public scene::IComparableNode { protected: // The entity class @@ -103,7 +106,7 @@ class EntityNode : // sure that everything will play nicely with entities as children of other // entities, and (2) storing entity node pointers instead of generic node // pointers avoids some extra dynamic_casting. - using AttachedEntity = std::pair; + using AttachedEntity = std::pair; using AttachedEntities = std::list; AttachedEntities _attachedEnts; @@ -126,9 +129,16 @@ class EntityNode : public: virtual ~EntityNode(); - // IEntityNode implementation - Entity& getEntity() override; - virtual void refreshModel() override; + /// Get a modifiable reference to the contained Entity + Entity& getEntity(); + + /** + * greebo: Tells the entity to reload the child model. This usually + * includes removal of the child model node and triggering + * a "skin changed" event. + */ + void refreshModel(); + virtual void transformChanged() override; // RenderEntity implementation @@ -184,9 +194,27 @@ class EntityNode : virtual void setRenderSystem(const RenderSystemPtr& renderSystem) override; virtual std::size_t getHighlightFlags() override; - // IEntityNode implementation - void observeKey(const std::string& key, KeyObserverFunc func) override; - void foreachAttachment(const std::function& functor) override; + /** + * @brief Observe key value changes using a callback function. + * + * This method provides a simpler interface for observing key value changes + * via the use of a callback function, rather than requiring a full + * KeyObserver object to be constructed and maintained by the calling code. + * + * @param key + * The key to observe. + * + * @param func + * Function to call when the key value changes. + */ + void observeKey(const std::string& key, KeyObserverFunc func); + + /** + * Invokes the given function object for each attached entity. + * At this point attachment entities are not accessible through the node's children, + * they have to be accessed through this method instead. + */ + void foreachAttachment(const std::function& functor); ModelKey& getModelKey(); // needed by the Doom3Group class, could be a fixme const ModelKey& getModelKey() const; @@ -283,4 +311,105 @@ class EntityNode : void detachFromRenderSystem(); }; -} // namespace entity +inline Entity* Node_getEntity(const scene::INodePtr& node) +{ + if (EntityNodePtr entityNode = std::dynamic_pointer_cast(node); entityNode) { + return &(entityNode->getEntity()); + } + return nullptr; +} + +class EntityNodeFindByClassnameWalker : + public scene::NodeVisitor +{ +protected: + // Name to search for + std::string _name; + + // The search result + scene::INodePtr _entityNode; + +public: + // Constructor + EntityNodeFindByClassnameWalker(const std::string& name) : + _name(name) + {} + + scene::INodePtr getEntityNode() { + return _entityNode; + } + + Entity* getEntity() { + return _entityNode != NULL ? Node_getEntity(_entityNode) : NULL; + } + + // Pre-descent callback + bool pre(const scene::INodePtr& node) { + if (_entityNode == NULL) { + // Entity not found yet + Entity* entity = Node_getEntity(node); + + if (entity != NULL) { + // Got an entity, let's see if the name matches + if (entity->getKeyValue("classname") == _name) { + _entityNode = node; + } + + return false; // don't traverse entities + } + else { + // Not an entity, traverse + return true; + } + } + else { + // Entity already found, don't traverse any further + return false; + } + } +}; + +/* greebo: Finds an entity with the given classname + */ +inline Entity* Scene_FindEntityByClass(const std::string& className) +{ + // Instantiate a walker to find the entity + EntityNodeFindByClassnameWalker walker(className); + + // Walk the scenegraph + GlobalSceneGraph().root()->traverse(walker); + + return walker.getEntity(); +} + +/** + * Tests the current selection and returns true if the selection is suitable + * for reparenting the selected primitives to the (last) selected entity. + */ +inline bool curSelectionIsSuitableForReparent() +{ + // Retrieve the selection information structure + const SelectionInfo& info = GlobalSelectionSystem().getSelectionInfo(); + + if (info.totalCount <= 1 || info.entityCount != 1) + { + return false; + } + + scene::INodePtr lastSelected = GlobalSelectionSystem().ultimateSelected(); + Entity* entity = Node_getEntity(lastSelected); + + // Reject non-entities or models + if (entity == nullptr || entity->isModel()) + { + return false; + } + + // Accept only group nodes as parent + if (!Node_getGroupNode(lastSelected)) + { + return false; + } + + return true; +} diff --git a/radiantcore/entity/EntitySettings.cpp b/libs/scene/EntitySettings.cpp similarity index 98% rename from radiantcore/entity/EntitySettings.cpp rename to libs/scene/EntitySettings.cpp index a166b4db31..d993edf10a 100644 --- a/radiantcore/entity/EntitySettings.cpp +++ b/libs/scene/EntitySettings.cpp @@ -3,9 +3,6 @@ #include "registry/registry.h" #include "registry/adaptors.h" -namespace entity -{ - EntitySettings::EntitySettings() : _lightVertexColours(static_cast(LightEditVertexType::NumberOfVertexTypes)) { @@ -59,5 +56,3 @@ void EntitySettings::onSettingsChanged() { _signalSettingsChanged.emit(); } - -} // namespace entity diff --git a/radiantcore/entity/EntitySettings.h b/libs/scene/EntitySettings.h similarity index 98% rename from radiantcore/entity/EntitySettings.h rename to libs/scene/EntitySettings.h index 8141fcfd75..3c29f06f5f 100644 --- a/radiantcore/entity/EntitySettings.h +++ b/libs/scene/EntitySettings.h @@ -7,8 +7,6 @@ #include #include "math/Vector3.h" -namespace entity { - class EntitySettings; typedef std::shared_ptr EntitySettingsPtr; @@ -158,5 +156,3 @@ class EntitySettings : void onSettingsChanged(); void initialiseAndObserveKey(const std::string& key, bool& targetBool); }; - -} // namespace entity diff --git a/radiantcore/entity/KeyObserverDelegate.h b/libs/scene/KeyObserverDelegate.h similarity index 94% rename from radiantcore/entity/KeyObserverDelegate.h rename to libs/scene/KeyObserverDelegate.h index 3b49641832..0643b13cd7 100644 --- a/radiantcore/entity/KeyObserverDelegate.h +++ b/libs/scene/KeyObserverDelegate.h @@ -3,9 +3,6 @@ #include "scene/Entity.h" #include -namespace entity -{ - /** * greebo: A KeyObserver wrapping around a function object. * The function is called as soon as the onKeyValueChanged @@ -40,5 +37,3 @@ class KeyObserverDelegate : void emptyCallback(const std::string& newValue) {} }; - -} // namespace entity diff --git a/radiantcore/entity/KeyObserverMap.h b/libs/scene/KeyObserverMap.h similarity index 99% rename from radiantcore/entity/KeyObserverMap.h rename to libs/scene/KeyObserverMap.h index 4896dd6e26..5ef3edb4b0 100644 --- a/radiantcore/entity/KeyObserverMap.h +++ b/libs/scene/KeyObserverMap.h @@ -30,9 +30,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include "scene/Entity.h" #include "KeyObserverDelegate.h" -namespace entity -{ - /** * @brief Map of key observers associated with a particular entity. * @@ -194,6 +191,4 @@ class KeyObserverMap : } }; -} // namespace entity - #endif diff --git a/radiantcore/entity/KeyValueObserver.cpp b/libs/scene/KeyValueObserver.cpp similarity index 96% rename from radiantcore/entity/KeyValueObserver.cpp rename to libs/scene/KeyValueObserver.cpp index ef60fd6f71..990a6d9ea5 100644 --- a/radiantcore/entity/KeyValueObserver.cpp +++ b/libs/scene/KeyValueObserver.cpp @@ -4,8 +4,6 @@ #include "scene/Entity.h" #include "scene/EntityKeyValue.h" -namespace entity { - KeyValueObserver::KeyValueObserver(EntityKeyValue& keyValue, INamespace* ns) : _keyValue(keyValue), _namespace(ns), @@ -44,5 +42,3 @@ void KeyValueObserver::onKeyValueChanged(const std::string& newValue) _namespace->addNameObserver(_observedValue, _keyValue); } } - -} // namespace entity diff --git a/radiantcore/entity/KeyValueObserver.h b/libs/scene/KeyValueObserver.h similarity index 96% rename from radiantcore/entity/KeyValueObserver.h rename to libs/scene/KeyValueObserver.h index 07d8cdfca5..b775d13ddd 100644 --- a/radiantcore/entity/KeyValueObserver.h +++ b/libs/scene/KeyValueObserver.h @@ -6,8 +6,6 @@ class INamespace; -namespace entity { - /** * greebo: This class observers a single EntityKeyValue and catches * any values that refer to a name in the attached namespace. @@ -42,6 +40,4 @@ class KeyValueObserver : }; typedef std::shared_ptr KeyValueObserverPtr; -} // namespace entity - #endif /* _KEY_VALUE_OBSERVER_H_ */ diff --git a/libs/scene/ModelFinder.cpp b/libs/scene/ModelFinder.cpp index 7040a8a516..bc2cec7e02 100644 --- a/libs/scene/ModelFinder.cpp +++ b/libs/scene/ModelFinder.cpp @@ -1,7 +1,7 @@ #include "ModelFinder.h" #include "ientity.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" namespace scene { diff --git a/radiantcore/entity/ModelKey.cpp b/libs/scene/ModelKey.cpp similarity index 100% rename from radiantcore/entity/ModelKey.cpp rename to libs/scene/ModelKey.cpp diff --git a/radiantcore/entity/ModelKey.h b/libs/scene/ModelKey.h similarity index 100% rename from radiantcore/entity/ModelKey.h rename to libs/scene/ModelKey.h diff --git a/radiantcore/entity/NameKey.h b/libs/scene/NameKey.h similarity index 94% rename from radiantcore/entity/NameKey.h rename to libs/scene/NameKey.h index d4bb5a7e58..1e571472be 100644 --- a/radiantcore/entity/NameKey.h +++ b/libs/scene/NameKey.h @@ -4,9 +4,6 @@ #include "scene/Entity.h" #include "scene/Entity.h" -namespace entity -{ - class NameKey : public KeyObserver { @@ -44,5 +41,3 @@ class NameKey : return _sigNameChanged; } }; - -} // namespace diff --git a/radiantcore/entity/NameKeyObserver.cpp b/libs/scene/NameKeyObserver.cpp similarity index 94% rename from radiantcore/entity/NameKeyObserver.cpp rename to libs/scene/NameKeyObserver.cpp index 0fed7e9c04..88c46d4d97 100644 --- a/radiantcore/entity/NameKeyObserver.cpp +++ b/libs/scene/NameKeyObserver.cpp @@ -4,8 +4,6 @@ #include "scene/Entity.h" #include "scene/EntityKeyValue.h" -namespace entity { - NameKeyObserver::NameKeyObserver(EntityKeyValue& keyValue, INamespace* ns) : _keyValue(keyValue), _namespace(ns) @@ -34,5 +32,3 @@ void NameKeyObserver::onKeyValueChanged(const std::string& newValue) // Remember the new value _oldValue = newValue; } - -} // namespace entity diff --git a/radiantcore/entity/NameKeyObserver.h b/libs/scene/NameKeyObserver.h similarity index 95% rename from radiantcore/entity/NameKeyObserver.h rename to libs/scene/NameKeyObserver.h index 7387f39f0c..2cbf526682 100644 --- a/radiantcore/entity/NameKeyObserver.h +++ b/libs/scene/NameKeyObserver.h @@ -5,8 +5,6 @@ class INamespace; -namespace entity { - /** * greebo: A NameKeyObserver is attached to all EntityKeyValues * which qualify as name in the context of the current game. @@ -36,6 +34,4 @@ class NameKeyObserver : }; typedef std::shared_ptr NameKeyObserverPtr; -} // namespace entity - #endif /* _NAME_KEY_OBSERVER_H_ */ diff --git a/radiantcore/entity/NamespaceManager.cpp b/libs/scene/NamespaceManager.cpp similarity index 99% rename from radiantcore/entity/NamespaceManager.cpp rename to libs/scene/NamespaceManager.cpp index e6f468de26..9a248378fb 100644 --- a/radiantcore/entity/NamespaceManager.cpp +++ b/libs/scene/NamespaceManager.cpp @@ -5,9 +5,6 @@ #include "string/predicate.h" #include "gamelib.h" -namespace entity -{ - namespace { const char* const NAME_KEY("name"); @@ -251,5 +248,3 @@ void NamespaceManager::detachKeyObservers() detachKeyObserver(key, value); }); } - -} // namespace entity diff --git a/radiantcore/entity/NamespaceManager.h b/libs/scene/NamespaceManager.h similarity index 98% rename from radiantcore/entity/NamespaceManager.h rename to libs/scene/NamespaceManager.h index 65a214fbe5..ef32fe2cc7 100644 --- a/radiantcore/entity/NamespaceManager.h +++ b/libs/scene/NamespaceManager.h @@ -9,8 +9,6 @@ #include "NameKeyObserver.h" #include "util/Noncopyable.h" -namespace entity { - class NamespaceManager : public Entity::Observer, public Namespaced, @@ -101,5 +99,3 @@ class NamespaceManager : void attachKeyObserver(const std::string& key, EntityKeyValue& keyValue); void detachKeyObserver(const std::string& key, EntityKeyValue& keyValue); }; - -} // namespace entity diff --git a/radiantcore/entity/OriginKey.h b/libs/scene/OriginKey.h similarity index 100% rename from radiantcore/entity/OriginKey.h rename to libs/scene/OriginKey.h diff --git a/radiantcore/entity/RenderableEntityName.cpp b/libs/scene/RenderableEntityName.cpp similarity index 95% rename from radiantcore/entity/RenderableEntityName.cpp rename to libs/scene/RenderableEntityName.cpp index 1693ec42fd..29643e4219 100644 --- a/radiantcore/entity/RenderableEntityName.cpp +++ b/libs/scene/RenderableEntityName.cpp @@ -1,9 +1,6 @@ #include "RenderableEntityName.h" #include "EntityNode.h" -namespace entity -{ - const Vector3& RenderableEntityName::getWorldPosition() { return _entity.getWorldPosition(); @@ -21,5 +18,3 @@ const Vector4& RenderableEntityName::getColour() return _colour; } - -} diff --git a/radiantcore/entity/RenderableEntityName.h b/libs/scene/RenderableEntityName.h similarity index 96% rename from radiantcore/entity/RenderableEntityName.h rename to libs/scene/RenderableEntityName.h index ea1e287b7a..03aed539c4 100644 --- a/radiantcore/entity/RenderableEntityName.h +++ b/libs/scene/RenderableEntityName.h @@ -4,9 +4,6 @@ #include "render/RenderableTextBase.h" #include "NameKey.h" -namespace entity -{ - class EntityNode; class RenderableEntityName : @@ -29,5 +26,3 @@ class RenderableEntityName : const Vector4& getColour() override; }; - -} diff --git a/radiantcore/entity/RenderableObjectCollection.h b/libs/scene/RenderableObjectCollection.h similarity index 98% rename from radiantcore/entity/RenderableObjectCollection.h rename to libs/scene/RenderableObjectCollection.h index 1eecf348e4..4f593e3799 100644 --- a/radiantcore/entity/RenderableObjectCollection.h +++ b/libs/scene/RenderableObjectCollection.h @@ -8,9 +8,6 @@ #include "irenderableobject.h" #include "itextstream.h" -namespace entity -{ - class RenderableObjectCollection : public sigc::trackable { @@ -35,7 +32,7 @@ class RenderableObjectCollection : { sigc::connection subscription = object->signal_boundsChanged().connect( sigc::mem_fun(*this, &RenderableObjectCollection::onObjectBoundsChanged)); - + if (!_objects.try_emplace(object, ObjectData{ shader, subscription }).second) { // We've already been subscribed to this one @@ -83,7 +80,7 @@ class RenderableObjectCollection : // If the whole collection doesn't intersect, quit early if (!_collectionBounds.intersects(bounds)) return; - + for (const auto& [object, objectData] : _objects) { if (objectIntersectsBounds(bounds, *object)) @@ -115,7 +112,7 @@ class RenderableObjectCollection : void ensureBoundsUpToDate() { if (!_collectionBoundsNeedUpdate) return; - + _collectionBoundsNeedUpdate = false; _collectionBounds = AABB(); @@ -127,5 +124,3 @@ class RenderableObjectCollection : } } }; - -} diff --git a/libs/scene/RenderableTargetLines.cpp b/libs/scene/RenderableTargetLines.cpp new file mode 100644 index 0000000000..e59cf40c9f --- /dev/null +++ b/libs/scene/RenderableTargetLines.cpp @@ -0,0 +1,60 @@ +#include "RenderableTargetLines.h" +#include "EntityNode.h" + +void RenderableTargetLines::addTargetLine(const Vector3& startPosition, const Vector3& endPosition, + std::vector& vertices, std::vector& indices) +{ + // Take the mid-point + Vector3 mid((startPosition + endPosition) * 0.5f); + + // Get the normalised target direction + Vector3 targetDir = (endPosition - startPosition); + + // Normalise the length manually to get the scale for the arrows + double length = targetDir.getLength(); + targetDir *= 1 / length; + + // Get the orthogonal direction (in the xy plane) + Vector3 xyDir(endPosition.y() - startPosition.y(), startPosition.x() - endPosition.x(), 0); + xyDir.normalise(); + + // Let the target arrow not be any longer than one tenth of the total distance + double targetArrowLength = length * 0.10f; + + // Clamp the length to a few units anyway + if (targetArrowLength > TARGET_MAX_ARROW_LENGTH) { + targetArrowLength = TARGET_MAX_ARROW_LENGTH; + } + + targetDir *= targetArrowLength; + xyDir *= targetArrowLength; + + // Get a point slightly away from the target + Vector3 arrowBase(mid - targetDir); + + // The arrow points for the XY plane + Vector3 xyPoint1 = arrowBase + xyDir; + Vector3 xyPoint2 = arrowBase - xyDir; + + auto colour = _entity.getEntityColour(); + + auto indexOffset = static_cast(vertices.size()); + + // The line from this to the other entity + vertices.push_back(render::RenderVertex(startPosition, { 1,0,0 }, { 0, 0 }, colour)); + vertices.push_back(render::RenderVertex(endPosition, { 1,0,0 }, { 0, 0 }, colour)); + + // The "arrow indicators" in the xy plane + vertices.push_back(render::RenderVertex(mid, { 1,0,0 }, { 0, 0 }, colour)); + vertices.push_back(render::RenderVertex(xyPoint1, { 1,0,0 }, { 0, 0 }, colour)); + + vertices.push_back(render::RenderVertex(mid, { 1,0,0 }, { 0, 0 }, colour)); + vertices.push_back(render::RenderVertex(xyPoint2, { 1,0,0 }, { 0, 0 }, colour)); + + indices.push_back(indexOffset + 0); + indices.push_back(indexOffset + 1); + indices.push_back(indexOffset + 2); + indices.push_back(indexOffset + 3); + indices.push_back(indexOffset + 4); + indices.push_back(indexOffset + 5); +} diff --git a/radiantcore/entity/target/RenderableTargetLines.h b/libs/scene/RenderableTargetLines.h similarity index 54% rename from radiantcore/entity/target/RenderableTargetLines.h rename to libs/scene/RenderableTargetLines.h index db5d1a1b96..3442b474e5 100644 --- a/radiantcore/entity/target/RenderableTargetLines.h +++ b/libs/scene/RenderableTargetLines.h @@ -8,9 +8,6 @@ #include "math/Segment.h" #include "render/RenderableGeometry.h" -namespace entity -{ - namespace { constexpr const double TARGET_MAX_ARROW_LENGTH = 10; @@ -29,7 +26,7 @@ class RenderableTargetLines : public render::RenderableGeometry { private: - const IEntityNode& _entity; + const EntityNode& _entity; const TargetKeyCollection& _targetKeys; Vector3 _worldPosition; @@ -37,7 +34,7 @@ class RenderableTargetLines : bool _updateNeeded; public: - RenderableTargetLines(const IEntityNode& entity, const TargetKeyCollection& targetKeys) : + RenderableTargetLines(const EntityNode& entity, const TargetKeyCollection& targetKeys) : _entity(entity), _targetKeys(targetKeys), _updateNeeded(true) @@ -103,62 +100,5 @@ class RenderableTargetLines : // Adds points to the vector, defining a line from start to end, with arrow indicators // in the XY plane (located at the midpoint between start/end). void addTargetLine(const Vector3& startPosition, const Vector3& endPosition, - std::vector& vertices, std::vector& indices) - { - // Take the mid-point - Vector3 mid((startPosition + endPosition) * 0.5f); - - // Get the normalised target direction - Vector3 targetDir = (endPosition - startPosition); - - // Normalise the length manually to get the scale for the arrows - double length = targetDir.getLength(); - targetDir *= 1 / length; - - // Get the orthogonal direction (in the xy plane) - Vector3 xyDir(endPosition.y() - startPosition.y(), startPosition.x() - endPosition.x(), 0); - xyDir.normalise(); - - // Let the target arrow not be any longer than one tenth of the total distance - double targetArrowLength = length * 0.10f; - - // Clamp the length to a few units anyway - if (targetArrowLength > TARGET_MAX_ARROW_LENGTH) { - targetArrowLength = TARGET_MAX_ARROW_LENGTH; - } - - targetDir *= targetArrowLength; - xyDir *= targetArrowLength; - - // Get a point slightly away from the target - Vector3 arrowBase(mid - targetDir); - - // The arrow points for the XY plane - Vector3 xyPoint1 = arrowBase + xyDir; - Vector3 xyPoint2 = arrowBase - xyDir; - - auto colour = _entity.getEntityColour(); - - auto indexOffset = static_cast(vertices.size()); - - // The line from this to the other entity - vertices.push_back(render::RenderVertex(startPosition, { 1,0,0 }, { 0, 0 }, colour)); - vertices.push_back(render::RenderVertex(endPosition, { 1,0,0 }, { 0, 0 }, colour)); - - // The "arrow indicators" in the xy plane - vertices.push_back(render::RenderVertex(mid, { 1,0,0 }, { 0, 0 }, colour)); - vertices.push_back(render::RenderVertex(xyPoint1, { 1,0,0 }, { 0, 0 }, colour)); - - vertices.push_back(render::RenderVertex(mid, { 1,0,0 }, { 0, 0 }, colour)); - vertices.push_back(render::RenderVertex(xyPoint2, { 1,0,0 }, { 0, 0 }, colour)); - - indices.push_back(indexOffset + 0); - indices.push_back(indexOffset + 1); - indices.push_back(indexOffset + 2); - indices.push_back(indexOffset + 3); - indices.push_back(indexOffset + 4); - indices.push_back(indexOffset + 5); - } + std::vector& vertices, std::vector& indices); }; - -} // namespace entity diff --git a/radiantcore/entity/ShaderParms.cpp b/libs/scene/ShaderParms.cpp similarity index 97% rename from radiantcore/entity/ShaderParms.cpp rename to libs/scene/ShaderParms.cpp index 6656a34f7a..8fa8633242 100644 --- a/radiantcore/entity/ShaderParms.cpp +++ b/libs/scene/ShaderParms.cpp @@ -7,9 +7,6 @@ #include #include -namespace entity -{ - ShaderParms::ShaderParms(KeyObserverMap& keyObserverMap, ColourKey& colourKey) : _keyObserverMap(keyObserverMap), _colourKey(colourKey), @@ -51,5 +48,3 @@ void ShaderParms::onShaderParmKeyValueChanged(std::size_t parm, const std::strin _parmValues[parm] = string::convert(value); } } - -} // namespace diff --git a/radiantcore/entity/ShaderParms.h b/libs/scene/ShaderParms.h similarity index 96% rename from radiantcore/entity/ShaderParms.h rename to libs/scene/ShaderParms.h index 991448cb03..32b5fb37d3 100644 --- a/radiantcore/entity/ShaderParms.h +++ b/libs/scene/ShaderParms.h @@ -4,9 +4,6 @@ #include "KeyObserverDelegate.h" -namespace entity -{ - class ColourKey; class KeyObserverMap; @@ -42,5 +39,3 @@ class ShaderParms: public sigc::trackable private: void onShaderParmKeyValueChanged(std::size_t parm, const std::string& value); }; - -} // namespace diff --git a/radiantcore/entity/target/Target.h b/libs/scene/Target.h similarity index 98% rename from radiantcore/entity/target/Target.h rename to libs/scene/Target.h index 22e923c8ab..9880508fa5 100644 --- a/radiantcore/entity/target/Target.h +++ b/libs/scene/Target.h @@ -7,8 +7,6 @@ #include "math/AABB.h" #include -namespace entity { - /** * greebo: This is an abstract representation of a target. * In Doom3 maps, a Target can be any map entity, that's @@ -106,5 +104,3 @@ class Target : } }; typedef std::shared_ptr TargetPtr; - -} // namespace entity diff --git a/radiantcore/entity/target/TargetKey.cpp b/libs/scene/TargetKey.cpp similarity index 97% rename from radiantcore/entity/target/TargetKey.cpp rename to libs/scene/TargetKey.cpp index f3b25393a9..48dc2578b5 100644 --- a/radiantcore/entity/target/TargetKey.cpp +++ b/libs/scene/TargetKey.cpp @@ -4,8 +4,6 @@ #include "TargetKeyCollection.h" #include "scene/EntityKeyValue.h" -namespace entity { - TargetKey::TargetKey(TargetKeyCollection& owner) : _owner(owner) {} @@ -67,5 +65,3 @@ void TargetKey::onTargetPositionChanged() { _owner.onTargetPositionChanged(); } - -} // namespace entity diff --git a/radiantcore/entity/target/TargetKey.h b/libs/scene/TargetKey.h similarity index 97% rename from radiantcore/entity/target/TargetKey.h rename to libs/scene/TargetKey.h index 71603696ca..f4e75289d8 100644 --- a/radiantcore/entity/target/TargetKey.h +++ b/libs/scene/TargetKey.h @@ -5,9 +5,6 @@ #include "Target.h" -namespace entity -{ - class TargetKeyCollection; /** @@ -54,5 +51,3 @@ class TargetKey : private: void onTargetPositionChanged(); }; - -} // namespace entity diff --git a/radiantcore/entity/target/TargetKeyCollection.cpp b/libs/scene/TargetKeyCollection.cpp similarity index 95% rename from radiantcore/entity/target/TargetKeyCollection.cpp rename to libs/scene/TargetKeyCollection.cpp index 1850c8ce7d..c89f563f01 100644 --- a/radiantcore/entity/target/TargetKeyCollection.cpp +++ b/libs/scene/TargetKeyCollection.cpp @@ -3,8 +3,6 @@ #include "TargetableNode.h" #include "string/predicate.h" -namespace entity { - TargetKeyCollection::TargetKeyCollection(TargetableNode& owner) : _owner(owner) {} @@ -40,7 +38,7 @@ bool TargetKeyCollection::empty() const return _targetKeys.empty(); } -bool TargetKeyCollection::isTargetKey(const std::string& key) +bool TargetKeyCollection::isTargetKey(const std::string& key) { // A key is a target key if it starts with "target" (any case) return (string::istarts_with(key, "target")); @@ -95,5 +93,3 @@ void TargetKeyCollection::onTargetPositionChanged() { _sigTargetPositionChanged.emit(); } - -} // namespace entity diff --git a/radiantcore/entity/target/TargetKeyCollection.h b/libs/scene/TargetKeyCollection.h similarity index 97% rename from radiantcore/entity/target/TargetKeyCollection.h rename to libs/scene/TargetKeyCollection.h index 0510e6c148..a30e0420b8 100644 --- a/radiantcore/entity/target/TargetKeyCollection.h +++ b/libs/scene/TargetKeyCollection.h @@ -4,9 +4,6 @@ #include #include "TargetKey.h" -namespace entity -{ - class TargetableNode; class TargetKeyCollection : @@ -58,5 +55,3 @@ class TargetKeyCollection : // Returns TRUE if the given key matches the pattern for target keys bool isTargetKey(const std::string& key); }; - -} // namespace entity diff --git a/radiantcore/entity/target/TargetLineNode.cpp b/libs/scene/TargetLineNode.cpp similarity index 98% rename from radiantcore/entity/target/TargetLineNode.cpp rename to libs/scene/TargetLineNode.cpp index c5da233b75..2d7677c021 100644 --- a/radiantcore/entity/target/TargetLineNode.cpp +++ b/libs/scene/TargetLineNode.cpp @@ -1,11 +1,8 @@ #include "TargetLineNode.h" -#include "../EntityNode.h" +#include "EntityNode.h" #include "ilightnode.h" -namespace entity -{ - TargetLineNode::TargetLineNode(EntityNode& owner) : scene::Node(), _owner(owner), @@ -126,5 +123,3 @@ void TargetLineNode::queueRenderableUpdate() { _targetLines.queueUpdate(); } - -} diff --git a/radiantcore/entity/target/TargetLineNode.h b/libs/scene/TargetLineNode.h similarity index 94% rename from radiantcore/entity/target/TargetLineNode.h rename to libs/scene/TargetLineNode.h index 7cceccdac4..70a42eaa3d 100644 --- a/radiantcore/entity/target/TargetLineNode.h +++ b/libs/scene/TargetLineNode.h @@ -4,14 +4,11 @@ #include "scene/Node.h" #include "RenderableTargetLines.h" -namespace entity -{ - class EntityNode; /** * Non-selectable node representing one ore more connection lines between - * entities, displayed as a line with an arrow in the middle. + * entities, displayed as a line with an arrow in the middle. * It is owned and managed by the EntityNode hosting the corresponding TargetKey. * The rationale of inserting these lines as separate node type is to prevent * the lines from disappearing from the view when the targeting/targeted entities @@ -54,5 +51,3 @@ class TargetLineNode final : private: Vector3 getOwnerPosition() const; }; - -} diff --git a/radiantcore/entity/target/TargetManager.cpp b/libs/scene/TargetManager.cpp similarity index 100% rename from radiantcore/entity/target/TargetManager.cpp rename to libs/scene/TargetManager.cpp diff --git a/radiantcore/entity/target/TargetManager.h b/libs/scene/TargetManager.h similarity index 100% rename from radiantcore/entity/target/TargetManager.h rename to libs/scene/TargetManager.h diff --git a/radiantcore/entity/target/TargetableNode.cpp b/libs/scene/TargetableNode.cpp similarity index 98% rename from radiantcore/entity/target/TargetableNode.cpp rename to libs/scene/TargetableNode.cpp index 6975efa957..e8371a5125 100644 --- a/radiantcore/entity/target/TargetableNode.cpp +++ b/libs/scene/TargetableNode.cpp @@ -1,10 +1,9 @@ #include "TargetableNode.h" #include "TargetManager.h" -#include "../EntityNode.h" +#include "EntityNode.h" #include "TargetLineNode.h" - -namespace entity { +#include "scenelib.h" TargetableNode::TargetableNode(Entity& entity, EntityNode& node) : _d3entity(entity), @@ -178,5 +177,3 @@ void TargetableNode::onRenderSystemChanged() _targetLineNode->onRenderSystemChanged(); } } - -} // namespace entity diff --git a/radiantcore/entity/target/TargetableNode.h b/libs/scene/TargetableNode.h similarity index 97% rename from radiantcore/entity/target/TargetableNode.h rename to libs/scene/TargetableNode.h index f6b7ef61e8..8ebd7eea4c 100644 --- a/radiantcore/entity/target/TargetableNode.h +++ b/libs/scene/TargetableNode.h @@ -3,14 +3,10 @@ #include "selectionlib.h" #include "scene/Node.h" #include "scene/Entity.h" -#include "entitylib.h" #include "TargetKeyCollection.h" #include "RenderableTargetLines.h" -namespace entity -{ - class EntityNode; class TargetLineNode; @@ -76,5 +72,3 @@ class TargetableNode : void onTransformationChanged(); void onRenderSystemChanged(); }; - -} // namespace entity diff --git a/libs/scene/merge/GraphComparer.cpp b/libs/scene/merge/GraphComparer.cpp index 7699fd031c..62ab08c782 100644 --- a/libs/scene/merge/GraphComparer.cpp +++ b/libs/scene/merge/GraphComparer.cpp @@ -1,7 +1,7 @@ #include "GraphComparer.h" #include -#include "ientity.h" +#include "scene/EntityNode.h" #include "i18n.h" #include "itextstream.h" #include "iselectiongroup.h" diff --git a/libs/scene/merge/MergeAction.h b/libs/scene/merge/MergeAction.h index e051d371da..5c5e1f3cd3 100644 --- a/libs/scene/merge/MergeAction.h +++ b/libs/scene/merge/MergeAction.h @@ -5,7 +5,7 @@ #include "imapmerge.h" #include "../Clone.h" #include "../scenelib.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" namespace scene { diff --git a/libs/scene/scene_fwd.h b/libs/scene/scene_fwd.h new file mode 100644 index 0000000000..fb81aea4ae --- /dev/null +++ b/libs/scene/scene_fwd.h @@ -0,0 +1,9 @@ +/// \file Forward declarations and typedefs for the scene library +#pragma once + +#include + +class EntityKeyValue; +class Entity; +class EntityNode; +using EntityNodePtr = std::shared_ptr; diff --git a/libs/selection/EntitiesFirstSelector.h b/libs/selection/EntitiesFirstSelector.h index c06d4f761b..3c60308b7b 100644 --- a/libs/selection/EntitiesFirstSelector.h +++ b/libs/selection/EntitiesFirstSelector.h @@ -4,7 +4,7 @@ #include "iselectable.h" #include "iselectiontest.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" namespace selection { @@ -57,7 +57,7 @@ class EntitiesFirstSelector : if (!intersection.isValid()) return; // skip invalid intersections auto existing = _currentSelectables.find(selectable); - auto isEntity = dynamic_cast(selectable) != nullptr; + auto isEntity = dynamic_cast(selectable) != nullptr; auto& pool = isEntity ? _entityPool : _primitivePool; if (existing != _currentSelectables.end()) diff --git a/libs/selection/EntitySelection.h b/libs/selection/EntitySelection.h index cbc1947b0f..47a60f6d0b 100644 --- a/libs/selection/EntitySelection.h +++ b/libs/selection/EntitySelection.h @@ -3,7 +3,7 @@ #include #include "inode.h" #include "ieclass.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" #include "iselection.h" #include "iselectable.h" #include "CollectiveSpawnargs.h" @@ -150,7 +150,7 @@ class EntitySelection final : { IEntityClassPtr result; - foreachEntity([&](const IEntityNodePtr& entityNode) + foreachEntity([&](const EntityNodePtr& entityNode) { auto eclass = entityNode->getEntity().getEntityClass(); @@ -180,7 +180,7 @@ class EntitySelection final : { std::set values; - foreachEntity([&](const IEntityNodePtr& entity) + foreachEntity([&](const EntityNodePtr& entity) { values.emplace(std::move(entity->getEntity().getKeyValue(key))); }); @@ -191,7 +191,7 @@ class EntitySelection final : return _spawnargs.getSharedKeyValue(key); } - void foreachEntity(const std::function& functor) override + void foreachEntity(const std::function& functor) override { for (auto& tracked : _trackedEntities) { @@ -199,7 +199,7 @@ class EntitySelection final : if (!node) continue; - auto entityNode = scene::node_cast(node); + auto entityNode = scene::node_cast(node); assert(entityNode); if (entityNode) diff --git a/libs/selectionlib.h b/libs/selectionlib.h index 914db7d2a2..26961e3a67 100644 --- a/libs/selectionlib.h +++ b/libs/selectionlib.h @@ -6,11 +6,9 @@ #include "igroupnode.h" #include "iselectiongroup.h" #include "iscenegraph.h" -#include "scene/Entity.h" #include "ipatch.h" #include "math/Vector3.h" #include "math/AABB.h" -#include "scene/Entity.h" /** * @brief A structure containing information about the current Selection. @@ -180,38 +178,6 @@ inline void applyShaderToSelection(const std::string& shaderName) SceneChangeNotify(); } -/** - * Tests the current selection and returns true if the selection is suitable - * for reparenting the selected primitives to the (last) selected entity. - */ -inline bool curSelectionIsSuitableForReparent() -{ - // Retrieve the selection information structure - const SelectionInfo& info = GlobalSelectionSystem().getSelectionInfo(); - - if (info.totalCount <= 1 || info.entityCount != 1) - { - return false; - } - - scene::INodePtr lastSelected = GlobalSelectionSystem().ultimateSelected(); - Entity* entity = Node_getEntity(lastSelected); - - // Reject non-entities or models - if (entity == nullptr || entity->isModel()) - { - return false; - } - - // Accept only group nodes as parent - if (!Node_getGroupNode(lastSelected)) - { - return false; - } - - return true; -} - // Replaces the group assignments of the given node with the given groups inline void assignNodeToSelectionGroups(const scene::INodePtr& node, const IGroupSelectable::GroupIds& groups) { diff --git a/libs/wxutil/preview/EntityPreview.cpp b/libs/wxutil/preview/EntityPreview.cpp index 65f739d280..26e3655fe7 100644 --- a/libs/wxutil/preview/EntityPreview.cpp +++ b/libs/wxutil/preview/EntityPreview.cpp @@ -4,7 +4,7 @@ #include "ieclass.h" #include "ifilter.h" #include "scene/BasicRootNode.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" #include "../dialog/MessageBox.h" #include "scene/PrefabBoundsAccumulator.h" @@ -24,12 +24,12 @@ void EntityPreview::setDefaultCamDistanceFactor(float factor) _defaultCamDistanceFactor = factor; } -const IEntityNodePtr& EntityPreview::getEntity() +const EntityNodePtr& EntityPreview::getEntity() { return _entity; } -void EntityPreview::setEntity(const IEntityNodePtr& entity) +void EntityPreview::setEntity(const EntityNodePtr& entity) { if (_entity == entity) return; diff --git a/libs/wxutil/preview/EntityPreview.h b/libs/wxutil/preview/EntityPreview.h index 5fae27722e..7788761ea8 100644 --- a/libs/wxutil/preview/EntityPreview.h +++ b/libs/wxutil/preview/EntityPreview.h @@ -23,7 +23,7 @@ class EntityPreview : scene::IMapRootNodePtr _rootNode; // The previewed entity - IEntityNodePtr _entity; + EntityNodePtr _entity; AABB _untransformedEntityBounds; @@ -56,8 +56,8 @@ class EntityPreview : // defaults to 6. void setDefaultCamDistanceFactor(float factor); - const IEntityNodePtr& getEntity(); - void setEntity(const IEntityNodePtr& entity); + const EntityNodePtr& getEntity(); + void setEntity(const EntityNodePtr& entity); }; } diff --git a/plugins/dm.conversation/ActorNodeFinder.h b/plugins/dm.conversation/ActorNodeFinder.h index 7c767893b6..7591396b20 100644 --- a/plugins/dm.conversation/ActorNodeFinder.h +++ b/plugins/dm.conversation/ActorNodeFinder.h @@ -1,7 +1,7 @@ #pragma once #include "iscenegraph.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" namespace scene { diff --git a/plugins/dm.conversation/ConversationDialog.cpp b/plugins/dm.conversation/ConversationDialog.cpp index f9ba6e6e0c..bd24239174 100644 --- a/plugins/dm.conversation/ConversationDialog.cpp +++ b/plugins/dm.conversation/ConversationDialog.cpp @@ -7,6 +7,7 @@ #include "ui/imainframe.h" #include "iscenegraph.h" #include "string/string.h" +#include "scene/EntityNode.h" #include "wxutil/dataview/TreeModel.h" #include "wxutil/dialog/MessageBox.h" @@ -65,7 +66,7 @@ void ConversationDialog::populateWindow() _entityView->AppendTextColumn("", _convEntityColumns.displayName.getColumnIndex(), wxDATAVIEW_CELL_INERT, wxCOL_WIDTH_AUTOSIZE, wxALIGN_NOT, wxDATAVIEW_COL_SORTABLE); - _entityView->Connect(wxEVT_DATAVIEW_SELECTION_CHANGED, + _entityView->Connect(wxEVT_DATAVIEW_SELECTION_CHANGED, wxDataViewEventHandler(ConversationDialog::onEntitySelectionChanged), NULL, this); // Wire up button signals @@ -197,7 +198,7 @@ int ConversationDialog::ShowModal() { save(); } - + return returnCode; } @@ -277,7 +278,7 @@ void ConversationDialog::onAddEntity(wxCommandEvent& ev) if (eclass) { // Construct a Node of this entity type - IEntityNodePtr node(GlobalEntityModule().createEntity(eclass)); + EntityNodePtr node(GlobalEntityModule().createEntity(eclass)); // Create a random offset node->getEntity().setKeyValue( diff --git a/plugins/dm.conversation/ConversationEntity.cpp b/plugins/dm.conversation/ConversationEntity.cpp index 4c4fa2b797..726d07eb0a 100644 --- a/plugins/dm.conversation/ConversationEntity.cpp +++ b/plugins/dm.conversation/ConversationEntity.cpp @@ -4,8 +4,7 @@ #include "i18n.h" #include "itextstream.h" -#include "scene/Entity.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" #include "string/convert.h" #include "ConversationKeyExtractor.h" diff --git a/plugins/dm.difficulty/DifficultySettingsManager.cpp b/plugins/dm.difficulty/DifficultySettingsManager.cpp index 681713f97d..bdac7c26c5 100644 --- a/plugins/dm.difficulty/DifficultySettingsManager.cpp +++ b/plugins/dm.difficulty/DifficultySettingsManager.cpp @@ -211,7 +211,7 @@ void DifficultySettingsManager::saveSettings() } // Create and insert a new entity node into the scenegraph root - IEntityNodePtr entNode = GlobalEntityModule().createEntity(diffEclass); + EntityNodePtr entNode = GlobalEntityModule().createEntity(diffEclass); GlobalSceneGraph().root()->addChildNode(entNode); // Add the entity to the list diff --git a/plugins/dm.editing/AIEditingPanel.cpp b/plugins/dm.editing/AIEditingPanel.cpp index a497f7216b..31b3e2df55 100644 --- a/plugins/dm.editing/AIEditingPanel.cpp +++ b/plugins/dm.editing/AIEditingPanel.cpp @@ -8,6 +8,7 @@ #include "ui/ientityinspector.h" #include "iundo.h" #include "selectionlib.h" +#include "scene/EntityNode.h" #include "SpawnargLinkedCheckbox.h" #include "SpawnargLinkedSpinButton.h" @@ -159,7 +160,7 @@ void AIEditingPanel::constructWidgets() { // Behaviour widgets vbox->Add(createSectionLabel(_("Behaviour")), 0, wxTOP | wxBOTTOM, 6); - + wxGridSizer* table = new wxGridSizer(10, 2, 4, 12); vbox->Add(table, 0, wxLEFT, 18); @@ -205,7 +206,7 @@ void AIEditingPanel::constructWidgets() { // Abilities widgets vbox->Add(createSectionLabel(_("Abilities")), 0, wxTOP | wxBOTTOM, 6); - + wxGridSizer* table = new wxGridSizer(3, 2, 4, 12); vbox->Add(table, 0, wxLEFT, 18); @@ -220,7 +221,7 @@ void AIEditingPanel::constructWidgets() { // Optimization widgets vbox->Add(createSectionLabel(_("Optimization")), 0, wxTOP | wxBOTTOM, 6); - + wxGridSizer* table = new wxGridSizer(3, 1, 4, 12); vbox->Add(table, 0, wxLEFT, 18); @@ -232,7 +233,7 @@ void AIEditingPanel::constructWidgets() { // Health / Combat widgets vbox->Add(createSectionLabel(_("Health / Combat")), 0, wxTOP | wxBOTTOM, 6); - + wxGridSizer* table = new wxGridSizer(5, 2, 4, 12); vbox->Add(table, 0, wxLEFT, 18); @@ -262,7 +263,7 @@ wxSizer* AIEditingPanel::createSpinButtonHbox(SpawnargLinkedSpinButton* spinButt return hbox; } -void AIEditingPanel::createChooserRow(wxSizer* table, const std::string& rowLabel, +void AIEditingPanel::createChooserRow(wxSizer* table, const std::string& rowLabel, const std::string& buttonLabel, const std::string& buttonIcon, const std::string& key) { @@ -365,7 +366,7 @@ void AIEditingPanel::updateWidgetsFromSelection() std::for_each(_labels.begin(), _labels.end(), [&] (LabelMap::value_type& pair) { pair.second->SetLabelText(_entity != nullptr ? _entity->getKeyValue(pair.first) : ""); - }); + }); } void AIEditingPanel::rescanSelection() @@ -420,7 +421,7 @@ void AIEditingPanel::onBrowseButton(wxCommandEvent& ev, const std::string& key) } else { - rError() << "Could not find a property editor implementing the IPropertyEditorDialog interface for key " + rError() << "Could not find a property editor implementing the IPropertyEditorDialog interface for key " << key << std::endl; } } diff --git a/plugins/dm.editing/AIHeadPropertyEditor.cpp b/plugins/dm.editing/AIHeadPropertyEditor.cpp index 3232731b5e..3dc0f54290 100644 --- a/plugins/dm.editing/AIHeadPropertyEditor.cpp +++ b/plugins/dm.editing/AIHeadPropertyEditor.cpp @@ -2,7 +2,7 @@ #include "i18n.h" #include "ieclass.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" #include #include @@ -71,7 +71,7 @@ void AIHeadPropertyEditor::onChooseButton(wxCommandEvent& ev) { auto selectedHead = dialog->getSelectedHead(); - _entities.foreachEntity([&](const IEntityNodePtr& entity) + _entities.foreachEntity([&](const EntityNodePtr& entity) { entity->getEntity().setKeyValue(DEF_HEAD_KEY, selectedHead); }); diff --git a/plugins/dm.editing/AIVocalSetPropertyEditor.cpp b/plugins/dm.editing/AIVocalSetPropertyEditor.cpp index 822658b7f9..9fc23b3fd8 100644 --- a/plugins/dm.editing/AIVocalSetPropertyEditor.cpp +++ b/plugins/dm.editing/AIVocalSetPropertyEditor.cpp @@ -2,7 +2,7 @@ #include "i18n.h" #include "ieclass.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" #include #include @@ -71,7 +71,7 @@ void AIVocalSetPropertyEditor::onChooseButton(wxCommandEvent& ev) { auto selectedSet = dialog->getSelectedVocalSet(); - _entities.foreachEntity([&](const IEntityNodePtr& entity) + _entities.foreachEntity([&](const EntityNodePtr& entity) { entity->getEntity().setKeyValue(DEF_VOCAL_SET_KEY, selectedSet); }); diff --git a/plugins/dm.gameconnection/DiffDoom3MapWriter.cpp b/plugins/dm.gameconnection/DiffDoom3MapWriter.cpp index 0c79b04ee3..18d524a35e 100644 --- a/plugins/dm.gameconnection/DiffDoom3MapWriter.cpp +++ b/plugins/dm.gameconnection/DiffDoom3MapWriter.cpp @@ -1,7 +1,7 @@ #include "DiffDoom3MapWriter.h" #include "DiffStatus.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" namespace gameconn { @@ -36,7 +36,7 @@ void DiffDoom3MapWriter::writeRemoveEntityStub(const std::string& name, std::ost stream << "}" << std::endl; } -void DiffDoom3MapWriter::beginWriteEntity(const IEntityNodePtr& entity, std::ostream& stream) { +void DiffDoom3MapWriter::beginWriteEntity(const EntityNodePtr& entity, std::ostream& stream) { const std::string& name = entity->name(); writeEntityPreamble(name, stream); stream << "{" << std::endl; @@ -48,7 +48,7 @@ void DiffDoom3MapWriter::beginWriteEntity(const IEntityNodePtr& entity, std::ost }); } -void DiffDoom3MapWriter::endWriteEntity(const IEntityNodePtr& entity, std::ostream& stream) { +void DiffDoom3MapWriter::endWriteEntity(const EntityNodePtr& entity, std::ostream& stream) { stream << "}" << std::endl; } diff --git a/plugins/dm.gameconnection/DiffDoom3MapWriter.h b/plugins/dm.gameconnection/DiffDoom3MapWriter.h index 41459a2215..8f0b7d93df 100644 --- a/plugins/dm.gameconnection/DiffDoom3MapWriter.h +++ b/plugins/dm.gameconnection/DiffDoom3MapWriter.h @@ -1,6 +1,7 @@ #pragma once #include "imapformat.h" +#include "scene/scene_fwd.h" #include @@ -26,8 +27,8 @@ class DiffDoom3MapWriter : public map::IMapWriter void endWriteMap(const scene::IMapRootNodePtr& root, std::ostream& stream) override; // Entity export methods - void beginWriteEntity(const IEntityNodePtr& entity, std::ostream& stream) override; - void endWriteEntity(const IEntityNodePtr& entity, std::ostream& stream) override; + void beginWriteEntity(const EntityNodePtr& entity, std::ostream& stream) override; + void endWriteEntity(const EntityNodePtr& entity, std::ostream& stream) override; void writeRemoveEntityStub(const std::string& name, std::ostream& stream); // Brush export methods diff --git a/plugins/dm.gameconnection/GameConnection.cpp b/plugins/dm.gameconnection/GameConnection.cpp index 53afa7c1b7..5fb29a3a23 100644 --- a/plugins/dm.gameconnection/GameConnection.cpp +++ b/plugins/dm.gameconnection/GameConnection.cpp @@ -8,7 +8,7 @@ #include "icameraview.h" #include "inode.h" #include "imap.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" #include "iselection.h" #include "ui/iuserinterface.h" #include "ui/imenumanager.h" diff --git a/plugins/dm.gameconnection/MapObserver.cpp b/plugins/dm.gameconnection/MapObserver.cpp index 4cdaca0a16..c5e41e82f1 100644 --- a/plugins/dm.gameconnection/MapObserver.cpp +++ b/plugins/dm.gameconnection/MapObserver.cpp @@ -1,7 +1,6 @@ #include "MapObserver.h" #include "inode.h" -#include "scene/Entity.h" - +#include "scene/EntityNode.h" #include "scene/EntityKeyValue.h" namespace gameconn @@ -10,10 +9,10 @@ namespace gameconn class EntityNodeCollector : public scene::NodeVisitor { public: - std::vector foundEntities; + std::vector foundEntities; bool pre(const scene::INodePtr& node) override { - if (auto ptr = std::dynamic_pointer_cast(node)) + if (auto ptr = std::dynamic_pointer_cast(node)) { // Ignore worldspawn entities if (ptr->getEntity().isWorldspawn()) @@ -28,7 +27,7 @@ class EntityNodeCollector : public scene::NodeVisitor } }; -static std::vector getEntitiesInSubgraph(const scene::INodePtr& node) +static std::vector getEntitiesInSubgraph(const scene::INodePtr& node) { EntityNodeCollector visitor; if (node) @@ -82,7 +81,7 @@ class MapObserver_SceneObserver : public scene::Graph::Observer { if (node->isRoot()) return; // ignore the map root auto entityNodes = getEntitiesInSubgraph(node); - for (const IEntityNodePtr& entNode : entityNodes) + for (const EntityNodePtr& entNode : entityNodes) _owner.entityUpdated(entNode->name(), DiffStatus::added()); _owner.enableEntityObservers(entityNodes); } @@ -92,12 +91,12 @@ class MapObserver_SceneObserver : public scene::Graph::Observer { auto entityNodes = getEntitiesInSubgraph(node); _owner.disableEntityObservers(entityNodes); - for (const IEntityNodePtr& entNode : entityNodes) + for (const EntityNodePtr& entNode : entityNodes) _owner.entityUpdated(entNode->name(), DiffStatus::removed()); } }; -void MapObserver::enableEntityObservers(const std::vector& entityNodes) +void MapObserver::enableEntityObservers(const std::vector& entityNodes) { for (auto entNode : entityNodes) { @@ -110,7 +109,7 @@ void MapObserver::enableEntityObservers(const std::vector& entit } } -void MapObserver::disableEntityObservers(const std::vector& entityNodes) +void MapObserver::disableEntityObservers(const std::vector& entityNodes) { for (auto entNode : entityNodes) { diff --git a/plugins/dm.gameconnection/MapObserver.h b/plugins/dm.gameconnection/MapObserver.h index ccf34980ab..db38d79dbd 100644 --- a/plugins/dm.gameconnection/MapObserver.h +++ b/plugins/dm.gameconnection/MapObserver.h @@ -35,13 +35,13 @@ class MapObserver //receives events about entity changes void entityUpdated(const std::string& name, const DiffStatus& diff); //add/remove entity observers on the set of entity nodes - void enableEntityObservers(const std::vector& entityNodes); - void disableEntityObservers(const std::vector& entityNodes); + void enableEntityObservers(const std::vector& entityNodes); + void disableEntityObservers(const std::vector& entityNodes); //the observer put onto global scene std::unique_ptr _sceneObserver; //observers put on every entity on scene - std::map _entityObservers; //note: values owned + std::map _entityObservers; //note: values owned //set of entities with changes since last clear DiffEntityStatuses _entityChanges; diff --git a/plugins/dm.gui/ReadableEditorDialog.cpp b/plugins/dm.gui/ReadableEditorDialog.cpp index 9d679cd464..0a6ee2f101 100644 --- a/plugins/dm.gui/ReadableEditorDialog.cpp +++ b/plugins/dm.gui/ReadableEditorDialog.cpp @@ -22,7 +22,7 @@ // Modules #include "iundo.h" #include "ui/imainframe.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" #include "imap.h" #include "igame.h" #include "ui/idialogmanager.h" diff --git a/plugins/dm.objectives/ObjectiveEntity.cpp b/plugins/dm.objectives/ObjectiveEntity.cpp index 3a82a67448..b6515277a3 100644 --- a/plugins/dm.objectives/ObjectiveEntity.cpp +++ b/plugins/dm.objectives/ObjectiveEntity.cpp @@ -6,9 +6,8 @@ #include "itextstream.h" #include "iscenegraph.h" #include "iundo.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" -#include "scene/Entity.h" #include "string/convert.h" #include "string/predicate.h" #include "string/split.h" diff --git a/plugins/dm.objectives/ObjectiveEntityFinder.cpp b/plugins/dm.objectives/ObjectiveEntityFinder.cpp index 62c2763ad8..b35a156758 100644 --- a/plugins/dm.objectives/ObjectiveEntityFinder.cpp +++ b/plugins/dm.objectives/ObjectiveEntityFinder.cpp @@ -1,7 +1,6 @@ #include "ObjectiveEntityFinder.h" -#include "scene/Entity.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" namespace objectives { diff --git a/plugins/dm.objectives/ObjectivesEditor.cpp b/plugins/dm.objectives/ObjectivesEditor.cpp index 9a6cf3a5ae..e1724f8260 100644 --- a/plugins/dm.objectives/ObjectivesEditor.cpp +++ b/plugins/dm.objectives/ObjectivesEditor.cpp @@ -14,7 +14,7 @@ #include "iregistry.h" #include "ieclass.h" #include "igame.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" #include "wxutil/dialog/MessageBox.h" @@ -469,7 +469,7 @@ void ObjectivesEditor::_onAddEntity(wxCommandEvent& ev) if (eclass) { // Construct a Node of this entity type - IEntityNodePtr node(GlobalEntityModule().createEntity(eclass)); + EntityNodePtr node(GlobalEntityModule().createEntity(eclass)); // Create a random offset node->getEntity().setKeyValue("origin", RandomOrigin::generate(128)); diff --git a/plugins/dm.objectives/ce/specpanel/EntityNameSpecifierPanel.cpp b/plugins/dm.objectives/ce/specpanel/EntityNameSpecifierPanel.cpp index 1be821d2a5..05a73e44ac 100644 --- a/plugins/dm.objectives/ce/specpanel/EntityNameSpecifierPanel.cpp +++ b/plugins/dm.objectives/ce/specpanel/EntityNameSpecifierPanel.cpp @@ -3,7 +3,7 @@ #include #include "inode.h" #include "imap.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" namespace objectives { diff --git a/plugins/dm.stimresponse/StimResponseEditor.cpp b/plugins/dm.stimresponse/StimResponseEditor.cpp index ab21b81415..5690c6ddab 100644 --- a/plugins/dm.stimresponse/StimResponseEditor.cpp +++ b/plugins/dm.stimresponse/StimResponseEditor.cpp @@ -8,6 +8,7 @@ #include "selectionlib.h" #include "wxutil/dialog/MessageBox.h" #include "string/string.h" +#include "scene/EntityNode.h" #include "i18n.h" #include @@ -83,17 +84,17 @@ int StimResponseEditor::ShowModal() void StimResponseEditor::populateWindow() { auto mainPanel = loadNamedPanel(this, "SREditorMainPanel"); - + _notebook = findNamedObject(this, "SREditorNotebook"); _stimEditor = std::make_unique(mainPanel, _stimTypes); _responseEditor = std::make_unique(mainPanel, _stimTypes); - + // Custom Stim Editor auto customStimPanel = findNamedObject(mainPanel, "SREditorCustomStimEditorContainer"); _customStimEditor = std::make_unique(customStimPanel, _stimTypes); - _notebook->Connect(wxEVT_NOTEBOOK_PAGE_CHANGED, + _notebook->Connect(wxEVT_NOTEBOOK_PAGE_CHANGED, wxBookCtrlEventHandler(StimResponseEditor::onPageChanged), nullptr, this); findNamedObject(this, "SREditorOkButton")->Bind( @@ -176,10 +177,10 @@ void StimResponseEditor::save() bool StimResponseEditor::Destroy() { - // We experience crashes in Linux/GTK during dialog destruction when GTK+ - // apparently starts sending out the notebook page changed event right + // We experience crashes in Linux/GTK during dialog destruction when GTK+ + // apparently starts sending out the notebook page changed event right // before removing the pages. - _notebook->Disconnect(wxEVT_NOTEBOOK_PAGE_CHANGED, + _notebook->Disconnect(wxEVT_NOTEBOOK_PAGE_CHANGED, wxBookCtrlEventHandler(StimResponseEditor::onPageChanged), nullptr, this); return wxutil::DialogBase::Destroy(); diff --git a/plugins/script/interfaces/EntityInterface.cpp b/plugins/script/interfaces/EntityInterface.cpp index 0305595c71..eddb40644c 100644 --- a/plugins/script/interfaces/EntityInterface.cpp +++ b/plugins/script/interfaces/EntityInterface.cpp @@ -2,7 +2,7 @@ #include -#include "scene/Entity.h" +#include "scene/EntityNode.h" #include "ieclass.h" #include "itextstream.h" @@ -78,7 +78,7 @@ bool ScriptEntityNode::isEntity(const ScriptSceneNode& node) { // The returned node is non-NULL if the cast succeeded ScriptEntityNode ScriptEntityNode::getEntity(const ScriptSceneNode& node) { // Try to cast the node onto a brush - IEntityNodePtr entityNode = std::dynamic_pointer_cast( + EntityNodePtr entityNode = std::dynamic_pointer_cast( static_cast(node) ); diff --git a/radiant/ui/animationpreview/AnimationPreview.cpp b/radiant/ui/animationpreview/AnimationPreview.cpp index 2e68656004..26f4d611da 100644 --- a/radiant/ui/animationpreview/AnimationPreview.cpp +++ b/radiant/ui/animationpreview/AnimationPreview.cpp @@ -2,7 +2,7 @@ #include "i18n.h" #include "imodel.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" #include "ieclass.h" #include "imd5anim.h" #include "itextstream.h" diff --git a/radiant/ui/animationpreview/AnimationPreview.h b/radiant/ui/animationpreview/AnimationPreview.h index 85a40158b1..d6390d10b4 100644 --- a/radiant/ui/animationpreview/AnimationPreview.h +++ b/radiant/ui/animationpreview/AnimationPreview.h @@ -23,7 +23,7 @@ class AnimationPreview : scene::INodePtr _model; // Each model node needs a parent entity to be properly renderable - IEntityNodePtr _entity; + EntityNodePtr _entity; // The animation to play on this model md5::IMD5AnimPtr _anim; diff --git a/radiant/ui/common/EntityChooser.cpp b/radiant/ui/common/EntityChooser.cpp index 9977225b2f..e2e5427c0a 100644 --- a/radiant/ui/common/EntityChooser.cpp +++ b/radiant/ui/common/EntityChooser.cpp @@ -2,7 +2,7 @@ #include "i18n.h" #include "inode.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" #include "ui/imainframe.h" #include "iscenegraph.h" diff --git a/radiant/ui/eclasstree/EClassTree.cpp b/radiant/ui/eclasstree/EClassTree.cpp index 27959216d9..476b51c399 100644 --- a/radiant/ui/eclasstree/EClassTree.cpp +++ b/radiant/ui/eclasstree/EClassTree.cpp @@ -1,7 +1,7 @@ #include "EClassTree.h" #include "ieclass.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" #include "iselection.h" #include "i18n.h" diff --git a/radiant/ui/einspector/EntityInspector.cpp b/radiant/ui/einspector/EntityInspector.cpp index 5f7fb0571f..c826aba14d 100644 --- a/radiant/ui/einspector/EntityInspector.cpp +++ b/radiant/ui/einspector/EntityInspector.cpp @@ -4,7 +4,7 @@ #include "AddPropertyDialog.h" #include "i18n.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" #include "ideclmanager.h" #include "ieclass.h" #include "iregistry.h" @@ -1531,7 +1531,7 @@ std::string EntityInspector::getPropertyTypeForAttachmentKey(const std::string& // Check if there is a single attachment eclass on all selected entities std::optional attachmentClass; - _entitySelection->foreachEntity([&](const IEntityNodePtr& entity) + _entitySelection->foreachEntity([&](const EntityNodePtr& entity) { entity->getEntity().forEachAttachment([&](const EntityAttachment& attachment) { diff --git a/radiant/ui/einspector/ModelPropertyEditor.cpp b/radiant/ui/einspector/ModelPropertyEditor.cpp index ee02c13a42..5d4b02e4cf 100644 --- a/radiant/ui/einspector/ModelPropertyEditor.cpp +++ b/radiant/ui/einspector/ModelPropertyEditor.cpp @@ -5,7 +5,7 @@ #include "ui/particles/ParticleChooserDialog.h" #include "i18n.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" #include "scenelib.h" #include "wxutil/dialog/MessageBox.h" @@ -66,7 +66,7 @@ void ModelPropertyEditor::_onModelButton(wxCommandEvent& ev) UndoableCommand cmd("setModelProperty"); - _entities.foreachEntity([&](const IEntityNodePtr& node) + _entities.foreachEntity([&](const EntityNodePtr& node) { auto& entity = node->getEntity(); std::string prevModel = entity.getKeyValue(_key->getFullKey()); diff --git a/radiant/ui/einspector/PropertyEditor.cpp b/radiant/ui/einspector/PropertyEditor.cpp index e6cbca897c..f9743231dd 100644 --- a/radiant/ui/einspector/PropertyEditor.cpp +++ b/radiant/ui/einspector/PropertyEditor.cpp @@ -1,6 +1,6 @@ #include "PropertyEditor.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" #include "iundo.h" #include #include @@ -53,7 +53,7 @@ void PropertyEditor::setKeyValueOnSelection(const std::string& key, const std::s UndoableCommand cmd("setProperty"); - _entities.foreachEntity([&](const IEntityNodePtr& entity) + _entities.foreachEntity([&](const EntityNodePtr& entity) { entity->getEntity().setKeyValue(key, value); }); diff --git a/radiant/ui/lightinspector/LightInspector.cpp b/radiant/ui/lightinspector/LightInspector.cpp index fc6b5f69f2..a702ca5f12 100644 --- a/radiant/ui/lightinspector/LightInspector.cpp +++ b/radiant/ui/lightinspector/LightInspector.cpp @@ -1,7 +1,7 @@ #include "LightInspector.h" #include "icameraview.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" #include "ieclass.h" #include "ishaders.h" #include "iselection.h" diff --git a/radiant/ui/modelexport/ExportAsModelDialog.cpp b/radiant/ui/modelexport/ExportAsModelDialog.cpp index f5ec884792..f8bbfb6e7b 100644 --- a/radiant/ui/modelexport/ExportAsModelDialog.cpp +++ b/radiant/ui/modelexport/ExportAsModelDialog.cpp @@ -18,6 +18,7 @@ #include #include "string/case_conv.h" #include "string/split.h" +#include "scene/EntityNode.h" #include "selectionlib.h" #include "os/path.h" @@ -57,7 +58,7 @@ void ExportAsModelDialog::populateWindow() wxPanel* panel = loadNamedPanel(this, "ExportDialogMainPanel"); GetSizer()->Add(panel, 1, wxEXPAND); - + makeLabelBold(this, "ExportDialogOutputLabel"); makeLabelBold(this, "ExportDialogOriginLabel"); makeLabelBold(this, "ExportDialogOptionLabel"); @@ -118,7 +119,7 @@ void ExportAsModelDialog::populateWindow() // Replace the filepicker control with our own PathEntry wxWindow* existing = findNamedObject(this, "ExportDialogFilePicker"); - wxutil::PathEntry* pathEntry = new wxutil::PathEntry(existing->GetParent(), + wxutil::PathEntry* pathEntry = new wxutil::PathEntry(existing->GetParent(), filetype::TYPE_MODEL_EXPORT, false, recentFormat); pathEntry->setValue(recentPath); @@ -207,8 +208,8 @@ void ExportAsModelDialog::onExport(wxCommandEvent& ev) auto invalidOrigin = Vector3(65536, 65536, 65536); if (string::convert(customOrigin, invalidOrigin) == invalidOrigin) { - wxutil::Messagebox::Show(_("Invalid Origin"), - _("The origin you entered could not be parsed.\nUse the format \"x y z\" (without quotes, separated with spaces)"), + wxutil::Messagebox::Show(_("Invalid Origin"), + _("The origin you entered could not be parsed.\nUse the format \"x y z\" (without quotes, separated with spaces)"), IDialog::MessageType::MESSAGE_ERROR); return; } @@ -226,7 +227,7 @@ void ExportAsModelDialog::onExport(wxCommandEvent& ev) // Warn the user if the output file extension doesn't match what the format says (#5741) if (string::to_lower_copy(os::getExtension(outputFilename)) != string::to_lower_copy(outputFormat) && - wxutil::Messagebox::Show(_("Format/Extension Mismatch"), _("The file extension doesn't match the selected format - continue?"), + wxutil::Messagebox::Show(_("Format/Extension Mismatch"), _("The file extension doesn't match the selected format - continue?"), IDialog::MessageType::MESSAGE_ASK) == IDialog::RESULT_NO) { return; // abort @@ -234,7 +235,7 @@ void ExportAsModelDialog::onExport(wxCommandEvent& ev) // Check if the target file already exists if (os::fileOrDirExists(outputFilename) && - wxutil::Messagebox::Show(_("Confirm Replacement"), + wxutil::Messagebox::Show(_("Confirm Replacement"), fmt::format(_("The file {0} already exists.\nReplace this file?"), outputFilename), IDialog::MessageType::MESSAGE_ASK) != IDialog::RESULT_YES) { @@ -327,13 +328,13 @@ bool ExportAsModelDialog::_onDeleteEvent() void ExportAsModelDialog::saveOptionsToRegistry() { - registry::setValue(RKEY_MODEL_EXPORT_OUTPUT_FORMAT, + registry::setValue(RKEY_MODEL_EXPORT_OUTPUT_FORMAT, wxutil::ChoiceHelper::GetSelectedStoredString(findNamedObject(this, "ExportDialogFormatChoice"))); - registry::setValue(RKEY_MODEL_EXPORT_OUTPUT_PATH, + registry::setValue(RKEY_MODEL_EXPORT_OUTPUT_PATH, findNamedObject(this, "ExportDialogFilePicker")->getValue()); - registry::setValue(RKEY_MODEL_EXPORT_SKIP_CAULK, + registry::setValue(RKEY_MODEL_EXPORT_SKIP_CAULK, findNamedObject(this, "ExportDialogSkipCaulk")->GetValue()); registry::setValue(RKEY_MODEL_EXPORT_EXPORT_ORIGIN, static_cast(getSelectedExportOrigin())); diff --git a/radiant/ui/ortho/OrthoContextMenu.cpp b/radiant/ui/ortho/OrthoContextMenu.cpp index 0505d5f53d..615b48b1c8 100644 --- a/radiant/ui/ortho/OrthoContextMenu.cpp +++ b/radiant/ui/ortho/OrthoContextMenu.cpp @@ -213,7 +213,7 @@ bool OrthoContextMenu::checkMergeEntities() bool OrthoContextMenu::checkReparentPrimitives() { - return selection::curSelectionIsSuitableForReparent(); + return curSelectionIsSuitableForReparent(); } bool OrthoContextMenu::checkRevertToWorldspawnPartial() diff --git a/radiantcore/CMakeLists.txt b/radiantcore/CMakeLists.txt index e82cc4c11b..f4a900ddbb 100644 --- a/radiantcore/CMakeLists.txt +++ b/radiantcore/CMakeLists.txt @@ -37,28 +37,15 @@ add_library(radiantcore MODULE entity/doom3group/StaticGeometryNode.cpp entity/eclassmodel/EclassModelNode.cpp entity/EntityModule.cpp - entity/EntityNode.cpp - entity/EntitySettings.cpp entity/generic/GenericEntityNode.cpp - entity/KeyValueObserver.cpp entity/light/LightNode.cpp entity/light/Renderables.cpp - entity/ModelKey.cpp - entity/NameKeyObserver.cpp - entity/NamespaceManager.cpp entity/RenderableArrow.cpp entity/RenderableEntityBox.cpp - entity/RenderableEntityName.cpp entity/RotationKey.cpp entity/RotationMatrix.cpp - entity/ShaderParms.cpp entity/speaker/SpeakerNode.cpp entity/speaker/SpeakerRenderables.cpp - entity/target/TargetableNode.cpp - entity/target/TargetKeyCollection.cpp - entity/target/TargetKey.cpp - entity/target/TargetLineNode.cpp - entity/target/TargetManager.cpp filetypes/FileTypeRegistry.cpp filters/BasicFilterSystem.cpp filters/XMLFilter.cpp diff --git a/radiantcore/entity/EntityModule.cpp b/radiantcore/entity/EntityModule.cpp index 50662193ec..261a9b2bac 100644 --- a/radiantcore/entity/EntityModule.cpp +++ b/radiantcore/entity/EntityModule.cpp @@ -13,15 +13,15 @@ #include "string/replace.h" #include "scene/Entity.h" +#include "scene/TargetManager.h" +#include "scene/EntitySettings.h" #include "light/LightNode.h" #include "doom3group/StaticGeometryNode.h" #include "speaker/SpeakerNode.h" #include "generic/GenericEntityNode.h" #include "eclassmodel/EclassModelNode.h" -#include "target/TargetManager.h" #include "module/StaticModule.h" -#include "EntitySettings.h" #include "selection/algorithm/General.h" #include "selection/algorithm/Group.h" #include "selection/algorithm/Entity.h" @@ -54,7 +54,7 @@ AABB Doom3Light_getBounds(AABB aabb) return aabb; } -IEntityNodePtr createNodeForEntity(const IEntityClassPtr& eclass) +EntityNodePtr createNodeForEntity(const IEntityClassPtr& eclass) { // Null entityclass check if (!eclass) @@ -88,9 +88,9 @@ IEntityNodePtr createNodeForEntity(const IEntityClassPtr& eclass) } } -IEntityNodePtr Doom3EntityModule::createEntity(const IEntityClassPtr& eclass) +EntityNodePtr Doom3EntityModule::createEntity(const IEntityClassPtr& eclass) { - IEntityNodePtr node = createNodeForEntity(eclass); + EntityNodePtr node = createNodeForEntity(eclass); if (GlobalMapModule().getRoot()) { @@ -120,7 +120,7 @@ IEntityNodePtr Doom3EntityModule::createEntity(const IEntityClassPtr& eclass) return node; } -IEntityNodePtr Doom3EntityModule::createEntityFromSelection(const std::string& name, const Vector3& origin) +EntityNodePtr Doom3EntityModule::createEntityFromSelection(const std::string& name, const Vector3& origin) { // Obtain the structure containing the selection counts const SelectionInfo& info = GlobalSelectionSystem().getSelectionInfo(); @@ -143,7 +143,7 @@ IEntityNodePtr Doom3EntityModule::createEntityFromSelection(const std::string& n AABB workzone = GlobalSelectionSystem().getWorkZone().bounds; // Create the new node for the entity - IEntityNodePtr node(GlobalEntityModule().createEntity(entityClass)); + EntityNodePtr node(GlobalEntityModule().createEntity(entityClass)); GlobalSceneGraph().root()->addChildNode(node); diff --git a/radiantcore/entity/EntityModule.h b/radiantcore/entity/EntityModule.h index 93e72ecae3..4a56ca2c91 100644 --- a/radiantcore/entity/EntityModule.h +++ b/radiantcore/entity/EntityModule.h @@ -16,7 +16,7 @@ class Doom3EntityModule final : public: // EntityCreator implementation - IEntityNodePtr createEntity(const IEntityClassPtr& eclass) override; + EntityNodePtr createEntity(const IEntityClassPtr& eclass) override; ITargetManagerPtr createTargetManager() override; IEntitySettings& getSettings() override; @@ -26,7 +26,7 @@ class Doom3EntityModule final : * * @returns: the scene::INodePtr referring to the new entity. */ - IEntityNodePtr createEntityFromSelection(const std::string& name, const Vector3& origin) override; + EntityNodePtr createEntityFromSelection(const std::string& name, const Vector3& origin) override; // RegisterableModule implementation const std::string& getName() const override; diff --git a/radiantcore/entity/RenderableArrow.cpp b/radiantcore/entity/RenderableArrow.cpp index 59d2579a48..528757989e 100644 --- a/radiantcore/entity/RenderableArrow.cpp +++ b/radiantcore/entity/RenderableArrow.cpp @@ -1,6 +1,6 @@ #include "RenderableArrow.h" -#include "EntityNode.h" +#include "scene/EntityNode.h" namespace entity { @@ -20,7 +20,7 @@ void RenderableArrow::updateGeometry() if (!_needsUpdate) return; _needsUpdate = false; - + // The starting point of the arrow is at the center of the entity's visible bounding box auto origin = _node.getWorldPosition() + _node.localAABB().getOrigin(); @@ -37,7 +37,7 @@ void RenderableArrow::updateGeometry() { left = g_vector3_axis_y; } - + Vector3 up = direction.cross(left); Vector3 endpoint(origin + direction * 32.0); diff --git a/radiantcore/entity/RenderableArrow.h b/radiantcore/entity/RenderableArrow.h index 1b599825da..a1dfebdf31 100644 --- a/radiantcore/entity/RenderableArrow.h +++ b/radiantcore/entity/RenderableArrow.h @@ -2,11 +2,11 @@ #include "render/RenderableGeometry.h" +class EntityNode; + namespace entity { -class EntityNode; - class RenderableArrow : public render::RenderableGeometry { diff --git a/radiantcore/entity/RenderableEntityBox.cpp b/radiantcore/entity/RenderableEntityBox.cpp index 242fd11937..0448cfb0eb 100644 --- a/radiantcore/entity/RenderableEntityBox.cpp +++ b/radiantcore/entity/RenderableEntityBox.cpp @@ -1,11 +1,11 @@ #include "RenderableEntityBox.h" -#include "EntityNode.h" +#include "scene/EntityNode.h" namespace entity { -RenderableEntityBox::RenderableEntityBox(const IEntityNode& entity, const AABB& bounds, const Vector3& worldPos) : +RenderableEntityBox::RenderableEntityBox(const EntityNode& entity, const AABB& bounds, const Vector3& worldPos) : RenderableBox(bounds, worldPos), _entity(entity) {} diff --git a/radiantcore/entity/RenderableEntityBox.h b/radiantcore/entity/RenderableEntityBox.h index c574f31faa..a262f368fb 100644 --- a/radiantcore/entity/RenderableEntityBox.h +++ b/radiantcore/entity/RenderableEntityBox.h @@ -2,7 +2,7 @@ #include "render/RenderableBox.h" -class IEntityNode; +class EntityNode; namespace entity { @@ -11,10 +11,10 @@ class RenderableEntityBox final : public render::RenderableBox { private: - const IEntityNode& _entity; + const EntityNode& _entity; public: - RenderableEntityBox(const IEntityNode& entity, const AABB& bounds, const Vector3& worldPos); + RenderableEntityBox(const EntityNode& entity, const AABB& bounds, const Vector3& worldPos); protected: Vector4 getVertexColour() override; diff --git a/radiantcore/entity/VertexInstance.h b/radiantcore/entity/VertexInstance.h index 2700156e84..accd0474f3 100644 --- a/radiantcore/entity/VertexInstance.h +++ b/radiantcore/entity/VertexInstance.h @@ -5,7 +5,7 @@ #include "iselectiontest.h" #include "math/Vector3.h" #include "ObservedSelectable.h" -#include "EntitySettings.h" +#include "scene/EntitySettings.h" class VertexInstance : public ISelectable @@ -23,7 +23,7 @@ class VertexInstance : VertexInstance(Vector3& vertex, const SelectionChangedSlot& observer) : _vertex(vertex), _selectable(observer), - _colour(entity::EntitySettings::InstancePtr()->getLightVertexColour(LightEditVertexType::Deselected)) + _colour(EntitySettings::InstancePtr()->getLightVertexColour(LightEditVertexType::Deselected)) {} void setVertex(const Vector3& vertex) { @@ -37,7 +37,7 @@ class VertexInstance : void setSelected(bool select) { _selectable.setSelected(select); // Change the colour according to the selection - _colour = entity::EntitySettings::InstancePtr()->getLightVertexColour( + _colour = EntitySettings::InstancePtr()->getLightVertexColour( select ? LightEditVertexType::Selected : LightEditVertexType::Deselected ); } diff --git a/radiantcore/entity/curve/Curve.cpp b/radiantcore/entity/curve/Curve.cpp index b975f2af29..3ba9bf0db1 100644 --- a/radiantcore/entity/curve/Curve.cpp +++ b/radiantcore/entity/curve/Curve.cpp @@ -20,7 +20,7 @@ namespace entity { } // namespace -Curve::Curve(const IEntityNode& entity, const Callback& boundsChanged) : +Curve::Curve(const EntityNode& entity, const Callback& boundsChanged) : _renderCurve(entity), _boundsChanged(boundsChanged) {} @@ -129,7 +129,7 @@ bool Curve::parseCurve(const std::string& value) { return true; } -void Curve::curveChanged() +void Curve::curveChanged() { // Recalculate the tesselation tesselate(); diff --git a/radiantcore/entity/curve/Curve.h b/radiantcore/entity/curve/Curve.h index 3cf7eb5887..7657ea8dd2 100644 --- a/radiantcore/entity/curve/Curve.h +++ b/radiantcore/entity/curve/Curve.h @@ -34,7 +34,7 @@ class Curve : sigc::signal _sigCurveChanged; public: - Curve(const IEntityNode& entity, const Callback& boundsChanged); + Curve(const EntityNode& entity, const Callback& boundsChanged); virtual ~Curve() {} diff --git a/radiantcore/entity/curve/CurveCatmullRom.cpp b/radiantcore/entity/curve/CurveCatmullRom.cpp index 29e486e94d..2dd221feee 100644 --- a/radiantcore/entity/curve/CurveCatmullRom.cpp +++ b/radiantcore/entity/curve/CurveCatmullRom.cpp @@ -2,7 +2,7 @@ namespace entity { -CurveCatmullRom::CurveCatmullRom(const IEntityNode& entity, const Callback& callback) : +CurveCatmullRom::CurveCatmullRom(const EntityNode& entity, const Callback& callback) : Curve(entity, callback) {} diff --git a/radiantcore/entity/curve/CurveCatmullRom.h b/radiantcore/entity/curve/CurveCatmullRom.h index cce1399405..2ccc52c91c 100644 --- a/radiantcore/entity/curve/CurveCatmullRom.h +++ b/radiantcore/entity/curve/CurveCatmullRom.h @@ -13,7 +13,7 @@ class CurveCatmullRom : public Curve { public: - CurveCatmullRom(const IEntityNode& entity, const Callback& callback); + CurveCatmullRom(const EntityNode& entity, const Callback& callback); // Subdivides the segments between the control points virtual void tesselate(); diff --git a/radiantcore/entity/curve/CurveNURBS.cpp b/radiantcore/entity/curve/CurveNURBS.cpp index 736b42fa87..ee87a5569b 100644 --- a/radiantcore/entity/curve/CurveNURBS.cpp +++ b/radiantcore/entity/curve/CurveNURBS.cpp @@ -6,7 +6,7 @@ namespace entity { const int NURBS_degree = 3; } -CurveNURBS::CurveNURBS(const IEntityNode& entity, const Callback& callback) : +CurveNURBS::CurveNURBS(const EntityNode& entity, const Callback& callback) : Curve(entity, callback) {} diff --git a/radiantcore/entity/curve/CurveNURBS.h b/radiantcore/entity/curve/CurveNURBS.h index e65223b157..f2876dd3b4 100644 --- a/radiantcore/entity/curve/CurveNURBS.h +++ b/radiantcore/entity/curve/CurveNURBS.h @@ -15,7 +15,7 @@ class CurveNURBS : NURBSWeights _weights; Knots _knots; public: - CurveNURBS(const IEntityNode& entity, const Callback& callback); + CurveNURBS(const EntityNode& entity, const Callback& callback); // Subdivides the segments between the control points virtual void tesselate(); diff --git a/radiantcore/entity/curve/RenderableCurve.h b/radiantcore/entity/curve/RenderableCurve.h index 3c71579b49..81bb5e167f 100644 --- a/radiantcore/entity/curve/RenderableCurve.h +++ b/radiantcore/entity/curve/RenderableCurve.h @@ -4,6 +4,7 @@ #include "irenderable.h" #include "render.h" #include "render/RenderableGeometry.h" +#include "scene/EntityNode.h" namespace entity { @@ -12,13 +13,13 @@ class RenderableCurve : public render::RenderableGeometry { private: - const IEntityNode& _entity; + const EntityNode& _entity; bool _needsUpdate; public: std::vector m_vertices; - RenderableCurve(const IEntityNode& entity) : + RenderableCurve(const EntityNode& entity) : _entity(entity), _needsUpdate(true) {} diff --git a/radiantcore/entity/doom3group/RenderableVertex.h b/radiantcore/entity/doom3group/RenderableVertex.h index f1d68a80f7..6add65527b 100644 --- a/radiantcore/entity/doom3group/RenderableVertex.h +++ b/radiantcore/entity/doom3group/RenderableVertex.h @@ -1,7 +1,7 @@ #pragma once #include "../VertexInstance.h" -#include "../EntitySettings.h" +#include "scene/EntitySettings.h" #include "render/RenderableGeometry.h" namespace entity diff --git a/radiantcore/entity/doom3group/StaticGeometryNode.h b/radiantcore/entity/doom3group/StaticGeometryNode.h index 066d9f9c8c..8bb36ce650 100644 --- a/radiantcore/entity/doom3group/StaticGeometryNode.h +++ b/radiantcore/entity/doom3group/StaticGeometryNode.h @@ -4,17 +4,17 @@ #include "icurve.h" #include "irenderable.h" #include "editable.h" -#include "../OriginKey.h" +#include "scene/OriginKey.h" #include "../RotationKey.h" -#include "../NameKey.h" +#include "scene/NameKey.h" #include "../curve/CurveEditInstance.h" #include "../curve/CurveNURBS.h" #include "../curve/CurveCatmullRom.h" #include "../curve/RenderableCurveVertices.h" #include "../VertexInstance.h" -#include "../target/TargetableNode.h" -#include "../EntityNode.h" -#include "../KeyObserverDelegate.h" +#include "scene/TargetableNode.h" +#include "scene/EntityNode.h" +#include "scene/KeyObserverDelegate.h" #include "render/RenderablePivot.h" #include "RenderableVertex.h" diff --git a/radiantcore/entity/eclassmodel/EclassModelNode.h b/radiantcore/entity/eclassmodel/EclassModelNode.h index 2b9e099a30..f4aa388d7a 100644 --- a/radiantcore/entity/eclassmodel/EclassModelNode.h +++ b/radiantcore/entity/eclassmodel/EclassModelNode.h @@ -10,11 +10,11 @@ #include "transformlib.h" #include "selectionlib.h" #include "render/RenderablePivot.h" -#include "../target/TargetableNode.h" -#include "../EntityNode.h" -#include "../KeyObserverDelegate.h" +#include "scene/TargetableNode.h" +#include "scene/EntityNode.h" +#include "scene/KeyObserverDelegate.h" #include "../RotationKey.h" -#include "../OriginKey.h" +#include "scene/OriginKey.h" namespace entity { diff --git a/radiantcore/entity/generic/GenericEntityNode.cpp b/radiantcore/entity/generic/GenericEntityNode.cpp index 92b23a7786..daa8492377 100644 --- a/radiantcore/entity/generic/GenericEntityNode.cpp +++ b/radiantcore/entity/generic/GenericEntityNode.cpp @@ -1,5 +1,6 @@ #include "GenericEntityNode.h" -#include "../EntitySettings.h" +#include "scene/EntitySettings.h" +#include "entitylib.h" #include "math/Frustum.h" diff --git a/radiantcore/entity/generic/GenericEntityNode.h b/radiantcore/entity/generic/GenericEntityNode.h index dcd6e8a665..2071c89e00 100644 --- a/radiantcore/entity/generic/GenericEntityNode.h +++ b/radiantcore/entity/generic/GenericEntityNode.h @@ -7,9 +7,9 @@ #include "irenderable.h" #include "math/Ray.h" -#include "../target/TargetableNode.h" -#include "../EntityNode.h" -#include "../OriginKey.h" +#include "scene/TargetableNode.h" +#include "scene/EntityNode.h" +#include "scene/OriginKey.h" #include "../AngleKey.h" #include "../RotationKey.h" #include "scene/Entity.h" diff --git a/radiantcore/entity/light/LightNode.cpp b/radiantcore/entity/light/LightNode.cpp index ffd300fea4..0dd27ddac2 100644 --- a/radiantcore/entity/light/LightNode.cpp +++ b/radiantcore/entity/light/LightNode.cpp @@ -3,7 +3,8 @@ #include "igrid.h" #include "ishaders.h" #include "icolourscheme.h" -#include "../EntitySettings.h" +#include "scene/EntitySettings.h" +#include "entitylib.h" #include #include "registry/CachedKey.h" @@ -16,7 +17,7 @@ std::string LightShader::m_defaultShader = ""; // --------- LightNode implementation ------------------------------------ LightNode::LightNode(const IEntityClassPtr& eclass) -: EntityNode(eclass), +: EntityNode(eclass), m_originKey(std::bind(&LightNode::originChanged, this)), _originTransformed(ORIGINKEY_IDENTITY), m_rotationKey(std::bind(&LightNode::rotationChanged, this)), @@ -36,7 +37,7 @@ LightNode::LightNode(const IEntityClassPtr& eclass) } LightNode::LightNode(const LightNode& other) -: EntityNode(other), ILightNode(other), +: EntityNode(other), ILightNode(other), m_originKey(std::bind(&LightNode::originChanged, this)), _originTransformed(ORIGINKEY_IDENTITY), m_rotationKey(std::bind(&LightNode::rotationChanged, this)), diff --git a/radiantcore/entity/light/LightNode.h b/radiantcore/entity/light/LightNode.h index 9dbe9ee5bc..48f4a622db 100644 --- a/radiantcore/entity/light/LightNode.h +++ b/radiantcore/entity/light/LightNode.h @@ -12,8 +12,8 @@ #include "dragplanes.h" #include "../VertexInstance.h" -#include "../EntityNode.h" -#include "../OriginKey.h" +#include "scene/EntityNode.h" +#include "scene/OriginKey.h" #include "../RotationKey.h" #include "Renderables.h" #include "LightVertexInstanceSet.h" diff --git a/radiantcore/entity/light/Renderables.cpp b/radiantcore/entity/light/Renderables.cpp index d628786b3c..ba7b24e095 100644 --- a/radiantcore/entity/light/Renderables.cpp +++ b/radiantcore/entity/light/Renderables.cpp @@ -1,7 +1,7 @@ #include "Renderables.h" #include "LightNode.h" -#include "../EntitySettings.h" +#include "scene/EntitySettings.h" namespace entity { diff --git a/radiantcore/entity/speaker/SpeakerNode.cpp b/radiantcore/entity/speaker/SpeakerNode.cpp index d172a86816..bbdbcb2483 100644 --- a/radiantcore/entity/speaker/SpeakerNode.cpp +++ b/radiantcore/entity/speaker/SpeakerNode.cpp @@ -1,5 +1,6 @@ #include "SpeakerNode.h" -#include "../EntitySettings.h" +#include "scene/EntitySettings.h" +#include "entitylib.h" #include "math/Frustum.h" #include diff --git a/radiantcore/entity/speaker/SpeakerNode.h b/radiantcore/entity/speaker/SpeakerNode.h index ccc3cb65b5..f012f73fbd 100644 --- a/radiantcore/entity/speaker/SpeakerNode.h +++ b/radiantcore/entity/speaker/SpeakerNode.h @@ -1,6 +1,6 @@ #pragma once -#include "../OriginKey.h" +#include "scene/OriginKey.h" #include "SpeakerRenderables.h" #include "isound.h" @@ -12,8 +12,8 @@ #include "irenderable.h" #include "selectionlib.h" #include "dragplanes.h" -#include "../target/TargetableNode.h" -#include "../EntityNode.h" +#include "scene/TargetableNode.h" +#include "scene/EntityNode.h" #include "../RenderableEntityBox.h" namespace entity diff --git a/radiantcore/entity/speaker/SpeakerRenderables.cpp b/radiantcore/entity/speaker/SpeakerRenderables.cpp index c2a864ddbf..10f86b9879 100644 --- a/radiantcore/entity/speaker/SpeakerRenderables.cpp +++ b/radiantcore/entity/speaker/SpeakerRenderables.cpp @@ -2,7 +2,7 @@ #include "scene/Entity.h" #include "render.h" -#include "entity/EntityNode.h" +#include "scene/EntityNode.h" namespace entity { diff --git a/radiantcore/entity/speaker/SpeakerRenderables.h b/radiantcore/entity/speaker/SpeakerRenderables.h index ee169d7a42..7837ebfd96 100644 --- a/radiantcore/entity/speaker/SpeakerRenderables.h +++ b/radiantcore/entity/speaker/SpeakerRenderables.h @@ -4,7 +4,7 @@ #include "math/Vector3.h" #include "render/RenderableGeometry.h" -class IEntityNode; +class EntityNode; namespace entity { @@ -15,7 +15,7 @@ class RenderableSpeakerRadiiBase : protected: bool _needsUpdate; - const IEntityNode& _entity; + const EntityNode& _entity; const Vector3& _origin; @@ -24,7 +24,7 @@ class RenderableSpeakerRadiiBase : const SoundRadii& _radii; protected: - RenderableSpeakerRadiiBase(const IEntityNode& entity, const Vector3& origin, const SoundRadii& radii) : + RenderableSpeakerRadiiBase(const EntityNode& entity, const Vector3& origin, const SoundRadii& radii) : _entity(entity), _origin(origin), _radii(radii) @@ -47,7 +47,7 @@ class RenderableSpeakerRadiiWireframe : { public: // Construct an instance with the given origin and radius. - RenderableSpeakerRadiiWireframe(const IEntityNode& entity, const Vector3& origin, const SoundRadii& radii) : + RenderableSpeakerRadiiWireframe(const EntityNode& entity, const Vector3& origin, const SoundRadii& radii) : RenderableSpeakerRadiiBase(entity, origin, radii) {} @@ -64,7 +64,7 @@ class RenderableSpeakerRadiiFill : { public: // Construct an instance with the given origin and radius. - RenderableSpeakerRadiiFill(const IEntityNode& entity, const Vector3& origin, const SoundRadii& radii) : + RenderableSpeakerRadiiFill(const EntityNode& entity, const Vector3& origin, const SoundRadii& radii) : RenderableSpeakerRadiiBase(entity, origin, radii) {} diff --git a/radiantcore/filters/InstanceUpdateWalker.h b/radiantcore/filters/InstanceUpdateWalker.h index 708ab68c0f..e0545e02c5 100644 --- a/radiantcore/filters/InstanceUpdateWalker.h +++ b/radiantcore/filters/InstanceUpdateWalker.h @@ -1,7 +1,8 @@ #pragma once +#include "ifilter.h" #include "inode.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" #include "iselectable.h" #include "ipatch.h" #include "ibrush.h" diff --git a/radiantcore/filters/SetObjectSelectionByFilterWalker.h b/radiantcore/filters/SetObjectSelectionByFilterWalker.h index 3595f39fd7..f5256116d3 100644 --- a/radiantcore/filters/SetObjectSelectionByFilterWalker.h +++ b/radiantcore/filters/SetObjectSelectionByFilterWalker.h @@ -4,7 +4,7 @@ #include "ifilter.h" #include "ipatch.h" #include "ibrush.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" #include "iselectable.h" #include "XMLFilter.h" diff --git a/radiantcore/map/RegionManager.h b/radiantcore/map/RegionManager.h index 7a5b30e38e..7e906d0876 100644 --- a/radiantcore/map/RegionManager.h +++ b/radiantcore/map/RegionManager.h @@ -44,7 +44,7 @@ class RegionManager : scene::INodePtr _brushes[6]; // The pointer to the info_player_start entity - IEntityNodePtr _playerStart; + EntityNodePtr _playerStart; public: RegionManager(); diff --git a/radiantcore/map/algorithm/Export.cpp b/radiantcore/map/algorithm/Export.cpp index e26f9ca9f5..b407a368dd 100644 --- a/radiantcore/map/algorithm/Export.cpp +++ b/radiantcore/map/algorithm/Export.cpp @@ -5,7 +5,7 @@ #include "ieclass.h" #include "ifilesystem.h" #include "imodelcache.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" #include "iundo.h" #include "itextstream.h" diff --git a/radiantcore/map/algorithm/MapExporter.cpp b/radiantcore/map/algorithm/MapExporter.cpp index eb88ba29cd..134ebcfd2f 100644 --- a/radiantcore/map/algorithm/MapExporter.cpp +++ b/radiantcore/map/algorithm/MapExporter.cpp @@ -5,7 +5,7 @@ #include "itextstream.h" #include "ibrush.h" #include "ipatch.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" #include "imapresource.h" #include "imap.h" #include "igroupnode.h" @@ -141,7 +141,7 @@ bool MapExporter::pre(const scene::INodePtr& node) { try { - auto entity = std::dynamic_pointer_cast(node); + auto entity = std::dynamic_pointer_cast(node); if (entity) { @@ -195,7 +195,7 @@ void MapExporter::post(const scene::INodePtr& node) { try { - auto entity = std::dynamic_pointer_cast(node); + auto entity = std::dynamic_pointer_cast(node); if (entity) { diff --git a/radiantcore/map/algorithm/MapImporter.cpp b/radiantcore/map/algorithm/MapImporter.cpp index 4904ff3126..1aeb406486 100644 --- a/radiantcore/map/algorithm/MapImporter.cpp +++ b/radiantcore/map/algorithm/MapImporter.cpp @@ -1,7 +1,7 @@ #include "MapImporter.h" #include "i18n.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" #include "imap.h" #include "iradiant.h" diff --git a/radiantcore/map/algorithm/Models.cpp b/radiantcore/map/algorithm/Models.cpp index c5d819da2d..8339c31afb 100644 --- a/radiantcore/map/algorithm/Models.cpp +++ b/radiantcore/map/algorithm/Models.cpp @@ -5,7 +5,7 @@ #include "i18n.h" #include "inode.h" #include "iselection.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" #include "imodel.h" #include "imodelcache.h" #include "iscenegraph.h" @@ -23,7 +23,7 @@ class ModelFinder : public scene::NodeVisitor { public: - typedef std::set Entities; + typedef std::set Entities; typedef std::set ModelPaths; private: @@ -41,7 +41,7 @@ class ModelFinder : { _modelNames.insert(model->getIModel().getModelPath()); - IEntityNodePtr ent = std::dynamic_pointer_cast(node->getParent()); + EntityNodePtr ent = std::dynamic_pointer_cast(node->getParent()); if (ent) { @@ -76,7 +76,7 @@ class ModelRefreshWalker : public: bool pre(const scene::INodePtr& node) { - IEntityNodePtr entity = std::dynamic_pointer_cast(node); + EntityNodePtr entity = std::dynamic_pointer_cast(node); if (entity) { @@ -134,7 +134,7 @@ void refreshSelectedModels(bool blockScreenUpdates) // Traverse the entities and submit a refresh call ModelFinder::Entities entities = walker.getEntities(); - for (const IEntityNodePtr& entityNode : entities) + for (const EntityNodePtr& entityNode : entities) { entityNode->refreshModel(); } @@ -150,7 +150,7 @@ void refreshModelsByPath(const std::string& relativeModelPath) GlobalMapModule().getRoot()->foreachNode([&](const scene::INodePtr& node) { - auto entity = std::dynamic_pointer_cast(node); + auto entity = std::dynamic_pointer_cast(node); if (entity && entity->getEntity().getKeyValue("model") == relativeModelPath) { diff --git a/radiantcore/map/format/Doom3MapReader.cpp b/radiantcore/map/format/Doom3MapReader.cpp index ae3f13691c..9e15478a3b 100644 --- a/radiantcore/map/format/Doom3MapReader.cpp +++ b/radiantcore/map/format/Doom3MapReader.cpp @@ -3,7 +3,7 @@ #include "itextstream.h" #include "ieclass.h" #include "igame.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" #include "string/string.h" #include "Doom3MapFormat.h" @@ -179,7 +179,7 @@ scene::INodePtr Doom3MapReader::createEntity(const EntityKeyValues& keyValues) } // Create the actual entity node - IEntityNodePtr node(GlobalEntityModule().createEntity(classPtr)); + EntityNodePtr node(GlobalEntityModule().createEntity(classPtr)); for (EntityKeyValues::const_iterator i = keyValues.begin(); i != keyValues.end(); diff --git a/radiantcore/map/format/Doom3MapWriter.cpp b/radiantcore/map/format/Doom3MapWriter.cpp index 9788961c7c..a84bed81f0 100644 --- a/radiantcore/map/format/Doom3MapWriter.cpp +++ b/radiantcore/map/format/Doom3MapWriter.cpp @@ -1,7 +1,7 @@ #include "Doom3MapWriter.h" #include "igame.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" #include "primitivewriters/BrushDef3Exporter.h" #include "primitivewriters/PatchDefExporter.h" @@ -38,7 +38,7 @@ void Doom3MapWriter::endWriteMap(const scene::IMapRootNodePtr& root, std::ostrea // nothing } -void Doom3MapWriter::beginWriteEntity(const IEntityNodePtr& entity, std::ostream& stream) +void Doom3MapWriter::beginWriteEntity(const EntityNodePtr& entity, std::ostream& stream) { // Write out the entity number comment stream << "// entity " << _entityCount++ << std::endl; @@ -50,7 +50,7 @@ void Doom3MapWriter::beginWriteEntity(const IEntityNodePtr& entity, std::ostream writeEntityKeyValues(entity, stream); } -void Doom3MapWriter::writeEntityKeyValues(const IEntityNodePtr& entity, std::ostream& stream) +void Doom3MapWriter::writeEntityKeyValues(const EntityNodePtr& entity, std::ostream& stream) { // Export the entity key values entity->getEntity().forEachKeyValue([&](const std::string& key, const std::string& value) @@ -59,7 +59,7 @@ void Doom3MapWriter::writeEntityKeyValues(const IEntityNodePtr& entity, std::ost }); } -void Doom3MapWriter::endWriteEntity(const IEntityNodePtr& entity, std::ostream& stream) +void Doom3MapWriter::endWriteEntity(const EntityNodePtr& entity, std::ostream& stream) { // Write the closing brace for the entity stream << "}" << std::endl; diff --git a/radiantcore/map/format/Doom3MapWriter.h b/radiantcore/map/format/Doom3MapWriter.h index e3dfa8c2a0..1d78adf113 100644 --- a/radiantcore/map/format/Doom3MapWriter.h +++ b/radiantcore/map/format/Doom3MapWriter.h @@ -1,6 +1,7 @@ #pragma once #include "imapformat.h" +#include "scene/scene_fwd.h" namespace map { @@ -25,8 +26,8 @@ class Doom3MapWriter : virtual void endWriteMap(const scene::IMapRootNodePtr& root, std::ostream& stream) override; // Entity export methods - virtual void beginWriteEntity(const IEntityNodePtr& entity, std::ostream& stream) override; - virtual void endWriteEntity(const IEntityNodePtr& entity, std::ostream& stream) override; + virtual void beginWriteEntity(const EntityNodePtr& entity, std::ostream& stream) override; + virtual void endWriteEntity(const EntityNodePtr& entity, std::ostream& stream) override; // Brush export methods virtual void beginWriteBrush(const IBrushNodePtr& brush, std::ostream& stream) override; @@ -37,7 +38,7 @@ class Doom3MapWriter : virtual void endWritePatch(const IPatchNodePtr& patch, std::ostream& stream) override; protected: - void writeEntityKeyValues(const IEntityNodePtr& entity, std::ostream& stream); + void writeEntityKeyValues(const EntityNodePtr& entity, std::ostream& stream); }; } // namespace diff --git a/radiantcore/map/format/Quake3MapReader.cpp b/radiantcore/map/format/Quake3MapReader.cpp index 7aa426f317..bc41c79d5c 100644 --- a/radiantcore/map/format/Quake3MapReader.cpp +++ b/radiantcore/map/format/Quake3MapReader.cpp @@ -3,7 +3,7 @@ #include "itextstream.h" #include "ieclass.h" #include "igame.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" #include "string/string.h" #include "i18n.h" @@ -137,7 +137,7 @@ scene::INodePtr Quake3MapReader::createEntity(const EntityKeyValues& keyValues) } // Create the actual entity node - IEntityNodePtr node(GlobalEntityModule().createEntity(classPtr)); + EntityNodePtr node(GlobalEntityModule().createEntity(classPtr)); for (EntityKeyValues::const_iterator i = keyValues.begin(); i != keyValues.end(); diff --git a/radiantcore/map/format/portable/PortableMapReader.cpp b/radiantcore/map/format/portable/PortableMapReader.cpp index ceb6367dcd..5a0f85a65d 100644 --- a/radiantcore/map/format/portable/PortableMapReader.cpp +++ b/radiantcore/map/format/portable/PortableMapReader.cpp @@ -7,7 +7,7 @@ #include "ilayer.h" #include "ibrush.h" #include "ieclass.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" #include "PortableMapFormat.h" #include "Constants.h" diff --git a/radiantcore/map/format/portable/PortableMapWriter.cpp b/radiantcore/map/format/portable/PortableMapWriter.cpp index 4ae75f17ee..e65ed2e4c5 100644 --- a/radiantcore/map/format/portable/PortableMapWriter.cpp +++ b/radiantcore/map/format/portable/PortableMapWriter.cpp @@ -1,7 +1,7 @@ #include "PortableMapWriter.h" #include "igame.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" #include "ipatch.h" #include "imap.h" #include "ibrush.h" @@ -133,7 +133,7 @@ void PortableMapWriter::endWriteMap(const scene::IMapRootNodePtr& root, std::ost stream << _document.saveToString(); } -void PortableMapWriter::beginWriteEntity(const IEntityNodePtr& entity, std::ostream& stream) +void PortableMapWriter::beginWriteEntity(const EntityNodePtr& entity, std::ostream& stream) { auto node = _map.createChild(TAG_ENTITY); node.setAttributeValue(ATTR_ENTITY_NUMBER, string::to_string(_entityCount++)); @@ -156,7 +156,7 @@ void PortableMapWriter::beginWriteEntity(const IEntityNodePtr& entity, std::ostr appendSelectionSetInformation(node, entity); } -void PortableMapWriter::endWriteEntity(const IEntityNodePtr& entity, std::ostream& stream) +void PortableMapWriter::endWriteEntity(const EntityNodePtr& entity, std::ostream& stream) { // Reset the primitive count again _primitiveCount = 0; diff --git a/radiantcore/map/format/portable/PortableMapWriter.h b/radiantcore/map/format/portable/PortableMapWriter.h index 50f587c209..9e53074fff 100644 --- a/radiantcore/map/format/portable/PortableMapWriter.h +++ b/radiantcore/map/format/portable/PortableMapWriter.h @@ -46,8 +46,8 @@ class PortableMapWriter : virtual void endWriteMap(const scene::IMapRootNodePtr& root, std::ostream& stream) override; // Entity export methods - virtual void beginWriteEntity(const IEntityNodePtr& entity, std::ostream& stream) override; - virtual void endWriteEntity(const IEntityNodePtr& entity, std::ostream& stream) override; + virtual void beginWriteEntity(const EntityNodePtr& entity, std::ostream& stream) override; + virtual void endWriteEntity(const EntityNodePtr& entity, std::ostream& stream) override; // Brush export methods virtual void beginWriteBrush(const IBrushNodePtr& brush, std::ostream& stream) override; diff --git a/radiantcore/model/export/ModelScalePreserver.cpp b/radiantcore/model/export/ModelScalePreserver.cpp index 7aef027eaa..357e3b66c6 100644 --- a/radiantcore/model/export/ModelScalePreserver.cpp +++ b/radiantcore/model/export/ModelScalePreserver.cpp @@ -1,6 +1,6 @@ #include "ModelScalePreserver.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" #include "itransformable.h" #include "imapresource.h" #include "itextstream.h" diff --git a/radiantcore/model/export/ScaledModelExporter.cpp b/radiantcore/model/export/ScaledModelExporter.cpp index 95df4b6dd4..b1b454b320 100644 --- a/radiantcore/model/export/ScaledModelExporter.cpp +++ b/radiantcore/model/export/ScaledModelExporter.cpp @@ -6,7 +6,7 @@ #include "iundo.h" #include "itextstream.h" #include "igame.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" #include "iscenegraph.h" #include "os/fs.h" #include "os/path.h" diff --git a/radiantcore/selection/SceneManipulationPivot.cpp b/radiantcore/selection/SceneManipulationPivot.cpp index e5b6d6e32b..fbdfe29064 100644 --- a/radiantcore/selection/SceneManipulationPivot.cpp +++ b/radiantcore/selection/SceneManipulationPivot.cpp @@ -2,7 +2,7 @@ #include "iselection.h" #include "ilightnode.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" #include "igrid.h" #include "selectionlib.h" #include "registry/registry.h" diff --git a/radiantcore/selection/algorithm/Curves.cpp b/radiantcore/selection/algorithm/Curves.cpp index 1ef4a49e09..3fcec86fd0 100644 --- a/radiantcore/selection/algorithm/Curves.cpp +++ b/radiantcore/selection/algorithm/Curves.cpp @@ -5,7 +5,7 @@ #include "iundo.h" #include "ieclass.h" #include "itransformable.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" #include "iorthoview.h" #include "scenelib.h" @@ -53,7 +53,7 @@ void createCurve(const std::string& key) ); // Create a new entity node deriving from this entityclass - IEntityNodePtr curve(GlobalEntityModule().createEntity(entityClass)); + EntityNodePtr curve(GlobalEntityModule().createEntity(entityClass)); // Insert this new node into the scenegraph root GlobalSceneGraph().root()->addChildNode(curve); diff --git a/radiantcore/selection/manipulators/ManipulatorComponents.cpp b/radiantcore/selection/manipulators/ManipulatorComponents.cpp index 75c692c277..c6ece1f9b7 100644 --- a/radiantcore/selection/manipulators/ManipulatorComponents.cpp +++ b/radiantcore/selection/manipulators/ManipulatorComponents.cpp @@ -1,6 +1,6 @@ #include "ManipulatorComponents.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" #include "itransformable.h" #include "igrid.h" #include "math/FloatTools.h" diff --git a/test/Entity.cpp b/test/Entity.cpp index 66a38fe6ce..c4762b5173 100644 --- a/test/Entity.cpp +++ b/test/Entity.cpp @@ -32,7 +32,7 @@ namespace // global map to enable undo. struct TestEntity { - IEntityNodePtr node; + EntityNodePtr node; Entity* spawnArgs = nullptr; // Create an entity with the given class name @@ -53,7 +53,7 @@ struct TestEntity }; // Obtain entity attachments as a simple std::list -std::list getAttachments(const IEntityNodePtr& node) +std::list getAttachments(const EntityNodePtr& node) { std::list attachments; if (node) @@ -672,7 +672,7 @@ TEST_F(EntityTest, ForeachAttachment) scene::addNodeToContainer(torch, GlobalMapModule().getRoot()); int attachmentCount = 0; - torch->foreachAttachment([&](const IEntityNodePtr& attachment) + torch->foreachAttachment([&](const EntityNodePtr& attachment) { attachmentCount++; EXPECT_TRUE(attachment->getEntity().isOfType("light_cageflame_small")); @@ -1716,11 +1716,11 @@ TEST_F(EntityTest, EntityNodeObserveKeyAutoDisconnect) spawnArgs->setKeyValue(TEST_KEY, "whatever"); } -inline IEntityNodePtr findPlayerStartEntity() +inline EntityNodePtr findPlayerStartEntity() { - IEntityNodePtr found; + EntityNodePtr found; - algorithm::findFirstEntity(GlobalMapModule().getRoot(), [&](const IEntityNodePtr& entity) + algorithm::findFirstEntity(GlobalMapModule().getRoot(), [&](const EntityNodePtr& entity) { if (entity->getEntity().getEntityClass()->getDeclName() == "info_player_start") { @@ -1783,8 +1783,8 @@ TEST_F(EntityTest, CreateSpeaker) }); // The speaker should be in the map now - auto speaker = std::dynamic_pointer_cast( - algorithm::findFirstEntity(GlobalMapModule().getRoot(), [](const IEntityNodePtr& entity) + auto speaker = std::dynamic_pointer_cast( + algorithm::findFirstEntity(GlobalMapModule().getRoot(), [](const EntityNodePtr& entity) { return entity->getEntity().getEntityClass()->getDeclName() == "speaker"; })); @@ -1816,11 +1816,11 @@ TEST_F(EntityTest, MovingSpeakerNotRemovingDistanceArgs) cmd::Argument("test/jorge"), cmd::Argument("50 30 47") }); - auto node = algorithm::findFirstEntity(GlobalMapModule().getRoot(), [](const IEntityNodePtr& entity) + auto node = algorithm::findFirstEntity(GlobalMapModule().getRoot(), [](const EntityNodePtr& entity) { return entity->getEntity().getEntityClass()->getDeclName() == "speaker"; }); - auto speaker = std::dynamic_pointer_cast(node); + auto speaker = std::dynamic_pointer_cast(node); EXPECT_TRUE(speaker); EXPECT_NE(speaker->getEntity().getKeyValue("s_mindistance"), ""); diff --git a/test/EntityInspector.cpp b/test/EntityInspector.cpp index f8be0072ed..70192fa7e7 100644 --- a/test/EntityInspector.cpp +++ b/test/EntityInspector.cpp @@ -693,7 +693,7 @@ TEST_F(EntityInspectorTest, DeletedEntitiesAreSafelyUntracked) auto guardNode = GlobalEntityModule().createEntity(cls); // Create a weak reference to check whether the entity is gone - std::weak_ptr weakGuardNode(guardNode); + std::weak_ptr weakGuardNode(guardNode); scene::addNodeToContainer(guardNode, GlobalMapModule().getRoot()); Node_setSelected(guardNode, true); diff --git a/test/MapMerging.cpp b/test/MapMerging.cpp index 3f3eb40721..91392a4c47 100644 --- a/test/MapMerging.cpp +++ b/test/MapMerging.cpp @@ -138,7 +138,7 @@ TEST_F(MapMergeTest, EntityFingerprint) auto comparable = std::dynamic_pointer_cast(entityNode); EXPECT_TRUE(comparable) << "EntityNode is not implementing IComparableNode"; - auto entity = std::dynamic_pointer_cast(entityNode); + auto entity = std::dynamic_pointer_cast(entityNode); auto originalFingerprint = comparable->getFingerprint(); EXPECT_FALSE(originalFingerprint.empty()); // shouldn't be empty @@ -184,7 +184,7 @@ TEST_F(MapMergeTest, EntityFingerprintConsidersChildNodes) auto comparable = std::dynamic_pointer_cast(entityNode); EXPECT_TRUE(comparable) << "EntityNode is not implementing IComparableNode"; - auto entity = std::dynamic_pointer_cast(entityNode); + auto entity = std::dynamic_pointer_cast(entityNode); auto originalFingerprint = comparable->getFingerprint(); // Add a child patch @@ -207,14 +207,14 @@ TEST_F(MapMergeTest, EntityFingerprintInsensitiveToChildOrder) auto comparable = std::dynamic_pointer_cast(entityNode); EXPECT_TRUE(comparable) << "EntityNode is not implementing IComparableNode"; - auto entity = std::dynamic_pointer_cast(entityNode); + auto entity = std::dynamic_pointer_cast(entityNode); auto originalFingerprint = comparable->getFingerprint(); // Find the first brush of this func_static scene::INodePtr firstChild; std::size_t numChildren = 0; - entity->foreachNode([&](const scene::INodePtr& node) - { + entity->foreachNode([&](const scene::INodePtr& node) + { numChildren++; if (!firstChild) { @@ -248,7 +248,7 @@ inline ComparisonResult::Ptr performComparison(const std::string& targetMap, con return GraphComparer::Compare(resource->getRootNode(), GlobalMapModule().getRoot()); } -inline std::size_t countPrimitiveDifference(const ComparisonResult::EntityDifference& diff, +inline std::size_t countPrimitiveDifference(const ComparisonResult::EntityDifference& diff, const ComparisonResult::PrimitiveDifference::Type type) { std::size_t count = 0; @@ -264,7 +264,7 @@ inline std::size_t countPrimitiveDifference(const ComparisonResult::EntityDiffer return count; } -inline bool hasKeyValueDifference(const ComparisonResult::EntityDifference& diff, +inline bool hasKeyValueDifference(const ComparisonResult::EntityDifference& diff, const std::string& key, const std::string& value, ComparisonResult::KeyValueDifference::Type type) { for (const auto& difference : diff.differingKeyValues) @@ -297,7 +297,7 @@ TEST_F(MapMergeTest, DetectMissingEntities) // The player start has been removed in the changed map auto diff = getEntityDifference(result, "info_player_start_1"); - + EXPECT_EQ(diff.type, ComparisonResult::EntityDifference::Type::EntityMissingInSource); EXPECT_TRUE(diff.baseNode); EXPECT_FALSE(diff.sourceNode); // source node is missing, so it must be empty @@ -786,7 +786,7 @@ TEST_F(MapMergeTest, DeactivatedChangePrimitiveActions) { auto addChildAction = std::dynamic_pointer_cast(action); - if (addChildAction && Node_isEntity(addChildAction->getParent()) && + if (addChildAction && Node_isEntity(addChildAction->getParent()) && Node_getEntity(addChildAction->getParent())->getKeyValue("name") == "func_static_1") { addChildAction->deactivate(); @@ -800,7 +800,7 @@ TEST_F(MapMergeTest, DeactivatedChangePrimitiveActions) removeChildAction->deactivate(); } }); - + // func_static_1 has 2 brushes, this should stay the same // Check prerequisites and execute action auto entityNode = algorithm::getEntityByName(result->getBaseRootNode(), "func_static_1"); @@ -868,7 +868,7 @@ TEST_F(MapMergeTest, FinishMergeOperationWithHiddenNodes) EXPECT_NE(mergeNodes.size(), 0) << "No merge action nodes found in the scene"; - // Pick and hide every other node + // Pick and hide every other node for (auto i = 0; i < mergeNodes.size(); i += 2) { EXPECT_TRUE(mergeNodes[i]->visible()) << "Node should not be hidden: " << mergeNodes[i]->name(); @@ -891,7 +891,7 @@ TEST_F(MapMergeTest, FinishMergeOperationWithHiddenNodes) EXPECT_TRUE(resultAfterExecution->differingEntities.empty()); } -inline bool hasChange(const std::vector& log, +inline bool hasChange(const std::vector& log, const std::function& predicate) { return std::find_if(log.begin(), log.end(), predicate) != log.end(); @@ -926,9 +926,9 @@ inline bool groupContains(const selection::ISelectionGroupPtr& group, const scen { bool found = false; - group->foreachNode([&](const scene::INodePtr& member) - { - if (member == node) found = true; + group->foreachNode([&](const scene::INodePtr& member) + { + if (member == node) found = true; }); return found; @@ -988,7 +988,7 @@ TEST_F(SelectionGroupMergeTest, GroupAdded) // 2 new groups in this map EXPECT_EQ(changeCountByType(merger->getChangeLog(), SelectionGroupMerger::Change::Type::BaseGroupCreated), 2); EXPECT_EQ(changeCountByType(merger->getChangeLog(), SelectionGroupMerger::Change::Type::BaseGroupRemoved), 0); - + // Brushes 15/16/17/18 are now in a group EXPECT_EQ(std::dynamic_pointer_cast(brush15)->getGroupIds().size(), 1); EXPECT_EQ(std::dynamic_pointer_cast(brush16)->getGroupIds().size(), 1); @@ -1059,7 +1059,7 @@ TEST_F(SelectionGroupMergeTest, GroupInsertion) EXPECT_TRUE(groupContains(thirdGroup, brush7)); EXPECT_TRUE(groupContains(thirdGroup, brush17)); EXPECT_TRUE(groupContains(thirdGroup, funcStatic7)); - + // Run another merger, it shouldn't find any actions to take merger = std::make_unique(merger->getSourceRoot(), merger->getBaseRoot()); merger->adjustBaseGroups(); @@ -1154,7 +1154,7 @@ TEST_F(SelectionGroupMergeTest, BrushesKeptDuringMerge) operation->foreachAction([&](const scene::merge::IMergeAction::Ptr& action) { auto brush = Node_getIBrush(action->getAffectedNode()); - + if (brush && brush->hasShader("textures/numbers/8")) { action->deactivate(); @@ -1242,7 +1242,7 @@ TEST_F(SelectionGroupMergeTest, MergeSelectionGroupsFlagSet) auto result = GraphComparer::Compare(changedResource->getRootNode(), originalResource->getRootNode()); auto operation = MergeOperation::CreateFromComparisonResult(*result); - + operation->setMergeSelectionGroups(true); operation->applyActions(); @@ -1293,14 +1293,14 @@ std::unique_ptr setupLayerMerger(const std::string& sourceMap, cons bool nodeIsMemberOfLayer(const scene::INodePtr& node, const std::set& layerNames) { auto& layerManager = node->getRootNode()->getLayerManager(); - + std::set nodeLayerNames; for (auto layerId : node->getLayers()) { nodeLayerNames.emplace(layerManager.getLayerName(layerId)); } - + return std::includes(nodeLayerNames.begin(), nodeLayerNames.end(), layerNames.begin(), layerNames.end()); } @@ -1348,7 +1348,7 @@ TEST_F(LayerMergeTest, AddedLayer) EXPECT_EQ(brush0->getLayers().size(), 1); // only part of the active layer merger->adjustBaseLayers(); - + EXPECT_FALSE(merger->getChangeLog().empty()); // 1 created layer @@ -1560,7 +1560,7 @@ TEST_F(LayerMergeTest, MergeLayersFlagNotSet) } // Map changelog of source and target against their base (threeway_merge_base.mapx), used in several test cases below: -// +// // Source (threeway_merge_source_1.mapx): // - light_2 has been added // - brush 16 added to worldspawn @@ -1571,7 +1571,7 @@ TEST_F(LayerMergeTest, MergeLayersFlagNotSet) // - both brush_6 have been deleted from worldspawn // - func_static_5 had two brush_5 added (were part of worldspawn before) // - brush_11 got moved to the left -// +// // Target Map (threeway_merge_target_1.mapx): // - light_3 has been added // - brush_17 been added to worldspawn @@ -1796,7 +1796,7 @@ TEST_F(ThreeWayMergeTest, NonconflictingSpawnargManipulation) removeAction->applyChanges(); EXPECT_EQ(Node_getEntity(expandable)->getKeyValue("extra1"), ""); - + modifyAction->applyChanges(); EXPECT_EQ(Node_getEntity(expandable)->getKeyValue("extra3"), "value3_changed"); @@ -1850,7 +1850,7 @@ TEST_F(ThreeWayMergeTest, SourceAndTargetAreTheSame) } // Map changelog of source and target against their base (threeway_merge_base.mapx), used in several test cases below: -// +// // Source (threeway_merge_source_2.mapx): // - add all brush "2" to func_static_1 (a subset of the target change) // - add all brush "4" & "5" to func_static_4 @@ -1861,7 +1861,7 @@ TEST_F(ThreeWayMergeTest, SourceAndTargetAreTheSame) // - func_static_6 changes its origin to 224 180 32 (CONFLICT) // - expandable entity has its spwnarg "extra3" modified to "value3_changed" (CONFLICT) // - expandable entity has its spwnarg "extra2" removed (CONFLICT) -// +// // Target Map (threeway_merge_target_2.mapx): // - add all brush "1" & "2" to func_static_1 // - add all brush "4" to func_static_4 (a subset of the source change) @@ -1879,7 +1879,7 @@ TEST_F(ThreeWayMergeTest, MergePrimitiveChangesOfSameEntity) // Expected result would be that func_static_1 will not be changed since it contains // all changes of the source map, and more // func_static_4 should be changed, but only the missing "5" brushes should be added - + auto func_static_1 = algorithm::getEntityByName(operation->getTargetRoot(), "func_static_1"); auto func_static_4 = algorithm::getEntityByName(operation->getTargetRoot(), "func_static_4"); auto worldspawn = algorithm::findWorldspawn(operation->getTargetRoot()); @@ -2228,7 +2228,7 @@ inline bool groupContainsNodes(selection::ISelectionGroup& group, const std::vec } // Three-Way Selection Group Merge Scenarios -// +// // Base Map Groups: // N1-N2-N3 // [N1-N2-N3]-L1 @@ -2238,7 +2238,7 @@ inline bool groupContainsNodes(selection::ISelectionGroup& group, const std::vec // 1-2 // [1-2]-3 // 5-6 -// +// // Target Map Changes: // L2 deleted // FS1-FS2 group dissolved @@ -2399,7 +2399,7 @@ TEST_F(ThreeWaySelectionGroupMergeTest, RedundantGroupNotAdded) } // Layer Merge Base map: merging_layers1.mapx -// +// // func_static_1 is part of layer 1 // func_static_X is part of layer X // Shared Layer: all func_static_s, expandable entity and light_1 @@ -2417,7 +2417,7 @@ TEST_F(ThreeWaySelectionGroupMergeTest, RedundantGroupNotAdded) // Layer "2": Brush 2 removed, Brush "0" added // Layer "3": Brush 3 removed (only removals) // Layer "4": Brush 4 removed, Brush "0" added -// +// // Layer merge Target Map: threeway_merge_layers_target_1 // --- Deletions --- // Layer 6 was not modified diff --git a/test/Renderer.cpp b/test/Renderer.cpp index 8defe4cd03..f943718615 100644 --- a/test/Renderer.cpp +++ b/test/Renderer.cpp @@ -1,7 +1,7 @@ #include "RadiantTest.h" #include "ieclass.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" #include "irender.h" #include "ilightnode.h" #include "math/Matrix4.h" @@ -13,7 +13,7 @@ namespace test using RendererTest = RadiantTest; using RenderSystemTest = RadiantTest; -IEntityNodePtr createByClassName(const std::string& className) +EntityNodePtr createByClassName(const std::string& className) { auto cls = GlobalEntityClassManager().findClass(className); return GlobalEntityModule().createEntity(cls); @@ -100,7 +100,7 @@ TEST_F(RendererTest, ConstructRenderVertex) // Wrapper for a light entity and its respective node interfaces struct Light { - IEntityNodePtr node; + EntityNodePtr node; ILightNodePtr iLightNode; Entity* entity = nullptr; diff --git a/test/SceneNode.cpp b/test/SceneNode.cpp index 2dcc652895..ae6291f687 100644 --- a/test/SceneNode.cpp +++ b/test/SceneNode.cpp @@ -2,6 +2,7 @@ #include "scene/BasicRootNode.h" #include "scene/Node.h" +#include "scene/EntityNode.h" #include "scenelib.h" #include "algorithm/Entity.h" @@ -217,7 +218,7 @@ TEST_F(SceneNodeTest, SetMultipleHideFlags) TEST_F(SceneNodeTest, ForcedVisibility) { std::vector allFlags(allPossibleHideFlags); - + // Try all possible hide flags for (auto flag : allFlags) { @@ -226,7 +227,7 @@ TEST_F(SceneNodeTest, ForcedVisibility) node->enable(flag); EXPECT_FALSE(node->visible()) << "Should be invisible after setting a flag"; - + // Set the forced visibility flag node->visibilityMethodCalled = false; node->setProtectedForcedVisibility(true); @@ -364,7 +365,7 @@ TEST_F(SceneNodeTest, SetRenderStateAffectsAttachments) bool hasAttachments = false; // All attachments should be there and active - torch->foreachAttachment([&](const IEntityNodePtr& attachment) + torch->foreachAttachment([&](const EntityNodePtr& attachment) { hasAttachments = true; EXPECT_EQ(attachment->getRenderState(), scene::INode::RenderState::Active) << "Should be active after construction"; @@ -373,14 +374,14 @@ TEST_F(SceneNodeTest, SetRenderStateAffectsAttachments) // Set the state of the host entity to inactive torch->setRenderState(scene::INode::RenderState::Inactive); - torch->foreachAttachment([&](const IEntityNodePtr& attachment) + torch->foreachAttachment([&](const EntityNodePtr& attachment) { EXPECT_EQ(attachment->getRenderState(), scene::INode::RenderState::Inactive) << "Attachments not set to inactive"; }); // Back to active torch->setRenderState(scene::INode::RenderState::Active); - torch->foreachAttachment([&](const IEntityNodePtr& attachment) + torch->foreachAttachment([&](const EntityNodePtr& attachment) { EXPECT_EQ(attachment->getRenderState(), scene::INode::RenderState::Active) << "Attachments not set to active"; }); diff --git a/test/Transformation.cpp b/test/Transformation.cpp index cd32b6a73f..9c158cc75b 100644 --- a/test/Transformation.cpp +++ b/test/Transformation.cpp @@ -1,7 +1,7 @@ #include "RadiantTest.h" #include "ieclass.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" #include "itransformable.h" #include "icommandsystem.h" #include "scenelib.h" diff --git a/test/UndoRedo.cpp b/test/UndoRedo.cpp index 078b992698..ab7aab8583 100644 --- a/test/UndoRedo.cpp +++ b/test/UndoRedo.cpp @@ -542,7 +542,7 @@ TEST_F(UndoTest, CreateBrushBasedEntity) EXPECT_TRUE(GlobalEntityClassManager().findClass("atdm:mover_door_sliding")) << "Need atdm:mover_door_sliding for this test"; - IEntityNodePtr entity; + EntityNodePtr entity; { UndoableCommand command("createEntity"); entity = GlobalEntityModule().createEntityFromSelection("atdm:mover_door_sliding", Vector3(7.5, 12, 0)); diff --git a/test/WorldspawnColour.cpp b/test/WorldspawnColour.cpp index 1865aba333..ef06218803 100644 --- a/test/WorldspawnColour.cpp +++ b/test/WorldspawnColour.cpp @@ -3,7 +3,7 @@ #include #include "ieclass.h" #include "icolourscheme.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" #include "imap.h" #include "ibrush.h" #include "ifilesystem.h" diff --git a/test/algorithm/Entity.h b/test/algorithm/Entity.h index a1c61b222c..ea5ab7f325 100644 --- a/test/algorithm/Entity.h +++ b/test/algorithm/Entity.h @@ -23,7 +23,7 @@ inline std::vector> getAllKeyValuePairs(Enti } // Create an entity from a simple classname string -inline IEntityNodePtr createEntityByClassName(const std::string& className) +inline EntityNodePtr createEntityByClassName(const std::string& className) { auto cls = GlobalEntityClassManager().findClass(className); return GlobalEntityModule().createEntity(cls); diff --git a/test/algorithm/Scene.h b/test/algorithm/Scene.h index 195d0553c7..1d211aa44c 100644 --- a/test/algorithm/Scene.h +++ b/test/algorithm/Scene.h @@ -3,7 +3,7 @@ #include #include "inode.h" #include "iundo.h" -#include "scene/Entity.h" +#include "scene/EntityNode.h" #include "ibrush.h" #include "ipatch.h" #include "imodel.h" @@ -106,13 +106,13 @@ inline scene::INodePtr findFirstPatchWithMaterial(const scene::INodePtr& parent, } inline scene::INodePtr findFirstEntity(const scene::INodePtr& parent, - const std::function& predicate) + const std::function& predicate) { - IEntityNodePtr candidate; + EntityNodePtr candidate; parent->foreachNode([&](const scene::INodePtr& node) { - auto entity = std::dynamic_pointer_cast(node); + auto entity = std::dynamic_pointer_cast(node); if (entity && predicate(entity)) { @@ -128,7 +128,7 @@ inline scene::INodePtr findFirstEntity(const scene::INodePtr& parent, inline scene::INodePtr findWorldspawn(const scene::INodePtr& root) { - return findFirstEntity(root, [&](const IEntityNodePtr& entity) + return findFirstEntity(root, [&](const EntityNodePtr& entity) { return entity->getEntity().isWorldspawn(); }); @@ -136,7 +136,7 @@ inline scene::INodePtr findWorldspawn(const scene::INodePtr& root) inline scene::INodePtr getEntityByName(const scene::INodePtr& parent, const std::string& name) { - return findFirstEntity(parent, [&](const IEntityNodePtr& entity) + return findFirstEntity(parent, [&](const EntityNodePtr& entity) { return entity->getEntity().getKeyValue("name") == name; }); From 207fb49304f1da075f0a8b9e0b6cd6d16e07d813 Mon Sep 17 00:00:00 2001 From: jonri Date: Sun, 7 Apr 2024 16:05:04 -0400 Subject: [PATCH 07/49] Fix segfaults in skin editor --- libs/wxutil/dataview/ThreadedResourceTreePopulator.cpp | 3 +++ radiant/ui/skin/SkinEditor.cpp | 7 ++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/libs/wxutil/dataview/ThreadedResourceTreePopulator.cpp b/libs/wxutil/dataview/ThreadedResourceTreePopulator.cpp index 2430a108bc..885d377e52 100644 --- a/libs/wxutil/dataview/ThreadedResourceTreePopulator.cpp +++ b/libs/wxutil/dataview/ThreadedResourceTreePopulator.cpp @@ -30,6 +30,9 @@ ThreadedResourceTreePopulator::~ThreadedResourceTreePopulator() void ThreadedResourceTreePopulator::ThrowIfCancellationRequested() { + if (This() != this) + return; + if (TestDestroy()) { throw ThreadAbortedException(); diff --git a/radiant/ui/skin/SkinEditor.cpp b/radiant/ui/skin/SkinEditor.cpp index 7dd705e559..cb4c242bc3 100644 --- a/radiant/ui/skin/SkinEditor.cpp +++ b/radiant/ui/skin/SkinEditor.cpp @@ -709,13 +709,14 @@ void SkinEditor::onSkinNameChanged(wxCommandEvent& ev) // Rename the active skin decl auto nameEntry = static_cast(ev.GetEventObject()); - - GlobalModelSkinCache().renameSkin(_skin->getDeclName(), nameEntry->GetValue().ToStdString()); - auto item = _skinTreeView->GetTreeModel()->FindString(_skin->getDeclName(), _columns.declName); + auto newSkin = nameEntry->GetValue().ToStdString(); + GlobalModelSkinCache().renameSkin(_skin->getDeclName(), newSkin); + auto item = _skinTreeView->GetTreeModel()->FindString(newSkin, _columns.declName); // Make sure the item is selected again, it will be de-selected by the rename operation _skinTreeView->Select(item); _skinTreeView->EnsureVisible(item); + _skin = getSelectedSkin(); handleSkinSelectionChanged(); // also updates all controls nameEntry->SetFocus(); From c6280c31e5cf01f2ed5482c39f578afdbc3d726b Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Wed, 10 Apr 2024 20:40:06 +0100 Subject: [PATCH 08/49] Skin Editor decl info line no longer appears on top of Close button Instead of replacing the wxPanel in the XML form with a DeclFileInfo panel, we now use the XML form's panel as a container and pack the DeclFileInfo inside (so it's a wxPanel inside a wxPanel). It is unclear why the original code was not working correctly, since wxSizer::Replace() is a supported method and the docs do not suggest there should be any layout problems after using it (provided you call Layout(), which we were doing). --- install/ui/skineditor.fbp | 15 ++++++++++++++- install/ui/skineditor.xrc | 12 +++++++++++- radiant/ui/skin/SkinEditor.cpp | 23 +++++++++++------------ radiant/ui/skin/SkinEditor.h | 8 ++++---- 4 files changed, 40 insertions(+), 18 deletions(-) diff --git a/install/ui/skineditor.fbp b/install/ui/skineditor.fbp index fc2f12551b..bc19084c62 100644 --- a/install/ui/skineditor.fbp +++ b/install/ui/skineditor.fbp @@ -14,6 +14,7 @@ skineditor 1000 none + 0 MyProject1 @@ -25,6 +26,7 @@ 1 1 UI + 0 0 0 @@ -46,6 +48,7 @@ 998,884 + 0 wxTAB_TRAVERSAL @@ -361,6 +364,7 @@ + 0 @@ -433,6 +437,7 @@ + 0 @@ -505,6 +510,7 @@ + 0 @@ -577,6 +583,7 @@ + 0 @@ -649,6 +656,7 @@ + 0 @@ -1207,6 +1215,7 @@ + 0 @@ -1279,6 +1288,7 @@ + 0 @@ -1568,6 +1578,7 @@ + 0 @@ -1640,6 +1651,7 @@ + 0 @@ -2031,7 +2043,7 @@ none 6 - wxALIGN_CENTER_VERTICAL|wxRIGHT + wxEXPAND|wxRIGHT 1 1 @@ -2099,6 +2111,7 @@ + 0 diff --git a/install/ui/skineditor.xrc b/install/ui/skineditor.xrc index 2e7a3899d7..758250ec72 100644 --- a/install/ui/skineditor.xrc +++ b/install/ui/skineditor.xrc @@ -83,6 +83,7 @@ 0 + 0 0 @@ -94,6 +95,7 @@ 0 + 0 0 @@ -105,6 +107,7 @@ 0 + 0 0 @@ -116,6 +119,7 @@ 0 + 0 0 @@ -127,6 +131,7 @@ 0 + 0 0 @@ -241,6 +246,7 @@ 0 + 0 0 @@ -252,6 +258,7 @@ 0 + 0 0 @@ -315,6 +322,7 @@ 0 + 0 0 @@ -326,6 +334,7 @@ 0 + 0 0 @@ -393,7 +402,7 @@ wxHORIZONTAL - wxALIGN_CENTER_VERTICAL|wxRIGHT + wxEXPAND|wxRIGHT 6 @@ -406,6 +415,7 @@ 0 + 0 0 diff --git a/radiant/ui/skin/SkinEditor.cpp b/radiant/ui/skin/SkinEditor.cpp index cb4c242bc3..1a0fa8d483 100644 --- a/radiant/ui/skin/SkinEditor.cpp +++ b/radiant/ui/skin/SkinEditor.cpp @@ -66,9 +66,12 @@ SkinEditor::SkinEditor() : _remappingList->CancelEditing(); // Stop editing when switching tabs }); - auto oldInfoPanel = getControl("SkinEditorSaveNotePanel"); - auto declFileInfo = new wxutil::DeclFileInfo(oldInfoPanel->GetParent(), decl::Type::Skin); - replaceControl(oldInfoPanel, declFileInfo); + // Pack in the decl info at the bottom of the window (next to the Close button) + wxPanel* saveNotePanelContainer = getControl("SkinEditorSaveNotePanel"); + _saveNotePanel = new wxutil::DeclFileInfo(saveNotePanelContainer, decl::Type::Skin); + auto sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(_saveNotePanel.get(), 1, wxEXPAND | wxLEFT, 12); + saveNotePanelContainer->SetSizerAndFit(sizer); // Set the default size of the window FitToScreen(0.9f, 0.9f); @@ -374,16 +377,12 @@ void SkinEditor::updateSkinControlsFromSelection() void SkinEditor::updateDeclFileInfo() { - auto declFileInfo = getControl("SkinEditorSaveNotePanel"); - - if (_skin) - { - declFileInfo->Show(); - declFileInfo->SetDeclarationName(_skin->getDeclName()); + if (_skin) { + _saveNotePanel->Show(); + _saveNotePanel->SetDeclarationName(_skin->getDeclName()); } - else - { - declFileInfo->Hide(); + else { + _saveNotePanel->Hide(); } } diff --git a/radiant/ui/skin/SkinEditor.h b/radiant/ui/skin/SkinEditor.h index fadba55288..55284662c2 100644 --- a/radiant/ui/skin/SkinEditor.h +++ b/radiant/ui/skin/SkinEditor.h @@ -13,17 +13,16 @@ #include "wxutil/preview/ModelPreview.h" #include "wxutil/sourceview/SourceView.h" +namespace wxutil { class DeclFileInfo; } namespace ui { class SkinEditorTreeView; class ModelTreeView; -class SkinEditor final : - public wxutil::DialogBase, - private wxutil::XmlResourceBasedWidget +/// Graphical editor for .skin files +class SkinEditor final: public wxutil::DialogBase, private wxutil::XmlResourceBasedWidget { -private: decl::ISkin::Ptr _skin; ModelTreeView* _modelTreeView; @@ -46,6 +45,7 @@ class SkinEditor final : SelectedModelColumns _selectedModelColumns; wxutil::TreeModel::Ptr _selectedModels; wxutil::TreeView* _selectedModelList; + wxWindowPtr _saveNotePanel; struct RemappingColumns : public wxutil::TreeModel::ColumnRecord From 261ce7501f2f619c03c3ead326ed6cebdabe02b9 Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Wed, 10 Apr 2024 21:23:02 +0100 Subject: [PATCH 09/49] Skin Editor layout improvements - Add a top-level sizer to the window. Previously there was no sizer at all, and the return value of loadNamedPanel() was being ignored rather than packed into the top-level window's sizer. This might have been a factor in the previous layout issues. - The immediate result of adding a top-level sizer with SetSizerAndFit() is that the window can no longer be shrunk to zero size, but obeys the size hints of the contents. - Set the LIVE_UPDATE flag on the main splitter so that the contents are updated when the sash is dragged. GTK does not show any feedback at all without this flag (the quick sash preview mode is broken). - Set minimum sizes on the main splitter so that the panels cannot be dragged to zero size. - Set the sash gravity to 0.5 so that resizing the window causes both panels to resize evenly, rather than only resizing the right-hand panel. --- install/ui/skineditor.fbp | 2 +- install/ui/skineditor.xrc | 2 +- radiant/ui/skin/SkinEditor.cpp | 8 ++++++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/install/ui/skineditor.fbp b/install/ui/skineditor.fbp index bc19084c62..b5e70711f4 100644 --- a/install/ui/skineditor.fbp +++ b/install/ui/skineditor.fbp @@ -111,7 +111,7 @@ 1 wxSPLIT_VERTICAL - wxSP_3D|wxSP_3DBORDER + wxSP_3D|wxSP_3DBORDER|wxSP_LIVE_UPDATE 0 diff --git a/install/ui/skineditor.xrc b/install/ui/skineditor.xrc index 758250ec72..e4831b9d10 100644 --- a/install/ui/skineditor.xrc +++ b/install/ui/skineditor.xrc @@ -10,7 +10,7 @@ wxALL|wxEXPAND 12 - + 0 0 0 diff --git a/radiant/ui/skin/SkinEditor.cpp b/radiant/ui/skin/SkinEditor.cpp index 1a0fa8d483..142dcb8f50 100644 --- a/radiant/ui/skin/SkinEditor.cpp +++ b/radiant/ui/skin/SkinEditor.cpp @@ -46,7 +46,7 @@ SkinEditor::SkinEditor() : _controlUpdateInProgress(false), _skinUpdateInProgress(false) { - loadNamedPanel(this, "SkinEditorMainPanel"); + wxPanel* mainPanel = loadNamedPanel(this, "SkinEditorMainPanel"); makeLabelBold(this, "SkinEditorSkinDefinitionsLabel"); makeLabelBold(this, "SkinEditorEditSkinDefinitionLabel"); @@ -76,8 +76,10 @@ SkinEditor::SkinEditor() : // Set the default size of the window FitToScreen(0.9f, 0.9f); + auto* mainPanelSizer = new wxBoxSizer(wxVERTICAL); + mainPanelSizer->Add(mainPanel, 1, wxEXPAND, 0); + SetSizerAndFit(mainPanelSizer); Layout(); - Fit(); // Connect the window position tracker _windowPosition.loadFromPath(RKEY_WINDOW_STATE); @@ -85,6 +87,8 @@ SkinEditor::SkinEditor() : _windowPosition.applyPosition(); auto leftSplitter = getControl("SkinEditorLeftSplitter"); + leftSplitter->SetMinimumPaneSize(50); + leftSplitter->SetSashGravity(0.5); _leftPanePosition.connect(leftSplitter); _leftPanePosition.loadFromPath(RKEY_SPLIT_POS_LEFT); From f359038c9198e456b46907cfc4861e4a12259f74 Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Sat, 13 Apr 2024 14:59:55 +0100 Subject: [PATCH 10/49] Set skin editor splitter parameters in XRC file I mistakenly thought these properties were not working when set in the XRC file, so set them in code instead, but it turns out I had just failed to install the modified XRC file into the runtime location. --- install/ui/skineditor.fbp | 4 ++-- install/ui/skineditor.xrc | 4 ++-- radiant/ui/skin/SkinEditor.cpp | 2 -- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/install/ui/skineditor.fbp b/install/ui/skineditor.fbp index b5e70711f4..c331b6c087 100644 --- a/install/ui/skineditor.fbp +++ b/install/ui/skineditor.fbp @@ -92,7 +92,7 @@ 0 - 0 + 50 0 @@ -105,7 +105,7 @@ 1 Resizable - 0.0 + 0.5 0 -1 1 diff --git a/install/ui/skineditor.xrc b/install/ui/skineditor.xrc index e4831b9d10..48c13c87c7 100644 --- a/install/ui/skineditor.xrc +++ b/install/ui/skineditor.xrc @@ -12,8 +12,8 @@ 0 - 0 - 0 + 0.5 + 50 vertical diff --git a/radiant/ui/skin/SkinEditor.cpp b/radiant/ui/skin/SkinEditor.cpp index 142dcb8f50..918ba51b8a 100644 --- a/radiant/ui/skin/SkinEditor.cpp +++ b/radiant/ui/skin/SkinEditor.cpp @@ -87,8 +87,6 @@ SkinEditor::SkinEditor() : _windowPosition.applyPosition(); auto leftSplitter = getControl("SkinEditorLeftSplitter"); - leftSplitter->SetMinimumPaneSize(50); - leftSplitter->SetSashGravity(0.5); _leftPanePosition.connect(leftSplitter); _leftPanePosition.loadFromPath(RKEY_SPLIT_POS_LEFT); From 03a5b6f8bc962bfd1657a78fe5b80033567825b2 Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Sat, 13 Apr 2024 15:17:04 +0100 Subject: [PATCH 11/49] ConversationDialog no longer shrinks below minimum panel size Set the [[nodiscard]] attribute on loadNamedPanel(); this emits a warning in several dialogs which are ignoring the return value rather than packing the loaded panel into a sizer. One consequence of this is that (on GTK at least) the dialog ignores the panel's size hints and can be shrunk to zero size. This is now corrected for the conversation editor. --- libs/wxutil/XmlResourceBasedWidget.h | 20 ++++++++++--------- .../dm.conversation/ConversationDialog.cpp | 9 ++++++--- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/libs/wxutil/XmlResourceBasedWidget.h b/libs/wxutil/XmlResourceBasedWidget.h index acde7db3d1..483e33ce0f 100644 --- a/libs/wxutil/XmlResourceBasedWidget.h +++ b/libs/wxutil/XmlResourceBasedWidget.h @@ -17,15 +17,17 @@ namespace wxutil class XmlResourceBasedWidget { protected: - // Loads a named Panel from the XRC resources - wxPanel* loadNamedPanel(wxWindow* parent, const std::string& name) - { - wxPanel* panel = wxXmlResource::Get()->LoadPanel(parent, name); - - assert(panel != NULL); - - return panel; - } + /// Loads a named panel from the XRC resources. + /// + /// The panel is parented to the given window, but should be packed into a sizer by the + /// calling code to avoid layout problems (unpredictable alignment, minimum panel size + /// being ignored, etc). + [[nodiscard]] wxPanel* loadNamedPanel(wxWindow* parent, const std::string& name) + { + wxPanel* panel = wxXmlResource::Get()->LoadPanel(parent, name); + wxASSERT(panel); + return panel; + } static const wxToolBarToolBase* getToolBarToolByLabel(wxToolBarBase* toolbar, const std::string& name) { diff --git a/plugins/dm.conversation/ConversationDialog.cpp b/plugins/dm.conversation/ConversationDialog.cpp index 87d2bb7637..e48290be01 100644 --- a/plugins/dm.conversation/ConversationDialog.cpp +++ b/plugins/dm.conversation/ConversationDialog.cpp @@ -55,11 +55,10 @@ ConversationDialog::ConversationDialog() : void ConversationDialog::populateWindow() { - loadNamedPanel(this, "ConvDialogMainPanel"); - - wxPanel* entityPanel = findNamedObject(this, "ConvDialogEntityPanel"); + wxPanel* mainPanel = loadNamedPanel(this, "ConvDialogMainPanel"); // Entity Tree View + wxPanel* entityPanel = findNamedObject(this, "ConvDialogEntityPanel"); _entityView = wxutil::TreeView::CreateWithModel(entityPanel, _entityList.get(), wxDV_NO_HEADER); entityPanel->GetSizer()->Add(_entityView, 1, wxEXPAND); @@ -128,6 +127,10 @@ void ConversationDialog::populateWindow() wxEVT_BUTTON, wxCommandEventHandler(ConversationDialog::onCancel), NULL, this); findNamedObject(this, "ConvDialogOkButton")->Connect( wxEVT_BUTTON, wxCommandEventHandler(ConversationDialog::onOK), NULL, this); + + wxSizer* mainSizer = new wxBoxSizer(wxVERTICAL); + mainSizer->Add(mainPanel, 1, wxEXPAND, 0); + SetSizerAndFit(mainSizer); } void ConversationDialog::save() From a23b472a2bc56829d44b84c4f4b3d6845b396e5b Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Sat, 13 Apr 2024 17:56:22 +0100 Subject: [PATCH 12/49] AuiLayout respects windows' minimum sizes Instead of applying a hard-coded minimum size of 128x128 (which is far too small for many windows, such as the Surface Inspector), we now ask the window's main sizer for its minimum size, and set this as the minimum size of the AUI pane. This makes windows created by the AUI layout behave more similarly to standard dialogs populated with SetSizerAndFit(). The possible downside of this change is that if users are docking widgets like the Surface Inspector (rather than using them as floating dialogs), they may find that their ability to resize dock panels is more limited than before, since the docked widget can no longer be shrunk below the size hint of the widget contents. The Surface Inspector in particular seems to be claiming a minimum width which is larger than necessary. --- radiant/ui/mainframe/AuiLayout.cpp | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/radiant/ui/mainframe/AuiLayout.cpp b/radiant/ui/mainframe/AuiLayout.cpp index b6d43138fe..4f2e2df95a 100644 --- a/radiant/ui/mainframe/AuiLayout.cpp +++ b/radiant/ui/mainframe/AuiLayout.cpp @@ -46,7 +46,7 @@ namespace void setupFloatingPane(wxAuiPaneInfo& pane) { - pane.Float().CloseButton(true).MinSize(128, 128); + pane.Float().CloseButton(true); } inline bool isCenterPane(const wxAuiPaneInfo& pane) @@ -316,14 +316,19 @@ void AuiLayout::createPane(const std::string& controlName, const std::string& pa auto managedWindow = _auiMgr.GetManagedWindow(); - auto pane = DEFAULT_PANE_INFO(control->getDisplayName(), MIN_SIZE); - pane.name = paneName; - // Create the widget and determine the best default size auto widget = control->createWidget(managedWindow); + wxSize minSize = MIN_SIZE; + if (wxSizer* sizer = widget->GetSizer(); sizer != nullptr) { + minSize = sizer->GetMinSize(); + } + else { + rWarning() << "wxWindow for '" << controlName + << "' has no sizer; size hints may not be respected." << std::endl; + } - // Fit and determine the floating size automatically - widget->Fit(); + auto pane = DEFAULT_PANE_INFO(control->getDisplayName(), minSize); + pane.name = paneName; // Some additional height for the window title bar pane.FloatingSize(widget->GetSize().x, widget->GetSize().y + 30); @@ -354,7 +359,7 @@ void AuiLayout::createFloatingControl(const std::string& controlName) if (defaultSettings != _defaultControlSettings.end()) { - paneInfo.FloatingSize(defaultSettings->second.defaultFloatingWidth, + paneInfo.FloatingSize(defaultSettings->second.defaultFloatingWidth, defaultSettings->second.defaultFloatingHeight); } @@ -558,7 +563,7 @@ void AuiLayout::toggleControl(const std::string& controlName) ensureControlIsActive(p->control); } - + _auiMgr.Update(); // Reset the mininum size again to not interfere with any drag operations @@ -639,7 +644,7 @@ void AuiLayout::toggleMainControl(const std::string& controlName) addPane(newControlName, newWidget, DEFAULT_PANE_INFO(control->getDisplayName(), MIN_SIZE).CenterPane()); ensureControlIsActive(newWidget); } - + _auiMgr.Update(); break; } From e06a16298fffeedbae845ff30209d38075e86f28 Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Sun, 14 Apr 2024 16:40:25 +0100 Subject: [PATCH 13/49] Add a Debug.h for printing out wxWidgets objects --- libs/wxutil/Debug.h | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 libs/wxutil/Debug.h diff --git a/libs/wxutil/Debug.h b/libs/wxutil/Debug.h new file mode 100644 index 0000000000..d37de3e0db --- /dev/null +++ b/libs/wxutil/Debug.h @@ -0,0 +1,9 @@ +#pragma once + +#include +#include + +inline std::ostream& operator<< (std::ostream& s, const wxSize& size) +{ + return s << "wxSize(w=" << size.GetWidth() << ", h=" << size.GetHeight() << ")"; +} From d5470cc85ffbc85adc2c3ab605b67fb3716d4cac Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Sun, 14 Apr 2024 17:09:44 +0100 Subject: [PATCH 14/49] Reclaim some space in SurfaceInspector dialog Remove largely useless "Texture Properties" and "Texture Operations" labels, which in turn allows the removal of an extra 18 pixels of indentation. Shorten some labels (e.g. "Horiz. Shift" is now "X Shift" and "Rotation" is now "Rotate"). Slightly reduce minimum size of Fit spin boxes. Note that because the wxWidgets AUI system saves the minimum size of each window into preferences, changes to size hints in code do not have any effect unless user.xml is wiped out. --- .../ui/surfaceinspector/SurfaceInspector.cpp | 29 ++++++------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/radiant/ui/surfaceinspector/SurfaceInspector.cpp b/radiant/ui/surfaceinspector/SurfaceInspector.cpp index c43179c1b1..7cf1285530 100644 --- a/radiant/ui/surfaceinspector/SurfaceInspector.cpp +++ b/radiant/ui/surfaceinspector/SurfaceInspector.cpp @@ -26,20 +26,17 @@ namespace ui namespace { - constexpr const char* const LABEL_PROPERTIES = N_("Texture Properties"); - constexpr const char* const LABEL_OPERATIONS = N_("Texture Operations"); - const std::string HSHIFT = "horizshift"; const std::string VSHIFT = "vertshift"; const std::string HSCALE = "horizscale"; const std::string VSCALE = "vertscale"; const std::string ROTATION = "rotation"; - constexpr const char* const LABEL_HSHIFT = N_("Horiz. Shift:"); - constexpr const char* const LABEL_VSHIFT = N_("Vert. Shift:"); - constexpr const char* const LABEL_HSCALE = N_("Horiz. Scale:"); - constexpr const char* const LABEL_VSCALE = N_("Vert. Scale:"); - constexpr const char* const LABEL_ROTATION = N_("Rotation:"); + constexpr const char* const LABEL_HSHIFT = N_("X Shift:"); + constexpr const char* const LABEL_VSHIFT = N_("Y Shift:"); + constexpr const char* const LABEL_HSCALE = N_("X Scale:"); + constexpr const char* const LABEL_VSCALE = N_("Y Scale:"); + constexpr const char* const LABEL_ROTATION = N_("Rotate:"); constexpr const char* const LABEL_SHADER = N_("Shader:"); constexpr const char* const FOLDER_ICON = "treeView16.png"; constexpr const char* const LABEL_STEP = N_("Step:"); @@ -80,7 +77,7 @@ namespace #ifdef __WXMSW__ constexpr int SPINBOX_WIDTH_CHARS = 7; #else - constexpr int SPINBOX_WIDTH_CHARS = 16; + constexpr int SPINBOX_WIDTH_CHARS = 14; #endif // Minimum pixel size for a widget. Converts to a wxSize of the specified @@ -382,10 +379,6 @@ void SurfaceInspector::populateWindow() { wxBoxSizer* dialogVBox = new wxBoxSizer(wxVERTICAL); - // Create the title label (bold font) - wxStaticText* topLabel = new wxStaticText(this, wxID_ANY, _(LABEL_PROPERTIES)); - topLabel->SetFont(topLabel->GetFont().Bold()); - // Two-column form layout wxutil::FormLayout table(this); @@ -411,8 +404,7 @@ void SurfaceInspector::populateWindow() table.add(_(LABEL_SHADER), shaderHBox); // Pack everything into the vbox - dialogVBox->Add(topLabel, 0, wxEXPAND | wxBOTTOM, 6); - dialogVBox->Add(table.getSizer(), 0, wxEXPAND | wxLEFT, 18); // 18 pixels left indentation + dialogVBox->Add(table.getSizer(), 0, wxEXPAND); // Initial parameter editing rows _manipulators[HSHIFT] = createManipulatorRow(_(LABEL_HSHIFT), table, "arrow_left_blue.png", @@ -432,9 +424,7 @@ void SurfaceInspector::populateWindow() // ======================== Texture Operations ==================================== - // Create the texture operations label (bold font) - wxStaticText* operLabel = new wxStaticText(this, wxID_ANY, _(LABEL_OPERATIONS)); - operLabel->SetFont(operLabel->GetFont().Bold()); + table.addFullWidth(new wxStaticLine(this)); // Setup the table with default spacings // 5x2 table with 12 pixel hspacing and 6 pixels vspacing @@ -443,8 +433,7 @@ void SurfaceInspector::populateWindow() // Pack label & table into the dialog dialogVBox->AddSpacer(6); - dialogVBox->Add(operLabel, 0, wxEXPAND | wxTOP | wxBOTTOM, 6); - dialogVBox->Add(operTable, 0, wxEXPAND | wxLEFT, 18); // 18 pixels left indentation + dialogVBox->Add(operTable, 0, wxEXPAND); // ------------------------ Fit Texture ----------------------------------- From 893526d4326430292f7829b3a06c5ed54eea6846 Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Tue, 16 Apr 2024 19:18:13 +0100 Subject: [PATCH 15/49] Fix some IDE-only parsing errors caused by missing includes --- radiantcore/entity/algorithm/Speaker.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/radiantcore/entity/algorithm/Speaker.h b/radiantcore/entity/algorithm/Speaker.h index 4c3a785745..3ecd41ef68 100644 --- a/radiantcore/entity/algorithm/Speaker.h +++ b/radiantcore/entity/algorithm/Speaker.h @@ -1,10 +1,12 @@ #pragma once +#include "i18n.h" #include "icommandsystem.h" #include "itextstream.h" #include "iundo.h" #include "iselection.h" #include "isound.h" +#include "scene/EntityNode.h" #include "fmt/format.h" #include "command/ExecutionFailure.h" #include "string/convert.h" From 74b062feb747d7e922342976c6bd133f7f128296 Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Tue, 16 Apr 2024 19:43:00 +0100 Subject: [PATCH 16/49] Remove CTest cruft from CMake project outline --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 502d513e72..bf1ecbfe36 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -122,7 +122,7 @@ add_subdirectory(radiant) pkg_check_modules(GTEST gtest) pkg_check_modules(GTEST_MAIN gtest_main) if (${GTEST_FOUND} AND ${GTEST_MAIN_FOUND}) - include(CTest) + enable_testing() add_subdirectory(test) endif() From 1707555ae386b6c0204dad132edf32a5761bf50f Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Tue, 16 Apr 2024 20:16:53 +0100 Subject: [PATCH 17/49] Various Doxygen fixes Fix several instances of comments intended for classes being incorrectly attached to the containing namespace. Exclude certain 3rd party header libraries. Add the generated 'dox' directory to .gitignore, and remove ignore patterns for obsolete autotools files. --- .gitignore | 8 +- Doxyfile | 1074 +++++++++++++++--------------- include/igame.h | 1 + libs/render/VertexHashing.h | 24 +- libs/wxutil/Button.h | 8 +- libs/wxutil/WindowPosition.h | 7 +- plugins/sound/OggFileStream.h | 6 +- radiant/settings/Win32Registry.h | 4 +- radiant/ui/about/AboutDialog.h | 7 +- 9 files changed, 575 insertions(+), 564 deletions(-) diff --git a/.gitignore b/.gitignore index c929ac4859..0fa9e4e346 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,9 @@ cmake_install.cmake # Linux profiling perf.data* +# Doxygen output +/dox + # Editor temporary files *.swp tags @@ -66,11 +69,6 @@ testsuite.ilk .DS_Store xcuserdata -Makefile.in -/config.h.in -/configure -/ltmain.sh -/aclocal.m4 /install/*.zip /tools/msvc/.vs /tools/msvc/DarkRadiant.VC.db diff --git a/Doxyfile b/Doxyfile index 1c9dd3d638..67399bf221 100644 --- a/Doxyfile +++ b/Doxyfile @@ -14,198 +14,198 @@ # Project related configuration options #--------------------------------------------------------------------------- -# This tag specifies the encoding used for all characters in the config file that -# follow. The default is UTF-8 which is also the encoding used for all text before -# the first occurrence of this tag. Doxygen uses libiconv (or the iconv built into -# libc) for the transcoding. See http://www.gnu.org/software/libiconv for the list of +# This tag specifies the encoding used for all characters in the config file that +# follow. The default is UTF-8 which is also the encoding used for all text before +# the first occurrence of this tag. Doxygen uses libiconv (or the iconv built into +# libc) for the transcoding. See http://www.gnu.org/software/libiconv for the list of # possible encodings. DOXYFILE_ENCODING = UTF-8 -# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = DarkRadiant -# The PROJECT_NUMBER tag can be used to enter a project or revision number. -# This could be handy for archiving the generated documentation or +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or # if some version control system is used. -PROJECT_NUMBER = +PROJECT_NUMBER = -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) -# base path where the generated documentation will be put. -# If a relative path is entered, it will be relative to the location +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = dox -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create -# 4096 sub-directories (in 2 levels) under the output directory of each output -# format and will distribute the generated files over these directories. -# Enabling this option can be useful when feeding doxygen a huge amount of -# source files, where putting all generated files in the same directory would +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = YES -# The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all constant output in the proper language. -# The default language is English, other supported languages are: -# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, -# Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hungarian, -# Italian, Japanese, Japanese-en (Japanese with English messages), Korean, -# Korean-en, Lithuanian, Norwegian, Polish, Portuguese, Romanian, Russian, +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hungarian, +# Italian, Japanese, Japanese-en (Japanese with English messages), Korean, +# Korean-en, Lithuanian, Norwegian, Polish, Portuguese, Romanian, Russian, # Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian. OUTPUT_LANGUAGE = English -# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will -# include brief member descriptions after the members that are listed in -# the file and class documentation (similar to JavaDoc). +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES -# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend -# the brief description of a member or function before the detailed description. -# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES -# This tag implements a quasi-intelligent brief description abbreviator -# that is used to form the text in various listings. Each string -# in this list, if found as the leading text of the brief description, will be -# stripped from the text and the result after processing the whole list, is -# used as the annotated text. Otherwise, the brief description is used as-is. -# If left blank, the following values are used ("$name" is automatically -# replaced with the name of the entity): "The $name class" "The $name widget" -# "The $name file" "is" "provides" "specifies" "contains" +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" -ABBREVIATE_BRIEF = +ABBREVIATE_BRIEF = -# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# Doxygen will generate a detailed section even if there is only a brief +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all -# inherited members of a class in the documentation of that class as if those -# members were ordinary class members. Constructors, destructors and assignment +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO -# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full -# path before files name in the file list and in the header files. If set +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES -# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag -# can be used to strip a user-defined part of the path. Stripping is -# only done if one of the specified strings matches the left-hand part of -# the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the # path to strip. -STRIP_FROM_PATH = +STRIP_FROM_PATH = -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of -# the path mentioned in the documentation of a class, which tells -# the reader which header file to include in order to use a class. -# If left blank only the name of the header file containing the class -# definition is used. Otherwise one should specify the include paths that +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. -STRIP_FROM_INC_PATH = +STRIP_FROM_INC_PATH = -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter -# (but less readable) file names. This can be useful is your file systems +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO -# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen -# will interpret the first line (until the first dot) of a JavaDoc-style -# comment as the brief description. If set to NO, the JavaDoc -# comments will behave just like regular Qt-style comments +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = YES -# If the QT_AUTOBRIEF tag is set to YES then Doxygen will -# interpret the first line (until the first dot) of a Qt-style -# comment as the brief description. If set to NO, the comments -# will behave just like regular Qt-style comments (thus requiring +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen -# treat a multi-line C++ special comment block (i.e. a block of //! or /// -# comments) as a brief description. This used to be the default behaviour. -# The new default is to treat a multi-line C++ comment block as a detailed +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO -# If the DETAILS_AT_TOP tag is set to YES then Doxygen +# If the DETAILS_AT_TOP tag is set to YES then Doxygen # will output the detailed description near the top, like JavaDoc. -# If set to NO, the detailed description appears after the member +# If set to NO, the detailed description appears after the member # documentation. DETAILS_AT_TOP = NO -# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented -# member inherits the documentation from any documented member that it +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce -# a new page for each member. If set to NO, the documentation of a member will +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO -# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 -# This tag can be used to specify a number of aliases that acts -# as commands in the documentation. An alias has the form "name=value". -# For example adding "sideeffect=\par Side Effects:\n" will allow you to -# put the command \sideeffect (or @sideeffect) in the documentation, which -# will result in a user-defined paragraph with heading "Side Effects:". +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. -ALIASES = +ALIASES = -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C -# sources only. Doxygen will then generate output that is more tailored for C. -# For instance, some of the names that are used will be different. The list +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java -# sources only. Doxygen will then generate output that is more tailored for Java. -# For instance, namespaces will be presented as packages, qualified scopes +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for Java. +# For instance, namespaces will be presented as packages, qualified scopes # will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO -# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to -# include (a tag file for) the STL sources as input, then you should -# set this tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. -# func(std::string) {}). This also make the inheritance and collaboration +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to +# include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO @@ -215,17 +215,17 @@ BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO -# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first -# member in the group (if any) for the other members of the group. By default +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO -# Set the SUBGROUPING tag to YES (the default) to allow class member groups of -# the same type (for instance a group of public functions) to be put as a -# subgroup of that type (e.g. under the Public Functions section). Set it to -# NO to prevent subgrouping. Alternatively, this can be done per class using +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES @@ -234,349 +234,351 @@ SUBGROUPING = YES # Build related configuration options #--------------------------------------------------------------------------- -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in -# documentation are documented, even if no documentation was available. -# Private class members and static file members will be hidden unless +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = NO -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO -# If the EXTRACT_STATIC tag is set to YES all static members of a file +# If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) -# defined locally in source files will be included in the documentation. +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES -# This flag is only useful for Objective-C code. When set to YES local -# methods, which are defined in the implementation section but not in -# the interface are included in the documentation. +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO -# If this flag is set to YES, the members of anonymous namespaces will be extracted -# and appear in the documentation as a namespace called 'anonymous_namespace{file}', -# where file will be replaced with the base name of the file that contains the anonymous +# If this flag is set to YES, the members of anonymous namespaces will be extracted +# and appear in the documentation as a namespace called 'anonymous_namespace{file}', +# where file will be replaced with the base name of the file that contains the anonymous # namespace. By default anonymous namespace are hidden. EXTRACT_ANON_NSPACES = NO -# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all -# undocumented members of documented classes, files or namespaces. -# If set to NO (the default) these members will be included in the -# various overviews, but no documentation section is generated. +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO -# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. -# If set to NO (the default) these classes will be included in the various +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all -# friend (class|struct|union) declarations. -# If set to NO (the default) these declarations will be included in the +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO -# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any -# documentation blocks found inside the body of a function. -# If set to NO (the default) these blocks will be appended to the +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO -# The INTERNAL_DOCS tag determines if documentation -# that is typed after a \internal command is included. If the tag is set -# to NO (the default) then the documentation will be excluded. +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO -# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate -# file names in lower-case letters. If set to YES upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES -# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen -# will show members with their full class and namespace scopes in the +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO -# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen -# will put a list of the files that are included by a file in the documentation +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES -# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES -# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen -# will sort the (detailed) documentation of file and class members -# alphabetically by member name. If set to NO the members will appear in +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the -# brief documentation of file, namespace and class members alphabetically -# by member name. If set to NO (the default) the members will appear in +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be -# sorted by fully-qualified names, including namespaces. If set to -# NO (the default), the class list will be sorted only by class name, -# not including the namespace part. +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the +# Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO -# The GENERATE_TODOLIST tag can be used to enable (YES) or -# disable (NO) the todo list. This list is created by putting \todo +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES -# The GENERATE_TESTLIST tag can be used to enable (YES) or -# disable (NO) the test list. This list is created by putting \test +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES -# The GENERATE_BUGLIST tag can be used to enable (YES) or -# disable (NO) the bug list. This list is created by putting \bug +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES -# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or -# disable (NO) the deprecated list. This list is created by putting +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES -# The ENABLED_SECTIONS tag can be used to enable conditional +# The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. -ENABLED_SECTIONS = +ENABLED_SECTIONS = -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines -# the initial value of a variable or define consists of for it to appear in -# the documentation. If the initializer consists of more lines than specified -# here it will be hidden. Use a value of 0 to hide initializers completely. -# The appearance of the initializer of individual variables and defines in the -# documentation can be controlled using \showinitializer or \hideinitializer +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated -# at the bottom of the documentation of classes and structs. If set to YES the +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES -# If the sources in your project are distributed over multiple directories -# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO -# The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from the -# version control system). Doxygen will invoke the program by executing (via -# popen()) the command , where is the value of -# the FILE_VERSION_FILTER tag, and is the name of an input file -# provided by doxygen. Whatever the program writes to standard output +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from the +# version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. -FILE_VERSION_FILTER = +FILE_VERSION_FILTER = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- -# The QUIET tag can be used to turn on/off the messages that are generated +# The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = YES -# The WARNINGS tag can be used to turn on/off the warning messages that are -# generated by doxygen. Possible values are YES and NO. If left blank +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES -# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings -# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = NO -# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some -# parameters in a documented function, or documenting parameters that +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES -# This WARN_NO_PARAMDOC option can be abled to get warnings for -# functions that are documented, but have no documentation for their parameters -# or return value. If set to NO (the default) doxygen will only warn about -# wrong or incomplete parameter documentation, but not about the absence of +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO -# The WARN_FORMAT tag determines the format of the warning messages that -# doxygen can produce. The string should contain the $file, $line, and $text -# tags, which will be replaced by the file and line number from which the -# warning originated and the warning text. Optionally the format may contain -# $version, which will be replaced by the version of the file (if it could +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" -# The WARN_LOGFILE tag can be used to specify a file to which warning -# and error messages should be written. If left blank the output is written +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written # to stderr. -WARN_LOGFILE = +WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- -# The INPUT tag can be used to specify the files and/or directories that contain -# documented source files. You may enter file names like "myfile.cpp" or -# directories like "/usr/src/myproject". Separate the files or directories +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = include libs radiant plugins -# This tag can be used to specify the character encoding of the source files that -# doxygen parses. Internally doxygen uses the UTF-8 encoding, which is also the default -# input encoding. Doxygen uses libiconv (or the iconv built into libc) for the transcoding. +# This tag can be used to specify the character encoding of the source files that +# doxygen parses. Internally doxygen uses the UTF-8 encoding, which is also the default +# input encoding. Doxygen uses libiconv (or the iconv built into libc) for the transcoding. # See http://www.gnu.org/software/libiconv for the list of possible encodings. INPUT_ENCODING = UTF-8 -# If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank the following patterns are tested: -# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py -FILE_PATTERNS = +FILE_PATTERNS = -# The RECURSIVE tag can be used to turn specify whether or not subdirectories -# should be searched for input files as well. Possible values are YES and NO. +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = YES -# The EXCLUDE tag can be used to specify files and/or directories that should -# excluded from the INPUT source files. This way you can easily exclude a +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. -EXCLUDE = plugins/dm.d3hook/boost \ - plugins/dm.d3hook/RCF \ - plugins/dm.d3hook/SF +EXCLUDE = plugins/dm.d3hook/boost \ + plugins/dm.d3hook/RCF \ + plugins/dm.d3hook/SF \ + libs/libfmt \ + libs/pybind -# The EXCLUDE_SYMLINKS tag can be used select whether or not files or -# directories that are symbolic links (a Unix filesystem feature) are excluded +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO -# If the value of the INPUT tag contains directories, you can use the -# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. Note that the wildcards are matched -# against the file with absolute path, so to exclude all test directories +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* -EXCLUDE_PATTERNS = +EXCLUDE_PATTERNS = -# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names -# (namespaces, classes, functions, etc.) that should be excluded from the output. -# The symbol name can be a fully qualified name, a word, or if the wildcard * is used, +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the output. +# The symbol name can be a fully qualified name, a word, or if the wildcard * is used, # a substring. Examples: ANamespace, AClass, AClass::ANamespace, ANamespace::*Test -EXCLUDE_SYMBOLS = +EXCLUDE_SYMBOLS = -# The EXAMPLE_PATH tag can be used to specify one or more files or -# directories that contain example code fragments that are included (see +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see # the \include command). -EXAMPLE_PATH = +EXAMPLE_PATH = -# If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left # blank all files are included. -EXAMPLE_PATTERNS = +EXAMPLE_PATTERNS = -# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude -# commands irrespective of the value of the RECURSIVE tag. +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO -# The IMAGE_PATH tag can be used to specify one or more files or -# directories that contain image that are included in the documentation (see +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see # the \image command). -IMAGE_PATH = +IMAGE_PATH = -# The INPUT_FILTER tag can be used to specify a program that doxygen should -# invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command , where -# is the value of the INPUT_FILTER tag, and is the name of an -# input file. Doxygen will then use the output that the filter program writes -# to standard output. If FILTER_PATTERNS is specified, this tag will be +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be # ignored. -INPUT_FILTER = +INPUT_FILTER = -# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. The filters are a list of the form: -# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further -# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. -FILTER_PATTERNS = +FILTER_PATTERNS = -# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER) will be used to filter the input files when producing source +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO @@ -585,34 +587,34 @@ FILTER_SOURCE_FILES = NO # configuration options related to source browsing #--------------------------------------------------------------------------- -# If the SOURCE_BROWSER tag is set to YES then a list of source files will -# be generated. Documented entities will be cross-referenced with these sources. -# Note: To get rid of all source code in the generated output, make sure also -# VERBATIM_HEADERS is set to NO. If you have enabled CALL_GRAPH or CALLER_GRAPH -# then you must also enable this option. If you don't then doxygen will produce +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. If you have enabled CALL_GRAPH or CALLER_GRAPH +# then you must also enable this option. If you don't then doxygen will produce # a warning and turn it on anyway SOURCE_BROWSER = NO -# Setting the INLINE_SOURCES tag to YES will include the body +# Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO -# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct -# doxygen to hide any special comment blocks from generated source code +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES -# If the REFERENCED_BY_RELATION tag is set to YES (the default) -# then for each documented function all documented +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES -# If the REFERENCES_RELATION tag is set to YES (the default) -# then for each documented function all documented entities +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES @@ -624,16 +626,16 @@ REFERENCES_RELATION = YES REFERENCES_LINK_SOURCE = YES -# If the USE_HTAGS tag is set to YES then the references to source code -# will point to the HTML generated by the htags(1) tool instead of doxygen -# built-in source browser. The htags tool is part of GNU's global source -# tagging system (see http://www.gnu.org/software/global/global.html). You +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO -# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen -# will generate a verbatim copy of the header file for each class for +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES @@ -642,141 +644,141 @@ VERBATIM_HEADERS = YES # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index -# of all compounds will be generated. Enable this if the project +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO -# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then -# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 -# In case all classes in a project start with a common prefix, all -# classes will be put under the same header in the alphabetical index. -# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. -IGNORE_PREFIX = +IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- -# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for -# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html -# The HTML_HEADER tag can be used to specify a personal HTML header for -# each generated HTML page. If it is left blank doxygen will generate a +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a # standard header. -HTML_HEADER = +HTML_HEADER = -# The HTML_FOOTER tag can be used to specify a personal HTML footer for -# each generated HTML page. If it is left blank doxygen will generate a +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a # standard footer. -HTML_FOOTER = +HTML_FOOTER = -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading -# style sheet that is used by each HTML page. It can be used to -# fine-tune the look of the HTML output. If the tag is left blank doxygen -# will generate a default style sheet. Note that doxygen will try to copy -# the style sheet file to the HTML output directory, so don't put your own +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! -HTML_STYLESHEET = +HTML_STYLESHEET = -# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, -# files or namespaces will be aligned in HTML using tables. If set to +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES -# If the GENERATE_HTMLHELP tag is set to YES, additional index files -# will be generated that can be used as input for tools like the -# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO -# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML -# documentation will contain sections that can be hidden and shown after the -# page has loaded. For this to work a browser that supports -# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can -# be used to specify the file name of the resulting .chm file. You -# can add a path in front of the file if the result should not be +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be # written to the html output directory. -CHM_FILE = +CHM_FILE = -# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can -# be used to specify the location (absolute path including file name) of -# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. -HHC_LOCATION = +HHC_LOCATION = -# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag -# controls if a separate .chi index file is generated (YES) or that +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO -# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag -# controls whether a binary table of contents is generated (YES) or a +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO -# The TOC_EXPAND flag can be set to YES to add extra items for group members +# The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO -# The DISABLE_INDEX tag can be used to turn on/off the condensed index at -# top of each HTML page. The value NO (the default) enables the index and +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO -# This tag can be used to set the number of enum values (range [1..20]) +# This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # If the GENERATE_TREEVIEW tag is set to YES, a side panel will be -# generated containing a tree-like index structure (just like the one that -# is generated for HTML Help). For this to work a browser that supports -# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, -# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are # probably better off using the HTML help feature. GENERATE_TREEVIEW = NO -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be -# used to set the initial width (in pixels) of the frame in which the tree +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 @@ -785,74 +787,74 @@ TREEVIEW_WIDTH = 250 # configuration options related to the LaTeX output #--------------------------------------------------------------------------- -# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO -# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex -# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. LATEX_CMD_NAME = latex -# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to -# generate index for LaTeX. If left blank `makeindex' will be used as the +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex -# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact -# LaTeX documents. This may be useful for small projects and may help to +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO -# The PAPER_TYPE tag can be used to set the paper type that is used -# by the printer. Possible values are: a4, a4wide, letter, legal and +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4 -# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. -EXTRA_PACKAGES = +EXTRA_PACKAGES = -# The LATEX_HEADER tag can be used to specify a personal LaTeX header for -# the generated latex document. The header should contain everything until -# the first chapter. If it is left blank doxygen will generate a +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! -LATEX_HEADER = +LATEX_HEADER = -# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated -# is prepared for conversion to pdf (using ps2pdf). The pdf file will -# contain links (just like the HTML output) instead of page references +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES -# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of -# plain latex in the generated Makefile. Set this option to YES to get a +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES -# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. -# command to the generated LaTeX files. This will instruct LaTeX to keep -# running if errors occur, instead of asking the user for help. +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO -# If LATEX_HIDE_INDICES is set to YES then doxygen will not -# include the index chapters (such as File Index, Compound Index, etc.) +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO @@ -861,68 +863,68 @@ LATEX_HIDE_INDICES = NO # configuration options related to the RTF output #--------------------------------------------------------------------------- -# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output -# The RTF output is optimized for Word 97 and may not look very pretty with +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO -# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf -# If the COMPACT_RTF tag is set to YES Doxygen generates more compact -# RTF documents. This may be useful for small projects and may help to +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO -# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated -# will contain hyperlink fields. The RTF file will -# contain links (just like the HTML output) instead of page references. -# This makes the output suitable for online browsing using WORD or other -# programs which support those fields. +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO -# Load stylesheet definitions from file. Syntax is similar to doxygen's -# config file, i.e. a series of assignments. You only have to provide +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. -RTF_STYLESHEET_FILE = +RTF_STYLESHEET_FILE = -# Set optional variables used in the generation of an rtf document. +# Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. -RTF_EXTENSIONS_FILE = +RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- -# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO -# The MAN_OUTPUT tag is used to specify where the man pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man -# The MAN_EXTENSION tag determines the extension that is added to +# The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 -# If the MAN_LINKS tag is set to YES and Doxygen generates man output, -# then it will generate one additional man file for each entity -# documented in the real man page(s). These additional files -# only source the real man page, but without them the man command +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO @@ -931,33 +933,33 @@ MAN_LINKS = NO # configuration options related to the XML output #--------------------------------------------------------------------------- -# If the GENERATE_XML tag is set to YES Doxygen will -# generate an XML file that captures the structure of +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO -# The XML_OUTPUT tag is used to specify where the XML pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml -# The XML_SCHEMA tag can be used to specify an XML schema, -# which can be used by a validating XML parser to check the +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the # syntax of the XML files. -XML_SCHEMA = +XML_SCHEMA = -# The XML_DTD tag can be used to specify an XML DTD, -# which can be used by a validating XML parser to check the +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the # syntax of the XML files. -XML_DTD = +XML_DTD = -# If the XML_PROGRAMLISTING tag is set to YES Doxygen will -# dump the program listings (including syntax highlighting -# and cross-referencing information) to the XML output. Note that +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES @@ -966,10 +968,10 @@ XML_PROGRAMLISTING = YES # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- -# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will -# generate an AutoGen Definitions (see autogen.sf.net) file -# that captures the structure of the code including all -# documentation. Note that this feature is still experimental +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO @@ -978,319 +980,319 @@ GENERATE_AUTOGEN_DEF = NO # configuration options related to the Perl module output #--------------------------------------------------------------------------- -# If the GENERATE_PERLMOD tag is set to YES Doxygen will -# generate a Perl module file that captures the structure of -# the code including all documentation. Note that this -# feature is still experimental and incomplete at the +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO -# If the PERLMOD_LATEX tag is set to YES Doxygen will generate -# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO -# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be -# nicely formatted so it can be parsed by a human reader. This is useful -# if you want to understand what is going on. On the other hand, if this -# tag is set to NO the size of the Perl module output will be much smaller +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES -# The names of the make variables in the generated doxyrules.make file -# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. -# This is useful so different doxyrules.make files included by the same +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. -PERLMOD_MAKEVAR_PREFIX = +PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- -# Configuration options related to the preprocessor +# Configuration options related to the preprocessor #--------------------------------------------------------------------------- -# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will -# evaluate all C-preprocessor directives found in the sources and include +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES -# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro -# names in the source code. If set to NO (the default) only conditional -# compilation will be performed. Macro expansion can be done in a controlled +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO -# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES -# then the macro expansion is limited to the macros specified with the +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO -# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES -# The INCLUDE_PATH tag can be used to specify one or more directories that -# contain include files that are not input files but should be processed by +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by # the preprocessor. -INCLUDE_PATH = +INCLUDE_PATH = -# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard -# patterns (like *.h and *.hpp) to filter out the header-files in the -# directories. If left blank, the patterns specified with FILE_PATTERNS will +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. -INCLUDE_FILE_PATTERNS = +INCLUDE_FILE_PATTERNS = -# The PREDEFINED tag can be used to specify one or more macro names that -# are defined before the preprocessor is started (similar to the -D option of -# gcc). The argument of the tag is a list of macros of the form: name -# or name=definition (no spaces). If the definition and the = are -# omitted =1 is assumed. To prevent a macro definition from being -# undefined via #undef or recursively expanded use the := operator +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator # instead of the = operator. -PREDEFINED = +PREDEFINED = -# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then -# this tag can be used to specify a list of macro names that should be expanded. -# The macro definition that is found in the sources will be used. +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. -EXPAND_AS_DEFINED = +EXPAND_AS_DEFINED = -# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then -# doxygen's preprocessor will remove all function-like macros that are alone -# on a line, have an all uppercase name, and do not end with a semicolon. Such -# function macros are typically used for boiler-plate code, and will confuse +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- -# Configuration::additions related to external references +# Configuration::additions related to external references #--------------------------------------------------------------------------- -# The TAGFILES option can be used to specify one or more tagfiles. -# Optionally an initial location of the external documentation -# can be added for each tagfile. The format of a tag file without -# this location is as follows: -# TAGFILES = file1 file2 ... -# Adding location for the tag files is done as follows: -# TAGFILES = file1=loc1 "file2 = loc2" ... -# where "loc1" and "loc2" can be relative or absolute paths or -# URLs. If a location is present for each tag, the installdox tool +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) -# If a tag file is not located in the directory in which doxygen +# If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. -TAGFILES = +TAGFILES = -# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. -GENERATE_TAGFILE = +GENERATE_TAGFILE = -# If the ALLEXTERNALS tag is set to YES all external classes will be listed -# in the class index. If set to NO only the inherited external classes +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO -# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed -# in the modules index. If set to NO, only the current project's groups will +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES -# The PERL_PATH should be the absolute path and name of the perl script +# The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- -# Configuration options related to the dot tool +# Configuration options related to the dot tool #--------------------------------------------------------------------------- -# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will -# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base -# or super classes. Setting the tag to NO turns the diagrams off. Note that -# this option is superseded by the HAVE_DOT option below. This is only a -# fallback. It is recommended to install and use dot, since it yields more +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = YES -# You can define message sequence charts within doxygen comments using the \msc -# command. Doxygen will then run the mscgen tool (see http://www.mcternan.me.uk/mscgen/) to -# produce the chart and insert it in the documentation. The MSCGEN_PATH tag allows you to -# specify the directory where the mscgen tool resides. If left empty the tool is assumed to +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see http://www.mcternan.me.uk/mscgen/) to +# produce the chart and insert it in the documentation. The MSCGEN_PATH tag allows you to +# specify the directory where the mscgen tool resides. If left empty the tool is assumed to # be found in the default search path. -MSCGEN_PATH = +MSCGEN_PATH = -# If set to YES, the inheritance and collaboration graphs will hide -# inheritance and usage relations if the target is undocumented +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES -# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is -# available from the path. This tool is part of Graphviz, a graph visualization -# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = YES -# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect inheritance relations. Setting this tag to YES will force the +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = NO -# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect implementation dependencies (inheritance, containment, and +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = NO -# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES -# If the UML_LOOK tag is set to YES doxygen will generate inheritance and -# collaboration diagrams in a style similar to the OMG's Unified Modeling +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO -# If set to YES, the inheritance and collaboration graphs will show the +# If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT -# tags are set to YES then doxygen will generate a graph for each documented -# file showing the direct and indirect include dependencies of the file with +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = NO -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and -# HAVE_DOT tags are set to YES then doxygen will generate a graph for each -# documented header file showing the documented files that directly or +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = NO -# If the CALL_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will -# generate a call dependency graph for every global function or class method. -# Note that enabling this option will significantly increase the time of a run. -# So in most cases it will be better to enable call graphs for selected +# If the CALL_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will +# generate a call dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected # functions only using the \callgraph command. CALL_GRAPH = NO -# If the CALLER_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will -# generate a caller dependency graph for every global function or class method. -# Note that enabling this option will significantly increase the time of a run. -# So in most cases it will be better to enable caller graphs for selected +# If the CALLER_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will +# generate a caller dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable caller graphs for selected # functions only using the \callergraph command. CALLER_GRAPH = NO -# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = NO -# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES -# then doxygen will show the dependencies a directory has on other directories +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES -# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png -# The tag DOT_PATH can be used to specify the path where the dot tool can be +# The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. -DOT_PATH = +DOT_PATH = -# The DOTFILE_DIRS tag can be used to specify one or more directories that -# contain dot files that are included in the documentation (see the +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the # \dotfile command). -DOTFILE_DIRS = +DOTFILE_DIRS = -# The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of -# nodes that will be shown in the graph. If the number of nodes in a graph -# becomes larger than this value, doxygen will truncate the graph, which is -# visualized by representing a node as a red box. Note that doxygen if the number -# of direct children of the root node in a graph is already larger than -# MAX_DOT_GRAPH_NOTES then the graph will not be shown at all. Also note +# The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the number +# of direct children of the root node in a graph is already larger than +# MAX_DOT_GRAPH_NOTES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 -# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the -# graphs generated by dot. A depth value of 3 means that only nodes reachable -# from the root by following a path via at most 3 edges will be shown. Nodes -# that lay further from the root node will be omitted. Note that setting this -# option to 1 or 2 may greatly reduce the computation time needed for large -# code bases. Also note that the size of a graph can be further restricted by +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 -# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is disabled by default, which results in a white background. -# Warning: Depending on the platform used, enabling this option may lead to -# badly anti-aliased labels on the edges of a graph (i.e. they become hard to +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, which results in a white background. +# Warning: Depending on the platform used, enabling this option may lead to +# badly anti-aliased labels on the edges of a graph (i.e. they become hard to # read). DOT_TRANSPARENT = NO -# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output -# files in one run (i.e. multiple -o and -T options on the command line). This -# makes dot run faster, but since only newer versions of dot (>1.8.10) +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO -# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will -# generate a legend page explaining the meaning of the various boxes and +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES -# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will -# remove the intermediate dot files that are used to generate +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES #--------------------------------------------------------------------------- -# Configuration::additions related to the search engine +# Configuration::additions related to the search engine #--------------------------------------------------------------------------- -# The SEARCHENGINE tag specifies whether or not a search engine should be +# The SEARCHENGINE tag specifies whether or not a search engine should be # used. If set to NO the values of all tags below this one will be ignored. SEARCHENGINE = NO diff --git a/include/igame.h b/include/igame.h index a36c864d35..e30afa037e 100644 --- a/include/igame.h +++ b/include/igame.h @@ -15,6 +15,7 @@ const char* const RKEY_ENGINE_PATH = "user/paths/enginePath"; const char* const RKEY_MOD_PATH = "user/paths/modPath"; const char* const RKEY_MOD_BASE_PATH = "user/paths/modBasePath"; +/// Code for dealing with the per-game configuration items contained in .game files namespace game { diff --git a/libs/render/VertexHashing.h b/libs/render/VertexHashing.h index bb5a945cda..bed99b2b74 100644 --- a/libs/render/VertexHashing.h +++ b/libs/render/VertexHashing.h @@ -5,23 +5,23 @@ #include "render/RenderVertex.h" #include "math/Hash.h" -/** -* greebo: When creating model surfaces from ASE models, the in-game loading code +/* +* greebo: When creating model surfaces from ASE models, the in-game loading code * will aim to merge vertices that are near each other (and have otherwise negligible * differences for their other components like normal, texcoords and colour). -* +* * The in-game code is using a custom vertex hash index implementation to quickly -* look up almost-similar vertices and prevent such "duplicates" from ending up in the -* final mesh. This is based on a set of epsilon (one for each type, in CVARs like +* look up almost-similar vertices and prevent such "duplicates" from ending up in the +* final mesh. This is based on a set of epsilon (one for each type, in CVARs like * r_slopVertex, r_slopNormal, etc.) and to speed things up, a hash index is built * for all the vertices in a single mesh. -* +* * The hash functors below aim to reproduce this behaviour without fully re-implementing * those custom containers in the engine code, designed to be used in a std::unordered_map * with MeshVertex used as Key type. -* +* * The MeshVertex hash functions is deliberately coarse and will produce lots of -* collisions for vertices in a certain vicinity, only provide a fast way of looking up +* collisions for vertices in a certain vicinity, only provide a fast way of looking up * duplicates in meshes with many verts. Each "colliding" vertex will then be compared * in-depth by the std::equal_to<> specialisation, where the epsilon-comparison is performed. */ @@ -34,7 +34,7 @@ namespace render } // Coarse hash of the 3-component vector -// This is rounding the doubles to the SignificantVertexDigits defined above, +// This is rounding the doubles to the SignificantVertexDigits defined above, // so any two vectors with their components only differing after the few significant digits // will produce the same hash. template<> @@ -49,7 +49,7 @@ struct std::hash }; // Coarse hash of the 3-component float vector -// This is rounding the doubles to the SignificantVertexDigits defined above, +// This is rounding the doubles to the SignificantVertexDigits defined above, // so any two vectors with their components only differing after the few significant digits // will produce the same hash. template<> @@ -76,7 +76,7 @@ struct std::hash } }; -// Assumes equality of two MeshVertices if all of (vertex, normal, texcoord, colour) +// Assumes equality of two MeshVertices if all of (vertex, normal, texcoord, colour) // are equal within defined epsilons (VertexEpsilon, NormalEpsilon, TexCoordEpsilon) template<> struct std::equal_to @@ -103,7 +103,7 @@ struct std::hash } }; -// Assumes equality of two MeshVertices if all of (vertex, normal, texcoord, colour) +// Assumes equality of two MeshVertices if all of (vertex, normal, texcoord, colour) // are equal within defined epsilons (VertexEpsilon, NormalEpsilon, TexCoordEpsilon) template<> struct std::equal_to diff --git a/libs/wxutil/Button.h b/libs/wxutil/Button.h index 52122598fc..e77b637ce1 100644 --- a/libs/wxutil/Button.h +++ b/libs/wxutil/Button.h @@ -11,7 +11,13 @@ namespace wxutil namespace button { -// Connects the click event of the given button to the specified command +/// Connects the click event of the given button to the specified command +/// +/// @internal While connecting buttons to commands can be convenient, it bypasses +/// compile-time type checking and can make IDE navigation more difficult (since you can't +/// jump to the definition of a command like you can with an actual C++ function). Consider +/// if it's possible to connect the button directly to the actual method which implements +/// the command instead of using this function. inline void connectToCommand(wxButton* button, const char* command) { button->Bind(wxEVT_BUTTON, [=](wxCommandEvent&) diff --git a/libs/wxutil/WindowPosition.h b/libs/wxutil/WindowPosition.h index 9d3e9cd0b5..791c95ab3a 100644 --- a/libs/wxutil/WindowPosition.h +++ b/libs/wxutil/WindowPosition.h @@ -8,6 +8,10 @@ class wxTopLevelWindow; +/// Utility classes and functions for dealing with wxWidgets +namespace wxutil +{ + /** * greebo: A WindowPosition object keeps track of the window's size and position. * @@ -16,9 +20,6 @@ class wxTopLevelWindow; * * Use the connect() method to connect a wxTopLevelWindow to this object. */ -namespace wxutil -{ - class WindowPosition : public wxEvtHandler, public ui::IPersistableObject diff --git a/plugins/sound/OggFileStream.h b/plugins/sound/OggFileStream.h index 6c5907d544..a560483477 100644 --- a/plugins/sound/OggFileStream.h +++ b/plugins/sound/OggFileStream.h @@ -1,6 +1,10 @@ #pragma once #include "stream/ScopedArchiveBuffer.h" +#include + +/// Functionality related to loading and playing sounds +namespace sound { /** greebo: A wrapper class for use with the ov_open_callbacks() method * in vorbsfile.h. This provides the four callback @@ -9,8 +13,6 @@ * Parts of this code has been written after * http://www.devmaster.net/articles/openal-ogg-file/ */ -namespace sound { - class OggFileStream { archive::ScopedArchiveBuffer _source; diff --git a/radiant/settings/Win32Registry.h b/radiant/settings/Win32Registry.h index 0657a9f4cb..69788e3af6 100644 --- a/radiant/settings/Win32Registry.h +++ b/radiant/settings/Win32Registry.h @@ -3,14 +3,14 @@ #include +namespace game { + /** greebo: This provides a convenience method to retrieve a string * from the Windows Registry using the Windows API. * * It is safe to call this function in Linux, the non-Win32 implementations * just return an empty string. */ -namespace game { - class Win32Registry { public: diff --git a/radiant/ui/about/AboutDialog.h b/radiant/ui/about/AboutDialog.h index 7506a588e2..e1cabd3fa0 100644 --- a/radiant/ui/about/AboutDialog.h +++ b/radiant/ui/about/AboutDialog.h @@ -5,13 +5,14 @@ #include "wxutil/dialog/DialogBase.h" #include "wxutil/XmlResourceBasedWidget.h" +/// Core user interface functionality +namespace ui +{ + /** * greebo: The AboutDialog displays information about DarkRadiant and * the subsystems OpenGL and GTK+. */ -namespace ui -{ - class AboutDialog : public wxutil::DialogBase, private wxutil::XmlResourceBasedWidget From daa7cfd39e85b3b8dce4f8ba8e868612a5a0c8c8 Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Wed, 17 Apr 2024 19:36:50 +0100 Subject: [PATCH 18/49] Fix "usage" message in console when using texture align buttons Missing 'else' keywords caused the usage message (intended for an invalid edge argument) to be displayed even when the argument was valid. --- radiantcore/selection/algorithm/Shader.cpp | 61 +++++++++------------- 1 file changed, 25 insertions(+), 36 deletions(-) diff --git a/radiantcore/selection/algorithm/Shader.cpp b/radiantcore/selection/algorithm/Shader.cpp index d56501de5d..72fde2339a 100644 --- a/radiantcore/selection/algorithm/Shader.cpp +++ b/radiantcore/selection/algorithm/Shader.cpp @@ -244,7 +244,7 @@ void pasteTextureCoords(SelectionTest& test) radiant::TextureChangedMessage::Send(); } -void pasteShaderName(SelectionTest& test) +void pasteShaderName(SelectionTest& test) { // Initialise an empty Texturable structure Texturable target; @@ -436,12 +436,12 @@ void fitTexture(const double repeatS, const double repeatT) { UndoableCommand command("fitTexture"); - GlobalSelectionSystem().foreachFace([&] (IFace& face) - { + GlobalSelectionSystem().foreachFace([&] (IFace& face) + { face.fitTexture(static_cast(repeatS), static_cast(repeatT)); }); GlobalSelectionSystem().foreachPatch([&] (IPatch& patch) - { + { patch.fitTexture(static_cast(repeatS), static_cast(repeatT)); }); @@ -506,19 +506,19 @@ void naturalTexture(const cmd::ArgumentList& args) radiant::TextureChangedMessage::Send(); } -void shiftTexture(const Vector2& shift) +void shiftTexture(const Vector2& shift) { std::string command("shiftTexture: "); command += "s=" + string::to_string(shift[0]) + ", t=" + string::to_string(shift[1]); UndoableCommand undo(command); - GlobalSelectionSystem().foreachFace([&] (IFace& face) - { + GlobalSelectionSystem().foreachFace([&] (IFace& face) + { face.shiftTexdefByPixels(static_cast(shift[0]), static_cast(shift[1])); }); GlobalSelectionSystem().foreachPatch([&] (IPatch& patch) - { + { patch.translateTexture(static_cast(shift[0]), static_cast(shift[1])); }); @@ -587,7 +587,7 @@ void scaleTextureUp() scaleTexture(Vector2(0.0f, registry::getValue("user/ui/textures/surfaceInspector/vScaleStep"))); } -void scaleTextureDown() +void scaleTextureDown() { // Correct the factor such that clicking the down and up button in the surface inspector // brings us back to the original state @@ -724,34 +724,23 @@ void alignTexture(EAlignType align) void alignTextureCmd(const cmd::ArgumentList& args) { - if (args.size() != 1) - { - rMessage() << "Usage: TexAlign [top|bottom|left|right]" << std::endl; - return; - } - - std::string arg = string::to_lower_copy(args[0].getString()); + if (args.size() != 1) { + rMessage() << "Usage: TexAlign [top|bottom|left|right]" << std::endl; + return; + } - if (arg == "top") - { - alignTexture(ALIGN_TOP); - } - else if (arg == "bottom") - { - alignTexture(ALIGN_BOTTOM); - } - if (arg == "left") - { - alignTexture(ALIGN_LEFT); - } - if (arg == "right") - { - alignTexture(ALIGN_RIGHT); - } - else - { - rMessage() << "Usage: TexAlign [top|bottom|left|right]" << std::endl; - } + const std::string arg = string::to_lower_copy(args[0].getString()); + + if (arg == "top") + alignTexture(ALIGN_TOP); + else if (arg == "bottom") + alignTexture(ALIGN_BOTTOM); + else if (arg == "left") + alignTexture(ALIGN_LEFT); + else if (arg == "right") + alignTexture(ALIGN_RIGHT); + else + rMessage() << "Usage: TexAlign [top|bottom|left|right]" << std::endl; } void normaliseTexture(const cmd::ArgumentList& args) From a57156a6cbcd66ff9148f3a0b8e64b3fdd667b00 Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Sat, 20 Apr 2024 13:43:24 +0100 Subject: [PATCH 19/49] Use icons for align buttons in Surface Inspector --- install/bitmaps/align_bottom.png | Bin 0 -> 1266 bytes install/bitmaps/align_left.png | Bin 0 -> 1353 bytes install/bitmaps/align_right.png | Bin 0 -> 1351 bytes install/bitmaps/align_top.png | Bin 0 -> 324 bytes .../ui/surfaceinspector/SurfaceInspector.cpp | 16 +++- resources/icons/align_top.svg | 88 ++++++++++++++++++ 6 files changed, 100 insertions(+), 4 deletions(-) create mode 100644 install/bitmaps/align_bottom.png create mode 100644 install/bitmaps/align_left.png create mode 100644 install/bitmaps/align_right.png create mode 100644 install/bitmaps/align_top.png create mode 100644 resources/icons/align_top.svg diff --git a/install/bitmaps/align_bottom.png b/install/bitmaps/align_bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..e0d2b034306e57d18f974cb600054a7b987db5f3 GIT binary patch literal 1266 zcmV zaB^>EX>4U6ba`-PAZ2)IW&i+q+O1Ywmg6W4{nsjH3FrZce!S^SP{}iSHh<8-@lXo!No)WeFqWj+p0t6buhFG-3-qw6I|`E-b8JvT^6ilgBO_H$XHQk|9N! z=&p0kQlx26RwFevo|3g_S69&{Lls)NW8pn+*~2w<1nvw=VK`%~*aw}HcgIUdbGDTt z#_rEt0hh?4fmarF{f89@;d$0k+!K6`#|y;5iNR{6*|EVIw!_61`J=X!cA^!A5z>yu zx!)WhMTi{%h7}Bi#2Ptq$Re2;d35kofn1bKHc-GZN(V|Y)dX@}R&1=vyxU5YLyZ;! z5mA$7P*b#mE3;^cESsZ-YSd^k#uRh(SYl0@EX9;kPOBGd)@(WElyml6a&>j{=*hFY z7q3*LD0(%PRC4iBO05W5;kjaU^$Nu%n{KwnEp2)8t!%ZSe44e`Qp?R-X_dNk?bc&Y zJ$LV=*TDm&G;G9?Mjk%Os1v0&b=r(G%{+aUS?{dvtkN%>Ju~-b*7(L62&0`dgVD(v z)H$CncpWEkaR$c37>wt|018?bXTjM_7IU;X3q{ZsQGhZoayl=@z@QD5IQh=q2Xjxn z5&Hgbd6Un~(W34O=4es(g1KkjUa;2J`QBC^1{`dw4OdcwqWxf0#qk%yH>HlePf50E zt^*t`JwiOnS#|8w*?ODX_8X7%?a*I9qIwY|@{1smUj&KdB1j|`L85vQB&rudBEJX{ zg^M83_d4mzNXOP*{qRF+N3wDLA!1~U9B0|e%(mthapK-&ZsVBdQScyPx5n9glk}+g zvFtmPLe9X_Pb@rfwm)Eb#@6R&Y`(20ig;PPM?M2e?(``y{uP!N*tQcJ$x7UDzLp;+ zrT~iQ?u+ukOQ1J@Gv`cG^NsduWd^*3_R26jUAKFw+)^WXpVR<2thcRYeAe{wb_wjP zea1JB01IB?Sw=|*oYuPAS-LeID3pisnEiO7hUK-qkBQ#j)Q=ot(|+d!-&W5hM?b8= z-^xcvVDxMxd_VN=--Sec5u_(ae+!AiMUb8yJwht@ph7#-|8BH|4qFxc`v3p{24YJ` zL;wH)0002_L%V+f000SaNLh0L01m?d01m?e$8V@)00007bV*G`2j~P83@HcC0H$jI z007cSL_t(IjqQ^?4gp~hhM!>ziB4AG26_#N#0luQgia$7KN0H)8V7KJtsG(-Zm@-B zQK%GlM-Z5}#a+TL1t607*qoM6N<$g7Dx-EC2ui literal 0 HcmV?d00001 diff --git a/install/bitmaps/align_left.png b/install/bitmaps/align_left.png new file mode 100644 index 0000000000000000000000000000000000000000..02e948b9d479fff2847fcd7ed489cb96f6f93e76 GIT binary patch literal 1353 zcmV-P1-AN$P) zaB^>EX>4U6ba`-PAZ2)IW&i+q+O1b@cIzq({I6BqB_Ih9EC=&(&fCFT{vGT%O=_oc z+iRMNC}J2G2@>l6|2OCl9&yf-50+w%(c$sgXDR3;_wn?b&CZS4_jvKm$M-0vrw{fX zqn-H$R$+B>-sjT^qz?n)-SU~A!zaJqr;#T{X$KOt2fsIG?(s<8kL`oeQt?=5@Nt2@^L)q%go3Bi2TH=eO+?XpB0y zh_U*8ufR)W%D{6Mb^b>b1mUsgQCK&4Z=W9s3lNW}xy=a+ENI0R{6DPA=~oFsE@j4@tgCCKJR z2|fn$+H7r%rhF1Dm4FQp=Vs;{BOYBkkd3pHul zd-e zcmUFFTYnioX@}_356-(CMf=(vwbijKFOcW}eL%j3MCm=!C64sPvR@*eA<>nMyuy*L zY$TB4ctrAH`(U1)9)CCkaIm%64@P%%37*2a!9&vly#BdQ@;YYvV^TdnJbHDGopNiqj?u~BL*XInS{h+}O+D z00(qQO+^Rj1QZM@7(xiEo&W#<<4Ht8R5*>L)4fUqK@`RD-)y!MEb;-`Sld|TA*@WN z;4|2XAc!c7YiH{#SlGtK(pT^iEJV;)2ttZv$0{Hun_V{pw>fj~{~RteA;xHxlXP=& zR%K1y+-WJG%AuDV;JOi@ccmY>L+jswc4jLEL#!<`SqSK4){~Y8Y%Uvy^sf*yX(x3r z+WD=QEqN|MJ9R7Ct#U2}gu=M)@It_7+p%a^7cjSjxNo{~<8U(eg#JwbI2URqfVUIh zVQPSvssyF);Qi8Tk=VmmBLIA!d5(nxOd0`Tbm1{XM`ib6)$jZR7fm>$Cz$0K00000 LNkvXXu0mjf+E;p^ literal 0 HcmV?d00001 diff --git a/install/bitmaps/align_right.png b/install/bitmaps/align_right.png new file mode 100644 index 0000000000000000000000000000000000000000..108d25b481c95d3cb505b547fc5fa6c8b6ee886e GIT binary patch literal 1351 zcmV-N1-SZ&P) zaB^>EX>4U6ba`-PAZ2)IW&i+q+O1dFmE$-J{6`cU0&(yPhvMvs}<9a5B3>j zoaqHpA$3z;{pkeK!-8;?e5PlAr`M|+`4Lgtfdu2Ax9FlfUg>s^(bB-4Xxd9&-Xg}(BWX4Z8($a6zvD2A_;%aeJII&x$xIO z+QSy~GAwCl8^z`$r7~)(V=3<=MR!DUtGeLu-zLaU(Y{VMoy%t+(_mM{hdyG3dFnXCR;=~4Y-bdd%{=1(JUR~qAjrB$3^cL@~zTM zj+K5WU^?%O&n11d;Gsos^-50)_G3x!8Hy}h^%qWg*s`}m#!G`#B00006VoOIv00000008+zyMF)x010qNS#tmY4#NNd4#NS*Z>VGd000Mc zNliru=mZoDC^zuJr-lFk0OUzTK~y-6WBmXBKLaIz1($?qGeg?zT83HJQ@HU>R%4PankVBll=$FNznkwJ=F0~i<>807vl zGi;LRVc^Et@$48FoQ82ZqKi8p|Of#E+B0|)sA zFf#0X_lF@HU6|!f4Fe-K^=M&p`Trk=u>bW8e=rOn!tUq){xbNyt7rIxO_GoozB2x2 z@O@X$@CcUzdR85k+e&Hy!sIsiYhfq?)3002ov JPDHLkV1kEeeXRfh literal 0 HcmV?d00001 diff --git a/install/bitmaps/align_top.png b/install/bitmaps/align_top.png new file mode 100644 index 0000000000000000000000000000000000000000..c44135c451aefa6088d032b03033478eae186a9e GIT binary patch literal 324 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9F5he4R}c>anMprB-l zYeY$Kep*R+Vo@qXd3m{BW?pu2a$-TMUVc&f>~}U&Kt&flT^vI!dhbrMCM)iIw3lA<~(`5{Q@f|U*wxC zp4zZO<5-*PKeq%E4Xv{R9D6n9b8>xIB=)pjbluMV=So?>>U{t0{fGIWU}}q#FyEh@ R20+g-c)I$ztaD0e0ssKNcmeSetMinSize(PixelSize(20, -1)); _alignTexture.bottom->SetMinSize(PixelSize(20, -1)); diff --git a/resources/icons/align_top.svg b/resources/icons/align_top.svg new file mode 100644 index 0000000000..b3f0e618ac --- /dev/null +++ b/resources/icons/align_top.svg @@ -0,0 +1,88 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + From c97aae8dfb03a965a97fb9c12202c6f551b6dd22 Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Sat, 20 Apr 2024 13:52:41 +0100 Subject: [PATCH 20/49] Add a wxutil::IconButton() convenience function --- libs/wxutil/Bitmap.h | 8 +++++++- radiant/ui/surfaceinspector/SurfaceInspector.cpp | 16 ++++------------ 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/libs/wxutil/Bitmap.h b/libs/wxutil/Bitmap.h index fe542d2843..bee3d0dc38 100644 --- a/libs/wxutil/Bitmap.h +++ b/libs/wxutil/Bitmap.h @@ -1,7 +1,7 @@ #pragma once -#include "wxutil/Bitmap.h" #include +#include namespace wxutil { @@ -31,4 +31,10 @@ inline wxBitmap GetLocalBitmap(const std::string& name, const wxArtClient& clien return wxArtProvider::GetBitmap("darkradiant:" + name, client); } +/// Construct and return a wxBitmapButton displaying the given local icon +inline wxBitmapButton* IconButton(wxWindow* parent, const std::string& iconFile) +{ + return new wxBitmapButton(parent, wxID_ANY, GetLocalBitmap(iconFile)); +} + } diff --git a/radiant/ui/surfaceinspector/SurfaceInspector.cpp b/radiant/ui/surfaceinspector/SurfaceInspector.cpp index b079eb0124..3ab78e26b3 100644 --- a/radiant/ui/surfaceinspector/SurfaceInspector.cpp +++ b/radiant/ui/surfaceinspector/SurfaceInspector.cpp @@ -445,18 +445,10 @@ void SurfaceInspector::populateWindow() _alignTexture.label = new wxStaticText(this, wxID_ANY, _(LABEL_ALIGN_TEXTURE)); - _alignTexture.top = new wxBitmapButton( - this, wxID_ANY, wxutil::GetLocalBitmap("align_top.png") - ); - _alignTexture.bottom = new wxBitmapButton( - this, wxID_ANY, wxutil::GetLocalBitmap("align_bottom.png") - ); - _alignTexture.left = new wxBitmapButton( - this, wxID_ANY, wxutil::GetLocalBitmap("align_left.png") - ); - _alignTexture.right = new wxBitmapButton( - this, wxID_ANY, wxutil::GetLocalBitmap("align_right.png") - ); + _alignTexture.top = wxutil::IconButton(this, "align_top.png"); + _alignTexture.bottom = wxutil::IconButton(this, "align_bottom.png"); + _alignTexture.left = wxutil::IconButton(this, "align_left.png"); + _alignTexture.right = wxutil::IconButton(this, "align_right.png"); _alignTexture.top->SetMinSize(PixelSize(20, -1)); _alignTexture.bottom->SetMinSize(PixelSize(20, -1)); From 9995276812e384cdf784bd936cf5c70ab3f962e1 Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Sat, 20 Apr 2024 14:13:36 +0100 Subject: [PATCH 21/49] Use icons for flip texture buttons in SurfaceInspector Remove the dedicated "flip" row, replacing its large text buttons with much smaller icon buttons on the same row as the 4 existing alignment buttons. Also use these new flip icons for the similar buttons in the Texture Tool. --- install/bitmaps/flip_horiz.png | Bin 0 -> 231 bytes install/bitmaps/flip_vert.png | Bin 0 -> 239 bytes install/user.xml | 6 +- .../ui/surfaceinspector/SurfaceInspector.cpp | 124 ++++++++---------- .../ui/surfaceinspector/SurfaceInspector.h | 1 - resources/icons/flip_horiz.svg | 85 ++++++++++++ resources/icons/flip_vert.svg | 89 +++++++++++++ 7 files changed, 233 insertions(+), 72 deletions(-) create mode 100644 install/bitmaps/flip_horiz.png create mode 100644 install/bitmaps/flip_vert.png create mode 100644 resources/icons/flip_horiz.svg create mode 100644 resources/icons/flip_vert.svg diff --git a/install/bitmaps/flip_horiz.png b/install/bitmaps/flip_horiz.png new file mode 100644 index 0000000000000000000000000000000000000000..99d06f83e9c4caca04de3df0228c1a1a5c5a1a3a GIT binary patch literal 231 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9F5he4R}c>anMprB-l zYeY$Kep*R+Vo@qXd3m{BW?pu2a$-TMUVc&f>~}U&Kt&~ss5`s!YF3s`D$e{L?k<7anMprB-l zYeY$Kep*R+Vo@qXd3m{BW?pu2a$-TMUVc&f>~}U&Kt(m4E{-7^YI$A-5!Bp{QYY gPvhq1t!z6OUgUcm?dsc51at?3r>mdKI;Vst0A*2DQUCw| literal 0 HcmV?d00001 diff --git a/install/user.xml b/install/user.xml index d6d5304d53..2eba382d34 100644 --- a/install/user.xml +++ b/install/user.xml @@ -304,8 +304,8 @@ - - + + @@ -321,7 +321,7 @@ - diff --git a/radiant/ui/surfaceinspector/SurfaceInspector.cpp b/radiant/ui/surfaceinspector/SurfaceInspector.cpp index 3ab78e26b3..f51e5f0130 100644 --- a/radiant/ui/surfaceinspector/SurfaceInspector.cpp +++ b/radiant/ui/surfaceinspector/SurfaceInspector.cpp @@ -377,19 +377,19 @@ void SurfaceInspector::createScaleLinkButtons(wxutil::FormLayout& table) void SurfaceInspector::populateWindow() { - wxBoxSizer* dialogVBox = new wxBoxSizer(wxVERTICAL); + wxBoxSizer* dialogVBox = new wxBoxSizer(wxVERTICAL); // Two-column form layout - wxutil::FormLayout table(this); + wxutil::FormLayout table(this); // Shader entry box - wxBoxSizer* shaderHBox = new wxBoxSizer(wxHORIZONTAL); - _shaderEntry = new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER); - _shaderEntry->SetMinSize(wxSize(100, -1)); - _shaderEntry->Connect(wxEVT_TEXT_ENTER, wxCommandEventHandler(SurfaceInspector::onShaderEntryActivate), NULL, this); - shaderHBox->Add(_shaderEntry, 1, wxEXPAND); + wxBoxSizer* shaderHBox = new wxBoxSizer(wxHORIZONTAL); + _shaderEntry = new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER); + _shaderEntry->SetMinSize(wxSize(100, -1)); + _shaderEntry->Connect(wxEVT_TEXT_ENTER, wxCommandEventHandler(SurfaceInspector::onShaderEntryActivate), NULL, this); + shaderHBox->Add(_shaderEntry, 1, wxEXPAND); - // Create the icon button to open the MaterialChooser + // Create the icon button to open the MaterialChooser _selectShaderButton = new wxBitmapButton( this, wxID_ANY, wxutil::GetLocalBitmap(FOLDER_ICON) ); @@ -401,12 +401,12 @@ void SurfaceInspector::populateWindow() ); shaderHBox->Add(_selectShaderButton, 0, wxLEFT, 6); - table.add(_(LABEL_SHADER), shaderHBox); + table.add(_(LABEL_SHADER), shaderHBox); - // Pack everything into the vbox - dialogVBox->Add(table.getSizer(), 0, wxEXPAND); + // Pack everything into the vbox + dialogVBox->Add(table.getSizer(), 0, wxEXPAND); - // Initial parameter editing rows + // Initial parameter editing rows _manipulators[HSHIFT] = createManipulatorRow(_(LABEL_HSHIFT), table, "arrow_left_blue.png", "arrow_right_blue.png"); _manipulators[VSHIFT] = createManipulatorRow(_(LABEL_VSHIFT), table, "arrow_down_blue.png", @@ -427,88 +427,77 @@ void SurfaceInspector::populateWindow() table.addFullWidth(new wxStaticLine(this)); // Setup the table with default spacings - // 5x2 table with 12 pixel hspacing and 6 pixels vspacing - wxFlexGridSizer* operTable = new wxFlexGridSizer(5, 2, 6, 12); - operTable->AddGrowableCol(1); + // 5x2 table with 12 pixel hspacing and 6 pixels vspacing + wxFlexGridSizer* operTable = new wxFlexGridSizer(5, 2, 6, 12); + operTable->AddGrowableCol(1); // Pack label & table into the dialog dialogVBox->AddSpacer(6); - dialogVBox->Add(operTable, 0, wxEXPAND); + dialogVBox->Add(operTable, 0, wxEXPAND); - // ------------------------ Fit Texture ----------------------------------- + // ------------------------ Fit Texture ----------------------------------- - wxBoxSizer* fitTextureHBox = createFitTextureRow(); - operTable->Add(_fitTexture.label, 0, wxALIGN_CENTER_VERTICAL); - operTable->Add(fitTextureHBox, 1, wxEXPAND); + wxBoxSizer* fitTextureHBox = createFitTextureRow(); + operTable->Add(_fitTexture.label, 0, wxALIGN_CENTER_VERTICAL); + operTable->Add(fitTextureHBox, 1, wxEXPAND); - // ------------------------ Align Texture ----------------------------------- + // ------------------------ Align Texture ----------------------------------- - _alignTexture.label = new wxStaticText(this, wxID_ANY, _(LABEL_ALIGN_TEXTURE)); + _alignTexture.label = new wxStaticText(this, wxID_ANY, _(LABEL_ALIGN_TEXTURE)); _alignTexture.top = wxutil::IconButton(this, "align_top.png"); _alignTexture.bottom = wxutil::IconButton(this, "align_bottom.png"); _alignTexture.left = wxutil::IconButton(this, "align_left.png"); _alignTexture.right = wxutil::IconButton(this, "align_right.png"); + _flipTexture.flipX = wxutil::IconButton(this, "flip_horiz.png"); + _flipTexture.flipY = wxutil::IconButton(this, "flip_vert.png"); + _alignTexture.top->SetMinSize(PixelSize(20, -1)); _alignTexture.bottom->SetMinSize(PixelSize(20, -1)); _alignTexture.left->SetMinSize(PixelSize(20, -1)); _alignTexture.right->SetMinSize(PixelSize(20, -1)); - auto* alignTextureBox = new wxGridSizer(1, 4, 0, 6); - - alignTextureBox->Add(_alignTexture.top, 1, wxEXPAND); - alignTextureBox->Add(_alignTexture.bottom, 1, wxEXPAND); - alignTextureBox->Add(_alignTexture.left, 1, wxEXPAND); - alignTextureBox->Add(_alignTexture.right, 1, wxEXPAND); - - operTable->Add(_alignTexture.label, 0, wxALIGN_CENTER_VERTICAL); - operTable->Add(alignTextureBox, 1, wxEXPAND); - - // ------------------------ Flip Texture ----------------------------------- - - _flipTexture.label = new wxStaticText(this, wxID_ANY, _(LABEL_FLIP_TEXTURE)); - - _flipTexture.flipX = new wxButton(this, wxID_ANY, _(LABEL_FLIPX)); - _flipTexture.flipY = new wxButton(this, wxID_ANY, _(LABEL_FLIPY)); - - wxGridSizer* flipTextureBox = new wxGridSizer(1, 2, 0, 6); - - flipTextureBox->Add(_flipTexture.flipX, 1, wxEXPAND); - flipTextureBox->Add(_flipTexture.flipY, 1, wxEXPAND); + auto* alignTextureBox = new wxGridSizer(1, 6, 0, 6); + alignTextureBox->Add(_alignTexture.top, 1, wxEXPAND); + alignTextureBox->Add(_alignTexture.bottom, 1, wxEXPAND); + alignTextureBox->Add(_alignTexture.left, 1, wxEXPAND); + alignTextureBox->Add(_alignTexture.right, 1, wxEXPAND); + alignTextureBox->Add(_flipTexture.flipX, 1, wxEXPAND); + alignTextureBox->Add(_flipTexture.flipY, 1, wxEXPAND); - operTable->Add(_flipTexture.label, 0, wxALIGN_CENTER_VERTICAL); - operTable->Add(flipTextureBox, 1, wxEXPAND); + operTable->Add(_alignTexture.label, 0, wxALIGN_CENTER_VERTICAL); + operTable->Add(alignTextureBox, 1, wxEXPAND); - // ------------------------ Modify Texture ----------------------------------- + // ------------------------ Modify Texture ----------------------------------- - _modifyTex.label = new wxStaticText(this, wxID_ANY, _(LABEL_MODIFY_TEXTURE)); + _modifyTex.label = new wxStaticText(this, wxID_ANY, _(LABEL_MODIFY_TEXTURE)); - _modifyTex.natural = new wxButton(this, wxID_ANY, _(LABEL_NATURAL)); - _modifyTex.normalise = new wxButton(this, wxID_ANY, _(LABEL_NORMALISE)); + _modifyTex.natural = new wxButton(this, wxID_ANY, _(LABEL_NATURAL)); + _modifyTex.normalise = new wxButton(this, wxID_ANY, _(LABEL_NORMALISE)); - wxGridSizer* modTextureBox = new wxGridSizer(1, 2, 0, 6); + wxGridSizer* modTextureBox = new wxGridSizer(1, 2, 0, 6); - modTextureBox->Add(_modifyTex.natural, 1, wxEXPAND); - modTextureBox->Add(_modifyTex.normalise, 1, wxEXPAND); + modTextureBox->Add(_modifyTex.natural, 1, wxEXPAND); + modTextureBox->Add(_modifyTex.normalise, 1, wxEXPAND); - operTable->Add(_modifyTex.label, 0, wxALIGN_CENTER_VERTICAL); - operTable->Add(modTextureBox, 1, wxEXPAND); + operTable->Add(_modifyTex.label, 0, wxALIGN_CENTER_VERTICAL); + operTable->Add(modTextureBox, 1, wxEXPAND); - // ------------------------ Default Scale ----------------------------------- + // ------------------------ Default Scale ----------------------------------- - wxStaticText* defaultScaleLabel = new wxStaticText(this, wxID_ANY, _(LABEL_DEFAULT_SCALE)); + wxStaticText* defaultScaleLabel = new wxStaticText(this, wxID_ANY, _(LABEL_DEFAULT_SCALE)); - _defaultTexScale = new wxSpinCtrlDouble(this, wxID_ANY); - _defaultTexScale->SetMinSize(wxSize(50, -1)); - _defaultTexScale->SetRange(0.0, 1000.0); - _defaultTexScale->SetIncrement(0.1); - _defaultTexScale->SetDigits(3); + _defaultTexScale = new wxSpinCtrlDouble(this, wxID_ANY); + _defaultTexScale->SetMinSize(wxSize(50, -1)); + _defaultTexScale->SetRange(0.0, 1000.0); + _defaultTexScale->SetIncrement(0.1); + _defaultTexScale->SetDigits(3); - // Texture Lock Toggle - _texLockButton = new wxToggleButton(this, wxID_ANY, _(LABEL_TEXTURE_LOCK)); + // Texture Lock Toggle + _texLockButton = new wxToggleButton(this, wxID_ANY, _(LABEL_TEXTURE_LOCK)); - wxGridSizer* defaultScaleBox = new wxGridSizer(1, 2, 0, 6); + wxGridSizer* defaultScaleBox = new wxGridSizer(1, 2, 0, 6); wxBoxSizer* texScaleSizer = new wxBoxSizer(wxHORIZONTAL); texScaleSizer->Add(_defaultTexScale, 1, wxALIGN_CENTER_VERTICAL); @@ -516,12 +505,12 @@ void SurfaceInspector::populateWindow() defaultScaleBox->Add(texScaleSizer, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL); defaultScaleBox->Add(_texLockButton, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL); - operTable->Add(defaultScaleLabel, 0, wxALIGN_CENTER_VERTICAL); - operTable->Add(defaultScaleBox, 1, wxEXPAND); + operTable->Add(defaultScaleLabel, 0, wxALIGN_CENTER_VERTICAL); + operTable->Add(defaultScaleBox, 1, wxEXPAND); wxBoxSizer* border = new wxBoxSizer(wxVERTICAL); border->Add(dialogVBox, 1, wxEXPAND | wxALL, 12); - SetSizerAndFit(border); + SetSizerAndFit(border); } SurfaceInspector::ManipulatorRow @@ -687,7 +676,6 @@ void SurfaceInspector::doUpdate() _alignTexture.label->Enable(haveSelection); // The flip texture widget sensitivity - _flipTexture.label->Enable(haveSelection); _flipTexture.flipX->Enable(haveSelection); _flipTexture.flipY->Enable(haveSelection); diff --git a/radiant/ui/surfaceinspector/SurfaceInspector.h b/radiant/ui/surfaceinspector/SurfaceInspector.h index eeeb2287bf..f9d42a4bb8 100644 --- a/radiant/ui/surfaceinspector/SurfaceInspector.h +++ b/radiant/ui/surfaceinspector/SurfaceInspector.h @@ -65,7 +65,6 @@ class SurfaceInspector : struct FlipTextureWidgets { - wxStaticText* label; wxButton* flipX; wxButton* flipY; } _flipTexture; diff --git a/resources/icons/flip_horiz.svg b/resources/icons/flip_horiz.svg new file mode 100644 index 0000000000..b4319b023a --- /dev/null +++ b/resources/icons/flip_horiz.svg @@ -0,0 +1,85 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/resources/icons/flip_vert.svg b/resources/icons/flip_vert.svg new file mode 100644 index 0000000000..f2a1534e41 --- /dev/null +++ b/resources/icons/flip_vert.svg @@ -0,0 +1,89 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + From 81df38802245740ce53671a7560584ecfc18cf79 Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Sat, 20 Apr 2024 14:45:44 +0100 Subject: [PATCH 22/49] Reduce space wastage in bottom half of Surface Inspector Replace "Texture Lock" text button with an icon button. Remove "Normalise" button and align "Natural" button and default scale spinbox on the same row. Remove left-hand column of labels which provides little information. --- install/bitmaps/texture_lock.png | Bin 246 -> 322 bytes .../ui/surfaceinspector/SurfaceInspector.cpp | 100 +++++--------- .../ui/surfaceinspector/SurfaceInspector.h | 4 - resources/icons/flip_vert.svg | 2 +- resources/icons/texture_lock.svg | 127 ++++++++++++++++++ 5 files changed, 160 insertions(+), 73 deletions(-) create mode 100644 resources/icons/texture_lock.svg diff --git a/install/bitmaps/texture_lock.png b/install/bitmaps/texture_lock.png index 9046a76fbddd9549e42a171a36fe8d1f0d27dfc2..b76c96e7a2adc1b11a66c339cacf97bc40756d44 100644 GIT binary patch delta 308 zcmV-40n7gO0m1^17k>~41^@s6AM^iV00009a7bBm000id000id0mpBsWB>pF8FWQh zbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H10MAK8K~y-6-I6;B!e9`DzfEj| zkk;HqqJ^d4;||&cwDJ@N^%y=t&`K2SY_07SZ(;tmNx(!jNPjxF+G1vynO&Lq3#GtV zMaTf9WC;#!`Gq*GEj1QJk2(+*{ zh4pzQS3oTj0xrTP65~Z;JkTxR+G>rn`1c60?FiEw?7Me>adq7<*^evG$aN6gCLse1 zkIh@WmcKeknk~)lS$PPZ58+&K7osYt84~ZkwH|TO^n@?FX z_ftn{YiegrU(h2Hvsq3(n>CjEnable(enabled); x->Enable(enabled); fitButton->Enable(enabled); preserveAspectButton->Enable(enabled); @@ -211,7 +202,6 @@ void SurfaceInspector::connectButtons() _alignTexture.right->Connect(wxEVT_BUTTON, wxCommandEventHandler(SurfaceInspector::onUpdateAfterButtonClick), NULL, this); _alignTexture.left->Connect(wxEVT_BUTTON, wxCommandEventHandler(SurfaceInspector::onUpdateAfterButtonClick), NULL, this); _modifyTex.natural->Connect(wxEVT_BUTTON, wxCommandEventHandler(SurfaceInspector::onUpdateAfterButtonClick), NULL, this); - _modifyTex.normalise->Connect(wxEVT_BUTTON, wxCommandEventHandler(SurfaceInspector::onUpdateAfterButtonClick), NULL, this); for (ManipulatorMap::iterator i = _manipulators.begin(); i != _manipulators.end(); ++i) { @@ -222,7 +212,6 @@ void SurfaceInspector::connectButtons() wxutil::button::connectToCommand(_flipTexture.flipX, "FlipTextureX"); wxutil::button::connectToCommand(_flipTexture.flipY, "FlipTextureY"); wxutil::button::connectToCommand(_modifyTex.natural, "TextureNatural"); - wxutil::button::connectToCommand(_modifyTex.normalise, "NormaliseTexture"); wxutil::button::connectToCommand(_alignTexture.top, "TexAlignTop"); wxutil::button::connectToCommand(_alignTexture.bottom, "TexAlignBottom"); @@ -304,7 +293,6 @@ wxBoxSizer* SurfaceInspector::createFitTextureRow() auto* fitTextureHBox = new wxBoxSizer(wxHORIZONTAL); // Create widgets from left to right - _fitTexture.label = new wxStaticText(this, wxID_ANY, _(LABEL_FIT_TEXTURE)); _fitTexture.width = makeFitSpinBox(Axis::X); _fitTexture.width->SetToolTip( _("Number of whole texture images to fit horizontally. Use the spin " @@ -340,8 +328,8 @@ wxBoxSizer* SurfaceInspector::createFitTextureRow() widthTimesHeight->Add(_fitTexture.height, 1, wxALIGN_CENTER_VERTICAL); fitTextureHBox->Add(widthTimesHeight, 1, wxALIGN_CENTER_VERTICAL); - fitTextureHBox->Add(_fitTexture.preserveAspectButton, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, 6); - fitTextureHBox->Add(_fitTexture.fitButton, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, 6); + fitTextureHBox->Add(_fitTexture.preserveAspectButton, 0, wxEXPAND | wxLEFT, 6); + fitTextureHBox->Add(_fitTexture.fitButton, 0, wxEXPAND | wxLEFT, 6); return fitTextureHBox; } @@ -428,8 +416,7 @@ void SurfaceInspector::populateWindow() // Setup the table with default spacings // 5x2 table with 12 pixel hspacing and 6 pixels vspacing - wxFlexGridSizer* operTable = new wxFlexGridSizer(5, 2, 6, 12); - operTable->AddGrowableCol(1); + wxBoxSizer* operTable = new wxBoxSizer(wxVERTICAL); // Pack label & table into the dialog dialogVBox->AddSpacer(6); @@ -438,26 +425,17 @@ void SurfaceInspector::populateWindow() // ------------------------ Fit Texture ----------------------------------- wxBoxSizer* fitTextureHBox = createFitTextureRow(); - operTable->Add(_fitTexture.label, 0, wxALIGN_CENTER_VERTICAL); - operTable->Add(fitTextureHBox, 1, wxEXPAND); + operTable->Add(fitTextureHBox, 0, wxEXPAND); // ------------------------ Align Texture ----------------------------------- - _alignTexture.label = new wxStaticText(this, wxID_ANY, _(LABEL_ALIGN_TEXTURE)); - _alignTexture.top = wxutil::IconButton(this, "align_top.png"); _alignTexture.bottom = wxutil::IconButton(this, "align_bottom.png"); _alignTexture.left = wxutil::IconButton(this, "align_left.png"); _alignTexture.right = wxutil::IconButton(this, "align_right.png"); - _flipTexture.flipX = wxutil::IconButton(this, "flip_horiz.png"); _flipTexture.flipY = wxutil::IconButton(this, "flip_vert.png"); - _alignTexture.top->SetMinSize(PixelSize(20, -1)); - _alignTexture.bottom->SetMinSize(PixelSize(20, -1)); - _alignTexture.left->SetMinSize(PixelSize(20, -1)); - _alignTexture.right->SetMinSize(PixelSize(20, -1)); - auto* alignTextureBox = new wxGridSizer(1, 6, 0, 6); alignTextureBox->Add(_alignTexture.top, 1, wxEXPAND); alignTextureBox->Add(_alignTexture.bottom, 1, wxEXPAND); @@ -466,48 +444,37 @@ void SurfaceInspector::populateWindow() alignTextureBox->Add(_flipTexture.flipX, 1, wxEXPAND); alignTextureBox->Add(_flipTexture.flipY, 1, wxEXPAND); - operTable->Add(_alignTexture.label, 0, wxALIGN_CENTER_VERTICAL); - operTable->Add(alignTextureBox, 1, wxEXPAND); - - // ------------------------ Modify Texture ----------------------------------- + operTable->Add(alignTextureBox, 0, wxEXPAND | wxTOP, 6); - _modifyTex.label = new wxStaticText(this, wxID_ANY, _(LABEL_MODIFY_TEXTURE)); + // Natural / Scale / Texture lock row + wxBoxSizer* modTextureBox = new wxBoxSizer(wxHORIZONTAL); _modifyTex.natural = new wxButton(this, wxID_ANY, _(LABEL_NATURAL)); - _modifyTex.normalise = new wxButton(this, wxID_ANY, _(LABEL_NORMALISE)); - - wxGridSizer* modTextureBox = new wxGridSizer(1, 2, 0, 6); - - modTextureBox->Add(_modifyTex.natural, 1, wxEXPAND); - modTextureBox->Add(_modifyTex.normalise, 1, wxEXPAND); - - operTable->Add(_modifyTex.label, 0, wxALIGN_CENTER_VERTICAL); - operTable->Add(modTextureBox, 1, wxEXPAND); - - // ------------------------ Default Scale ----------------------------------- - + _modifyTex.natural->SetToolTip(_(TT_NATURAL)); + modTextureBox->Add(_modifyTex.natural, 0, wxEXPAND); wxStaticText* defaultScaleLabel = new wxStaticText(this, wxID_ANY, _(LABEL_DEFAULT_SCALE)); + modTextureBox->Add(defaultScaleLabel, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 6); _defaultTexScale = new wxSpinCtrlDouble(this, wxID_ANY); - _defaultTexScale->SetMinSize(wxSize(50, -1)); + _defaultTexScale->SetToolTip(_(TT_DEFAULT_SCALE)); + _defaultTexScale->SetMinSize( + wxSize(_defaultTexScale->GetCharWidth() * SPINBOX_WIDTH_CHARS, -1) + ); _defaultTexScale->SetRange(0.0, 1000.0); _defaultTexScale->SetIncrement(0.1); _defaultTexScale->SetDigits(3); + modTextureBox->Add(_defaultTexScale, 1, wxLEFT, 6); - // Texture Lock Toggle - _texLockButton = new wxToggleButton(this, wxID_ANY, _(LABEL_TEXTURE_LOCK)); - - wxGridSizer* defaultScaleBox = new wxGridSizer(1, 2, 0, 6); - - wxBoxSizer* texScaleSizer = new wxBoxSizer(wxHORIZONTAL); - texScaleSizer->Add(_defaultTexScale, 1, wxALIGN_CENTER_VERTICAL); - - defaultScaleBox->Add(texScaleSizer, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL); - defaultScaleBox->Add(_texLockButton, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL); - - operTable->Add(defaultScaleLabel, 0, wxALIGN_CENTER_VERTICAL); - operTable->Add(defaultScaleBox, 1, wxEXPAND); + _texLockButton = new wxBitmapToggleButton( + this, wxID_ANY, wxutil::GetLocalBitmap("texture_lock.png") + ); + _texLockButton->SetToolTip( + _("Lock texture to face(s) when moving brush or patch (global setting)") + ); + modTextureBox->Add(_texLockButton, 0, wxLEFT | wxEXPAND, 6); + operTable->Add(modTextureBox, 0, wxEXPAND | wxTOP, 6); + // Top-level sizer to provide margin wxBoxSizer* border = new wxBoxSizer(wxVERTICAL); border->Add(dialogVBox, 1, wxEXPAND | wxALL, 12); SetSizerAndFit(border); @@ -673,16 +640,13 @@ void SurfaceInspector::doUpdate() _alignTexture.left->Enable(haveSelection); _alignTexture.right->Enable(haveSelection); _alignTexture.top->Enable(haveSelection); - _alignTexture.label->Enable(haveSelection); // The flip texture widget sensitivity _flipTexture.flipX->Enable(haveSelection); _flipTexture.flipY->Enable(haveSelection); // The natural/normalise widget sensitivity - _modifyTex.label->Enable(haveSelection); _modifyTex.natural->Enable(haveSelection); - _modifyTex.normalise->Enable(haveSelection); // Current shader name _shaderEntry->SetValue(selection::getShaderFromSelection()); diff --git a/radiant/ui/surfaceinspector/SurfaceInspector.h b/radiant/ui/surfaceinspector/SurfaceInspector.h index f9d42a4bb8..6751aa0e31 100644 --- a/radiant/ui/surfaceinspector/SurfaceInspector.h +++ b/radiant/ui/surfaceinspector/SurfaceInspector.h @@ -52,7 +52,6 @@ class SurfaceInspector : struct FitTextureWidgets { - wxStaticText* label; wxStaticText* x; wxButton* fitButton; wxToggleButton* preserveAspectButton; @@ -71,7 +70,6 @@ class SurfaceInspector : struct AlignTextureWidgets { - wxStaticText* label; wxButton* top; wxButton* bottom; wxButton* left; @@ -80,9 +78,7 @@ class SurfaceInspector : struct ModifyTextureWidgets { - wxStaticText* label; wxButton* natural; - wxButton* normalise; } _modifyTex; wxSpinCtrlDouble* _defaultTexScale; diff --git a/resources/icons/flip_vert.svg b/resources/icons/flip_vert.svg index f2a1534e41..9a3b6709a1 100644 --- a/resources/icons/flip_vert.svg +++ b/resources/icons/flip_vert.svg @@ -2,7 +2,7 @@ + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + From 2c5b6eb7154dfde188a11a902b081a76010b79b3 Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Sun, 21 Apr 2024 13:07:46 +0100 Subject: [PATCH 23/49] Reduce default floating size of the Surface Inspector The dialog has now shrunk sufficiently that the hard-coded default floating size in the UserInterfaceModule was leaving extra blank space at the bottom. --- include/ui/imainframe.h | 8 +++++- radiant/ui/UserInterfaceModule.cpp | 45 ++++++++++++++++++++++-------- 2 files changed, 41 insertions(+), 12 deletions(-) diff --git a/include/ui/imainframe.h b/include/ui/imainframe.h index 4474c456f5..3707c746b2 100644 --- a/include/ui/imainframe.h +++ b/include/ui/imainframe.h @@ -116,6 +116,12 @@ class IMainFrame : // Default control height when packed in a floating window int defaultFloatingHeight = 128; + + /// Convenience constructor for a floating window with a particular default size + static ControlSettings floating(int width, int height) + { + return {Location::FloatingWindow, false, width, height}; + } }; /** @@ -123,7 +129,7 @@ class IMainFrame : * where the control is added to and whether it's visible by default. * Persisted user settings might still overrule these default values. * - * The suitable point in time to call this method is when + * The suitable point in time to call this method is when * signal_MainFrameConstructed is invoked. This gives the mainframe * time to restore the layout as customised by the user. * diff --git a/radiant/ui/UserInterfaceModule.cpp b/radiant/ui/UserInterfaceModule.cpp index 703235414a..45c5a293ed 100644 --- a/radiant/ui/UserInterfaceModule.cpp +++ b/radiant/ui/UserInterfaceModule.cpp @@ -268,17 +268,40 @@ void UserInterfaceModule::initialiseModule(const IApplicationContext& ctx) GlobalMainFrame().signal_MainFrameConstructed().connect([&]() { // Set default locations of some controls - GlobalMainFrame().addControl(UserControl::SurfaceInspector, { IMainFrame::Location::FloatingWindow, false, 330, 480 }); - GlobalMainFrame().addControl(UserControl::LayerControlPanel, { IMainFrame::Location::FloatingWindow, false, 180, 300 }); - GlobalMainFrame().addControl(UserControl::TextureTool, { IMainFrame::Location::FloatingWindow, false, 600, 400 }); - GlobalMainFrame().addControl(UserControl::PatchInspector, { IMainFrame::Location::FloatingWindow, false, 280, 480 }); - GlobalMainFrame().addControl(UserControl::LightInspector, { IMainFrame::Location::FloatingWindow, false, 780, 420 }); - GlobalMainFrame().addControl(UserControl::TransformPanel, { IMainFrame::Location::FloatingWindow, false, 260, 310 }); - GlobalMainFrame().addControl(UserControl::MapMergePanel, { IMainFrame::Location::FloatingWindow, false, 380, 440 }); - GlobalMainFrame().addControl(UserControl::EntityList, { IMainFrame::Location::FloatingWindow, false, 250, 400 }); - GlobalMainFrame().addControl(UserControl::AasVisualisationPanel, { IMainFrame::Location::FloatingWindow, false, 200, 200 }); - GlobalMainFrame().addControl(UserControl::FindAndReplaceMaterial, { IMainFrame::Location::FloatingWindow, false, 350, 200 }); - GlobalMainFrame().addControl(UserControl::OrthoBackgroundPanel, { IMainFrame::Location::FloatingWindow, false, 480, 350 }); + using ControlSettings = IMainFrame::ControlSettings; + GlobalMainFrame().addControl( + UserControl::SurfaceInspector, ControlSettings::floating(330, 400) + ); + GlobalMainFrame().addControl( + UserControl::LayerControlPanel, ControlSettings::floating(180, 300) + ); + GlobalMainFrame().addControl( + UserControl::TextureTool, ControlSettings::floating(600, 400) + ); + GlobalMainFrame().addControl( + UserControl::PatchInspector, ControlSettings::floating(280, 480) + ); + GlobalMainFrame().addControl( + UserControl::LightInspector, ControlSettings::floating(780, 420) + ); + GlobalMainFrame().addControl( + UserControl::TransformPanel, ControlSettings::floating(260, 310) + ); + GlobalMainFrame().addControl( + UserControl::MapMergePanel, ControlSettings::floating(380, 440) + ); + GlobalMainFrame().addControl( + UserControl::EntityList, ControlSettings::floating(250, 400) + ); + GlobalMainFrame().addControl( + UserControl::AasVisualisationPanel, ControlSettings::floating(200, 200) + ); + GlobalMainFrame().addControl( + UserControl::FindAndReplaceMaterial, ControlSettings::floating(350, 200) + ); + GlobalMainFrame().addControl( + UserControl::OrthoBackgroundPanel, ControlSettings::floating(480, 350) + ); _viewMenu = std::make_unique(); }); From b15b234d63f61bb9b778ac6754278810bc22a5e5 Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Sun, 21 Apr 2024 13:22:33 +0100 Subject: [PATCH 24/49] Filter dialog respects minimum widget sizes Pack the loaded wxPanel into a wxSizer to provide proper size hint handling. Also remove redundant title label and several pixels of indentation. --- install/ui/filterdialog.fbp | 101 +++++---------------- install/ui/filterdialog.xrc | 16 ++-- radiant/ui/filters/editor/FilterDialog.cpp | 35 +++---- radiant/ui/filters/editor/FilterDialog.h | 9 +- 4 files changed, 53 insertions(+), 108 deletions(-) diff --git a/install/ui/filterdialog.fbp b/install/ui/filterdialog.fbp index 6aa1551695..92d98eaa14 100644 --- a/install/ui/filterdialog.fbp +++ b/install/ui/filterdialog.fbp @@ -14,6 +14,7 @@ filterdialog 1000 none + 0 MyProject2 @@ -25,6 +26,7 @@ 1 1 UI + 0 0 0 @@ -46,6 +48,7 @@ 500,323 + 0 wxTAB_TRAVERSAL @@ -54,67 +57,6 @@ bSizer5 wxVERTICAL none - - 12 - wxLEFT|wxRIGHT|wxTOP - 0 - - 1 - 1 - 1 - 1 - - - - - - - - 1 - 0 - 1 - - 1 - 0 - Dock - 0 - Left - 1 - - 1 - - 0 - 0 - wxID_ANY - Filters - 0 - - 0 - - - 0 - - 1 - FilterDialogTopLabel - 1 - - - protected - 1 - - Resizable - 1 - - - - 0 - - - - - -1 - - 12 wxEXPAND|wxLEFT|wxRIGHT|wxTOP @@ -124,11 +66,11 @@ bSizer6 wxHORIZONTAL none - + 6 wxEXPAND|wxRIGHT 1 - + 1 1 1 @@ -179,7 +121,7 @@ wxTAB_TRAVERSAL - + bSizer9 wxVERTICAL @@ -187,20 +129,20 @@ - + 12 0 - + ButtonSizer wxVERTICAL none - + 6 wxBOTTOM|wxEXPAND 0 - + 1 1 1 @@ -209,6 +151,7 @@ + 0 @@ -268,11 +211,11 @@ - + 6 wxBOTTOM|wxEXPAND 0 - + 1 1 1 @@ -281,6 +224,7 @@ + 0 @@ -340,11 +284,11 @@ - + 6 wxBOTTOM|wxEXPAND 0 - + 1 1 1 @@ -353,6 +297,7 @@ + 0 @@ -412,11 +357,11 @@ - + 6 wxBOTTOM|wxEXPAND 0 - + 1 1 1 @@ -425,6 +370,7 @@ + 0 @@ -484,11 +430,11 @@ - + 6 wxEXPAND 0 - + 1 1 1 @@ -497,6 +443,7 @@ + 0 @@ -582,6 +529,7 @@ + 0 @@ -654,6 +602,7 @@ + 0 diff --git a/install/ui/filterdialog.xrc b/install/ui/filterdialog.xrc index ad086d3813..34195f5db1 100644 --- a/install/ui/filterdialog.xrc +++ b/install/ui/filterdialog.xrc @@ -5,15 +5,6 @@ 500,323 wxVERTICAL - - - wxLEFT|wxRIGHT|wxTOP - 12 - - - -1 - - wxEXPAND|wxLEFT|wxRIGHT|wxTOP @@ -44,6 +35,7 @@ 0 + 0 0 @@ -55,6 +47,7 @@ 0 + 0 0 @@ -66,6 +59,7 @@ 0 + 0 0 @@ -77,6 +71,7 @@ 0 + 0 0 @@ -88,6 +83,7 @@ 0 + 0 0 @@ -109,6 +105,7 @@ 0 + 0 0 @@ -120,6 +117,7 @@ 0 + 0 0 diff --git a/radiant/ui/filters/editor/FilterDialog.cpp b/radiant/ui/filters/editor/FilterDialog.cpp index f8b53b0559..8d03b0bdc7 100644 --- a/radiant/ui/filters/editor/FilterDialog.cpp +++ b/radiant/ui/filters/editor/FilterDialog.cpp @@ -24,8 +24,8 @@ namespace { WIDGET_ADD_FILTER_BUTTON, WIDGET_EDIT_FILTER_BUTTON, - WIDGET_VIEW_FILTER_BUTTON, - WIDGET_COPY_FILTER_BUTTON, + WIDGET_VIEW_FILTER_BUTTON, + WIDGET_COPY_FILTER_BUTTON, WIDGET_DELETE_FILTER_BUTTON, }; } @@ -142,19 +142,20 @@ void FilterDialog::update() void FilterDialog::populateWindow() { - loadNamedPanel(this, "FilterDialogMainPanel"); + wxPanel* panel = loadNamedPanel(this, "FilterDialogMainPanel"); - wxStaticText* label = findNamedObject(this, "FilterDialogTopLabel"); - label->SetFont(label->GetFont().Bold()); + // Pack the treeview into the main window's vbox + createFiltersPanel(); - // Pack the treeview into the main window's vbox - createFiltersPanel(); + wxButton* okButton = findNamedObject(this, "FilterDialogOkButton"); + wxButton* cancelButton = findNamedObject(this, "FilterDialogCancelButton"); - wxButton* okButton = findNamedObject(this, "FilterDialogOkButton"); - wxButton* cancelButton = findNamedObject(this, "FilterDialogCancelButton"); - - okButton->Connect(wxEVT_BUTTON, wxCommandEventHandler(FilterDialog::onSave), NULL, this); - cancelButton->Connect(wxEVT_BUTTON, wxCommandEventHandler(FilterDialog::onCancel), NULL, this); + okButton->Connect(wxEVT_BUTTON, wxCommandEventHandler(FilterDialog::onSave), NULL, this); + cancelButton->Connect(wxEVT_BUTTON, wxCommandEventHandler(FilterDialog::onCancel), NULL, this); + + wxSizer* mainSizer = new wxBoxSizer(wxVERTICAL); + mainSizer->Add(panel, 1, wxEXPAND, 0); + SetSizerAndFit(mainSizer); } void FilterDialog::createFiltersPanel() @@ -164,16 +165,16 @@ void FilterDialog::createFiltersPanel() // Create a new treeview _filterView = wxutil::TreeView::CreateWithModel(parent, _filterStore.get()); - _filterView->Connect(wxEVT_DATAVIEW_SELECTION_CHANGED, + _filterView->Connect(wxEVT_DATAVIEW_SELECTION_CHANGED, wxDataViewEventHandler(FilterDialog::onFilterSelectionChanged), NULL, this); - parent->GetSizer()->Add(_filterView, 1, wxEXPAND | wxLEFT, 12); + parent->GetSizer()->Add(_filterView, 1, wxEXPAND, 0); // Display name column with icon - _filterView->AppendTextColumn(_("Name"), _columns.name.getColumnIndex(), + _filterView->AppendTextColumn(_("Name"), _columns.name.getColumnIndex(), wxDATAVIEW_CELL_INERT, wxCOL_WIDTH_AUTOSIZE, wxALIGN_NOT, wxDATAVIEW_COL_SORTABLE); - _filterView->AppendTextColumn(_("State"), _columns.state.getColumnIndex(), + _filterView->AppendTextColumn(_("State"), _columns.state.getColumnIndex(), wxDATAVIEW_CELL_INERT, wxCOL_WIDTH_AUTOSIZE, wxALIGN_NOT, wxDATAVIEW_COL_SORTABLE); // Action buttons @@ -203,7 +204,7 @@ void FilterDialog::updateWidgetSensitivity() _buttons[WIDGET_VIEW_FILTER_BUTTON]->Show(i->second->readOnly); _buttons[WIDGET_EDIT_FILTER_BUTTON]->GetContainingSizer()->Layout(); - + _buttons[WIDGET_DELETE_FILTER_BUTTON]->Enable(!i->second->readOnly); _buttons[WIDGET_EDIT_FILTER_BUTTON]->Enable(!i->second->readOnly); _buttons[WIDGET_VIEW_FILTER_BUTTON]->Enable(i->second->readOnly); diff --git a/radiant/ui/filters/editor/FilterDialog.h b/radiant/ui/filters/editor/FilterDialog.h index 10a46d3e5c..c5d9fbfe80 100644 --- a/radiant/ui/filters/editor/FilterDialog.h +++ b/radiant/ui/filters/editor/FilterDialog.h @@ -12,14 +12,11 @@ class wxButton; namespace ui { -class FilterDialog : - public wxutil::DialogBase, - private wxutil::XmlResourceBasedWidget +/// Dialog for editing filters +class FilterDialog: public wxutil::DialogBase, private wxutil::XmlResourceBasedWidget { -private: // Treemodel definition - struct TreeColumns : - public wxutil::TreeModel::ColumnRecord + struct TreeColumns: public wxutil::TreeModel::ColumnRecord { TreeColumns() : name(add(wxutil::TreeModel::Column::String)), From 09e5ec1cae16b8350097fc97839de64cf96c4e88 Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Sun, 21 Apr 2024 20:33:14 +0100 Subject: [PATCH 25/49] Avoid creating speaker min/max spawnargs when moving a speaker Instead of unconditionally creating min/max distance spawnargs in SpeakerNode::freezeTransform, we now write them under two different conditions: if the modified speaker radii differ from the shader default, or if there were already min/max spawnargs to begin with. This prevents creating redundant spawnargs just because a speaker was moved, but also will not remove existing spawnargs just because they happen to match the shader default (which was the original complaint in #6062). --- radiantcore/entity/speaker/SpeakerNode.cpp | 32 +++++++++++++++------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/radiantcore/entity/speaker/SpeakerNode.cpp b/radiantcore/entity/speaker/SpeakerNode.cpp index bbdbcb2483..4aa8527dde 100644 --- a/radiantcore/entity/speaker/SpeakerNode.cpp +++ b/radiantcore/entity/speaker/SpeakerNode.cpp @@ -391,18 +391,30 @@ void SpeakerNode::revertTransform() void SpeakerNode::freezeTransform() { - m_originKey.set(m_origin); - m_originKey.write(_spawnArgs); + m_originKey.set(m_origin); + m_originKey.write(_spawnArgs); - _radii = _radiiTransformed; + _radii = _radiiTransformed; - // Write the s_mindistance/s_maxdistance keyvalues if we have a valid shader - if (!_spawnArgs.getKeyValue(KEY_S_SHADER).empty()) - { - // Note: Write the spawnargs in meters - _spawnArgs.setKeyValue(KEY_S_MAXDISTANCE, string::to_string(_radii.getMax(true))); - _spawnArgs.setKeyValue(KEY_S_MINDISTANCE, string::to_string(_radii.getMin(true))); - } + // Write the s_mindistance/s_maxdistance keyvalues if there is a valid shader and EITHER + // the radii are different from the shader default, OR there were already min/max + // keyvalues to begin with. This means we will not create or remove spawnargs when + // moving a speaker. + if (!_spawnArgs.getKeyValue(KEY_S_SHADER).empty()) + { + // Note: Write the spawnargs in meters + if (_radii.getMin(true) != _defaultRadii.getMin(true) + || !_spawnArgs.getKeyValue(KEY_S_MINDISTANCE).empty()) + { + _spawnArgs.setKeyValue(KEY_S_MINDISTANCE, string::to_string(_radii.getMin(true))); + } + + if (_radii.getMax(true) != _defaultRadii.getMax(true) + || !_spawnArgs.getKeyValue(KEY_S_MAXDISTANCE).empty()) + { + _spawnArgs.setKeyValue(KEY_S_MAXDISTANCE, string::to_string(_radii.getMax(true))); + } + } } void SpeakerNode::_onTransformationChanged() From debc715242b0c2df8b81d35d55313df38c13a255 Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Wed, 24 Apr 2024 19:29:33 +0100 Subject: [PATCH 26/49] #6514: minor layout tweaks in Skin Editor Remove low-information static labels; shorten button labels and set correct stock wxIDs (although I'm not sure if the IDs do anything here). --- install/ui/skineditor.fbp | 142 +++------------------------------ install/ui/skineditor.xrc | 34 ++------ radiant/ui/skin/SkinEditor.cpp | 10 --- 3 files changed, 18 insertions(+), 168 deletions(-) diff --git a/install/ui/skineditor.fbp b/install/ui/skineditor.fbp index c331b6c087..a4b6fc72c5 100644 --- a/install/ui/skineditor.fbp +++ b/install/ui/skineditor.fbp @@ -193,67 +193,6 @@ bSizer21 wxVERTICAL none - - 6 - wxBOTTOM - 0 - - 1 - 1 - 1 - 1 - - - - - - - - 1 - 0 - 1 - - 1 - 0 - Dock - 0 - Left - 1 - - 1 - - 0 - 0 - wxID_ANY - Skin Definitions - 0 - - 0 - - - 0 - - 1 - SkinEditorSkinDefinitionsLabel - 1 - - - protected - 1 - - Resizable - 1 - - - - 0 - - - - - -1 - - 12 wxEXPAND|wxLEFT @@ -389,7 +328,7 @@ 0 0 wxID_NEW - New Skin + New 0 @@ -462,7 +401,7 @@ 0 0 wxID_COPY - Copy Skin + Copy 0 @@ -499,7 +438,7 @@ 6 - wxTOP + wxEXPAND|wxTOP 0 1 @@ -534,8 +473,8 @@ 0 0 - wxID_COPY - Discard Changes + wxID_REVERT + Revert 0 @@ -608,7 +547,7 @@ 0 0 wxID_SAVE - Save Skin + Save 0 @@ -680,8 +619,8 @@ 0 0 - wxID_SAVE - Delete Skin + wxID_DELETE + Delete 0 @@ -735,67 +674,6 @@ bSizer130 wxVERTICAL none - - 6 - wxBOTTOM|wxTOP - 0 - - 1 - 1 - 1 - 1 - - - - - - - - 1 - 0 - 1 - - 1 - 0 - Dock - 0 - Left - 1 - - 1 - - 0 - 0 - wxID_ANY - Edit Skin Definition - 0 - - 0 - - - 0 - - 1 - SkinEditorEditSkinDefinitionLabel - 1 - - - protected - 1 - - Resizable - 1 - - - ; ; forward_declare - 0 - - - - - -1 - - 6 wxEXPAND|wxTOP @@ -837,7 +715,7 @@ 0 0 wxID_ANY - Skin Name: + Skin: 0 0 @@ -868,7 +746,7 @@ 6 - wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT + wxALIGN_CENTER_VERTICAL|wxEXPAND|wxLEFT|wxRIGHT 1 1 diff --git a/install/ui/skineditor.xrc b/install/ui/skineditor.xrc index 48c13c87c7..9c57b3cc68 100644 --- a/install/ui/skineditor.xrc +++ b/install/ui/skineditor.xrc @@ -32,15 +32,6 @@ 0 wxVERTICAL - - - wxBOTTOM - 6 - - - -1 - - wxEXPAND|wxLEFT @@ -81,7 +72,7 @@ wxALL|wxEXPAND 0 - + 0 0 0 @@ -93,7 +84,7 @@ wxEXPAND|wxTOP 6 - + 0 0 0 @@ -102,10 +93,10 @@ - wxTOP + wxEXPAND|wxTOP 6 - + 0 0 0 @@ -117,7 +108,7 @@ wxEXPAND|wxTOP 6 - + 0 0 0 @@ -129,7 +120,7 @@ wxEXPAND|wxTOP 6 - + 0 0 0 @@ -152,15 +143,6 @@ 12 wxVERTICAL - - - wxBOTTOM|wxTOP - 6 - - - -1 - - wxEXPAND|wxTOP @@ -172,13 +154,13 @@ wxALIGN_CENTER_VERTICAL 12 - + -1 - wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT + wxALIGN_CENTER_VERTICAL|wxEXPAND|wxLEFT|wxRIGHT 6 diff --git a/radiant/ui/skin/SkinEditor.cpp b/radiant/ui/skin/SkinEditor.cpp index 918ba51b8a..4db1b1cf30 100644 --- a/radiant/ui/skin/SkinEditor.cpp +++ b/radiant/ui/skin/SkinEditor.cpp @@ -48,8 +48,6 @@ SkinEditor::SkinEditor() : { wxPanel* mainPanel = loadNamedPanel(this, "SkinEditorMainPanel"); - makeLabelBold(this, "SkinEditorSkinDefinitionsLabel"); - makeLabelBold(this, "SkinEditorEditSkinDefinitionLabel"); makeLabelBold(this, "SkinEditorDeclarationSourceLabel"); setupModelTreeView(); @@ -142,11 +140,6 @@ void SkinEditor::setupSkinTreeView() auto* treeToolbar = new wxutil::ResourceTreeViewToolbar(panel, _skinTreeView); treeToolbar->EnableFavouriteManagement(false); - auto definitionLabel = getControl("SkinEditorSkinDefinitionsLabel"); - definitionLabel->GetContainingSizer()->Detach(definitionLabel); - definitionLabel->Reparent(treeToolbar); - treeToolbar->GetLeftSizer()->Add(definitionLabel, 0, wxALIGN_LEFT); - panel->GetSizer()->Add(treeToolbar, 0, wxEXPAND | wxBOTTOM, 6); panel->GetSizer()->Add(_skinTreeView, 1, wxEXPAND); @@ -354,9 +347,6 @@ void SkinEditor::updateSkinControlsFromSelection() // Enable/disable notebook tabs independently to allow page switching getControl("SkinEditorTargetModels")->Enable(skinCanBeModified); getControl("SkinEditorRemappings")->Enable(skinCanBeModified); - - getControl("SkinEditorEditSkinDefinitionLabel")->Enable(skinCanBeModified); - getControl("SkinEditorSkinNameLabel")->Enable(skinCanBeModified); getControl("SkinEditorSkinName")->Enable(skinCanBeModified); updateSourceView(skin); From b67318f52c69eea4ada3f8f287d5afc2e45e2253 Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Wed, 24 Apr 2024 19:48:46 +0100 Subject: [PATCH 27/49] #6514: Skin Name text control only commits changes on ENTER The previous event was emitted on every character change, which was far too frequent and led to performance problems and strange behaviour. By setting the PROCESS_ENTER style flag we can connect instead to an event which is only emitted when the ENTER key is pressed, which is the more usual behaviour when editing the name of something in a text field. --- install/ui/skineditor.fbp | 2 +- install/ui/skineditor.xrc | 1 + radiant/ui/skin/SkinEditor.cpp | 4 +++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/install/ui/skineditor.fbp b/install/ui/skineditor.fbp index a4b6fc72c5..50293fb837 100644 --- a/install/ui/skineditor.fbp +++ b/install/ui/skineditor.fbp @@ -794,7 +794,7 @@ Resizable 1 - + wxTE_PROCESS_ENTER ; ; forward_declare 0 diff --git a/install/ui/skineditor.xrc b/install/ui/skineditor.xrc index 9c57b3cc68..003d2a5ed1 100644 --- a/install/ui/skineditor.xrc +++ b/install/ui/skineditor.xrc @@ -163,6 +163,7 @@ wxALIGN_CENTER_VERTICAL|wxEXPAND|wxLEFT|wxRIGHT 6 + diff --git a/radiant/ui/skin/SkinEditor.cpp b/radiant/ui/skin/SkinEditor.cpp index 4db1b1cf30..9d61840138 100644 --- a/radiant/ui/skin/SkinEditor.cpp +++ b/radiant/ui/skin/SkinEditor.cpp @@ -57,7 +57,9 @@ SkinEditor::SkinEditor() : setupPreview(); getControl("SkinEditorCloseButton")->Bind(wxEVT_BUTTON, &SkinEditor::onCloseButton, this); - getControl("SkinEditorSkinName")->Bind(wxEVT_TEXT, &SkinEditor::onSkinNameChanged, this); + getControl("SkinEditorSkinName")->Bind( + wxEVT_TEXT_ENTER, &SkinEditor::onSkinNameChanged, this + ); getControl("SkinEditorNotebook")->Bind(wxEVT_NOTEBOOK_PAGE_CHANGED, [&](auto& ev) { From ba312b37962803b2855fd6e7afa2b8edcad6a991 Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Sun, 5 May 2024 20:57:15 +0100 Subject: [PATCH 28/49] Remove extraneous asterisks from background image manual section --- doc/manual.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/manual.adoc b/doc/manual.adoc index 5e9d262a60..7943c96f03 100644 --- a/doc/manual.adoc +++ b/doc/manual.adoc @@ -277,10 +277,10 @@ background image configuration dialog. *Use background image*:: Master toggle to show or hide the background image. *Image file*:: Click on the widget to show a file chooser which allows you to find and -*select the background image to show. +select the background image to show. *Opacity*:: Drag the slider to control the opacity of the image, from fully transparent -*(invisible) to fully opaque. +(invisible) to fully opaque. *Scale*:: Adjusts the size of the image. From 00f1140ae7be9a2428dccebae636c580cd1fc8f7 Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Wed, 8 May 2024 19:20:58 +0100 Subject: [PATCH 29/49] Update Surface Inspector screenshot in manual --- doc/img/SurfaceInspector.png | Bin 63777 -> 37110 bytes doc/manual.adoc | 8 ++++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/img/SurfaceInspector.png b/doc/img/SurfaceInspector.png index ee78968282c647dff8d9331d3b2e270ecf56924c..c236b84be692f065cec7701530dae7e0234f2dd8 100644 GIT binary patch literal 37110 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 ir-Bpp1kz*fbG&3$q-`KPe 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%0AmrKkMU=KYk)xO=MFp`4Gj=THLz<6?uzqYoXG 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 bSzddd=BVZ>21N_yXT} Date: Tue, 21 May 2024 19:51:09 +0100 Subject: [PATCH 30/49] Fix RGTC normal maps showing as black in lighting preview mode Although we were correctly loading RGTC normal maps into the appropriate destination format, there was no code in the shader to actually handle the missing Z value, resulting in RGTC normal maps showing as entirely black. The Z value is now recovered for all normal maps (because it's much easier than having to implement format-specific shader behaviour, and should be fairly quick), using a simple application of Pythagoras. I can't prove that this is 100% mathematically correct but it seems to give realistic results, and is a lot better than black. --- install/gl/interaction_fp.glsl | 36 ++++++++++++------- .../rendersystem/backend/GLProgramFactory.cpp | 11 +++--- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/install/gl/interaction_fp.glsl b/install/gl/interaction_fp.glsl index b4eafe1917..e675843127 100644 --- a/install/gl/interaction_fp.glsl +++ b/install/gl/interaction_fp.glsl @@ -92,6 +92,15 @@ float getDepthValueForVector(in sampler2D shadowMapTexture, vec4 shadowRect, vec return 1 / (1 - d); } +// Restore the Z coordinate of a normal vector for which only X and Y were specified +// (assuming the original was unit length). +void recoverZ(inout vec4 v) +{ + // x^2 + y^2 + z^2 = 1.0 + // z^2 = 1.0 - x^2 - y^2 + v.z = sqrt(1.0 - v.x * v.x - v.y * v.y); +} + void main() { vec3 totalColor; @@ -99,7 +108,11 @@ void main() // Perform the texture lookups vec4 diffuse = texture2D(u_Diffusemap, var_TexDiffuse); vec3 specular = texture2D(u_Specularmap, var_TexSpecular).rgb; - vec4 bumpTexel = texture2D(u_Bumpmap, var_TexBump) * 2. - 1.; + + // Normal map colours from [0, 255] map to [0.0, 1.0], so remap to [-1.0, 1.0] in each + // dimension. Then recover the Z value which may not be in the texture (if it was RGTC). + vec4 bumpTexel = texture2D(u_Bumpmap, var_TexBump) * 2.0 - 1.0; + recoverZ(bumpTexel); // Light texture lookups vec3 attenuation_xy = vec3(0,0,0); @@ -120,28 +133,28 @@ void main() // compute view direction in tangent space vec3 localV = normalize(var_mat_os2ts * (u_LocalViewOrigin - var_vertex)); - + // compute light direction in tangent space vec3 localL = normalize(var_mat_os2ts * (u_LocalLightOrigin - var_vertex)); - + vec3 RawN = normalize(bumpTexel.xyz); vec3 N = var_mat_os2ts * RawN; - + //must be done in tangent space, otherwise smoothing will suffer (see #4958) float NdotL = clamp(dot(RawN, localL), 0.0, 1.0); float NdotV = clamp(dot(RawN, localV), 0.0, 1.0); float NdotH = clamp(dot(RawN, normalize(localV + localL)), 0.0, 1.0); - + // fresnel part float fresnelTerm = pow(1.0 - NdotV, fresnelParms2.w); float rimLight = fresnelTerm * clamp(NdotL - 0.3, 0.0, fresnelParms.z) * lightParms.y; float specularPower = mix(lightParms.z, lightParms.w, specular.z); float specularCoeff = pow(NdotH, specularPower) * fresnelParms2.z; float fresnelCoeff = fresnelTerm * fresnelParms.y + fresnelParms2.y; - + vec3 specularColor = specularCoeff * fresnelCoeff * specular * (diffuse.rgb * 0.25 + vec3(0.75)); float R2f = clamp(localL.z * 4.0, 0.0, 1.0); - + float NdotL_adjusted = NdotL; float light = rimLight * R2f + NdotL_adjusted; @@ -177,10 +190,10 @@ void main() vec3 localNormal = vec3(bumpTexel.x, bumpTexel.y, sqrt(max(1. - bumpTexel.x*bumpTexel.x - bumpTexel.y*bumpTexel.y, 0))); vec3 N = normalize(var_mat_os2ts * localNormal); - + vec3 light1 = vec3(.5); // directionless half light1 += max(dot(N, u_WorldUpLocal) * (1. - specular) * .5, 0); - + // Calculate specularity vec3 nViewDir = normalize(var_LocalViewerDirection); vec3 reflect = - (nViewDir - 2 * N * dot(N, nViewDir)); @@ -188,18 +201,17 @@ void main() float spec = max(dot(reflect, u_WorldUpLocal), 0); float specPow = clamp((spec * spec), 0.0, 1.1); light1 += vec3(spec * specPow * specPow) * specular * 1.0; - + // Apply the light's colour (with light scale) and the vertex colour light1.rgb *= (u_LightColour * u_LightScale) * var_Colour.rgb; light.rgb *= diffuse.rgb * light1; light = max(light, vec4(0)); // avoid negative values, which with floating point render buffers can lead to NaN artefacts - + totalColor = light.rgb; } gl_FragColor.rgb = totalColor; gl_FragColor.a = diffuse.a; } - diff --git a/radiantcore/rendersystem/backend/GLProgramFactory.cpp b/radiantcore/rendersystem/backend/GLProgramFactory.cpp index 8dc382c607..2368b2cf7f 100644 --- a/radiantcore/rendersystem/backend/GLProgramFactory.cpp +++ b/radiantcore/rendersystem/backend/GLProgramFactory.cpp @@ -131,12 +131,13 @@ void assertShaderCompiled(GLuint shader, const std::string& filename) std::vector logBuf(logLength + 1, 0); glGetShaderInfoLog(shader, static_cast(logBuf.size()), NULL, &logBuf.front()); - // Convert to string and throw exception + // Convert to string and throw exception. Also output the string to console because + // we can't always be sure that the exception message will be displayed in a useful + // way. std::string logStr = std::string(&logBuf.front()); - throw std::runtime_error( - "Failed to compile GLSL shader \"" + filename + "\":\n" - + logStr - ); + std::string errStr = "Failed to compile GLSL shader \"" + filename + "\":\n" + logStr; + std::cerr << errStr << std::endl; + throw std::runtime_error(errStr); } } From 47bad8bd40aaa03c941fe390e3d93c9ef4c0da34 Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Wed, 22 May 2024 19:44:27 +0100 Subject: [PATCH 31/49] Fix some IDE code parsing errors --- radiant/camera/CameraSettings.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/radiant/camera/CameraSettings.cpp b/radiant/camera/CameraSettings.cpp index 5300dc3d31..483ce4ec69 100644 --- a/radiant/camera/CameraSettings.cpp +++ b/radiant/camera/CameraSettings.cpp @@ -2,6 +2,7 @@ #include "i18n.h" #include "ipreferencesystem.h" +#include "irender.h" #include "registry/registry.h" #include "util/ScopedBoolLock.h" From a830b03e3412a20d357ea7bd7354b988d04a00bb Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Sat, 25 May 2024 13:14:18 +0100 Subject: [PATCH 32/49] Update build scripts for 3.9.1 --- CMakeLists.txt | 2 +- debian/changelog | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bf1ecbfe36..4b2fff4b14 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.12) # Project name and version -project(darkradiant VERSION 3.9.0) +project(darkradiant VERSION 3.9.1) # C++ standard set(CMAKE_CXX_STANDARD 17) diff --git a/debian/changelog b/debian/changelog index 50ce100d77..6a36fd10c5 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,14 @@ +darkradiant (3.9.1~jammy1) jammy; urgency=medium + + * Textures with RGTC normal maps no longer show as black in lighting preview + mode. + * Skin editor no longer crashes when editing the skin name. + * Reduced space wastage in Surface Editor. + * Several dialogs which could previously be shrunk to zero size no longer do + so. + + -- Matthew Mott Sat, 25 May 2024 13:10:46 +0100 + darkradiant (3.9.0~jammy1) jammy; urgency=medium * Add "Show definition" button for the "inherit" spawnarg. From 004e409502147603e56643101ec963d3489a525c Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Mon, 27 May 2024 13:14:37 +0100 Subject: [PATCH 33/49] Redirect wx assertions to console on Linux --- radiant/RadiantApp.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/radiant/RadiantApp.cpp b/radiant/RadiantApp.cpp index 4b63d43181..25dd99bb9c 100644 --- a/radiant/RadiantApp.cpp +++ b/radiant/RadiantApp.cpp @@ -40,6 +40,16 @@ log_black_hole(GLogLevelFlags, const GLogField*, gsize, gpointer) { return G_LOG_WRITER_HANDLED; } + +// wxWidgets assertion handler +void assertToConsole( + const wxString& file, int line, const wxString& func, const wxString& cond, + const wxString& msg +) +{ + std::cerr << "wxASSERT: " << file << ":" << line << ":" << func << ": [" << cond + << "]: " << msg; +} #endif // The startup event which will be queued in App::OnInit() @@ -117,6 +127,10 @@ bool RadiantApp::OnInit() // output for debugging (due to fresh Gtk-CRITICAL messages being emitted // several times per second) g_log_set_writer_func(log_black_hole, nullptr, nullptr); + + // Avoid assertions from wxWidgets itself (particularly stuff to do with art provider + // destruction, which we have no control over). + wxSetAssertHandler(assertToConsole); #endif // Initialise the context (application path / settings path, is From 666ccbc9a80c46e04cd8d95482affffafe5efd0a Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Mon, 27 May 2024 13:14:48 +0100 Subject: [PATCH 34/49] Prevent exceptions escaping from Cam_Draw() Since this call happens in a wxWidgets event, uncaught exceptions (e.g. due to a shader error) will cause the program to terminate. --- radiant/camera/CamWnd.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/radiant/camera/CamWnd.cpp b/radiant/camera/CamWnd.cpp index aaa6171a53..267a060ac2 100644 --- a/radiant/camera/CamWnd.cpp +++ b/radiant/camera/CamWnd.cpp @@ -963,7 +963,13 @@ bool CamWnd::onRender() { debug::assertNoGlErrors(); - Cam_Draw(); + // Do not allow exceptions to escape from the rendering code + try { + Cam_Draw(); + } + catch (const std::exception& e) { + rError() << "Exception occurred during rendering:\n" << e.what() << std::endl; + } debug::assertNoGlErrors(); From 66f7882cf2d2ef7817f4be8720489bf63f64401e Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Wed, 29 May 2024 20:16:33 +0100 Subject: [PATCH 35/49] Fix incorrect vertex colours in imported LWO models The picomodel code was multiplying the vertex colours by the surface DIFF and COLR values, which are not used for any purpose when exporting models to TDM, and may be set to Blender's default of (0.8, 0.8, 0.8). This resulted in white vertex colours appearing grey. --- radiantcore/model/picomodel/lib/pm_lwo.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/radiantcore/model/picomodel/lib/pm_lwo.c b/radiantcore/model/picomodel/lib/pm_lwo.c index d71bebf9e4..5cf03fdcfa 100644 --- a/radiantcore/model/picomodel/lib/pm_lwo.c +++ b/radiantcore/model/picomodel/lib/pm_lwo.c @@ -343,9 +343,9 @@ static picoModel_t *_lwo_load( PM_PARAMS_LOAD ) else if (vm->vmap->type == LWID_('R','G','B','A')) { /* set rgba */ - color[ 0 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 0 ] * surface->color.rgb[ 0 ] * surface->diffuse.val * 0xFF); - color[ 1 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 1 ] * surface->color.rgb[ 1 ] * surface->diffuse.val * 0xFF); - color[ 2 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 2 ] * surface->color.rgb[ 2 ] * surface->diffuse.val * 0xFF); + color[ 0 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 0 ] * 0xFF); + color[ 1 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 1 ] * 0xFF); + color[ 2 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 2 ] * 0xFF); color[ 3 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 3 ] * 0xFF); } } @@ -362,9 +362,9 @@ static picoModel_t *_lwo_load( PM_PARAMS_LOAD ) else if (vm->vmap->type == LWID_('R','G','B','A')) { /* set rgba */ - color[ 0 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 0 ] * surface->color.rgb[ 0 ] * surface->diffuse.val * 0xFF); - color[ 1 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 1 ] * surface->color.rgb[ 1 ] * surface->diffuse.val * 0xFF); - color[ 2 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 2 ] * surface->color.rgb[ 2 ] * surface->diffuse.val * 0xFF); + color[ 0 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 0 ] * 0xFF); + color[ 1 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 1 ] * 0xFF); + color[ 2 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 2 ] * 0xFF); color[ 3 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 3 ] * 0xFF); } } From 1704aeb748dd1b18093aa9b8f93c551db0057852 Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Tue, 11 Jun 2024 20:20:36 +0100 Subject: [PATCH 36/49] #5718: fix rendering of vertex blend between two materials This required fixes in two places: - Remove the sorting of material stages in OpenGLShader, which placed ALL bump maps before ALL diffuse maps. This discarded vital information about which pairs or triplets of maps needed to be bound together in interaction passes. Sorting of bump maps before diffuse maps is necessary, but this must be done by the artist in the MTR decl, otherwise the engine will not render the blend correctly. - When we assemble the maps into D/B/S triplets in the RegularLight rendering code, treat bump maps as "delimiters" which start a new interaction pass (via a new clear() method). If we don't do this we end up with unwanted extra passes: a MTR which declares a sequence {B1, D1, B2, D2} generates interaction passes {B1, D1}, {D1, B2}, {B2, D2} which gives the wrong result. Note that #5718 is actually a Dark Mod bug, but the investigation led to the discovery of this incorrect rendering behaviour in DarkRadiant. The actual engine renderer seems to produce correct results provided the material stages are correctly sorted bumpmap-first in the decl. --- include/ishaderlayer.h | 37 ++++++++++++++++++- .../rendersystem/backend/OpenGLShader.cpp | 8 ---- .../rendersystem/backend/RegularLight.cpp | 28 ++++++++++++-- .../rendersystem/backend/RegularLight.h | 9 ++++- 4 files changed, 66 insertions(+), 16 deletions(-) diff --git a/include/ishaderlayer.h b/include/ishaderlayer.h index b7a3ee4e1a..05d59f4f46 100644 --- a/include/ishaderlayer.h +++ b/include/ishaderlayer.h @@ -277,7 +277,7 @@ class IShaderLayer virtual CubeMapMode getCubeMapMode() const = 0; /** - * Returns the dimensions specifying the map size for + * Returns the dimensions specifying the map size for * stages using the "mirrorRenderMap", "remoteRenderMap" keywords. */ virtual const Vector2& getRenderMapSize() const = 0; @@ -444,7 +444,7 @@ class IEditableShaderLayer : // Set the stage condition expression virtual void setConditionExpressionFromString(const std::string& expression) = 0; - + // Sets the texgen type of this stage virtual void setTexGenType(TexGenType type) = 0; @@ -469,3 +469,36 @@ class IEditableShaderLayer : // For stages with map type VideoMap, this is used to update the properties virtual void setVideoMapProperties(const std::string& filePath, bool looping) = 0; }; + +inline std::ostream& operator<< (std::ostream& os, IShaderLayer::VertexColourMode mode) +{ + switch (mode) { + case IShaderLayer::VERTEX_COLOUR_NONE: + return os << "noVertexColor"; + case IShaderLayer::VERTEX_COLOUR_MULTIPLY: + return os << "vertexColor"; + case IShaderLayer::VERTEX_COLOUR_INVERSE_MULTIPLY: + return os << "inverseVertexColor"; + } +} + +inline std::ostream& operator<< (std::ostream& os, IShaderLayer::Type type) +{ + switch (type) { + case IShaderLayer::BUMP: + return os << "bumpmap"; + case IShaderLayer::DIFFUSE: + return os << "diffusemap"; + case IShaderLayer::SPECULAR: + return os << "specularmap"; + case IShaderLayer::BLEND: + return os << "blend"; + } +} + +inline std::ostream& operator<< (std::ostream& os, IShaderLayer& layer) +{ + return os << "IShaderLayer(type=" << layer.getType() + << ", map=" << layer.getMapImageFilename() + << ", vertexColor=" << layer.getVertexColourMode() << ")"; +} diff --git a/radiantcore/rendersystem/backend/OpenGLShader.cpp b/radiantcore/rendersystem/backend/OpenGLShader.cpp index 16266f3e27..b7ab48f93b 100644 --- a/radiantcore/rendersystem/backend/OpenGLShader.cpp +++ b/radiantcore/rendersystem/backend/OpenGLShader.cpp @@ -469,13 +469,6 @@ void OpenGLShader::constructLightingPassesFromMaterial() return true; }); - // Sort interaction stages: bumps go first, then diffuses, speculars last - std::sort(interactionLayers.begin(), interactionLayers.end(), [](const IShaderLayer::Ptr& a, const IShaderLayer::Ptr& b) - { - // Use the enum value to sort stages - return static_cast(a->getType()) < static_cast(b->getType()); - }); - if (!interactionLayers.empty()) { // Translucent materials don't contribute to the depth buffer @@ -796,4 +789,3 @@ InteractionPass* OpenGLShader::getInteractionPass() const } } - diff --git a/radiantcore/rendersystem/backend/RegularLight.cpp b/radiantcore/rendersystem/backend/RegularLight.cpp index 0d7dce92e9..7cc16c2b00 100644 --- a/radiantcore/rendersystem/backend/RegularLight.cpp +++ b/radiantcore/rendersystem/backend/RegularLight.cpp @@ -21,7 +21,7 @@ RegularLight::RegularLight(RendererLight& light, IGeometryStore& store, IObjectR _shadowLightIndex(-1) { // Consider the "noshadows" flag and the setting of the light material - _isShadowCasting = _light.isShadowCasting() && _light.getShader() && + _isShadowCasting = _light.isShadowCasting() && _light.getShader() && _light.getShader()->getMaterial() && _light.getShader()->getMaterial()->lightCastsShadows(); } @@ -99,7 +99,7 @@ void RegularLight::collectSurfaces(const IRenderView& view, const std::set& untransformedObjectsWithoutAlphaTest) { std::vector untransformedObjects; @@ -153,7 +153,7 @@ void RegularLight::fillDepthBuffer(OpenGLState& state, DepthFillAlphaProgram& pr } } -void RegularLight::drawShadowMap(OpenGLState& state, const Rectangle& rectangle, +void RegularLight::drawShadowMap(OpenGLState& state, const Rectangle& rectangle, ShadowMapProgram& program, std::size_t renderTime) { // Set up the viewport to write to a specific area within the shadow map texture @@ -231,6 +231,16 @@ RegularLight::InteractionDrawCall::InteractionDrawCall(OpenGLState& state, Inter _untransformedObjects.reserve(10000); } +std::ostream& operator<< (std::ostream& os, IShaderLayer::Ptr p) +{ + if (p) { + return os << *p; + } + else { + return os << ""; + } +} + void RegularLight::InteractionDrawCall::submit(const ObjectList& objects) { // Every material without bump defines an implicit _flat bump (see in TDM sources: Material::AddImplicitStages) @@ -318,7 +328,7 @@ void RegularLight::InteractionDrawCall::setSpecular(const InteractionPass::Stage _specular = specular; } -void RegularLight::drawInteractions(OpenGLState& state, InteractionProgram& program, +void RegularLight::drawInteractions(OpenGLState& state, InteractionProgram& program, const IRenderView& view, std::size_t renderTime) { if (_objectsByEntity.empty()) @@ -349,12 +359,22 @@ void RegularLight::drawInteractions(OpenGLState& state, InteractionProgram& prog if (!interactionStage.stage->isVisible()) continue; // ignore inactive stages + // Assemble diffuse, bump and specular stages into interaction passes, each + // of which consumes a single map of each type (with defaults black or _flat + // used if the respective stage is not declared). Bump maps are treated + // specially, in that they delimit separate interaction passes, whereas + // diffuse or specular maps can be shared from one pass to the next. + // + // This allows the material to list {B1, D1, B2, D2} to blend between two + // completely different textures (typically using vertexColor), or {B1, D1, + // D2} to use a single bumpmap but blend between two different diffusemaps. switch (interactionStage.stage->getType()) { case IShaderLayer::BUMP: if (draw.hasBump()) { draw.submit(objects); // submit pending draws when changing bump maps + draw.clear(); // bump map starts a new interaction pass } draw.setBump(&interactionStage); break; diff --git a/radiantcore/rendersystem/backend/RegularLight.h b/radiantcore/rendersystem/backend/RegularLight.h index 9be15e60a0..622816ff51 100644 --- a/radiantcore/rendersystem/backend/RegularLight.h +++ b/radiantcore/rendersystem/backend/RegularLight.h @@ -86,11 +86,16 @@ class RegularLight return _interactionDrawCalls; } - void prepare(InteractionPass& pass) + void clear() { _bump = nullptr; _diffuse = nullptr; _specular = nullptr; + } + + void prepare(InteractionPass& pass) + { + clear(); _defaultBumpStage.texture = pass.getDefaultInteractionTextureBinding(IShaderLayer::BUMP); _defaultDiffuseStage.texture = pass.getDefaultInteractionTextureBinding(IShaderLayer::DIFFUSE); @@ -176,7 +181,7 @@ class RegularLight void collectSurfaces(const IRenderView& view, const std::set& entities); - void fillDepthBuffer(OpenGLState& state, DepthFillAlphaProgram& program, + void fillDepthBuffer(OpenGLState& state, DepthFillAlphaProgram& program, std::size_t renderTime, std::vector& untransformedObjectsWithoutAlphaTest); void drawShadowMap(OpenGLState& state, const Rectangle& rectangle, ShadowMapProgram& program, std::size_t renderTime); From 09605944ee663bf5ccc098e9aa2bf87945ea55a2 Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Wed, 12 Jun 2024 20:06:27 +0100 Subject: [PATCH 37/49] Update Surface Inspector documentation with new icons --- doc/img/align_bottom.png | Bin 0 -> 1266 bytes doc/img/align_left.png | Bin 0 -> 1353 bytes doc/img/align_right.png | Bin 0 -> 1351 bytes doc/img/align_top.png | Bin 0 -> 324 bytes doc/img/flip_horiz.png | Bin 0 -> 231 bytes doc/img/flip_vert.png | Bin 0 -> 239 bytes doc/img/texture_lock.png | Bin 0 -> 322 bytes doc/manual.adoc | 17 +++++++++++++---- 8 files changed, 13 insertions(+), 4 deletions(-) create mode 100644 doc/img/align_bottom.png create mode 100644 doc/img/align_left.png create mode 100644 doc/img/align_right.png create mode 100644 doc/img/align_top.png create mode 100644 doc/img/flip_horiz.png create mode 100644 doc/img/flip_vert.png create mode 100644 doc/img/texture_lock.png diff --git a/doc/img/align_bottom.png b/doc/img/align_bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..e0d2b034306e57d18f974cb600054a7b987db5f3 GIT binary patch literal 1266 zcmV zaB^>EX>4U6ba`-PAZ2)IW&i+q+O1Ywmg6W4{nsjH3FrZce!S^SP{}iSHh<8-@lXo!No)WeFqWj+p0t6buhFG-3-qw6I|`E-b8JvT^6ilgBO_H$XHQk|9N! z=&p0kQlx26RwFevo|3g_S69&{Lls)NW8pn+*~2w<1nvw=VK`%~*aw}HcgIUdbGDTt z#_rEt0hh?4fmarF{f89@;d$0k+!K6`#|y;5iNR{6*|EVIw!_61`J=X!cA^!A5z>yu zx!)WhMTi{%h7}Bi#2Ptq$Re2;d35kofn1bKHc-GZN(V|Y)dX@}R&1=vyxU5YLyZ;! z5mA$7P*b#mE3;^cESsZ-YSd^k#uRh(SYl0@EX9;kPOBGd)@(WElyml6a&>j{=*hFY z7q3*LD0(%PRC4iBO05W5;kjaU^$Nu%n{KwnEp2)8t!%ZSe44e`Qp?R-X_dNk?bc&Y zJ$LV=*TDm&G;G9?Mjk%Os1v0&b=r(G%{+aUS?{dvtkN%>Ju~-b*7(L62&0`dgVD(v z)H$CncpWEkaR$c37>wt|018?bXTjM_7IU;X3q{ZsQGhZoayl=@z@QD5IQh=q2Xjxn z5&Hgbd6Un~(W34O=4es(g1KkjUa;2J`QBC^1{`dw4OdcwqWxf0#qk%yH>HlePf50E zt^*t`JwiOnS#|8w*?ODX_8X7%?a*I9qIwY|@{1smUj&KdB1j|`L85vQB&rudBEJX{ zg^M83_d4mzNXOP*{qRF+N3wDLA!1~U9B0|e%(mthapK-&ZsVBdQScyPx5n9glk}+g zvFtmPLe9X_Pb@rfwm)Eb#@6R&Y`(20ig;PPM?M2e?(``y{uP!N*tQcJ$x7UDzLp;+ zrT~iQ?u+ukOQ1J@Gv`cG^NsduWd^*3_R26jUAKFw+)^WXpVR<2thcRYeAe{wb_wjP zea1JB01IB?Sw=|*oYuPAS-LeID3pisnEiO7hUK-qkBQ#j)Q=ot(|+d!-&W5hM?b8= z-^xcvVDxMxd_VN=--Sec5u_(ae+!AiMUb8yJwht@ph7#-|8BH|4qFxc`v3p{24YJ` zL;wH)0002_L%V+f000SaNLh0L01m?d01m?e$8V@)00007bV*G`2j~P83@HcC0H$jI z007cSL_t(IjqQ^?4gp~hhM!>ziB4AG26_#N#0luQgia$7KN0H)8V7KJtsG(-Zm@-B zQK%GlM-Z5}#a+TL1t607*qoM6N<$g7Dx-EC2ui literal 0 HcmV?d00001 diff --git a/doc/img/align_left.png b/doc/img/align_left.png new file mode 100644 index 0000000000000000000000000000000000000000..02e948b9d479fff2847fcd7ed489cb96f6f93e76 GIT binary patch literal 1353 zcmV-P1-AN$P) zaB^>EX>4U6ba`-PAZ2)IW&i+q+O1b@cIzq({I6BqB_Ih9EC=&(&fCFT{vGT%O=_oc z+iRMNC}J2G2@>l6|2OCl9&yf-50+w%(c$sgXDR3;_wn?b&CZS4_jvKm$M-0vrw{fX zqn-H$R$+B>-sjT^qz?n)-SU~A!zaJqr;#T{X$KOt2fsIG?(s<8kL`oeQt?=5@Nt2@^L)q%go3Bi2TH=eO+?XpB0y zh_U*8ufR)W%D{6Mb^b>b1mUsgQCK&4Z=W9s3lNW}xy=a+ENI0R{6DPA=~oFsE@j4@tgCCKJR z2|fn$+H7r%rhF1Dm4FQp=Vs;{BOYBkkd3pHul zd-e zcmUFFTYnioX@}_356-(CMf=(vwbijKFOcW}eL%j3MCm=!C64sPvR@*eA<>nMyuy*L zY$TB4ctrAH`(U1)9)CCkaIm%64@P%%37*2a!9&vly#BdQ@;YYvV^TdnJbHDGopNiqj?u~BL*XInS{h+}O+D z00(qQO+^Rj1QZM@7(xiEo&W#<<4Ht8R5*>L)4fUqK@`RD-)y!MEb;-`Sld|TA*@WN z;4|2XAc!c7YiH{#SlGtK(pT^iEJV;)2ttZv$0{Hun_V{pw>fj~{~RteA;xHxlXP=& zR%K1y+-WJG%AuDV;JOi@ccmY>L+jswc4jLEL#!<`SqSK4){~Y8Y%Uvy^sf*yX(x3r z+WD=QEqN|MJ9R7Ct#U2}gu=M)@It_7+p%a^7cjSjxNo{~<8U(eg#JwbI2URqfVUIh zVQPSvssyF);Qi8Tk=VmmBLIA!d5(nxOd0`Tbm1{XM`ib6)$jZR7fm>$Cz$0K00000 LNkvXXu0mjf+E;p^ literal 0 HcmV?d00001 diff --git a/doc/img/align_right.png b/doc/img/align_right.png new file mode 100644 index 0000000000000000000000000000000000000000..108d25b481c95d3cb505b547fc5fa6c8b6ee886e GIT binary patch literal 1351 zcmV-N1-SZ&P) zaB^>EX>4U6ba`-PAZ2)IW&i+q+O1dFmE$-J{6`cU0&(yPhvMvs}<9a5B3>j zoaqHpA$3z;{pkeK!-8;?e5PlAr`M|+`4Lgtfdu2Ax9FlfUg>s^(bB-4Xxd9&-Xg}(BWX4Z8($a6zvD2A_;%aeJII&x$xIO z+QSy~GAwCl8^z`$r7~)(V=3<=MR!DUtGeLu-zLaU(Y{VMoy%t+(_mM{hdyG3dFnXCR;=~4Y-bdd%{=1(JUR~qAjrB$3^cL@~zTM zj+K5WU^?%O&n11d;Gsos^-50)_G3x!8Hy}h^%qWg*s`}m#!G`#B00006VoOIv00000008+zyMF)x010qNS#tmY4#NNd4#NS*Z>VGd000Mc zNliru=mZoDC^zuJr-lFk0OUzTK~y-6WBmXBKLaIz1($?qGeg?zT83HJQ@HU>R%4PankVBll=$FNznkwJ=F0~i<>807vl zGi;LRVc^Et@$48FoQ82ZqKi8p|Of#E+B0|)sA zFf#0X_lF@HU6|!f4Fe-K^=M&p`Trk=u>bW8e=rOn!tUq){xbNyt7rIxO_GoozB2x2 z@O@X$@CcUzdR85k+e&Hy!sIsiYhfq?)3002ov JPDHLkV1kEeeXRfh literal 0 HcmV?d00001 diff --git a/doc/img/align_top.png b/doc/img/align_top.png new file mode 100644 index 0000000000000000000000000000000000000000..c44135c451aefa6088d032b03033478eae186a9e GIT binary patch literal 324 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9F5he4R}c>anMprB-l zYeY$Kep*R+Vo@qXd3m{BW?pu2a$-TMUVc&f>~}U&Kt&flT^vI!dhbrMCM)iIw3lA<~(`5{Q@f|U*wxC zp4zZO<5-*PKeq%E4Xv{R9D6n9b8>xIB=)pjbluMV=So?>>U{t0{fGIWU}}q#FyEh@ R20+g-c)I$ztaD0e0ssKNcmeanMprB-l zYeY$Kep*R+Vo@qXd3m{BW?pu2a$-TMUVc&f>~}U&Kt&~ss5`s!YF3s`D$e{L?k<7anMprB-l zYeY$Kep*R+Vo@qXd3m{BW?pu2a$-TMUVc&f>~}U&Kt(m4E{-7^YI$A-5!Bp{QYY gPvhq1t!z6OUgUcm?dsc51at?3r>mdKI;Vst0A*2DQUCw| literal 0 HcmV?d00001 diff --git a/doc/img/texture_lock.png b/doc/img/texture_lock.png new file mode 100644 index 0000000000000000000000000000000000000000..b76c96e7a2adc1b11a66c339cacf97bc40756d44 GIT binary patch literal 322 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9F5he4R}c>anMprB-l zYeY$Kep*R+Vo@qXd3m{BW?pu2a$-TMUVc&f>~}U&Kt<<0T^vI!dhbrM<~o!h(6Zk* zr+LD)JFbgbmp*vRcZ+4qClR)<5_S$3yhPsQtbHqzpZIU9=K&WJ53B9BlG0|(jGg&| zd+C7)*Aom&Q#ko@f3{kz)iqW9U}aqYbY-ejh!g{tb*BXD@o%c#wcQ4v!{>F)3w+7I z_kd%|2Fup3m8#(my24C)htz}~yC&IO(0y<#t9SE{?;IQ7a$J^vv%9>Zu=K9}&3^3* zCrSlwt0^+`^q-C1Gi|?>$E>sOBG0@&%Ju)CJjW8RKVRntGtG}WKl_m2yl4+cHHpc2 RlYm}h@O1TaS?83{1OTL6fX)B_ literal 0 HcmV?d00001 diff --git a/doc/manual.adoc b/doc/manual.adoc index 5ecdcabeee..7a8f111a95 100644 --- a/doc/manual.adoc +++ b/doc/manual.adoc @@ -917,13 +917,22 @@ _widths_ on a certain brush face but the number of _lengths_ is not important (since the texture is seamless); in this case, avoiding aspect ratio distortion is more useful than fitting an exact number of lengths. -|Align Texture|These buttons shift the texture so that the Top/Bottom/Left/Right +| +image:align_top.png[]  +image:align_bottom.png[]  +image:align_left.png[]  +image:align_right.png[] +|These buttons shift the texture so that the Top/Bottom/Left/Right edge of the face are aligned with a texture boundary, but otherwise do not modify the scaling of the texture (unlike the *Fit* operation). -|Flip Texture|Flips (mirrors) the texture along the horizontal or vertical axis. +| +image:flip_horiz.png[] image:flip_vert.png[] +|Flips (mirrors) the texture along the horizontal or vertical axis. |Natural|This button resets the texture to a default alignment and scale, based -the location and size of the face. -|Texture Lock|If this is enabled, the alignment of the texture will be preserved +the location and size of the face. The adjacent *Scale* spinbox allows you to control the +default scale used for *Natural* fitting. +|image:texture_lock.png[] +|If this is enabled, the alignment of the texture will be preserved relative to the face if the brush or patch is moved in 3D space. If disabled, the texture itself will remain fixed in 3D space as the brush or patch moves, resulting in the alignment changing. From b3aaf558dbfac757989bbe7b43dff7bee6dd505b Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Wed, 19 Jun 2024 19:40:07 +0100 Subject: [PATCH 38/49] #6514: initial material replacement edit boxes Add "Replace" and "With" text boxes underneath the skin editor's material replacements list. The text boxes are populated with the selected material information, although editing them does not have any effect. --- install/ui/skineditor.fbp | 595 ++++++++++++++++++++++++--------- install/ui/skineditor.xrc | 112 +++++-- radiant/ui/skin/SkinEditor.cpp | 9 +- radiant/ui/skin/SkinEditor.h | 4 +- 4 files changed, 517 insertions(+), 203 deletions(-) diff --git a/install/ui/skineditor.fbp b/install/ui/skineditor.fbp index 50293fb837..37b75801e5 100644 --- a/install/ui/skineditor.fbp +++ b/install/ui/skineditor.fbp @@ -1369,7 +1369,7 @@ none 6 - wxALL|wxEXPAND + wxEXPAND|wxLEFT|wxRIGHT|wxTOP 1 1 @@ -1427,169 +1427,436 @@ bSizer148 wxVERTICAL none - - 6 - wxEXPAND|wxTOP - 0 - - 2 - wxBOTH - 1 - - 0 - - fgSizer35 - wxFLEX_GROWMODE_SPECIFIED - none - 1 - 0 - - 6 - wxALIGN_CENTER_VERTICAL|wxRIGHT - 0 - - 1 - 1 - 1 - 1 - - - - - 0 - - - - - 1 - 0 - 1 - - 1 - - 0 - 0 - - Dock - 0 - Left - 1 - - 1 - - - 0 - 0 - wxID_ANY - Add Materials from Targeted Models - - 0 - - 0 - - - 0 - - 1 - SkinEditorAddMaterialsFromModelsButton - 1 - - - protected - 1 - - - - Resizable - 1 - - - ; ; forward_declare - 0 - - - wxFILTER_NONE - wxDefaultValidator - - - - - - - - 6 - wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT - 0 - - 1 - 1 - 1 - 1 - - - - - 0 - - - - - 1 - 0 - 1 - - 1 - - 0 - 0 - - Dock - 0 - Left - 1 - - 1 - - - 0 - 0 - wxID_ANY - Remove Remapping - - 0 - - 0 - - - 0 - - 1 - SkinEditorRemoveMappingButton - 1 - - - protected - 1 - - - - Resizable - 1 - - - ; ; forward_declare - 0 - - - wxFILTER_NONE - wxDefaultValidator - - - - - - - + + + + + 6 + wxEXPAND + 0 + + + wxHORIZONTAL + 1 + + 0 + + gbSizer1 + wxFLEX_GROWMODE_SPECIFIED + none + 0 + + 6 + 2 + 1 + wxEXPAND|wxRIGHT|wxTOP + 0 + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + SourceMaterialEdit + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + 6 + 1 + 1 + wxBOTTOM|wxEXPAND|wxTOP + 2 + 1 + + 1 + 1 + 1 + 1 + + + + + 0 + + + + + 1 + 0 + 1 + + 1 + + 0 + 0 + + Dock + 0 + Left + 1 + + 1 + + + 0 + 0 + wxID_ANY + Add Materials from Targeted Models + + 0 + + 0 + + + 0 + + 1 + SkinEditorAddMaterialsFromModelsButton + 1 + + + protected + 1 + + + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + 6 + 1 + 2 + wxALL|wxEXPAND + 2 + 1 + + 1 + 1 + 1 + 1 + + + + + 0 + + + + + 1 + 0 + 1 + + 1 + + 0 + 0 + + Dock + 0 + Left + 1 + + 1 + + + 0 + 0 + wxID_ANY + Remove Remapping + + 0 + + 0 + + + 0 + + 1 + SkinEditorRemoveMappingButton + 1 + + + protected + 1 + + + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + 6 + 1 + 0 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Replace: + 0 + + 0 + + + 0 + + 1 + m_staticText4 + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + + + -1 + + + + 6 + 1 + 0 + wxALL|wxALIGN_CENTER_VERTICAL + 1 + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + With: + 0 + + 0 + + + 0 + + 1 + m_staticText5 + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + + + -1 + + + + 6 + 2 + 1 + wxEXPAND|wxRIGHT|wxTOP + 1 + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + ReplacementMaterialEdit + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + diff --git a/install/ui/skineditor.xrc b/install/ui/skineditor.xrc index 003d2a5ed1..a3fa790cbd 100644 --- a/install/ui/skineditor.xrc +++ b/install/ui/skineditor.xrc @@ -281,48 +281,86 @@ wxVERTICAL - wxALL|wxEXPAND + wxEXPAND|wxLEFT|wxRIGHT|wxTOP 6 wxVERTICAL - - - wxEXPAND|wxTOP - 6 - - 1 - 2 - 0 - 0 - 1 - - - - wxALIGN_CENTER_VERTICAL|wxRIGHT - 6 - - - 0 - 0 - 0 - - - - - - wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT - 6 - - - 0 - 0 - 0 - - - - + + + + + + wxEXPAND + 6 + + 0 + 0 + 1 + + + 0,1 + 1,2 + wxEXPAND|wxRIGHT|wxTOP + 6 + + + + + + 2,1 + 1,1 + wxBOTTOM|wxEXPAND|wxTOP + 6 + + + 0 + 0 + 0 + + + + + 2,2 + 1,1 + wxALL|wxEXPAND + 6 + + + 0 + 0 + 0 + + + + + 0,0 + 1,1 + wxALL|wxALIGN_CENTER_VERTICAL + 6 + + + -1 + + + + 1,0 + 1,1 + wxALL|wxALIGN_CENTER_VERTICAL + 6 + + + -1 + + + + 1,1 + 1,2 + wxEXPAND|wxRIGHT|wxTOP + 6 + + diff --git a/radiant/ui/skin/SkinEditor.cpp b/radiant/ui/skin/SkinEditor.cpp index 9d61840138..8848f4a648 100644 --- a/radiant/ui/skin/SkinEditor.cpp +++ b/radiant/ui/skin/SkinEditor.cpp @@ -192,6 +192,8 @@ void SkinEditor::setupPreview() void SkinEditor::setupRemappingPanel() { auto panel = getControl("SkinEditorRemappingPanel"); + _sourceMaterialEdit = getControl("SourceMaterialEdit"); + _replacementMaterialEdit = getControl("ReplacementMaterialEdit"); _remappingList = wxutil::TreeView::CreateWithModel(panel, _remappings.get(), wxDV_SINGLE); @@ -846,9 +848,14 @@ void SkinEditor::onRemappingEditDone(wxDataViewEvent& ev) row[_remappingColumns.unchangedReplacement] = std::string(); } -void SkinEditor::onRemappingSelectionChanged(wxCommandEvent& ev) +void SkinEditor::onRemappingSelectionChanged(wxDataViewEvent& ev) { updateRemappingButtonSensitivity(); + + // Populate the Replace/With entry boxes + wxutil::TreeModel::Row row(ev.GetItem(), *_remappings); + _sourceMaterialEdit->SetValue(row[_remappingColumns.original].getString()); + _replacementMaterialEdit->SetValue(row[_remappingColumns.replacement].getString()); } void SkinEditor::onRemoveSelectedMapping(wxCommandEvent& ev) diff --git a/radiant/ui/skin/SkinEditor.h b/radiant/ui/skin/SkinEditor.h index 55284662c2..f416ed94c2 100644 --- a/radiant/ui/skin/SkinEditor.h +++ b/radiant/ui/skin/SkinEditor.h @@ -68,6 +68,8 @@ class SkinEditor final: public wxutil::DialogBase, private wxutil::XmlResourceBa RemappingColumns _remappingColumns; wxutil::TreeModel::Ptr _remappings; wxutil::TreeView* _remappingList; + wxWeakRef _sourceMaterialEdit; + wxWeakRef _replacementMaterialEdit; wxutil::WindowPosition _windowPosition; wxutil::PanedPosition _leftPanePosition; @@ -125,7 +127,7 @@ class SkinEditor final: public wxutil::DialogBase, private wxutil::XmlResourceBa void onRemappingEditStarted(wxDataViewEvent& ev); void onRemappingEditDone(wxDataViewEvent& ev); void onRemoveSelectedMapping(wxCommandEvent& ev); - void onRemappingSelectionChanged(wxCommandEvent& ev); + void onRemappingSelectionChanged(wxDataViewEvent& ev); void onPopulateMappingsFromModel(wxCommandEvent& ev); void onReplacementEntryChanged(const std::string& material); void onSaveChanges(wxCommandEvent& ev); From b97b7df691084d3ab84b379d490d5d3f0361ffa2 Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Tue, 25 Jun 2024 19:32:14 +0100 Subject: [PATCH 39/49] #6514: add "browse" buttons for Replace/With text boxes These are using a relative path to the bitmap set directly in the FBP/XRC file, rather than the "darkradiant:" art provider prefix, which allows the icons to appear in wxFormBuilder. This works correctly in GTK since the relative path between XRC and bitmap is the same, and hopefully will work in Windows too. The browse buttons show a material chooser to choose a new material for the associated text box, although the chosen material is not yet being applied to the skin. --- install/ui/skineditor.fbp | 152 +++++++++++++++++++++++++++++++++ install/ui/skineditor.xrc | 24 ++++++ radiant/ui/skin/SkinEditor.cpp | 32 ++++++- radiant/ui/skin/SkinEditor.h | 2 + 4 files changed, 209 insertions(+), 1 deletion(-) diff --git a/install/ui/skineditor.fbp b/install/ui/skineditor.fbp index 37b75801e5..26e7ddde45 100644 --- a/install/ui/skineditor.fbp +++ b/install/ui/skineditor.fbp @@ -1512,6 +1512,82 @@ + + 6 + 1 + 3 + wxEXPAND|wxRIGHT|wxTOP + 0 + 1 + + 1 + 1 + 1 + 1 + + + + + 0 + + + Load From File; ../bitmaps/treeView16.png + + 1 + 0 + 1 + + 1 + + 0 + 0 + + Dock + 0 + Left + 1 + + 1 + + + 0 + 0 + wxID_ANY + MyButton + + 0 + + 0 + + + 0 + + 1 + chooseRemappedSourceMaterialBtn + 1 + + + protected + 1 + + + + Resizable + 1 + + + ; ; forward_declare + 0 + Choose source material to replace + + wxFILTER_NONE + wxDefaultValidator + + + + + + 6 1 @@ -1859,6 +1935,82 @@ + + 6 + 1 + 3 + wxEXPAND|wxRIGHT|wxTOP + 1 + 1 + + 1 + 1 + 1 + 1 + + + + + 0 + + + Load From File; ../bitmaps/treeView16.png + + 1 + 0 + 1 + + 1 + + 0 + 0 + + Dock + 0 + Left + 1 + + 1 + + + 0 + 0 + wxID_ANY + MyButton + + 0 + + 0 + + + 0 + + 1 + chooseRemappedDestMaterialBtn + 1 + + + protected + 1 + + + + Resizable + 1 + + + ; ; forward_declare + 0 + Choose replacement material + + wxFILTER_NONE + wxDefaultValidator + + + + + +

diff --git a/install/ui/skineditor.xrc b/install/ui/skineditor.xrc index a3fa790cbd..2f11e2190b 100644 --- a/install/ui/skineditor.xrc +++ b/install/ui/skineditor.xrc @@ -308,6 +308,18 @@
+ + 0,3 + 1,1 + wxEXPAND|wxRIGHT|wxTOP + 6 + + Choose source material to replace + ../bitmaps/treeView16.png + 0 + 0 + + 2,1 1,1 @@ -363,6 +375,18 @@
+ + 1,3 + 1,1 + wxEXPAND|wxRIGHT|wxTOP + 6 + + Choose replacement material + ../bitmaps/treeView16.png + 0 + 0 + +
diff --git a/radiant/ui/skin/SkinEditor.cpp b/radiant/ui/skin/SkinEditor.cpp index 8848f4a648..f7a41d2a87 100644 --- a/radiant/ui/skin/SkinEditor.cpp +++ b/radiant/ui/skin/SkinEditor.cpp @@ -215,6 +215,14 @@ void SkinEditor::setupRemappingPanel() _remappingList->Bind(wxEVT_DATAVIEW_ITEM_EDITING_DONE, &SkinEditor::onRemappingEditDone, this); _remappingList->EnableSearchPopup(false); + // Material browse buttons + getControl("chooseRemappedSourceMaterialBtn")->Bind( + wxEVT_BUTTON, [=](wxCommandEvent&) { chooseRemappedSourceMaterial(); } + ); + getControl("chooseRemappedDestMaterialBtn")->Bind( + wxEVT_BUTTON, [=](wxCommandEvent&) { chooseRemappedDestMaterial(); } + ); + panel->GetSizer()->Prepend(_remappingList, 1, wxEXPAND, 0); auto populateButton = getControl("SkinEditorAddMaterialsFromModelsButton"); @@ -224,6 +232,22 @@ void SkinEditor::setupRemappingPanel() removeButton->Bind(wxEVT_BUTTON, &SkinEditor::onRemoveSelectedMapping, this); } +void SkinEditor::chooseRemappedSourceMaterial() +{ + MaterialChooser chooser( + this, MaterialSelector::TextureFilter::All, _sourceMaterialEdit + ); + chooser.ShowModal(); +} + +void SkinEditor::chooseRemappedDestMaterial() +{ + MaterialChooser chooser( + this, MaterialSelector::TextureFilter::All, _replacementMaterialEdit + ); + chooser.ShowModal(); +} + decl::ISkin::Ptr SkinEditor::getSelectedSkin() { auto selectedSkin = _skinTreeView->GetSelectedDeclName(); @@ -318,7 +342,13 @@ void SkinEditor::updateRemappingControlsFromSkin(const decl::ISkin::Ptr& skin) void SkinEditor::updateRemappingButtonSensitivity() { auto selectedSource = getSelectedRemappingSourceMaterial(); - getControl("SkinEditorRemoveMappingButton")->Enable(!selectedSource.empty() && selectedSource != "*"); + const bool isWildcardRow = (selectedSource == "*"); + + // The wildcard row cannot be removed and its source ("*") cannot be changed + getControl("SkinEditorRemoveMappingButton")->Enable( + !selectedSource.empty() && !isWildcardRow + ); + _sourceMaterialEdit->Enable(!isWildcardRow); } void SkinEditor::updateSourceView(const decl::ISkin::Ptr& skin) diff --git a/radiant/ui/skin/SkinEditor.h b/radiant/ui/skin/SkinEditor.h index f416ed94c2..65dfd4d6bc 100644 --- a/radiant/ui/skin/SkinEditor.h +++ b/radiant/ui/skin/SkinEditor.h @@ -136,6 +136,8 @@ class SkinEditor final: public wxutil::DialogBase, private wxutil::XmlResourceBa void onCopySkin(wxCommandEvent& ev); void onDeleteSkin(wxCommandEvent& ev); void onSkinDeclarationChanged(); + void chooseRemappedSourceMaterial(); + void chooseRemappedDestMaterial(); bool saveChanges(); void discardChanges(); From c0a5a24b6a609a128e858105d1aae02d457bc4c0 Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Tue, 2 Jul 2024 19:48:11 +0100 Subject: [PATCH 40/49] #6514: "Choose source material" disabled for wildcard row The text entry was already disabled when the "*" row is selected; now the browse button is also disabled. --- radiant/ui/skin/SkinEditor.cpp | 4 +++- radiant/ui/skin/SkinEditor.h | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/radiant/ui/skin/SkinEditor.cpp b/radiant/ui/skin/SkinEditor.cpp index f7a41d2a87..1e64e6e5ad 100644 --- a/radiant/ui/skin/SkinEditor.cpp +++ b/radiant/ui/skin/SkinEditor.cpp @@ -216,7 +216,8 @@ void SkinEditor::setupRemappingPanel() _remappingList->EnableSearchPopup(false); // Material browse buttons - getControl("chooseRemappedSourceMaterialBtn")->Bind( + _sourceMaterialBrowseBtn = getControl("chooseRemappedSourceMaterialBtn"); + _sourceMaterialBrowseBtn->Bind( wxEVT_BUTTON, [=](wxCommandEvent&) { chooseRemappedSourceMaterial(); } ); getControl("chooseRemappedDestMaterialBtn")->Bind( @@ -349,6 +350,7 @@ void SkinEditor::updateRemappingButtonSensitivity() !selectedSource.empty() && !isWildcardRow ); _sourceMaterialEdit->Enable(!isWildcardRow); + _sourceMaterialBrowseBtn->Enable(!isWildcardRow); } void SkinEditor::updateSourceView(const decl::ISkin::Ptr& skin) diff --git a/radiant/ui/skin/SkinEditor.h b/radiant/ui/skin/SkinEditor.h index 65dfd4d6bc..6ee79cc5db 100644 --- a/radiant/ui/skin/SkinEditor.h +++ b/radiant/ui/skin/SkinEditor.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include "icommandsystem.h" #include "modelskin.h" @@ -70,6 +71,7 @@ class SkinEditor final: public wxutil::DialogBase, private wxutil::XmlResourceBa wxutil::TreeView* _remappingList; wxWeakRef _sourceMaterialEdit; wxWeakRef _replacementMaterialEdit; + wxWeakRef _sourceMaterialBrowseBtn; wxutil::WindowPosition _windowPosition; wxutil::PanedPosition _leftPanePosition; From a154a7fb876feeae91248201edde01eabb76f955 Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Wed, 3 Jul 2024 19:19:03 +0100 Subject: [PATCH 41/49] #6514: replacement material is now applied to skin after browsing If the browse button was clicked and the result was OK, call the onReplacementEntryChanged method to apply the change to the data model. This does not yet apply if the replacement material entry box is edited manually. --- radiant/ui/skin/SkinEditor.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/radiant/ui/skin/SkinEditor.cpp b/radiant/ui/skin/SkinEditor.cpp index 1e64e6e5ad..5b7a9883de 100644 --- a/radiant/ui/skin/SkinEditor.cpp +++ b/radiant/ui/skin/SkinEditor.cpp @@ -235,18 +235,20 @@ void SkinEditor::setupRemappingPanel() void SkinEditor::chooseRemappedSourceMaterial() { - MaterialChooser chooser( - this, MaterialSelector::TextureFilter::All, _sourceMaterialEdit - ); - chooser.ShowModal(); + MaterialChooser chooser(this, MaterialSelector::TextureFilter::All); + if (chooser.ShowModal() == wxID_OK) { + _sourceMaterialEdit->SetValue(chooser.GetSelectedDeclName()); + } } void SkinEditor::chooseRemappedDestMaterial() { - MaterialChooser chooser( - this, MaterialSelector::TextureFilter::All, _replacementMaterialEdit - ); - chooser.ShowModal(); + MaterialChooser chooser(this, MaterialSelector::TextureFilter::All); + if (chooser.ShowModal() == wxID_OK) { + const std::string materialName = chooser.GetSelectedDeclName(); + _replacementMaterialEdit->SetValue(materialName); + onReplacementEntryChanged(materialName); + } } decl::ISkin::Ptr SkinEditor::getSelectedSkin() From 6fcd0d3fc09805e1f44602ff98f4f05de5dc2508 Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Wed, 3 Jul 2024 19:38:11 +0100 Subject: [PATCH 42/49] #6514: material text entries are read-only Updating the skin on manual editing isn't yet implemented (and may not be important) so keep the text entry boxes read-only for now. --- install/ui/skineditor.fbp | 4 ++-- install/ui/skineditor.xrc | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/install/ui/skineditor.fbp b/install/ui/skineditor.fbp index 26e7ddde45..c9c8ea0190 100644 --- a/install/ui/skineditor.fbp +++ b/install/ui/skineditor.fbp @@ -1498,7 +1498,7 @@ Resizable 1 - + wxTE_READONLY ; ; forward_declare 0 @@ -1921,7 +1921,7 @@ Resizable 1 - + wxTE_READONLY ; ; forward_declare 0 diff --git a/install/ui/skineditor.xrc b/install/ui/skineditor.xrc index 2f11e2190b..4904819a5d 100644 --- a/install/ui/skineditor.xrc +++ b/install/ui/skineditor.xrc @@ -305,6 +305,7 @@ wxEXPAND|wxRIGHT|wxTOP 6 + @@ -372,6 +373,7 @@ wxEXPAND|wxRIGHT|wxTOP 6 + From 0ed4f4d91895f3ff8958d2c8bea250bc90093b24 Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Wed, 3 Jul 2024 19:47:28 +0100 Subject: [PATCH 43/49] #6514: original material browse button now applies changes to list --- radiant/ui/skin/SkinEditor.cpp | 29 ++++++++++++++++++++--------- radiant/ui/skin/SkinEditor.h | 1 + 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/radiant/ui/skin/SkinEditor.cpp b/radiant/ui/skin/SkinEditor.cpp index 5b7a9883de..8c9d5ba518 100644 --- a/radiant/ui/skin/SkinEditor.cpp +++ b/radiant/ui/skin/SkinEditor.cpp @@ -200,15 +200,16 @@ void SkinEditor::setupRemappingPanel() _remappingList->AppendToggleColumn(_("Active"), _remappingColumns.active.getColumnIndex(), wxDATAVIEW_CELL_ACTIVATABLE, wxCOL_WIDTH_AUTOSIZE, wxALIGN_NOT, wxDATAVIEW_COL_SORTABLE); - auto originalColumn = new MaterialSelectorColumn(_("Original (click to edit)"), _remappingColumns.original.getColumnIndex()); + auto originalColumn = new MaterialSelectorColumn( + _("Original"), _remappingColumns.original.getColumnIndex() + ); _remappingList->AppendColumn(originalColumn); - auto replacementColumn = new MaterialSelectorColumn(_("Replacement (click to edit)"), _remappingColumns.replacement.getColumnIndex()); + auto replacementColumn = new MaterialSelectorColumn( + _("Replacement"), _remappingColumns.replacement.getColumnIndex() + ); _remappingList->AppendColumn(replacementColumn); - replacementColumn->signal_onMaterialSelected().connect( - sigc::mem_fun(*this, &SkinEditor::onReplacementEntryChanged)); - _remappingList->Bind(wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, &SkinEditor::onRemappingRowChanged, this); _remappingList->Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, &SkinEditor::onRemappingSelectionChanged, this); _remappingList->Bind(wxEVT_DATAVIEW_ITEM_EDITING_STARTED, &SkinEditor::onRemappingEditStarted, this); @@ -235,18 +236,19 @@ void SkinEditor::setupRemappingPanel() void SkinEditor::chooseRemappedSourceMaterial() { - MaterialChooser chooser(this, MaterialSelector::TextureFilter::All); + MaterialChooser chooser(this, MaterialSelector::TextureFilter::All, _sourceMaterialEdit); if (chooser.ShowModal() == wxID_OK) { - _sourceMaterialEdit->SetValue(chooser.GetSelectedDeclName()); + const std::string materialName = chooser.GetSelectedDeclName(); + onOriginalEntryChanged(materialName); } } void SkinEditor::chooseRemappedDestMaterial() { - MaterialChooser chooser(this, MaterialSelector::TextureFilter::All); + MaterialChooser chooser(this, MaterialSelector::TextureFilter::All, + _replacementMaterialEdit); if (chooser.ShowModal() == wxID_OK) { const std::string materialName = chooser.GetSelectedDeclName(); - _replacementMaterialEdit->SetValue(materialName); onReplacementEntryChanged(materialName); } } @@ -966,6 +968,15 @@ void SkinEditor::onPopulateMappingsFromModel(wxCommandEvent& ev) populateSkinListWithModelMaterials(); } +void SkinEditor::onOriginalEntryChanged(const std::string& material) +{ + wxutil::TreeModel::Row row(_remappingList->GetSelection(), *_remappings); + + row[_remappingColumns.original] = material; + row[_remappingColumns.active] = true; // activate edited rows + row.SendItemChanged(); +} + void SkinEditor::onReplacementEntryChanged(const std::string& material) { wxutil::TreeModel::Row row(_remappingList->GetSelection(), *_remappings); diff --git a/radiant/ui/skin/SkinEditor.h b/radiant/ui/skin/SkinEditor.h index 6ee79cc5db..8a1908eb95 100644 --- a/radiant/ui/skin/SkinEditor.h +++ b/radiant/ui/skin/SkinEditor.h @@ -131,6 +131,7 @@ class SkinEditor final: public wxutil::DialogBase, private wxutil::XmlResourceBa void onRemoveSelectedMapping(wxCommandEvent& ev); void onRemappingSelectionChanged(wxDataViewEvent& ev); void onPopulateMappingsFromModel(wxCommandEvent& ev); + void onOriginalEntryChanged(const std::string& material); void onReplacementEntryChanged(const std::string& material); void onSaveChanges(wxCommandEvent& ev); void onDiscardChanges(wxCommandEvent& ev); From 4ba6b8d521f61ffa3e77367981b7f9d40466a6bf Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Tue, 9 Jul 2024 19:54:17 +0100 Subject: [PATCH 44/49] #6514: remove custom MaterialSelectorColumn from SkinEditor None of this ever worked on GTK, and trying to debug such a rarely-used corner of the wxWidgets API seems like more trouble than it's worth now that the separate entry boxes and browse buttons are providing the same functionality. --- radiant/ui/skin/MaterialSelectorColumn.h | 141 ----------------------- radiant/ui/skin/SkinEditor.cpp | 50 ++------ radiant/ui/skin/SkinEditor.h | 4 +- 3 files changed, 13 insertions(+), 182 deletions(-) delete mode 100644 radiant/ui/skin/MaterialSelectorColumn.h diff --git a/radiant/ui/skin/MaterialSelectorColumn.h b/radiant/ui/skin/MaterialSelectorColumn.h deleted file mode 100644 index 67e5ce28b7..0000000000 --- a/radiant/ui/skin/MaterialSelectorColumn.h +++ /dev/null @@ -1,141 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include "ui/materials/MaterialChooser.h" -#include "wxutil/Bitmap.h" - -namespace ui -{ - -class MaterialSelectorRenderer : - public wxDataViewCustomRenderer -{ -private: - wxString _materialName; - - constexpr static auto EDITOR_TEXT_CONTROL_NAME = "TextEntry"; - - sigc::signal _sigMaterialSelected; - -public: - MaterialSelectorRenderer() : - wxDataViewCustomRenderer("string", wxDATAVIEW_CELL_EDITABLE) - {} - - sigc::signal& signal_onMaterialSelected() - { - return _sigMaterialSelected; - } - - bool Render(wxRect cell, wxDC* dc, int state) override - { - RenderText(_materialName, 0, cell, dc, state); - return true; - } - - wxSize GetSize() const override - { - auto size = GetTextExtent(_materialName); - size.IncTo(wxSize(wxDVC_DEFAULT_RENDERER_SIZE, -1)); - - return size; - } - - bool GetValue(wxVariant& value) const override - { - value = _materialName; - return true; - } - - bool SetValue(const wxVariant& value) override - { - _materialName = value.GetString(); - return true; - } - - bool HasEditorCtrl() const override - { - return true; - } - - wxWindow* CreateEditorCtrl(wxWindow* parent, wxRect rect, const wxVariant& value) override - { - auto panel = new wxPanel(parent, wxID_ANY, rect.GetPosition(), rect.GetSize()); - panel->SetSizer(new wxBoxSizer(wxHORIZONTAL)); - - auto ctrl = new wxTextCtrl(panel, wxID_ANY, value, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER); - ctrl->SetName(EDITOR_TEXT_CONTROL_NAME); - ctrl->SetSize(wxSize(rect.GetWidth() - 32, -1)); - - // Dispatch the signal when the control is receiving a value - ctrl->Bind(wxEVT_TEXT, [ctrl, this](auto& ev) { signal_onMaterialSelected().emit(ctrl->GetValue().ToStdString()); }); - - // select the text in the control an place the cursor at the end - ctrl->SetInsertionPointEnd(); - ctrl->SelectAll(); - - auto button = new wxBitmapButton(panel, wxID_ANY, wxutil::GetLocalBitmap("treeView16.png")); - button->Bind(wxEVT_BUTTON, [=](auto& ev) - { - auto selector = new MaterialChooser(panel, MaterialSelector::TextureFilter::All, ctrl); - selector->ShowModal(); - }); - - panel->GetSizer()->Add(ctrl, 1, wxALIGN_CENTER_VERTICAL | wxRIGHT, 6); - panel->GetSizer()->Add(button, 0, wxALIGN_CENTER_VERTICAL); - - panel->Layout(); - panel->Fit(); - - return panel; - } - - bool GetValueFromEditorCtrl(wxWindow* editPanel, wxVariant& value) override - { - auto textCtrl = static_cast(editPanel->FindWindow(EDITOR_TEXT_CONTROL_NAME)); - value = textCtrl->GetValue(); - return true; - } -}; - -/** - * Custom wxDataViewColumn showing a material name and selector button - */ -class MaterialSelectorColumn : - public wxDataViewColumn -{ -private: - sigc::signal _sigMaterialSelected; - sigc::connection _rendererSelectionConn; - -public: - MaterialSelectorColumn(const std::string& title, int modelColumn, int width = wxCOL_WIDTH_AUTOSIZE, - wxAlignment align = wxALIGN_LEFT, int flags = wxDATAVIEW_COL_RESIZABLE) : - wxDataViewColumn(title, new MaterialSelectorRenderer(), modelColumn, width, align, flags) - { - // Dispatch the browser selection signal - auto renderer = static_cast(GetRenderer()); - _rendererSelectionConn = renderer->signal_onMaterialSelected().connect( - [this](const std::string& value) { signal_onMaterialSelected().emit(value); }); - } - - ~MaterialSelectorColumn() override - { - _rendererSelectionConn.disconnect(); - } - - // A signal that is sent out as soon as a material is selected in the browse dialog - // This doesn't mean that the selected material is going to be accepted, but it can be - // used to live-update other preview widgets - sigc::signal& signal_onMaterialSelected() - { - return _sigMaterialSelected; - } -}; - - -} diff --git a/radiant/ui/skin/SkinEditor.cpp b/radiant/ui/skin/SkinEditor.cpp index 8c9d5ba518..3493120bc1 100644 --- a/radiant/ui/skin/SkinEditor.cpp +++ b/radiant/ui/skin/SkinEditor.cpp @@ -13,9 +13,9 @@ #include "gamelib.h" #include "os/file.h" #include "os/path.h" -#include "MaterialSelectorColumn.h" #include "SkinEditorTreeView.h" #include "decl/DeclLib.h" +#include "ui/materials/MaterialChooser.h" #include "ui/modelselector/ModelTreeView.h" #include "util/ScopedBoolLock.h" #include "wxutil/FileChooser.h" @@ -195,25 +195,24 @@ void SkinEditor::setupRemappingPanel() _sourceMaterialEdit = getControl("SourceMaterialEdit"); _replacementMaterialEdit = getControl("ReplacementMaterialEdit"); + // Construct list view and its visible columns _remappingList = wxutil::TreeView::CreateWithModel(panel, _remappings.get(), wxDV_SINGLE); - _remappingList->AppendToggleColumn(_("Active"), _remappingColumns.active.getColumnIndex(), wxDATAVIEW_CELL_ACTIVATABLE, wxCOL_WIDTH_AUTOSIZE, wxALIGN_NOT, wxDATAVIEW_COL_SORTABLE); - - auto originalColumn = new MaterialSelectorColumn( + _remappingList->AppendTextColumn( _("Original"), _remappingColumns.original.getColumnIndex() ); - _remappingList->AppendColumn(originalColumn); - - auto replacementColumn = new MaterialSelectorColumn( + _remappingList->AppendTextColumn( _("Replacement"), _remappingColumns.replacement.getColumnIndex() ); - _remappingList->AppendColumn(replacementColumn); - _remappingList->Bind(wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, &SkinEditor::onRemappingRowChanged, this); - _remappingList->Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, &SkinEditor::onRemappingSelectionChanged, this); - _remappingList->Bind(wxEVT_DATAVIEW_ITEM_EDITING_STARTED, &SkinEditor::onRemappingEditStarted, this); - _remappingList->Bind(wxEVT_DATAVIEW_ITEM_EDITING_DONE, &SkinEditor::onRemappingEditDone, this); + // Connect up list view events + _remappingList->Bind( + wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, &SkinEditor::onRemappingValueChanged, this + ); + _remappingList->Bind( + wxEVT_DATAVIEW_SELECTION_CHANGED, &SkinEditor::onRemappingSelectionChanged, this + ); _remappingList->EnableSearchPopup(false); // Material browse buttons @@ -833,7 +832,7 @@ void SkinEditor::onRemoveModelFromSkin(wxCommandEvent& ev) updateSkinButtonSensitivity(); } -void SkinEditor::onRemappingRowChanged(wxDataViewEvent& ev) +void SkinEditor::onRemappingValueChanged(wxDataViewEvent& ev) { if (_controlUpdateInProgress || !_skin) return; @@ -859,31 +858,6 @@ void SkinEditor::onRemappingRowChanged(wxDataViewEvent& ev) updateSkinButtonSensitivity(); } -void SkinEditor::onRemappingEditStarted(wxDataViewEvent& ev) -{ - // Save the previous values into the model row, to restore it on cancel - wxutil::TreeModel::Row row(ev.GetItem(), *_remappings); - - row[_remappingColumns.unchangedOriginal] = row[_remappingColumns.original].getString(); - row[_remappingColumns.unchangedReplacement] = row[_remappingColumns.replacement].getString(); -} - -void SkinEditor::onRemappingEditDone(wxDataViewEvent& ev) -{ - wxutil::TreeModel::Row row(ev.GetItem(), *_remappings); - - if (ev.IsEditCancelled()) - { - // Revert to previous value - row[_remappingColumns.original] = row[_remappingColumns.unchangedOriginal].getString(); - row[_remappingColumns.replacement] = row[_remappingColumns.unchangedReplacement].getString(); - row.SendItemChanged(); - } - - row[_remappingColumns.unchangedOriginal] = std::string(); - row[_remappingColumns.unchangedReplacement] = std::string(); -} - void SkinEditor::onRemappingSelectionChanged(wxDataViewEvent& ev) { updateRemappingButtonSensitivity(); diff --git a/radiant/ui/skin/SkinEditor.h b/radiant/ui/skin/SkinEditor.h index 8a1908eb95..b782aec0e4 100644 --- a/radiant/ui/skin/SkinEditor.h +++ b/radiant/ui/skin/SkinEditor.h @@ -125,9 +125,7 @@ class SkinEditor final: public wxutil::DialogBase, private wxutil::XmlResourceBa void onSkinModelSelectionChanged(wxDataViewEvent& ev); void onSkinSelectionChanged(wxDataViewEvent& ev); void handleSkinSelectionChanged(); - void onRemappingRowChanged(wxDataViewEvent& ev); - void onRemappingEditStarted(wxDataViewEvent& ev); - void onRemappingEditDone(wxDataViewEvent& ev); + void onRemappingValueChanged(wxDataViewEvent& ev); void onRemoveSelectedMapping(wxCommandEvent& ev); void onRemappingSelectionChanged(wxDataViewEvent& ev); void onPopulateMappingsFromModel(wxCommandEvent& ev); From 7f8db4fa13fbb57ca61d6cbad705bde9e10bbc71 Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Wed, 10 Jul 2024 19:31:17 +0100 Subject: [PATCH 45/49] #6514: autosize remapping list columns Columns now expand to contain the (possibly long) material names, with the widget showing a horizontal scrollbar if necessary. --- radiant/ui/skin/SkinEditor.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/radiant/ui/skin/SkinEditor.cpp b/radiant/ui/skin/SkinEditor.cpp index 3493120bc1..fb3e7e770e 100644 --- a/radiant/ui/skin/SkinEditor.cpp +++ b/radiant/ui/skin/SkinEditor.cpp @@ -200,10 +200,12 @@ void SkinEditor::setupRemappingPanel() _remappingList->AppendToggleColumn(_("Active"), _remappingColumns.active.getColumnIndex(), wxDATAVIEW_CELL_ACTIVATABLE, wxCOL_WIDTH_AUTOSIZE, wxALIGN_NOT, wxDATAVIEW_COL_SORTABLE); _remappingList->AppendTextColumn( - _("Original"), _remappingColumns.original.getColumnIndex() + _("Original"), _remappingColumns.original.getColumnIndex(), wxDATAVIEW_CELL_INERT, + wxCOL_WIDTH_AUTOSIZE ); _remappingList->AppendTextColumn( - _("Replacement"), _remappingColumns.replacement.getColumnIndex() + _("Replacement"), _remappingColumns.replacement.getColumnIndex(), + wxDATAVIEW_CELL_INERT, wxCOL_WIDTH_AUTOSIZE ); // Connect up list view events From 237069c16208dce6f9136bf802db5adb3d802395 Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Tue, 16 Jul 2024 18:55:47 +0100 Subject: [PATCH 46/49] #6514: remove Style::fontsize member variable Per-element font sizes are not used anywhere (and would be highly unusual in a source code editor). The value is still currently hard-coded, but is not stored separately in each Style object. --- libs/wxutil/sourceview/SourceView.cpp | 21 ++++++------ libs/wxutil/sourceview/SourceView.h | 49 +++++++++++---------------- 2 files changed, 30 insertions(+), 40 deletions(-) diff --git a/libs/wxutil/sourceview/SourceView.cpp b/libs/wxutil/sourceview/SourceView.cpp index 77239c8754..ed3910a033 100644 --- a/libs/wxutil/sourceview/SourceView.cpp +++ b/libs/wxutil/sourceview/SourceView.cpp @@ -45,20 +45,19 @@ SourceViewCtrl::SourceViewCtrl(wxWindow* parent) : void SourceViewCtrl::SetStyleMapping(int elementIndex, Element elementType) { - const Style& style = _predefinedStyles[elementType]; + const Style& style = _predefinedStyles[elementType]; - StyleSetForeground(elementIndex, wxColour(style.foreground)); + StyleSetForeground(elementIndex, wxColour(style.foreground)); - wxFont font(style.fontsize, - wxFONTFAMILY_MODERN, - (style.fontstyle & Italic) > 0 ? wxFONTSTYLE_ITALIC : wxFONTSTYLE_NORMAL, - (style.fontstyle & Bold) > 0 ? wxFONTWEIGHT_BOLD : wxFONTWEIGHT_NORMAL, - (style.fontstyle & Underline) > 0, - style.fontname); + wxFont font( + 11, wxFONTFAMILY_MODERN, + (style.fontstyle & Italic) > 0 ? wxFONTSTYLE_ITALIC : wxFONTSTYLE_NORMAL, + (style.fontstyle & Bold) > 0 ? wxFONTWEIGHT_BOLD : wxFONTWEIGHT_NORMAL, + (style.fontstyle & Underline) > 0, style.fontname + ); - StyleSetFont(elementIndex, font); - - StyleSetVisible(elementIndex, (style.fontstyle & Hidden) == 0); + StyleSetFont(elementIndex, font); + StyleSetVisible(elementIndex, (style.fontstyle & Hidden) == 0); } // Python specific diff --git a/libs/wxutil/sourceview/SourceView.h b/libs/wxutil/sourceview/SourceView.h index 76c4af2984..08e1ed5e50 100644 --- a/libs/wxutil/sourceview/SourceView.h +++ b/libs/wxutil/sourceview/SourceView.h @@ -9,7 +9,7 @@ namespace wxutil /** * greebo: This is a custom extension of the wxWidgets styles text control, * providing a few methods to make the actual code style mapping easier. - * It's advisable to subclass this control and map the various lexer-recognised + * It's advisable to subclass this control and map the various lexer-recognised * elements to a specific appearance. */ class SourceViewCtrl : @@ -26,32 +26,23 @@ class SourceViewCtrl : }; // Describes a specific style (e.g. a code comment) - struct Style - { - wxString foreground; - wxString fontname; - int fontsize; - FontStyle fontstyle; - - Style() : - foreground("BLACK"), - fontname(""), - fontsize(10), - fontstyle(Normal) - {}; - - Style(const char* foreground_, - FontStyle fontStyle_ = Normal, - int fontSize_ = 10, - const char* fontname_ = "") : - foreground(foreground_), - fontname(fontname_), - fontsize(fontSize_), - fontstyle(fontStyle_) - {} - }; - - // Elements as recognised by the STC lexer + struct Style + { + wxString foreground; + wxString fontname; + FontStyle fontstyle; + + Style(): foreground("BLACK"), fontname(""), fontstyle(Normal) + {}; + + Style(const char* foreground_, FontStyle fontStyle_ = Normal, + const char* fontname_ = "") + : foreground(foreground_), fontname(fontname_), fontstyle(fontStyle_) + { + } + }; + + // Elements as recognised by the STC lexer enum Element { Default = 0, @@ -98,13 +89,13 @@ class SourceViewCtrl : virtual ~SourceViewCtrl() {} // Use this method to set a lexer-recognised element to a certain style - // e.g. SetStyleMapping(0, Number), provided that the lexer is using + // e.g. SetStyleMapping(0, Number), provided that the lexer is using // the index 0 to represent numbers in the source. virtual void SetStyleMapping(int elementIndex, Element elementType); }; /** - * A special class providing syntax highlighting for the Python + * A special class providing syntax highlighting for the Python * scripting language. */ class PythonSourceViewCtrl : From 3c5bf0d7cd7428be60d7f962734f574835981c0e Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Tue, 16 Jul 2024 19:49:19 +0100 Subject: [PATCH 47/49] #6514: improve size hinting of preferences dialog - PrefPage no longer derives from wxScrolledWindow; instead it is just a simple wxPanel. This causes it to report an accurate size hint, instead of a suggested height of -1. - All hard-coded sizes or screen proportions removed from PrefDialog. The dialog is now sized entirely based on the size hints of the contained panels. - To prevent the dialog from become too large as a result of the lack of scrollable panels, several redundant visibility toggles have been removed from the Orthoview panel. All of the removed items are available in the View -> Show menu so there is no need for them to be duplicated in the Orthoview preferences. --- radiant/ui/prefdialog/PrefDialog.cpp | 44 ++++++++++++---------------- radiant/ui/prefdialog/PrefDialog.h | 3 +- radiant/ui/prefdialog/PrefPage.cpp | 9 +++--- radiant/ui/prefdialog/PrefPage.h | 4 +-- radiant/xyview/GlobalXYWnd.cpp | 7 ----- 5 files changed, 25 insertions(+), 42 deletions(-) diff --git a/radiant/ui/prefdialog/PrefDialog.cpp b/radiant/ui/prefdialog/PrefDialog.cpp index 33331bdbc8..9f75aa51be 100644 --- a/radiant/ui/prefdialog/PrefDialog.cpp +++ b/radiant/ui/prefdialog/PrefDialog.cpp @@ -18,28 +18,24 @@ namespace ui { -PrefDialog::PrefDialog(wxWindow* parent) : - DialogBase(_("DarkRadiant Preferences"), parent), - _notebook(nullptr) +PrefDialog::PrefDialog(wxWindow* parent) +: DialogBase(_("DarkRadiant Preferences"), parent) { - SetSizer(new wxBoxSizer(wxVERTICAL)); - SetMinClientSize(wxSize(640, -1)); + wxBoxSizer* mainVbox = new wxBoxSizer(wxVERTICAL); - // 12-pixel spacer - wxBoxSizer* mainVbox = new wxBoxSizer(wxVERTICAL); - GetSizer()->Add(mainVbox, 1, wxEXPAND | wxALL, 12); + // Notebook widget (shows tree of pages and the space for each page to be shown) + _notebook = new wxTreebook(this, wxID_ANY); + mainVbox->Add(_notebook, 1, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 12); - mainVbox->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL), 0, wxALIGN_RIGHT); - - _notebook = new wxTreebook(this, wxID_ANY); - - // Prevent the tree control from shrinking too much - _notebook->GetTreeCtrl()->SetMinClientSize(wxSize(200, -1)); + // Button box + mainVbox->AddSpacer(6); + mainVbox->Add( + CreateStdDialogButtonSizer(wxOK | wxCANCEL), 0, wxALIGN_RIGHT | wxBOTTOM, 12 + ); - mainVbox->Prepend(_notebook, 1, wxEXPAND); - - // Create the page widgets - createPages(); + // Create the page widgets + createPages(); + SetSizerAndFit(mainVbox); } void PrefDialog::createPages() @@ -62,7 +58,7 @@ void PrefDialog::createPages() { parts.pop_back(); std::string parentPath = string::join(parts, "/"); - + PageMap::const_iterator parent = _pages.find(parentPath); if (parent != _pages.end()) @@ -93,15 +89,13 @@ void PrefDialog::showModal(const std::string& requestedPage) p.second->resetValues(); } - // Trigger a resize of the treebook's TreeCtrl, do this by expanding all nodes + // Trigger a resize of the treebook's TreeCtrl, do this by expanding all nodes // (one would be enough, but we want to show the whole tree anyway) for (std::size_t page = 0; page < _notebook->GetPageCount(); ++page) { _notebook->ExpandNode(page, true); } - FitToScreen(0.7f, 0.6f); - // Is there a specific page display request? if (!requestedPage.empty()) { @@ -109,7 +103,7 @@ void PrefDialog::showModal(const std::string& requestedPage) } else { - // To prevent starting up with the "Game" node selected, + // To prevent starting up with the "Game" node selected, // select the first page below the Settings path for (const PageMap::value_type& p : _pages) { @@ -125,8 +119,8 @@ void PrefDialog::showModal(const std::string& requestedPage) { // Tell all pages to flush their buffer for (const PageMap::value_type& p : _pages) - { - p.second->saveChanges(); + { + p.second->saveChanges(); } // greebo: Check if the mainframe module is already "existing". It might be diff --git a/radiant/ui/prefdialog/PrefDialog.h b/radiant/ui/prefdialog/PrefDialog.h index a4850f2042..b38fc2dfee 100644 --- a/radiant/ui/prefdialog/PrefDialog.h +++ b/radiant/ui/prefdialog/PrefDialog.h @@ -15,8 +15,7 @@ namespace ui class PrefDialog : public wxutil::DialogBase { -private: - wxTreebook* _notebook; + wxTreebook* _notebook = nullptr; // Each notebook page is created and maintained by a PrefPage class // Map the page path to its widget diff --git a/radiant/ui/prefdialog/PrefPage.cpp b/radiant/ui/prefdialog/PrefPage.cpp index c9bea3e948..9b5d766d8e 100644 --- a/radiant/ui/prefdialog/PrefPage.cpp +++ b/radiant/ui/prefdialog/PrefPage.cpp @@ -8,15 +8,14 @@ #include "PreferenceItem.h" -namespace ui +namespace ui { PrefPage::PrefPage(wxWindow* parent, const IPreferencePage& settingsPage) : - wxScrolledWindow(parent, wxID_ANY), + wxPanel(parent, wxID_ANY), _settingsPage(settingsPage) { // Create the overall panel - SetScrollRate(0, 3); SetSizer(new wxBoxSizer(wxVERTICAL)); // 12 pixel border @@ -68,7 +67,7 @@ void PrefPage::createItemWidgets(const IPreferenceItemBase::Ptr& item) { // Construct a generic item and pass the common values PreferenceItem widget(this, item->getRegistryKey(), _registryBuffer, _resetValuesSignal); - + // Switch on the item type if (std::dynamic_pointer_cast(item)) { @@ -114,7 +113,7 @@ void PrefPage::createItemWidgets(const IPreferenceItemBase::Ptr& item) { auto info = std::dynamic_pointer_cast(item); - wxWindow* slider = widget.createSlider(info->getLower(), info->getUpper(), + wxWindow* slider = widget.createSlider(info->getLower(), info->getUpper(), info->getStepIncrement(), info->getPageIncrement()); appendNamedWidget(item->getLabel(), slider); diff --git a/radiant/ui/prefdialog/PrefPage.h b/radiant/ui/prefdialog/PrefPage.h index 7ca6e2d1dc..242e5957a9 100644 --- a/radiant/ui/prefdialog/PrefPage.h +++ b/radiant/ui/prefdialog/PrefPage.h @@ -14,10 +14,8 @@ namespace ui * A preference page as inserted into the PrefDialog's treebook control. * Each PrefPage renders the items found in the assigned settings::PreferencePage. */ -class PrefPage : - public wxScrolledWindow +class PrefPage: public wxPanel { -private: // The settings page we're representing const IPreferencePage& _settingsPage; diff --git a/radiant/xyview/GlobalXYWnd.cpp b/radiant/xyview/GlobalXYWnd.cpp index d05096853d..37368ec0a5 100644 --- a/radiant/xyview/GlobalXYWnd.cpp +++ b/radiant/xyview/GlobalXYWnd.cpp @@ -140,14 +140,7 @@ void XYWndManager::constructPreferences() page.appendCheckBox(_("Update Views on Camera Movement"), RKEY_CAMERA_XY_UPDATE); page.appendCheckBox(_("Show Crosshairs"), RKEY_SHOW_CROSSHAIRS); page.appendCheckBox(_("Show Grid"), RKEY_SHOW_GRID); - page.appendCheckBox(_("Show Size Info"), RKEY_SHOW_SIZE_INFO); - page.appendCheckBox(_("Show Entity Angle Arrow"), RKEY_SHOW_ENTITY_ANGLES); - page.appendCheckBox(_("Show Entity Names"), RKEY_SHOW_ENTITY_NAMES); page.appendCheckBox(_("Show Blocks"), RKEY_SHOW_BLOCKS); - page.appendCheckBox(_("Show Coordinates"), RKEY_SHOW_COORDINATES); - page.appendCheckBox(_("Show Axes"), RKEY_SHOW_AXES); - page.appendCheckBox(_("Show Window Outline"), RKEY_SHOW_OUTLINE); - page.appendCheckBox(_("Show Workzone"), RKEY_SHOW_WORKZONE); page.appendCheckBox(_("Translate Manipulator always constrained to Axis"), RKEY_TRANSLATE_CONSTRAINED); page.appendCheckBox(_("Higher Selection Priority for Entities"), RKEY_HIGHER_ENTITY_PRIORITY); page.appendSpinner(_("Maximum Zoom Factor"), RKEY_MAX_ZOOM_FACTOR, 1, 65536, 0); From 5c22066867d5327a98e2cb4d9daac3665ee73f79 Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Wed, 17 Jul 2024 19:39:30 +0100 Subject: [PATCH 48/49] #6514: remove placeholder "Game" page from preferences dialog It's been 7 years since this placeholder was added; I doubt anyone's still looking for the game settings here. --- radiantcore/settings/GameManager.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/radiantcore/settings/GameManager.cpp b/radiantcore/settings/GameManager.cpp index 1ed0078234..a43fc16056 100644 --- a/radiantcore/settings/GameManager.cpp +++ b/radiantcore/settings/GameManager.cpp @@ -118,10 +118,6 @@ void Manager::initialiseModule(const IApplicationContext& ctx) // The UI will call applyConfig on its own showGameSetupDialog(); } - - // Add a legacy note to the preference dialog for folks who are looking for the old settings page - IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Game")); - page.appendLabel(_("This page has been moved!\nPlease use the game settings dialog in the menu: File > Game/Project Setup...")); } const std::string& Manager::getModPath() const From ffe51db1489ab717260c33c68d899fb52c6bd389 Mon Sep 17 00:00:00 2001 From: Matthew Mott Date: Wed, 17 Jul 2024 19:52:38 +0100 Subject: [PATCH 49/49] #6514: remove "Settings" root node from preference page tree All preference pages exist at the same level in the tree; this empty root node was just a waste of space. --- plugins/dm.gui/plugin.cpp | 2 +- plugins/vcs/GitModule.cpp | 8 ++++---- radiant/camera/CameraSettings.cpp | 2 +- radiant/map/AutoSaveTimer.cpp | 2 +- radiant/settings/LocalisationModule.cpp | 2 +- radiant/ui/mainframe/MainFrame.cpp | 4 ++-- radiant/ui/texturebrowser/TextureBrowserManager.cpp | 2 +- radiant/xyview/GlobalXYWnd.cpp | 2 +- radiantcore/brush/BrushModule.cpp | 2 +- radiantcore/clipper/Clipper.cpp | 2 +- radiantcore/grid/GridManager.cpp | 2 +- radiantcore/map/autosaver/AutoSaver.cpp | 2 +- radiantcore/map/mru/MRU.cpp | 2 +- radiantcore/model/ModelFormatManager.cpp | 2 +- radiantcore/patch/PatchModule.cpp | 2 +- radiantcore/selection/RadiantSelectionSystem.cpp | 4 ++-- radiantcore/settings/PreferenceSystem.h | 4 +--- radiantcore/undo/UndoSystemFactory.cpp | 2 +- 18 files changed, 23 insertions(+), 25 deletions(-) diff --git a/plugins/dm.gui/plugin.cpp b/plugins/dm.gui/plugin.cpp index 81467c9663..4607d4c292 100644 --- a/plugins/dm.gui/plugin.cpp +++ b/plugins/dm.gui/plugin.cpp @@ -95,7 +95,7 @@ class GuiModule : void constructPreferences() { // Add a page to the given group - IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Settings/Readable Editor")); + IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Readable Editor")); ComboBoxValueList options; diff --git a/plugins/vcs/GitModule.cpp b/plugins/vcs/GitModule.cpp index 425991d461..340d60b255 100644 --- a/plugins/vcs/GitModule.cpp +++ b/plugins/vcs/GitModule.cpp @@ -36,7 +36,7 @@ ArchiveTextFilePtr GitModule::openTextFile(const std::string& vcsUri) } auto tree = _repository->getTreeByRevision(vcs::getVcsRevision(vcsUri)); - + return tree->openTextFile(vcs::getVcsFilePath(vcsUri), *_repository); } catch (const git::GitException& ex) @@ -54,7 +54,7 @@ const std::string& GitModule::getName() const const StringSet& GitModule::getDependencies() const { - static StringSet _dependencies{ MODULE_MAINFRAME, MODULE_STATUSBARMANAGER, + static StringSet _dependencies{ MODULE_MAINFRAME, MODULE_STATUSBARMANAGER, MODULE_PREFERENCESYSTEM, MODULE_MAP, MODULE_VERSION_CONTROL_MANAGER }; return _dependencies; } @@ -70,7 +70,7 @@ void GitModule::initialiseModule(const IApplicationContext& ctx) auto modPath = GlobalGameManager().getModPath(); _repository = std::make_unique(modPath); - + if (_repository->isOk()) { rMessage() << "Opened repository at " << modPath << std::endl; @@ -123,7 +123,7 @@ void GitModule::fetch(const cmd::ArgumentList& args) void GitModule::createPreferencePage() { - auto& page = GlobalPreferenceSystem().getPage(_("Settings/Version Control")); + auto& page = GlobalPreferenceSystem().getPage(_("Version Control")); page.appendCheckBox(_("Enable Auto-Fetch"), RKEY_AUTO_FETCH_ENABLED); page.appendSpinner(_("Fetch Interval (Minutes)"), RKEY_AUTO_FETCH_INTERVAL, 0.25, 900, 2); diff --git a/radiant/camera/CameraSettings.cpp b/radiant/camera/CameraSettings.cpp index 483ce4ec69..b9b190f289 100644 --- a/radiant/camera/CameraSettings.cpp +++ b/radiant/camera/CameraSettings.cpp @@ -69,7 +69,7 @@ void CameraSettings::observeKey(const std::string& key) void CameraSettings::constructPreferencePage() { - IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Settings/Camera")); + IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Camera")); // Add the sliders for the movement and angle speed and connect them to the observer page.appendSlider(_("Movement Speed (game units)"), RKEY_MOVEMENT_SPEED, 1, MAX_CAMERA_SPEED, 1, 1); diff --git a/radiant/map/AutoSaveTimer.cpp b/radiant/map/AutoSaveTimer.cpp index 056da6ebf7..3bc51bf6f9 100644 --- a/radiant/map/AutoSaveTimer.cpp +++ b/radiant/map/AutoSaveTimer.cpp @@ -31,7 +31,7 @@ AutoSaveTimer::~AutoSaveTimer() void AutoSaveTimer::initialise() { // Add a page to the given group - IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Settings/Autosave")); + IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Autosave")); // Add the checkboxes and connect them with the registry key page.appendCheckBox(_("Enable Autosave"), RKEY_AUTOSAVE_ENABLED); diff --git a/radiant/settings/LocalisationModule.cpp b/radiant/settings/LocalisationModule.cpp index 5a55ea4a8a..f66b57deca 100644 --- a/radiant/settings/LocalisationModule.cpp +++ b/radiant/settings/LocalisationModule.cpp @@ -46,7 +46,7 @@ void LocalisationModule::initialiseModule(const IApplicationContext& ctx) GlobalRegistry().setAttribute(registryKey, "volatile", "1"); // don't save this to user.xml // Add Preferences - IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Settings/Language")); + IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Language")); page.appendCombo(_("Language"), registryKey, langs); page.appendLabel(_("Note: You'll need to restart DarkRadiant\nafter changing the language setting.")); diff --git a/radiant/ui/mainframe/MainFrame.cpp b/radiant/ui/mainframe/MainFrame.cpp index 014531bb58..bf1ee895ce 100644 --- a/radiant/ui/mainframe/MainFrame.cpp +++ b/radiant/ui/mainframe/MainFrame.cpp @@ -78,7 +78,7 @@ const StringSet& MainFrame::getDependencies() const void MainFrame::initialiseModule(const IApplicationContext& ctx) { // Add another page for Multi-Monitor stuff - IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Settings/Multi Monitor")); + IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Multi Monitor")); // Initialise the registry, if no key is set if (GlobalRegistry().get(RKEY_MULTIMON_START_MONITOR).empty()) @@ -129,7 +129,7 @@ void MainFrame::initialiseModule(const IApplicationContext& ctx) if (dwmEnableComposition) { // Add a page for Desktop Composition stuff - IPreferencePage& compatPage = GlobalPreferenceSystem().getPage(_("Settings/Compatibility")); + IPreferencePage& compatPage = GlobalPreferenceSystem().getPage(_("Compatibility")); compatPage.appendCheckBox(_("Disable Windows Desktop Composition"), RKEY_DISABLE_WIN_DESKTOP_COMP); diff --git a/radiant/ui/texturebrowser/TextureBrowserManager.cpp b/radiant/ui/texturebrowser/TextureBrowserManager.cpp index 0aefea83f1..bbd4a18566 100644 --- a/radiant/ui/texturebrowser/TextureBrowserManager.cpp +++ b/radiant/ui/texturebrowser/TextureBrowserManager.cpp @@ -83,7 +83,7 @@ void TextureBrowserManager::updateAllWindows() void TextureBrowserManager::registerPreferencePage() { // Add a page to the given group - IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Settings/Texture Browser")); + IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Texture Browser")); std::list textureScaleList { diff --git a/radiant/xyview/GlobalXYWnd.cpp b/radiant/xyview/GlobalXYWnd.cpp index 37368ec0a5..a9b9ad3de8 100644 --- a/radiant/xyview/GlobalXYWnd.cpp +++ b/radiant/xyview/GlobalXYWnd.cpp @@ -133,7 +133,7 @@ void XYWndManager::registerCommands() void XYWndManager::constructPreferences() { - IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Settings/Orthoview")); + IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Orthoview")); page.appendCheckBox(_("View chases Mouse Cursor during Drags"), RKEY_CHASE_MOUSE); page.appendSlider(_("Maximum Chase Mouse Speed"), RKEY_CHASE_MOUSE_CAP, 0, 512, 1, 16); diff --git a/radiantcore/brush/BrushModule.cpp b/radiantcore/brush/BrushModule.cpp index 061b2e5657..8e412743b2 100644 --- a/radiantcore/brush/BrushModule.cpp +++ b/radiantcore/brush/BrushModule.cpp @@ -28,7 +28,7 @@ namespace brush void BrushModuleImpl::constructPreferences() { // Add a page to the given group - IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Settings/Primitives")); + IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Primitives")); // Add the default texture scale preference and connect it to the according registryKey // Note: this should be moved somewhere else, I think diff --git a/radiantcore/clipper/Clipper.cpp b/radiantcore/clipper/Clipper.cpp index fe59b3aa23..b16d3e63d4 100644 --- a/radiantcore/clipper/Clipper.cpp +++ b/radiantcore/clipper/Clipper.cpp @@ -37,7 +37,7 @@ void Clipper::keyChanged() } void Clipper::constructPreferences() { - IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Settings/Clipper")); + IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Clipper")); page.appendCheckBox(_("Clipper tool uses caulk texture"), RKEY_CLIPPER_USE_CAULK); page.appendEntry(_("Caulk shader name"), RKEY_CLIPPER_CAULK_SHADER); diff --git a/radiantcore/grid/GridManager.cpp b/radiantcore/grid/GridManager.cpp index 907b5982f7..ee726f075a 100644 --- a/radiantcore/grid/GridManager.cpp +++ b/radiantcore/grid/GridManager.cpp @@ -127,7 +127,7 @@ ComboBoxValueList GridManager::getGridList() void GridManager::constructPreferences() { - IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Settings/Grid")); + IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Grid")); page.appendCombo(_("Default Grid Size"), RKEY_DEFAULT_GRID_SIZE, getGridList()); diff --git a/radiantcore/map/autosaver/AutoSaver.cpp b/radiantcore/map/autosaver/AutoSaver.cpp index 87e7807c20..612228dbb1 100644 --- a/radiantcore/map/autosaver/AutoSaver.cpp +++ b/radiantcore/map/autosaver/AutoSaver.cpp @@ -276,7 +276,7 @@ void AutoMapSaver::performAutosave() void AutoMapSaver::constructPreferences() { // Add a page to the given group - IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Settings/Autosave")); + IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Autosave")); page.appendCheckBox(_("Save Snapshots"), RKEY_AUTOSAVE_SNAPSHOTS_ENABLED); page.appendEntry(_("Snapshot Folder (absolute, or relative to Map Folder)"), RKEY_AUTOSAVE_SNAPSHOTS_FOLDER); diff --git a/radiantcore/map/mru/MRU.cpp b/radiantcore/map/mru/MRU.cpp index 6292c760aa..4a7900423c 100644 --- a/radiantcore/map/mru/MRU.cpp +++ b/radiantcore/map/mru/MRU.cpp @@ -63,7 +63,7 @@ void MRU::saveRecentFiles() void MRU::constructPreferences() { - IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Settings/Map Files")); + IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Map Files")); page.appendEntry(_("Number of most recently used files"), RKEY_MRU_LENGTH); page.appendCheckBox(_("Open last map on startup"), RKEY_LOAD_LAST_MAP); diff --git a/radiantcore/model/ModelFormatManager.cpp b/radiantcore/model/ModelFormatManager.cpp index a4f2700944..022ed0aaa2 100644 --- a/radiantcore/model/ModelFormatManager.cpp +++ b/radiantcore/model/ModelFormatManager.cpp @@ -56,7 +56,7 @@ void ModelFormatManager::postModuleInitialisation() if (!_exporters.empty()) { // Construct and Register the patch-related preferences - IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Settings/Model Export")); + IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Model Export")); ComboBoxValueList choices; diff --git a/radiantcore/patch/PatchModule.cpp b/radiantcore/patch/PatchModule.cpp index 894e4ef8e1..06d71e90bb 100644 --- a/radiantcore/patch/PatchModule.cpp +++ b/radiantcore/patch/PatchModule.cpp @@ -69,7 +69,7 @@ void PatchModule::initialiseModule(const IApplicationContext& ctx) registerPatchCommands(); // Construct and Register the patch-related preferences - IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Settings/Patch")); + IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Patch")); page.appendEntry(_("Patch Subdivide Threshold"), RKEY_PATCH_SUBDIVIDE_THRESHOLD); _patchTextureChanged = Patch::signal_patchTextureChanged().connect( diff --git a/radiantcore/selection/RadiantSelectionSystem.cpp b/radiantcore/selection/RadiantSelectionSystem.cpp index 572a770177..8c1679366e 100644 --- a/radiantcore/selection/RadiantSelectionSystem.cpp +++ b/radiantcore/selection/RadiantSelectionSystem.cpp @@ -79,7 +79,7 @@ void RadiantSelectionSystem::toggleSelectionFocus() { rMessage() << "Leaving selection focus mode" << std::endl; _selectionFocusActive = false; - + GlobalSceneGraph().root()->foreachNode([&](const scene::INodePtr& node) { node->setRenderState(scene::INode::RenderState::Active); @@ -1036,7 +1036,7 @@ void RadiantSelectionSystem::initialiseModule(const IApplicationContext& ctx) GlobalCommandSystem().addCommand("RotateSelectedEulerXYZ", selection::algorithm::rotateSelectedEulerXYZ, { cmd::ARGTYPE_VECTOR3 }); GlobalCommandSystem().addCommand("ScaleSelected", selection::algorithm::scaleSelectedCmd, { cmd::ARGTYPE_VECTOR3 }); - IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Settings/Selection")); + IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Selection")); page.appendCheckBox(_("Ignore light volume bounds when calculating default rotation pivot location"), SceneManipulationPivot::RKEY_DEFAULT_PIVOT_LOCATION_IGNORES_LIGHT_VOLUMES); diff --git a/radiantcore/settings/PreferenceSystem.h b/radiantcore/settings/PreferenceSystem.h index 02d6d927e4..01f6f7955b 100644 --- a/radiantcore/settings/PreferenceSystem.h +++ b/radiantcore/settings/PreferenceSystem.h @@ -6,10 +6,8 @@ namespace settings { -class PreferenceSystem : - public IPreferenceSystem +class PreferenceSystem: public IPreferenceSystem { -private: PreferencePagePtr _rootPage; public: diff --git a/radiantcore/undo/UndoSystemFactory.cpp b/radiantcore/undo/UndoSystemFactory.cpp index eecce1d768..5ca7b6e098 100644 --- a/radiantcore/undo/UndoSystemFactory.cpp +++ b/radiantcore/undo/UndoSystemFactory.cpp @@ -38,7 +38,7 @@ class UndoSystemFactory final : private: void constructPreferences() { - IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Settings/Undo System")); + IPreferencePage& page = GlobalPreferenceSystem().getPage(_("Undo System")); page.appendSpinner(_("Undo Queue Size"), RKEY_UNDO_QUEUE_SIZE, 0, 1024, 1); } };