Skip to content

Commit

Permalink
#5622: Child primitive diff algorithm
Browse files Browse the repository at this point in the history
  • Loading branch information
codereader committed May 24, 2021
1 parent dd8a7e6 commit 383a01a
Show file tree
Hide file tree
Showing 5 changed files with 824 additions and 112 deletions.
92 changes: 81 additions & 11 deletions libs/scene/SceneGraphComparer.cpp
Expand Up @@ -11,6 +11,16 @@
namespace scene
{

namespace
{
inline std::string getEntityName(const scene::INodePtr& node)
{
auto entity = Node_getEntity(node);

return entity->isWorldspawn() ? "worldspawn" : entity->getKeyValue("name");
}
}

void SceneGraphComparer::compare()
{
auto sourceEntities = collectEntityFingerprints(_source);
Expand All @@ -37,8 +47,7 @@ void SceneGraphComparer::compare()
}
else
{
auto entityName = Node_getEntity(sourceEntity.second)->getKeyValue("name");

auto entityName = getEntityName(sourceEntity.second);
sourceMismatches.emplace(entityName, EntityMismatch{ sourceEntity.first, sourceEntity.second, entityName });
}
}
Expand All @@ -51,8 +60,7 @@ void SceneGraphComparer::compare()
// Matching nodes have already been checked in the above loop
if (sourceEntities.count(baseEntity.first) == 0)
{
auto entityName = Node_getEntity(baseEntity.second)->getKeyValue("name");

auto entityName = getEntityName(baseEntity.second);
baseMismatches.emplace(entityName, EntityMismatch{ baseEntity.first, baseEntity.second, entityName });
}
}
Expand Down Expand Up @@ -102,6 +110,9 @@ void SceneGraphComparer::processDifferingEntities(const EntityMismatchByName& so

// Analyse the key values
entityDiff.differingKeyValues = compareKeyValues(sourceNode, baseNode);

// Analyse the child nodes
entityDiff.differingChildren = compareChildNodes(sourceNode, baseNode);
}

for (const auto& mismatch : missingInSource)
Expand Down Expand Up @@ -210,18 +221,61 @@ std::list<ComparisonResult::KeyValueDifference> SceneGraphComparer::compareKeyVa
return result;
}

SceneGraphComparer::Fingerprints SceneGraphComparer::collectEntityFingerprints(const scene::INodePtr& root)
std::list<ComparisonResult::PrimitiveDifference> SceneGraphComparer::compareChildNodes(
const scene::INodePtr& sourceNode, const scene::INodePtr& baseNode)
{
Fingerprints result;
std::list<ComparisonResult::PrimitiveDifference> result;

auto sourceChildren = collectPrimitiveFingerprints(sourceNode);
auto baseChildren = collectPrimitiveFingerprints(baseNode);

root->foreachNode([&](const scene::INodePtr& node)
std::vector<Fingerprints::value_type> missingInSource;
std::vector<Fingerprints::value_type> missingInBase;

auto compareFingerprint = [](const Fingerprints::value_type& left, const Fingerprints::value_type& right)
{
return left.first < right.first;
};

std::set_difference(sourceChildren.begin(), sourceChildren.end(),
baseChildren.begin(), baseChildren.end(), std::back_inserter(missingInBase), compareFingerprint);
std::set_difference(baseChildren.begin(), baseChildren.end(),
sourceChildren.begin(), sourceChildren.end(), std::back_inserter(missingInSource), compareFingerprint);

for (const auto& pair : missingInBase)
{
if (node->getNodeType() != scene::INode::Type::Entity)
result.emplace_back(ComparisonResult::PrimitiveDifference
{
return true;
}
pair.first,
pair.second,
ComparisonResult::PrimitiveDifference::Type::PrimitiveAdded
});
}

for (const auto& pair : missingInSource)
{
result.emplace_back(ComparisonResult::PrimitiveDifference
{
pair.first,
pair.second,
ComparisonResult::PrimitiveDifference::Type::PrimitiveRemoved
});
}

return result;
}

