Navigation Menu

Skip to content

Commit

Permalink
Move attachment information from IEntityClass to Entity
Browse files Browse the repository at this point in the history
Attachments are per-entity, not per-entity class (they could even be defined
manually via spawnargs on a single entity), so this information needs to be on
the Entity/SpawnArgs objects.
  • Loading branch information
Matthew Mott committed Jan 30, 2021
1 parent a08b50a commit 3a2cf88
Show file tree
Hide file tree
Showing 9 changed files with 269 additions and 258 deletions.
36 changes: 4 additions & 32 deletions include/ieclass.h
Expand Up @@ -200,9 +200,10 @@ typedef std::shared_ptr<IEntityClass> IEntityClassPtr;
typedef std::shared_ptr<const IEntityClass> IEntityClassConstPtr;

/**
* Entity class interface. An entity class represents a single type
* of entity that can be created by the EntityCreator. Entity classes are parsed
* from .DEF files during startup.
* \brief Entity class interface.
*
* An entity class represents a single type of entity that can be created by
* the EntityCreator. Entity classes are parsed from .DEF files during startup.
*
* Entity class attribute names are compared case-insensitively, as in the
* Entity class.
Expand All @@ -226,35 +227,6 @@ class IEntityClass
/// Query whether this entity class represents a light.
virtual bool isLight() const = 0;

/* ENTITY ATTACHMENTS */

/// Details of an attached entity
struct Attachment
{
/// Entity class of the attached entity
std::string eclass;

/// Vector offset where the attached entity should appear
Vector3 offset;
};

/// A functor which can received Attachments
using AttachmentFunc = std::function<void(const Attachment&)>;

/**
* \brief
* Iterate over attached entities, if any.
*
* Each entity class 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;

/* ENTITY CLASS SIZE */

/// Query whether this entity has a fixed size.
Expand Down
28 changes: 28 additions & 0 deletions include/ientity.h
Expand Up @@ -209,6 +209,34 @@ class Entity
* 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;

/// Vector offset where the attached entity should appear
Vector3 offset;
};

/// A functor which can receive Attachment objects
using AttachmentFunc = std::function<void(const Attachment&)>;

/**
* \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;
};

/// Interface for a INode subclass that contains an Entity
Expand Down
1 change: 1 addition & 0 deletions radiantcore/CMakeLists.txt
Expand Up @@ -28,6 +28,7 @@ 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
Expand Down
206 changes: 0 additions & 206 deletions radiantcore/eclass/Doom3EntityClass.cpp
Expand Up @@ -11,199 +11,6 @@
namespace eclass
{

namespace
{

// Constants
const std::string DEF_ATTACH = "def_attach";
const std::string NAME_ATTACH = "name_attach";
const std::string POS_ATTACH = "pos_attach";

const std::string ATTACH_POS_NAME = "attach_pos_name";
const std::string ATTACH_POS_ORIGIN = "attach_pos_origin";
const std::string ATTACH_POS_JOINT = "attach_pos_joint";
const std::string ATTACH_POS_ANGLES = "attach_pos_angles";

// Extract and return the string suffix for a key (which might be the empty
// string if there is no suffix). Returns false if the key did not match
// the prefix.
bool tryGetSuffixedKey(const std::string& key, const std::string& prefix, std::string& suffixedOutput)
{
if (string::istarts_with(key, prefix))
{
suffixedOutput = key.substr(prefix.length());
return true;
}

suffixedOutput.clear();
return false;
}

} // namespace

// Attachment helper object
class Doom3EntityClass::Attachments
{
// Name of the entity class being parsed (for debug/error purposes)
std::string _parentClassname;

// Any def_attached entities. Each attachment has an entity class, a
// position and optionally a name.
struct Attachment
{
// Class of entity that is attached
std::string className;

// Name of the entity that is attached
std::string name;

// Name of the position (AttachPos) at which the entity should be
// attached
std::string posName;
};

// Attached object map initially indexed by key suffix (e.g. "1" for
// "name_attach1"), then by name.
typedef std::map<std::string, Attachment> AttachedObjects;
AttachedObjects _objects;

// Positions at which def_attached entities can be attached.
struct AttachPos
{
// Name of this attachment position (referred to in the
// Attachment::posName variable)
std::string name;

// 3D offset position from our origin or the model joint, if a joint is
// specified
Vector3 origin;

// Rotation of the attached entity
Vector3 angles;

// Optional model joint relative to which the origin should be
// calculated
std::string joint;
};

// Attach position map initially indexed by key suffix (e.g. "_zhandr" for
// "attach_pos_name_zhandr"), then by name. It appears that only attachpos
// keys are using arbitrary strings instead of numeric suffixes, but we
// might as well treat everything the same way.
typedef std::map<std::string, AttachPos> AttachPositions;
AttachPositions _positions;

private:

template<typename Map> void reindexMapByName(Map& inputMap)
{
Map copy(inputMap);
inputMap.clear();

// Take each item from the copied map, and insert it into the original
// map using the name as the key.
for (typename Map::value_type pair : copy)
{
if (!pair.second.name.empty()) // ignore empty names
{
inputMap.insert(
typename Map::value_type(pair.second.name, pair.second)
);
}
}
}

public:

// Initialise and set classname
Attachments(const std::string& name)
: _parentClassname(name)
{ }

// Clear all data
void clear()
{
_objects.clear();
_positions.clear();
}

// Attempt to extract attachment data from the given key/value pair
void parseDefAttachKeys(const std::string& key, const std::string& value)
{
std::string suffix;

if (tryGetSuffixedKey(key, DEF_ATTACH, suffix))
{
_objects[suffix].className = value;
}
else if (tryGetSuffixedKey(key, NAME_ATTACH, suffix))
{
_objects[suffix].name = value;
}
else if (tryGetSuffixedKey(key, POS_ATTACH, suffix))
{
_objects[suffix].posName = value;
}
else if (tryGetSuffixedKey(key, ATTACH_POS_NAME, suffix))
{
_positions[suffix].name = value;
}
else if (tryGetSuffixedKey(key, ATTACH_POS_ORIGIN, suffix))
{
_positions[suffix].origin = string::convert<Vector3>(value);
}
else if (tryGetSuffixedKey(key, ATTACH_POS_ANGLES, suffix))
{
_positions[suffix].angles = string::convert<Vector3>(value);
}
else if (tryGetSuffixedKey(key, ATTACH_POS_JOINT, suffix))
{
_positions[suffix].joint = value;
}
}

// Post-process after attachment parsing
void validateAttachments()
{
// During parsing we indexed spawnargs by string suffix so that matching
// keys could be found. From now on we are no longer interested in the
// suffixes so we will re-build the maps indexed by name instead.
reindexMapByName(_objects);
reindexMapByName(_positions);

// Drop any attached objects that specify a non-existent position (I
// assume new positions cannot be dynamically created in game).
for (AttachedObjects::iterator i = _objects.begin();
i != _objects.end();
/* in-loop increment */)
{
if (_positions.find(i->second.posName) == _positions.end())
{
rWarning()
<< "[eclassmgr] Entity class '" << _parentClassname
<< "' tries to attach '" << i->first << "' at non-existent "
<< "position '" << i->second.posName << "'\n";

_objects.erase(i++);
}
else
{
++i;
}
}
}

// Iterate over attachments
void forEachAttachment(IEntityClass::AttachmentFunc func)
{
for (auto i = _objects.begin(); i != _objects.end(); ++i)
{
IEntityClass::Attachment a;
a.eclass = i->second.className;
}
}
};

const std::string Doom3EntityClass::DefaultWireShader("<0.3 0.3 1>");
const std::string Doom3EntityClass::DefaultFillShader("(0.3 0.3 1)");
const Vector3 Doom3EntityClass::DefaultEntityColour(0.3, 0.3, 1);
Expand All @@ -225,7 +32,6 @@ Doom3EntityClass::Doom3EntityClass(const std::string& name, const vfs::FileInfo&
_inheritanceResolved(false),
_modName("base"),
_emptyAttribute("", "", ""),
_attachments(new Attachments(name)),
_parseStamp(0)
{}

Expand All @@ -247,11 +53,6 @@ sigc::signal<void>& Doom3EntityClass::changedSignal()
return _changedSignal;
}

void Doom3EntityClass::forEachAttachment(AttachmentFunc func) const
{
_attachments->forEachAttachment(func);
}

bool Doom3EntityClass::isFixedSize() const
{
if (_fixedSize) {
Expand Down Expand Up @@ -519,8 +320,6 @@ void Doom3EntityClass::clear()
_inheritanceResolved = false;

_modName = "base";

_attachments->clear();
}

void Doom3EntityClass::parseEditorSpawnarg(const std::string& key,
Expand Down Expand Up @@ -594,9 +393,6 @@ void Doom3EntityClass::parseFromTokens(parser::DefTokeniser& tokeniser)
parseEditorSpawnarg(key, value);
}

// Try parsing this key/value with the Attachments manager
_attachments->parseDefAttachKeys(key, value);

// Add the EntityClassAttribute for this key/val
if (getAttribute(key).getType().empty())
{
Expand All @@ -619,8 +415,6 @@ void Doom3EntityClass::parseFromTokens(parser::DefTokeniser& tokeniser)
}
} // while true

_attachments->validateAttachments();

// Notify the observers
_changedSignal.emit();
}
Expand Down
5 changes: 0 additions & 5 deletions radiantcore/eclass/Doom3EntityClass.h
Expand Up @@ -101,10 +101,6 @@ class Doom3EntityClass
// The empty attribute
EntityClassAttribute _emptyAttribute;

// Helper object to manage attached entities
class Attachments;
std::unique_ptr<Attachments> _attachments;

// The time this def has been parsed
std::size_t _parseStamp;

Expand Down Expand Up @@ -159,7 +155,6 @@ class Doom3EntityClass
std::string getName() const override;
const IEntityClass* getParent() const override;
sigc::signal<void>& changedSignal() override;
void forEachAttachment(AttachmentFunc func) const override;
bool isFixedSize() const override;
AABB getBounds() const override;
bool isLight() const override;
Expand Down

0 comments on commit 3a2cf88

Please sign in to comment.