micro-opt NetworkedAnimator drawables
sort before creating drawables
This commit is contained in:
parent
ed3793ab00
commit
7136c929ce
@ -106,7 +106,7 @@ StringList AnimatedPartSet::states(String const& stateTypeName) const {
|
|||||||
return m_stateTypes.get(stateTypeName).states.keys();
|
return m_stateTypes.get(stateTypeName).states.keys();
|
||||||
}
|
}
|
||||||
|
|
||||||
StringList AnimatedPartSet::parts() const {
|
StringList AnimatedPartSet::partNames() const {
|
||||||
return m_parts.keys();
|
return m_parts.keys();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,6 +148,14 @@ AnimatedPartSet::ActivePartInformation const& AnimatedPartSet::activePart(String
|
|||||||
return part.activePart;
|
return part.activePart;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StringMap<AnimatedPartSet::Part> const& AnimatedPartSet::constParts() const {
|
||||||
|
return m_parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringMap<AnimatedPartSet::Part>& AnimatedPartSet::parts() {
|
||||||
|
return m_parts;
|
||||||
|
}
|
||||||
|
|
||||||
void AnimatedPartSet::forEachActiveState(function<void(String const&, ActiveStateInformation const&)> callback) const {
|
void AnimatedPartSet::forEachActiveState(function<void(String const&, ActiveStateInformation const&)> callback) const {
|
||||||
for (auto const& p : m_stateTypes) {
|
for (auto const& p : m_stateTypes) {
|
||||||
const_cast<AnimatedPartSet*>(this)->freshenActiveState(const_cast<StateType&>(p.second));
|
const_cast<AnimatedPartSet*>(this)->freshenActiveState(const_cast<StateType&>(p.second));
|
||||||
|
@ -56,59 +56,7 @@ public:
|
|||||||
JsonObject properties;
|
JsonObject properties;
|
||||||
};
|
};
|
||||||
|
|
||||||
AnimatedPartSet();
|
enum AnimationMode {
|
||||||
AnimatedPartSet(Json config);
|
|
||||||
|
|
||||||
// Returns the available state types.
|
|
||||||
StringList stateTypes() const;
|
|
||||||
|
|
||||||
// If a state type is disabled, no parts will match against it even
|
|
||||||
// if they have entries for that state type.
|
|
||||||
void setStateTypeEnabled(String const& stateTypeName, bool enabled);
|
|
||||||
void setEnabledStateTypes(StringList const& stateTypeNames);
|
|
||||||
bool stateTypeEnabled(String const& stateTypeName) const;
|
|
||||||
|
|
||||||
// Returns the available states for the given state type.
|
|
||||||
StringList states(String const& stateTypeName) const;
|
|
||||||
|
|
||||||
StringList parts() const;
|
|
||||||
|
|
||||||
// Sets the active state for this state type. If the state is different than
|
|
||||||
// the previously set state, will start the new states animation off at the
|
|
||||||
// beginning. If alwaysStart is true, then starts the state animation off at
|
|
||||||
// the beginning even if no state change has occurred. Returns true if a
|
|
||||||
// state animation reset was done.
|
|
||||||
bool setActiveState(String const& stateTypeName, String const& stateName, bool alwaysStart = false);
|
|
||||||
|
|
||||||
// Restart this given state type's timer off at the beginning.
|
|
||||||
void restartState(String const& stateTypeName);
|
|
||||||
|
|
||||||
ActiveStateInformation const& activeState(String const& stateTypeName) const;
|
|
||||||
ActivePartInformation const& activePart(String const& partName) const;
|
|
||||||
|
|
||||||
// Function will be given the name of each state type, and the
|
|
||||||
// ActiveStateInformation for the active state for that state type.
|
|
||||||
void forEachActiveState(function<void(String const&, ActiveStateInformation const&)> callback) const;
|
|
||||||
|
|
||||||
// Function will be given the name of each part, and the
|
|
||||||
// ActivePartInformation for the active part.
|
|
||||||
void forEachActivePart(function<void(String const&, ActivePartInformation const&)> callback) const;
|
|
||||||
|
|
||||||
// Useful for serializing state changes. Since each set of states for a
|
|
||||||
// state type is ordered, it is possible to simply serialize and deserialize
|
|
||||||
// the state index for that state type.
|
|
||||||
size_t activeStateIndex(String const& stateTypeName) const;
|
|
||||||
bool setActiveStateIndex(String const& stateTypeName, size_t stateIndex, bool alwaysStart = false);
|
|
||||||
|
|
||||||
// Animate each state type forward 'dt' time, and either change state frames
|
|
||||||
// or transition to new states, depending on the config.
|
|
||||||
void update(float dt);
|
|
||||||
|
|
||||||
// Pushes all the animations into their final state
|
|
||||||
void finishAnimations();
|
|
||||||
|
|
||||||
private:
|
|
||||||
enum AnimationMode {
|
|
||||||
End,
|
End,
|
||||||
Loop,
|
Loop,
|
||||||
Transition
|
Transition
|
||||||
@ -148,6 +96,61 @@ private:
|
|||||||
bool activePartDirty;
|
bool activePartDirty;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
AnimatedPartSet();
|
||||||
|
AnimatedPartSet(Json config);
|
||||||
|
|
||||||
|
// Returns the available state types.
|
||||||
|
StringList stateTypes() const;
|
||||||
|
|
||||||
|
// If a state type is disabled, no parts will match against it even
|
||||||
|
// if they have entries for that state type.
|
||||||
|
void setStateTypeEnabled(String const& stateTypeName, bool enabled);
|
||||||
|
void setEnabledStateTypes(StringList const& stateTypeNames);
|
||||||
|
bool stateTypeEnabled(String const& stateTypeName) const;
|
||||||
|
|
||||||
|
// Returns the available states for the given state type.
|
||||||
|
StringList states(String const& stateTypeName) const;
|
||||||
|
|
||||||
|
StringList partNames() const;
|
||||||
|
|
||||||
|
// Sets the active state for this state type. If the state is different than
|
||||||
|
// the previously set state, will start the new states animation off at the
|
||||||
|
// beginning. If alwaysStart is true, then starts the state animation off at
|
||||||
|
// the beginning even if no state change has occurred. Returns true if a
|
||||||
|
// state animation reset was done.
|
||||||
|
bool setActiveState(String const& stateTypeName, String const& stateName, bool alwaysStart = false);
|
||||||
|
|
||||||
|
// Restart this given state type's timer off at the beginning.
|
||||||
|
void restartState(String const& stateTypeName);
|
||||||
|
|
||||||
|
ActiveStateInformation const& activeState(String const& stateTypeName) const;
|
||||||
|
ActivePartInformation const& activePart(String const& partName) const;
|
||||||
|
|
||||||
|
StringMap<Part> const& constParts() const;
|
||||||
|
StringMap<Part>& parts();
|
||||||
|
|
||||||
|
// Function will be given the name of each state type, and the
|
||||||
|
// ActiveStateInformation for the active state for that state type.
|
||||||
|
void forEachActiveState(function<void(String const&, ActiveStateInformation const&)> callback) const;
|
||||||
|
|
||||||
|
// Function will be given the name of each part, and the
|
||||||
|
// ActivePartInformation for the active part.
|
||||||
|
void forEachActivePart(function<void(String const&, ActivePartInformation const&)> callback) const;
|
||||||
|
|
||||||
|
// Useful for serializing state changes. Since each set of states for a
|
||||||
|
// state type is ordered, it is possible to simply serialize and deserialize
|
||||||
|
// the state index for that state type.
|
||||||
|
size_t activeStateIndex(String const& stateTypeName) const;
|
||||||
|
bool setActiveStateIndex(String const& stateTypeName, size_t stateIndex, bool alwaysStart = false);
|
||||||
|
|
||||||
|
// Animate each state type forward 'dt' time, and either change state frames
|
||||||
|
// or transition to new states, depending on the config.
|
||||||
|
void update(float dt);
|
||||||
|
|
||||||
|
// Pushes all the animations into their final state
|
||||||
|
void finishAnimations();
|
||||||
|
|
||||||
|
private:
|
||||||
static AnimationMode stringToAnimationMode(String const& string);
|
static AnimationMode stringToAnimationMode(String const& string);
|
||||||
|
|
||||||
void freshenActiveState(StateType& stateType);
|
void freshenActiveState(StateType& stateType);
|
||||||
|
@ -307,10 +307,18 @@ String NetworkedAnimator::state(String const& stateType) const {
|
|||||||
return m_animatedParts.activeState(stateType).stateName;
|
return m_animatedParts.activeState(stateType).stateName;
|
||||||
}
|
}
|
||||||
|
|
||||||
StringList NetworkedAnimator::parts() const {
|
StringMap<AnimatedPartSet::Part> const& NetworkedAnimator::constParts() const {
|
||||||
|
return m_animatedParts.constParts();
|
||||||
|
}
|
||||||
|
|
||||||
|
StringMap<AnimatedPartSet::Part>& NetworkedAnimator::parts() {
|
||||||
return m_animatedParts.parts();
|
return m_animatedParts.parts();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StringList NetworkedAnimator::partNames() const {
|
||||||
|
return m_animatedParts.partNames();
|
||||||
|
}
|
||||||
|
|
||||||
Json NetworkedAnimator::stateProperty(String const& stateType, String const& propertyName) const {
|
Json NetworkedAnimator::stateProperty(String const& stateType, String const& propertyName) const {
|
||||||
return m_animatedParts.activeState(stateType).properties.value(propertyName);
|
return m_animatedParts.activeState(stateType).properties.value(propertyName);
|
||||||
}
|
}
|
||||||
@ -573,6 +581,10 @@ List<Drawable> NetworkedAnimator::drawables(Vec2F const& position) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<pair<Drawable, float>> NetworkedAnimator::drawablesWithZLevel(Vec2F const& position) const {
|
List<pair<Drawable, float>> NetworkedAnimator::drawablesWithZLevel(Vec2F const& position) const {
|
||||||
|
size_t partCount = m_animatedParts.constParts().size();
|
||||||
|
if (!partCount)
|
||||||
|
return {};
|
||||||
|
|
||||||
List<Directives> baseProcessingDirectives = { m_processingDirectives.get() };
|
List<Directives> baseProcessingDirectives = { m_processingDirectives.get() };
|
||||||
for (auto& pair : m_effects) {
|
for (auto& pair : m_effects) {
|
||||||
auto const& effectState = pair.second;
|
auto const& effectState = pair.second;
|
||||||
@ -591,93 +603,103 @@ List<pair<Drawable, float>> NetworkedAnimator::drawablesWithZLevel(Vec2F const&
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<pair<Drawable, float>> drawables;
|
List<tuple<AnimatedPartSet::ActivePartInformation const*, String const*, float>> parts;
|
||||||
|
parts.reserve(partCount);
|
||||||
m_animatedParts.forEachActivePart([&](String const& partName, AnimatedPartSet::ActivePartInformation const& activePart) {
|
m_animatedParts.forEachActivePart([&](String const& partName, AnimatedPartSet::ActivePartInformation const& activePart) {
|
||||||
// Make sure we don't copy the original image
|
Maybe<float> maybeZLevel;
|
||||||
String fallback = "";
|
if (m_flipped.get()) {
|
||||||
Json jImage = activePart.properties.value("image", {});
|
if (auto maybeFlipped = activePart.properties.value("flippedZLevel").optFloat())
|
||||||
String const& image = jImage.isType(Json::Type::String) ? *jImage.stringPtr() : fallback;
|
maybeZLevel = *maybeFlipped;
|
||||||
|
}
|
||||||
|
if (!maybeZLevel)
|
||||||
|
maybeZLevel = activePart.properties.value("zLevel").optFloat();
|
||||||
|
|
||||||
bool centered = activePart.properties.value("centered").optBool().value(true);
|
parts.append(make_tuple(&activePart, &partName, maybeZLevel.value(0.0f)));
|
||||||
bool fullbright = activePart.properties.value("fullbright").optBool().value(false);
|
});
|
||||||
|
|
||||||
auto maybeZLevel = activePart.properties.value("zLevel").optFloat();
|
sort(parts, [](auto const& a, auto const& b) { return get<2>(a) < get<2>(b); });
|
||||||
if (m_flipped.get())
|
|
||||||
maybeZLevel = activePart.properties.value("flippedZLevel").optFloat().orMaybe(maybeZLevel);
|
|
||||||
float zLevel = maybeZLevel.value(0.0f);
|
|
||||||
|
|
||||||
size_t originalDirectivesSize = baseProcessingDirectives.size();
|
List<pair<Drawable, float>> drawables;
|
||||||
if (auto directives = activePart.properties.value("processingDirectives").optString()) {
|
drawables.reserve(partCount);
|
||||||
|
for (auto& entry : parts) {
|
||||||
|
auto& activePart = *get<0>(entry);
|
||||||
|
auto& partName = *get<1>(entry);
|
||||||
|
// Make sure we don't copy the original image
|
||||||
|
String fallback = "";
|
||||||
|
Json jImage = activePart.properties.value("image", {});
|
||||||
|
String const& image = jImage.isType(Json::Type::String) ? *jImage.stringPtr() : fallback;
|
||||||
|
|
||||||
|
bool centered = activePart.properties.value("centered").optBool().value(true);
|
||||||
|
bool fullbright = activePart.properties.value("fullbright").optBool().value(false);
|
||||||
|
|
||||||
|
size_t originalDirectivesSize = baseProcessingDirectives.size();
|
||||||
|
if (auto directives = activePart.properties.value("processingDirectives").optString()) {
|
||||||
|
baseProcessingDirectives.append(*directives);
|
||||||
|
}
|
||||||
|
|
||||||
|
Maybe<unsigned> frame;
|
||||||
|
String frameStr;
|
||||||
|
String frameIndexStr;
|
||||||
|
if (activePart.activeState) {
|
||||||
|
unsigned stateFrame = activePart.activeState->frame;
|
||||||
|
frame = stateFrame;
|
||||||
|
frameStr = static_cast<String>(toString(stateFrame + 1));
|
||||||
|
frameIndexStr = static_cast<String>(toString(stateFrame));
|
||||||
|
|
||||||
|
if (auto directives = activePart.activeState->properties.value("processingDirectives").optString()) {
|
||||||
baseProcessingDirectives.append(*directives);
|
baseProcessingDirectives.append(*directives);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Maybe<unsigned> frame;
|
auto const& partTags = m_partTags.get(partName);
|
||||||
String frameStr;
|
Maybe<String> processedImage = image.maybeLookupTagsView([&](StringView tag) -> StringView {
|
||||||
String frameIndexStr;
|
if (tag == "frame") {
|
||||||
if (activePart.activeState) {
|
if (frame)
|
||||||
unsigned stateFrame = activePart.activeState->frame;
|
return frameStr;
|
||||||
frame = stateFrame;
|
} else if (tag == "frameIndex") {
|
||||||
frameStr = static_cast<String>(toString(stateFrame + 1));
|
if (frame)
|
||||||
frameIndexStr = static_cast<String>(toString(stateFrame));
|
return frameIndexStr;
|
||||||
|
} else if (auto p = partTags.ptr(tag)) {
|
||||||
if (auto directives = activePart.activeState->properties.value("processingDirectives").optString()) {
|
return StringView(*p);
|
||||||
baseProcessingDirectives.append(*directives);
|
} else if (auto p = m_globalTags.ptr(tag)) {
|
||||||
}
|
return StringView(*p);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto const& partTags = m_partTags.get(partName);
|
return StringView("default");
|
||||||
Maybe<String> processedImage = image.maybeLookupTagsView([&](StringView tag) -> StringView {
|
|
||||||
if (tag == "frame") {
|
|
||||||
if (frame)
|
|
||||||
return frameStr;
|
|
||||||
} else if (tag == "frameIndex") {
|
|
||||||
if (frame)
|
|
||||||
return frameIndexStr;
|
|
||||||
} else if (auto p = partTags.ptr(tag)) {
|
|
||||||
return StringView(*p);
|
|
||||||
} else if (auto p = m_globalTags.ptr(tag)) {
|
|
||||||
return StringView(*p);
|
|
||||||
}
|
|
||||||
|
|
||||||
return StringView("default");
|
|
||||||
});
|
|
||||||
String const& usedImage = processedImage ? processedImage.get() : image;
|
|
||||||
|
|
||||||
if (!usedImage.empty() && usedImage[0] != ':' && usedImage[0] != '?') {
|
|
||||||
size_t hash = hashOf(usedImage);
|
|
||||||
auto find = m_cachedPartDrawables.find(partName);
|
|
||||||
bool fail = find == m_cachedPartDrawables.end() || find->second.first != hash;
|
|
||||||
if (fail) {
|
|
||||||
String relativeImage;
|
|
||||||
if (usedImage[0] != '/')
|
|
||||||
relativeImage = AssetPath::relativeTo(m_relativePath, usedImage);
|
|
||||||
|
|
||||||
Drawable drawable = Drawable::makeImage(!relativeImage.empty() ? relativeImage : usedImage, 1.0f / TilePixels, centered, Vec2F());
|
|
||||||
if (find == m_cachedPartDrawables.end())
|
|
||||||
find = m_cachedPartDrawables.emplace(partName, std::pair{ hash, std::move(drawable) }).first;
|
|
||||||
else {
|
|
||||||
find->second.first = hash;
|
|
||||||
find->second.second = std::move(drawable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Drawable drawable = find->second.second;
|
|
||||||
auto& imagePart = drawable.imagePart();
|
|
||||||
for (Directives const& directives : baseProcessingDirectives)
|
|
||||||
imagePart.addDirectives(directives, centered);
|
|
||||||
drawable.transform(partTransformation(partName));
|
|
||||||
drawable.transform(globalTransformation());
|
|
||||||
drawable.fullbright = fullbright;
|
|
||||||
drawable.translate(position);
|
|
||||||
|
|
||||||
drawables.append({std::move(drawable), zLevel});
|
|
||||||
}
|
|
||||||
|
|
||||||
baseProcessingDirectives.resize(originalDirectivesSize);
|
|
||||||
});
|
});
|
||||||
|
String const& usedImage = processedImage ? processedImage.get() : image;
|
||||||
|
|
||||||
sort(drawables, [](auto const& a, auto const& b) { return a.second < b.second; });
|
if (!usedImage.empty() && usedImage[0] != ':' && usedImage[0] != '?') {
|
||||||
|
size_t hash = hashOf(usedImage);
|
||||||
|
auto find = m_cachedPartDrawables.find(partName);
|
||||||
|
if (find == m_cachedPartDrawables.end() || find->second.first != hash) {
|
||||||
|
String relativeImage;
|
||||||
|
if (usedImage[0] != '/')
|
||||||
|
relativeImage = AssetPath::relativeTo(m_relativePath, usedImage);
|
||||||
|
|
||||||
|
Drawable drawable = Drawable::makeImage(!relativeImage.empty() ? relativeImage : usedImage, 1.0f / TilePixels, centered, Vec2F());
|
||||||
|
if (find == m_cachedPartDrawables.end())
|
||||||
|
find = m_cachedPartDrawables.emplace(partName, std::pair{ hash, std::move(drawable) }).first;
|
||||||
|
else {
|
||||||
|
find->second.first = hash;
|
||||||
|
find->second.second = std::move(drawable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Drawable drawable = find->second.second;
|
||||||
|
auto& imagePart = drawable.imagePart();
|
||||||
|
for (Directives const& directives : baseProcessingDirectives)
|
||||||
|
imagePart.addDirectives(directives, centered);
|
||||||
|
drawable.transform(partTransformation(partName));
|
||||||
|
drawable.transform(globalTransformation());
|
||||||
|
drawable.fullbright = fullbright;
|
||||||
|
drawable.translate(position);
|
||||||
|
|
||||||
|
drawables.append({std::move(drawable), get<2>(entry)});
|
||||||
|
}
|
||||||
|
|
||||||
|
baseProcessingDirectives.resize(originalDirectivesSize);
|
||||||
|
}
|
||||||
|
|
||||||
return drawables;
|
return drawables;
|
||||||
}
|
}
|
||||||
@ -981,7 +1003,7 @@ void NetworkedAnimator::setupNetStates() {
|
|||||||
|
|
||||||
addNetElement(&m_globalTags);
|
addNetElement(&m_globalTags);
|
||||||
|
|
||||||
for (auto const& part : sorted(m_animatedParts.parts()))
|
for (auto const& part : sorted(m_animatedParts.partNames()))
|
||||||
addNetElement(&m_partTags[part]);
|
addNetElement(&m_partTags[part]);
|
||||||
|
|
||||||
for (auto& pair : m_stateInfo) {
|
for (auto& pair : m_stateInfo) {
|
||||||
|
@ -82,7 +82,9 @@ public:
|
|||||||
bool setState(String const& stateType, String const& state, bool startNew = false);
|
bool setState(String const& stateType, String const& state, bool startNew = false);
|
||||||
String state(String const& stateType) const;
|
String state(String const& stateType) const;
|
||||||
|
|
||||||
StringList parts() const;
|
StringMap<AnimatedPartSet::Part> const& constParts() const;
|
||||||
|
StringMap<AnimatedPartSet::Part>& parts();
|
||||||
|
StringList partNames() const;
|
||||||
|
|
||||||
// Queries, if it exists, a property value from the underlying
|
// Queries, if it exists, a property value from the underlying
|
||||||
// AnimatedPartSet for the given state or part. If the property does not
|
// AnimatedPartSet for the given state or part. If the property does not
|
||||||
|
@ -251,7 +251,7 @@ Maybe<Direction> ActiveItem::facingDirection() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<Drawable> ActiveItem::handDrawables() const {
|
List<Drawable> ActiveItem::handDrawables() const {
|
||||||
if (m_itemAnimator.parts().empty()) {
|
if (m_itemAnimator.constParts().empty()) {
|
||||||
auto drawables = Item::iconDrawables();
|
auto drawables = Item::iconDrawables();
|
||||||
Drawable::scaleAll(drawables, 1.0f / TilePixels);
|
Drawable::scaleAll(drawables, 1.0f / TilePixels);
|
||||||
return drawables;
|
return drawables;
|
||||||
|
Loading…
Reference in New Issue
Block a user