SceneGraphComparer::Fingerprints SceneGraphComparer::collectNodeFingerprints(const scene::INodePtr& parent,
const std::function<bool(const scene::INodePtr& node)>& nodePredicate)
{
Fingerprints result;

parent->foreachNode([&](const scene::INodePtr& node)
{
if (!nodePredicate(node)) return true; // predicate says "skip"

auto comparable = std::dynamic_pointer_cast<scene::IComparableNode>(node);
assert(comparable);

if (!comparable) return true; // skip

Expand All @@ -230,7 +284,7 @@ SceneGraphComparer::Fingerprints SceneGraphComparer::collectEntityFingerprints(c

if (!insertResult.second)
{
rWarning() << "More than one entity with the same fingerprint found in the map " << node->name() << std::endl;
rWarning() << "More than one node with the same fingerprint found in the parent node with name " << parent->name() << std::endl;
}

return true;
Expand All @@ -239,4 +293,20 @@ SceneGraphComparer::Fingerprints SceneGraphComparer::collectEntityFingerprints(c
return result;
}

SceneGraphComparer::Fingerprints SceneGraphComparer::collectPrimitiveFingerprints(const scene::INodePtr& parent)
{
return collectNodeFingerprints(parent, [](const scene::INodePtr& node)
{
return node->getNodeType() == scene::INode::Type::Brush || node->getNodeType() == scene::INode::Type::Patch;
});
}

SceneGraphComparer::Fingerprints SceneGraphComparer::collectEntityFingerprints(const scene::INodePtr& root)
{
return collectNodeFingerprints(root, [](const scene::INodePtr& node)
{
return node->getNodeType() == scene::INode::Type::Entity;
});
}

}
49 changes: 43 additions & 6 deletions libs/scene/SceneGraphComparer.h
Expand Up @@ -10,10 +10,18 @@
namespace scene
{

struct ComparisonResult
class ComparisonResult
{
public:
using Ptr = std::shared_ptr<ComparisonResult>;

private:
// This result instance will hold references to the root nodes of the compared graph
// to ensure the graph stays alive as long as the result
scene::IMapRootNodePtr _sourceRoot;
scene::IMapRootNodePtr _baseRoot;

public:
// Represents a matching node pair
struct Match
{
Expand All @@ -29,14 +37,28 @@ struct ComparisonResult

enum class Type
{
KeyValueAdded, // key is present on the source entity, but not on the target
KeyValueRemoved, // key is present on the target entity, but not on the source
KeyValueAdded, // key is present on the source entity, but not on the base
KeyValueRemoved, // key is present on the base entity, but not on the source
KeyValueChanged, // key present on both, but value is different
};

Type type;
};

struct PrimitiveDifference
{
std::string fingerprint;
scene::INodePtr node;

enum class Type
{
PrimitiveAdded, // child is present on the source entity, but not on the base
PrimitiveRemoved, // child is present on the base entity, but not on the source
};

Type type;
};

struct EntityDifference
{
std::string fingerprint;
Expand All @@ -53,8 +75,16 @@ struct ComparisonResult
Type type;

std::list<KeyValueDifference> differingKeyValues;

std::list<PrimitiveDifference> differingChildren;
};

public:
ComparisonResult(const scene::IMapRootNodePtr& sourceRoot, const scene::IMapRootNodePtr& baseRoot) :
_sourceRoot(sourceRoot),
_baseRoot(baseRoot)
{}

// The collection of entities with the same fingerprint value
std::list<Match> equivalentEntities;

Expand Down Expand Up @@ -86,7 +116,7 @@ class SceneGraphComparer
SceneGraphComparer(const scene::IMapRootNodePtr& source, const scene::IMapRootNodePtr& base) :
_source(source),
_base(base),
_result(new ComparisonResult)
_result(new ComparisonResult(_source, _base))
{}

void compare();
Expand All @@ -97,12 +127,19 @@ class SceneGraphComparer
}

private:
void processDifferingEntities(const EntityMismatchByName& sourceMismatches, const EntityMismatchByName& targetMismatches);
void processDifferingEntities(const EntityMismatchByName& sourceMismatches, const EntityMismatchByName& baseMismatches);

Fingerprints collectEntityFingerprints(const scene::INodePtr& root);
Fingerprints collectPrimitiveFingerprints(const scene::INodePtr& parent);

Fingerprints collectNodeFingerprints(const scene::INodePtr& parent,
const std::function<bool(const scene::INodePtr& node)>& nodePredicate);

std::list<ComparisonResult::KeyValueDifference> compareKeyValues(
const scene::INodePtr& sourceNode, const scene::INodePtr& targetNode);
const scene::INodePtr& sourceNode, const scene::INodePtr& baseNode);

std::list<ComparisonResult::PrimitiveDifference> compareChildNodes(
const scene::INodePtr& sourceNode, const scene::INodePtr& baseNode);
};

}
16 changes: 16 additions & 0 deletions test/MapMerging.cpp
Expand Up @@ -293,6 +293,7 @@ TEST_F(MapMergeTest, DetectAddedEntities)

// light_3 start has been added to the changed map
EXPECT_TRUE(resultHasEntityDifference(result, "light_3", scene::ComparisonResult::EntityDifference::Type::EntityMissingInBase));
EXPECT_TRUE(resultHasEntityDifference(result, "func_static_2", scene::ComparisonResult::EntityDifference::Type::EntityMissingInBase));
}

TEST_F(MapMergeTest, DetectAddedKeyValues)
Expand Down Expand Up @@ -333,4 +334,19 @@ TEST_F(MapMergeTest, DetectChangedKeyValues)
EXPECT_TRUE(hasKeyValueDifference(diff, "origin", "280 160 0", scene::ComparisonResult::KeyValueDifference::Type::KeyValueChanged));
}

TEST_F(MapMergeTest, DetectAddedChildPrimitives)
{
auto result = performComparison("maps/fingerprinting.mapx", _context.getTestProjectPath() + "maps/fingerprinting_2.mapx");

// func_static_1 has changed primitived
auto diff = getEntityDifference(result, "func_static_30");

EXPECT_EQ(diff.type, scene::ComparisonResult::EntityDifference::Type::EntityPresentButDifferent);
EXPECT_EQ(diff.differingChildren.size(), 1);
EXPECT_EQ(diff.differingChildren.front().type, scene::ComparisonResult::PrimitiveDifference::Type::PrimitiveAdded);
EXPECT_EQ(diff.differingChildren.front().node->getNodeType(), scene::INode::Type::Brush);


}

}

0 comments on commit 383a01a

Please sign in to comment.