Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include "CharacterDisplayer.hpp"
- Display::CharacterDisplayer::CharacterDisplayer(DisplayInterface& display) :
- mDisplay(display),
- mResources(mDisplay),
- mCurrentAnimationElapsedTime(0)
- {
- }
- void Display::CharacterDisplayer::setCharacterResource(const std::string& name) {
- // clearing everything
- mBones.clear();
- mSlots.clear();
- mSkins.clear();
- mAnimations.clear();
- mCurrentAnimationElapsedTime = 0;
- // loading the YAML
- mCharacterInfos = YAML::Load(ExternalResource(name));
- // filling bones list structure
- for (auto& boneNode : mCharacterInfos[std::string("bones")]) {
- auto& boneInfos = mBones[boneNode[std::string("name")].as<std::string>()];
- // filling parent if possible
- { auto& parentBoneNode = boneNode[std::string("parent")];
- if (parentBoneNode.IsScalar())
- boneInfos.parentBone = parentBoneNode.as<std::string>();
- }
- // filling default informations
- { boneInfos.defaultSetup.x = 0;
- boneInfos.defaultSetup.y = 0;
- boneInfos.defaultSetup.rotation = 0;
- boneInfos.defaultSetup.scaleX = 1;
- boneInfos.defaultSetup.scaleY = 1;
- auto& nodeX = boneNode[std::string("x")];
- if (nodeX.IsScalar())
- boneInfos.defaultSetup.x = nodeX.as<float>();
- auto& nodeY = boneNode[std::string("y")];
- if (nodeY.IsScalar())
- boneInfos.defaultSetup.y = nodeY.as<float>();
- auto& nodeRotation = boneNode[std::string("rotation")];
- if (nodeRotation.IsScalar())
- boneInfos.defaultSetup.rotation = nodeRotation.as<float>() * 3.14159265f / 180.f;
- auto& nodeScaleX = boneNode[std::string("scaleX")];
- if (nodeScaleX.IsScalar())
- boneInfos.defaultSetup.scaleX = nodeScaleX.as<float>();
- auto& nodeScaleY = boneNode[std::string("scaleY")];
- if (nodeScaleY.IsScalar())
- boneInfos.defaultSetup.scaleY = nodeScaleY.as<float>();
- }
- }
- // filling slots list structure
- for (auto& slotNode : mCharacterInfos[std::string("slots")]) {
- auto& slotInfos = mSlots[slotNode[std::string("name")].as<std::string>()];
- // filling bone
- slotInfos.bone = slotNode[std::string("bone")].as<std::string>();
- // filling attachment if possible
- { auto& attachmentBoneNode = slotNode[std::string("attachment")];
- if (attachmentBoneNode.IsScalar())
- slotInfos.attributes.attachment = attachmentBoneNode.as<std::string>();
- }
- }
- // filling skins list structure
- for (auto& skinNode : mCharacterInfos[std::string("skins")]) {
- for (auto& skinSlotNode : skinNode.second) {
- for (auto& attachementNode : skinSlotNode.second) {
- // creating new attachment
- auto& attachmentInfos = mSkins[skinNode.first.as<std::string>()][skinSlotNode.first.as<std::string>()][attachementNode.first.as<std::string>()];
- // filling real name
- { auto& nameNode = attachementNode.second[std::string("name")];
- if (nameNode.IsScalar()) attachmentInfos.textureName = nameNode.as<std::string>();
- else attachmentInfos.textureName = attachementNode.first.as<std::string>();
- }
- // filling matrix
- { float x = 0;
- float y = 0;
- float scaleX = 1;
- float scaleY = 1;
- float rotation = 0;
- float width = 1;
- float height = 1;
- auto& nodeX = attachementNode.second[std::string("x")];
- if (nodeX.IsScalar())
- x = nodeX.as<float>();
- auto& nodeY = attachementNode.second[std::string("y")];
- if (nodeY.IsScalar())
- y = nodeY.as<float>();
- auto& nodeScaleX = attachementNode.second[std::string("scaleX")];
- if (nodeScaleX.IsScalar())
- scaleX = nodeScaleX.as<float>();
- auto& nodeScaleY = attachementNode.second[std::string("scaleY")];
- if (nodeScaleY.IsScalar())
- scaleY = nodeScaleY.as<float>();
- auto& nodeRotation = attachementNode.second[std::string("rotation")];
- if (nodeRotation.IsScalar())
- rotation = nodeRotation.as<float>() * 3.14159265f / 180.f;
- auto& nodeWidth = attachementNode.second[std::string("width")];
- if (nodeWidth.IsScalar())
- width = nodeWidth.as<float>();
- auto& nodeHeight = attachementNode.second[std::string("height")];
- if (nodeHeight.IsScalar())
- height = nodeHeight.as<float>();
- attachmentInfos.matrixToSlot = attachmentInfos.matrixToSlot.buildIdentityMatrix();
- attachmentInfos.matrixToSlot(1, 1) = cos(rotation);
- attachmentInfos.matrixToSlot(2, 2) = attachmentInfos.matrixToSlot(1, 1);
- attachmentInfos.matrixToSlot(1, 2) = sin(rotation);
- attachmentInfos.matrixToSlot(2, 1) = -attachmentInfos.matrixToSlot(1, 2);
- attachmentInfos.matrixToSlot(1, 1) *= scaleX * width;
- attachmentInfos.matrixToSlot(1, 2) *= scaleX * width;
- attachmentInfos.matrixToSlot(2, 1) *= scaleY * height;
- attachmentInfos.matrixToSlot(2, 2) *= scaleY * height;
- attachmentInfos.matrixToSlot(4, 1) = x;
- attachmentInfos.matrixToSlot(4, 2) = y;
- }
- // filling sprite displayer
- attachmentInfos.displayer = std::make_shared<SpriteDisplayer>(mDisplay);
- attachmentInfos.displayer->setTexture(mResources.loadTexture(attachmentInfos.textureName));
- attachmentInfos.displayer->setRectangleCoords(-0.5f, 0.5f, 0.5f, -0.5f);
- }
- }
- }
- // filling animations list structure
- for (auto& animationNode : mCharacterInfos[std::string("animations")]) {
- auto& animationInfos = mAnimations[animationNode.first.as<std::string>()];
- animationInfos.maxTime = 0;
- // dummy so that an animation is automatically selected
- mCurrentAnimation = animationNode.first.as<std::string>();
- // building timelines for bones
- for (auto& boneNode : animationNode.second[std::string("bones")]) {
- auto& timelineInfos = animationInfos.bonesTimelines[boneNode.first.as<std::string>()];
- // enumerating elements in timeline
- for (auto& timelineType : boneNode.second) {
- const auto type = timelineType.first.as<std::string>();
- BoneTimelineElement* previousElement = nullptr;
- for (auto& timelineElement : timelineType.second) {
- std::unique_ptr<BoneTimelineElement> element(new BoneTimelineElement);
- element->time = timelineElement[std::string("time")].as<float>();
- element->next = nullptr;
- element->curveFunction = parseCurveFunction(timelineElement[std::string("curve")]);
- // updating animation's max time
- if (animationInfos.maxTime < element->time)
- animationInfos.maxTime = element->time;
- // building the "apply" function
- if (type == "translate") {
- float x = 0;
- float y = 0;
- auto& nodeX = timelineElement[std::string("x")];
- if (nodeX.IsScalar()) x = nodeX.as<float>();
- auto& nodeY = timelineElement[std::string("y")];
- if (nodeY.IsScalar()) y = nodeY.as<float>();
- element->apply = [=](BoneAttributes& attr) { attr.x = x; attr.y = y; };
- } else if (type == "rotate") {
- float rotation = 0;
- auto& nodeRotation = timelineElement[std::string("angle")];
- if (nodeRotation.IsScalar()) rotation = nodeRotation.as<float>() * 3.14159265f / 180.f;
- element->apply = [=](BoneAttributes& attr) { attr.rotation = rotation; };
- } else if (type == "scale") {
- float x = 0;
- float y = 0;
- auto& nodeX = timelineElement[std::string("x")];
- if (nodeX.IsScalar()) x = nodeX.as<float>();
- auto& nodeY = timelineElement[std::string("y")];
- if (nodeY.IsScalar()) y = nodeY.as<float>();
- element->apply = [=](BoneAttributes& attr) { attr.scaleX = x; attr.scaleY = y; };
- }
- // inserting in list
- if (previousElement)
- previousElement->next = element.get();
- previousElement = element.get();
- timelineInfos.push_back(std::move(element));
- }
- }
- }
- // building timelines for slots
- for (auto& slotNode : animationNode.second[std::string("slots")]) {
- auto& timelineInfos = animationInfos.slotsTimelines[slotNode.first.as<std::string>()];
- // enumerating elements in timeline
- for (auto& timelineType : slotNode.second) {
- const auto type = timelineType.first.as<std::string>();
- SlotTimelineElement* previousElement = nullptr;
- for (auto& timelineElement : timelineType.second) {
- std::unique_ptr<SlotTimelineElement> element(new SlotTimelineElement);
- element->time = timelineElement[std::string("time")].as<float>();
- element->next = nullptr;
- element->curveFunction = parseCurveFunction(timelineElement[std::string("curve")]);
- // updating animation's max time
- if (animationInfos.maxTime < element->time)
- animationInfos.maxTime = element->time;
- // building the "apply" function
- if (type == "attachment") {
- const auto name = timelineElement[std::string("name")].as<std::string>();
- element->apply = [=](SlotAttributes& attr) { attr.attachment = name; };
- } else if (type == "color") {
- // TODO:
- }
- // inserting in list
- if (previousElement)
- previousElement->next = element.get();
- previousElement = element.get();
- timelineInfos.push_back(std::move(element));
- }
- }
- }
- }
- // setting default animation
- mCurrentAnimation = "walk";
- }
- void Display::CharacterDisplayer::draw(const Maths::Matrix<4>& matrix) const {
- auto currentAnimation = &mAnimations.at(mCurrentAnimation);
- mCurrentAnimationElapsedTime += 1.f / 60.f; // TODO:
- if (mCurrentAnimationElapsedTime > currentAnimation->maxTime)
- mCurrentAnimationElapsedTime -= currentAnimation->maxTime;
- // this variable will contain
- std::unordered_map<std::string,Maths::Matrix<4>> bonesAttributes;
- // updating bones
- for (auto& bone : mBones) {
- // this variable will contain the final attributes for this bone
- auto totalAttributes = bone.second.defaultSetup;
- //
- { const auto animationIter = currentAnimation->bonesTimelines.find(bone.first);
- if (animationIter != currentAnimation->bonesTimelines.end()) {
- for (auto& timelineElement : currentAnimation->bonesTimelines.at(bone.first)) {
- if (timelineElement->time > mCurrentAnimationElapsedTime || (timelineElement->next && timelineElement->next->time <= mCurrentAnimationElapsedTime))
- continue;
- // we have identified the current animation member
- BoneAttributes attributes;
- timelineElement->apply(attributes);
- // handling interpolation
- if (timelineElement->next) {
- const auto interpolationPercentage = timelineElement->curveFunction((mCurrentAnimationElapsedTime - timelineElement->time) / (timelineElement->next->time - timelineElement->time));
- BoneAttributes nextAttr;
- timelineElement->next->apply(nextAttr);
- attributes.x += interpolationPercentage * (nextAttr.x - attributes.x);
- attributes.y += interpolationPercentage * (nextAttr.y - attributes.y);
- attributes.rotation += interpolationPercentage * (nextAttr.rotation - attributes.rotation);
- attributes.scaleX += interpolationPercentage * (nextAttr.scaleX - attributes.scaleX);
- attributes.scaleY += interpolationPercentage * (nextAttr.scaleY - attributes.scaleY);
- }
- // commit to totalAttributes
- totalAttributes.x += attributes.x;
- totalAttributes.y += attributes.y;
- totalAttributes.rotation += attributes.rotation;
- totalAttributes.scaleX *= attributes.scaleX;
- totalAttributes.scaleY *= attributes.scaleY;
- }
- }
- }
- // computing matrix
- auto& matrix = bonesAttributes[bone.first];
- matrix = matrix.buildIdentityMatrix();
- matrix(4, 1) = totalAttributes.x;
- matrix(4, 2) = totalAttributes.y;
- matrix(1, 1) = cos(totalAttributes.rotation);
- matrix(2, 2) = matrix(1, 1);
- matrix(1, 2) = sin(totalAttributes.rotation);
- matrix(2, 1) = -matrix(1, 2);
- matrix(1, 1) *= totalAttributes.scaleX;
- matrix(1, 2) *= totalAttributes.scaleX;
- matrix(2, 1) *= totalAttributes.scaleY;
- matrix(2, 2) *= totalAttributes.scaleY;
- // multiplying by parent matrix
- if (!bone.second.parentBone.empty())
- matrix *= bonesAttributes.at(bone.second.parentBone);
- }
- // dezoom matrix
- Maths::Matrix<4> dezoomMatrix = Maths::Matrix<4>::buildIdentityMatrix();
- dezoomMatrix(1, 1) = 1.f / 500.f;
- dezoomMatrix(2, 2) = 1.f / 500.f;
- // updating slots
- for (auto& slot : mSlots) {
- SlotAttributes currentAttributes = slot.second.attributes;
- //
- { const auto animationIter = currentAnimation->slotsTimelines.find(slot.first);
- if (animationIter != currentAnimation->slotsTimelines.end()) {
- for (auto& timelineElement : currentAnimation->slotsTimelines.at(slot.first)) {
- if (timelineElement->time > mCurrentAnimationElapsedTime || (timelineElement->next && timelineElement->next->time <= mCurrentAnimationElapsedTime))
- continue;
- // we have identified the current animation member
- timelineElement->apply(currentAttributes);
- }
- }
- }
- // drawing
- if (!currentAttributes.attachment.empty()) {
- auto& attachment = mSkins.at("default").at(slot.first).at(currentAttributes.attachment);
- auto& boneMatrix = bonesAttributes.at(slot.second.bone);
- attachment.displayer->draw(attachment.matrixToSlot * boneMatrix * dezoomMatrix);
- }
- }
- }
- std::function<float (float)> Display::CharacterDisplayer::parseCurveFunction(const YAML::Node& curveNode) {
- // if not defined, assume linear
- if (!curveNode.IsDefined() || curveNode.IsNull())
- return [](float x) { return x; };
- // checking keywords "linear" and "stepped"
- if (curveNode.IsScalar()) {
- if (curveNode.as<std::string>() == "linear") return [](float x) -> float { return x; };
- if (curveNode.as<std::string>() == "stepped") return [](float) -> float { return 0.f; };
- }
- // bézier curve
- if (curveNode.IsSequence()) {
- const auto cx1 = curveNode[0].as<float>();
- const auto cy1 = curveNode[1].as<float>();
- const auto cx2 = curveNode[2].as<float>();
- const auto cy2 = curveNode[3].as<float>();
- return [=](float x) {
- auto previousX = 0;
- for (float t = 0; t <= 1; t += 0.01f) {
- const auto calculatedX = 3*cx1*t*(1-t)*(1-t) + 3*cx2*t*t*(1-t) + t*t*t;
- if (calculatedX < x)
- continue;
- const auto calculatedY = 3*cy1*t*(1-t)*(1-t) + 3*cy2*t*t*(1-t) + t*t*t;
- return calculatedY;
- }
- // TODO: assert false or something
- return x;
- };
- }
- throw "Unknown value for curve property";
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement