<commit_before>/**
 *  For conditions of distribution and use, see copyright notice in license.txt
 *
 *  @file   EC_HoveringText.cpp
 *  @brief  EC_HoveringText shows a hovering text attached to an entity.
 *  @note   The entity must EC_Placeable available in advance.
 */

#include "DebugOperatorNew.h"

#include "EC_HoveringText.h"
#include "Renderer.h"
#include "EC_Placeable.h"
#include "Entity.h"
#include "LoggingFunctions.h"
#include "Scene.h"
#include "Framework.h"
#include "OgreRenderingModule.h"
#include "OgreWorld.h"
#include "OgreMaterialUtils.h"
#include "AssetAPI.h"
#include "TextureAsset.h"

#include <Ogre.h>
#include <QFile>
#include <QPainter>
#include <QTimer>
#include <QTimeLine>

#include "MemoryLeakCheck.h"

EC_HoveringText::EC_HoveringText(Scene* scene) :
    IComponent(scene),
    font_(QFont("Arial", 100)),
    textColor_(Qt::black),
    billboardSet_(0),
    billboard_(0),
    usingGrad(this, "Use Gradient", false),
    text(this, "Text"),
    font(this, "Font", "Arial"),
    fontColor(this, "Font Color"),
    fontSize(this, "Font Size", 100),
    backgroundColor(this, "Background Color", Color(1.0f,1.0f,1.0f,0.0f)),
    position(this, "Position", float3(0.0f, 0.0f, 0.0f)),
    gradStart(this, "Gradient Start", Color(0.0f,0.0f,0.0f,1.0f)),
    gradEnd(this, "Gradient End", Color(1.0f,1.0f,1.0f,1.0f)),
    borderColor(this, "Border Color", Color(0.0f,0.0f,0.0f,0.0f)),
    borderThickness(this, "Border Thickness", 0.0),
    overlayAlpha(this, "Overlay Alpha", 1.0),
    width(this, "Width", 1.0),
    height(this, "Height", 1.0),
    texWidth(this, "Texture Width", 256),
    texHeight(this, "Texture Height", 256),
    cornerRadius(this, "Corner radius", float2(20.0, 20.0))
{
    if (scene)
        world_ = scene->GetWorld<OgreWorld>();
    connect(this, SIGNAL(ParentEntitySet()), this, SLOT(UpdateSignals()));
}

EC_HoveringText::~EC_HoveringText()
{
    if (texture_.get() != 0)
    {
        AssetAPI* asset = framework->Asset();
        asset->ForgetAsset(texture_,false);
    }
    Destroy();
}

void EC_HoveringText::Destroy()
{
    if (!ViewEnabled())
        return;

    if (!world_.expired())
    {
        Ogre::SceneManager* sceneMgr = world_.lock()->GetSceneManager();

        try{
        Ogre::MaterialManager::getSingleton().remove(materialName_);
        } catch(...)
        {
        }
        try{
        if (billboardSet_ && billboard_)
            billboardSet_->removeBillboard(billboard_);
        } catch(...)
        {
        }
        try{

        if (billboardSet_)
        {
            sceneMgr->destroyBillboardSet(billboardSet_);
        }

        } catch(...)
        {
        }
    }

    billboard_ = 0;
    billboardSet_ = 0;
    textureName_ = "";
    materialName_ = "";
}

void EC_HoveringText::SetPosition(const float3& position)
{
    if (!ViewEnabled())
        return;

    if (billboard_)
        billboard_->setPosition(Ogre::Vector3(position.x, position.y, position.z));
}

void EC_HoveringText::SetPosition(const QVector3D &position)
{
    SetPosition(float3(position.x(), position.y(), position.z()));
}

void EC_HoveringText::SetFont(const QFont &font)
{
    font_ = font;
    Redraw();
}

void EC_HoveringText::SetTextColor(const QColor &color)
{
    textColor_ = color;
    Redraw();
}

void EC_HoveringText::SetBackgroundGradient(const QColor &start_color, const QColor &end_color)
{
    bg_grad_.setColorAt(0.0, start_color);
    bg_grad_.setColorAt(1.0, end_color);
    //using_gradient_ = true;
}

void EC_HoveringText::Show()
{
    if (!ViewEnabled())
        return;

    if (billboardSet_)
        billboardSet_->setVisible(true);
}

void EC_HoveringText::Hide()
{
    if (!ViewEnabled())
        return;

    if (billboardSet_)
        billboardSet_->setVisible(false);
}

void EC_HoveringText::SetOverlayAlpha(float alpha)
{
    Ogre::MaterialManager &mgr = Ogre::MaterialManager::getSingleton();
    Ogre::MaterialPtr material = mgr.getByName(materialName_);
    if (!material.get() || material->getNumTechniques() < 1 || material->getTechnique(0)->getNumPasses() < 1 || material->getTechnique(0)->getPass(0)->getNumTextureUnitStates() < 1)
        return;

    material->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setAlphaOperation(
        Ogre::LBX_BLEND_MANUAL, Ogre::LBS_TEXTURE, Ogre::LBS_MANUAL, 1.0, 0.0, alpha);
}

void EC_HoveringText::SetBillboardSize(float width, float height)
{
    if (billboard_)
        billboard_->setDimensions(width, height);
}

bool EC_HoveringText::IsVisible() const
{
    if (!ViewEnabled())
        return false;

    if (billboardSet_)
        return billboardSet_->isVisible();
    else
        return false;
}

void EC_HoveringText::ShowMessage(const QString &text)
{
    if (!ViewEnabled())
        return;
    if (world_.expired())
        return;

    OgreWorldPtr world = world_.lock();
    Ogre::SceneManager *scene = world->GetSceneManager();
    assert(scene);
    if (!scene)
        return;

    //Scene::Entity *entity = ParentEntity();
    Entity* entity = ParentEntity();
    assert(entity);
    if (!entity)
        return;

    EC_Placeable *node = entity->GetComponent<EC_Placeable>().get();
    if (!node)
        return;

    Ogre::SceneNode *sceneNode = node->GetSceneNode();
    assert(sceneNode);
    if (!sceneNode)
        return;

    // Create billboard if it doesn't exist.
    if (!billboardSet_)
    {
        billboardSet_ = scene->createBillboardSet(world->GetUniqueObjectName("EC_HoveringText"), 1);
        assert(billboardSet_);

        materialName_ = world->GetUniqueObjectName("EC_HoveringText_material");
        OgreRenderer::CloneMaterial("HoveringText", materialName_);
        billboardSet_->setMaterialName(materialName_);
        billboardSet_->setCastShadows(false);

        sceneNode->attachObject(billboardSet_);
    }

    if (billboardSet_ && !billboard_)
    {
        billboard_ = billboardSet_->createBillboard(Ogre::Vector3(0, 0, 0.7f));

        SetBillboardSize(width.Get(), height.Get());
        SetPosition(position.Get());
    }

    Redraw();
}

void EC_HoveringText::Redraw()
{
    if (!ViewEnabled())
        return;

    if (world_.expired() || !billboardSet_ || !billboard_)
        return;

    bool textEmpty = text.Get().isEmpty();

    try
    {
        if (texture_.get() == 0)
        {
            AssetAPI* asset = framework->Asset();

            textureName_ = asset->GenerateUniqueAssetName("tex", "EC_HoveringText_").toStdString();
            QString name(textureName_.c_str());
            texture_  = boost::dynamic_pointer_cast<TextureAsset>(asset->CreateNewAsset("Texture", name));

            assert(texture_);

            if (texture_ == 0)
            {
                LogError("Failed to create texture " + textureName_);
                return;
            }
        }

        QBrush brush((QColor)backgroundColor.Get());

        if (usingGrad.Get())
        {
            QRect rect(0,0,texWidth.Get(), texHeight.Get());
            bg_grad_.setStart(QPointF(0,rect.top()));
            bg_grad_.setFinalStop(QPointF(0,rect.bottom()));
            brush = QBrush(bg_grad_);
        }

        QColor borderCol;
        Color col = borderColor.Get();
        borderCol.setRgbF(col.r, col.g, col.b, col.a);

        QPen borderPen;
        borderPen.setColor(borderCol);
        borderPen.setWidthF(borderThickness.Get());

        float2 corners =  cornerRadius.Get();

        if (!textEmpty)
        {
            // Disable mipmapping, as Ogre seems to bug with it
            texture_->SetContentsDrawText(texWidth.Get(),
                                    texHeight.Get(),
                                    text.Get(),
                                    textColor_,
                                    font_,
                                    brush,
                                    borderPen, Qt::AlignCenter | Qt::TextWordWrap, false, corners.x, corners.y);
        }
        else
            texture_->SetContentsDrawText(texWidth.Get(), texHeight.Get(), text.Get(), textColor_, font_, QBrush(), QPen(), Qt::AlignCenter | Qt::TextWordWrap, false, 0.0f, 0.0f);
    }
    catch(Ogre::Exception &e)
    {
        LogError("Failed to create texture " + textureName_  + ": " + std::string(e.what()));
        return;
    }

    // Set new texture for the material
    assert(!materialName_.empty());
    if (!materialName_.empty())
    {
        Ogre::MaterialManager &mgr = Ogre::MaterialManager::getSingleton();
        Ogre::MaterialPtr material = mgr.getByName(materialName_);
        assert(material.get());
        OgreRenderer::SetTextureUnitOnMaterial(material, textureName_);
    }
}

void EC_HoveringText::UpdateSignals()
{
    disconnect(this, SLOT(OnAttributeUpdated(IComponent *, IAttribute *)));
    if(ParentEntity())
    {
        Scene *scene = ParentEntity()->ParentScene();
        if(scene)
        connect(scene, SIGNAL(AttributeChanged(IComponent*, IAttribute*, AttributeChange::Type)),
                this, SLOT(OnAttributeUpdated(IComponent*, IAttribute*)));
    }
}

void EC_HoveringText::OnAttributeUpdated(IComponent *component, IAttribute *attribute)
{
    if(component != this)
        return;

    if(font.Name() == attribute->Name() || fontSize.Name() == attribute->Name())
    {
        SetFont(QFont(font.Get(), fontSize.Get()));
    }
    else if(fontColor.Name() == attribute->Name())
    {
        Color col = fontColor.Get();
        textColor_.setRgbF(col.r, col.g, col.b, col.a);
    }
    else if(position.Name() == attribute->Name())
    {
        SetPosition(position.Get());
    }
    else if(gradEnd.Name() == attribute->Name() || gradStart.Name() == attribute->Name() || gradEnd.Name() == attribute->Name())
    {
        QColor colStart;
        QColor colEnd;
        Color col = gradStart.Get();
        colStart.setRgbF(col.r, col.g, col.b);
        col = gradEnd.Get();
        colEnd.setRgbF(col.r, col.g, col.b);
        SetBackgroundGradient(colStart, colEnd);
    }
    else if (attribute == &overlayAlpha)
        SetOverlayAlpha(overlayAlpha.Get());
    else if (attribute == &width || attribute == &height)
        SetBillboardSize(width.Get(), height.Get());

    // Repaint the new text with new appearance.
    ShowMessage(text.Get());
}
<commit_msg>EC_HoveringText GCC build fix<commit_after>/**
 *  For conditions of distribution and use, see copyright notice in license.txt
 *
 *  @file   EC_HoveringText.cpp
 *  @brief  EC_HoveringText shows a hovering text attached to an entity.
 *  @note   The entity must EC_Placeable available in advance.
 */

#include "DebugOperatorNew.h"

#include "EC_HoveringText.h"
#include "Renderer.h"
#include "EC_Placeable.h"
#include "Entity.h"
#include "LoggingFunctions.h"
#include "Scene.h"
#include "Framework.h"
#include "OgreRenderingModule.h"
#include "OgreWorld.h"
#include "OgreMaterialUtils.h"
#include "AssetAPI.h"
#include "TextureAsset.h"

#include <Ogre.h>
#include <QFile>
#include <QPainter>
#include <QTimer>
#include <QTimeLine>

#include "MemoryLeakCheck.h"

EC_HoveringText::EC_HoveringText(Scene* scene) :
    IComponent(scene),
    font_(QFont("Arial", 100)),
    textColor_(Qt::black),
    billboardSet_(0),
    billboard_(0),
    usingGrad(this, "Use Gradient", false),
    text(this, "Text"),
    font(this, "Font", "Arial"),
    fontColor(this, "Font Color"),
    fontSize(this, "Font Size", 100),
    backgroundColor(this, "Background Color", Color(1.0f,1.0f,1.0f,0.0f)),
    position(this, "Position", float3(0.0f, 0.0f, 0.0f)),
    gradStart(this, "Gradient Start", Color(0.0f,0.0f,0.0f,1.0f)),
    gradEnd(this, "Gradient End", Color(1.0f,1.0f,1.0f,1.0f)),
    borderColor(this, "Border Color", Color(0.0f,0.0f,0.0f,0.0f)),
    borderThickness(this, "Border Thickness", 0.0),
    overlayAlpha(this, "Overlay Alpha", 1.0),
    width(this, "Width", 1.0),
    height(this, "Height", 1.0),
    texWidth(this, "Texture Width", 256),
    texHeight(this, "Texture Height", 256),
    cornerRadius(this, "Corner radius", float2(20.0, 20.0))
{
    if (scene)
        world_ = scene->GetWorld<OgreWorld>();
    connect(this, SIGNAL(ParentEntitySet()), this, SLOT(UpdateSignals()));
}

EC_HoveringText::~EC_HoveringText()
{
    if (texture_.get() != 0)
    {
        AssetAPI* asset = framework->Asset();
        asset->ForgetAsset(texture_,false);
    }
    Destroy();
}

void EC_HoveringText::Destroy()
{
    if (!ViewEnabled())
        return;

    if (!world_.expired())
    {
        Ogre::SceneManager* sceneMgr = world_.lock()->GetSceneManager();

        try{
        Ogre::MaterialManager::getSingleton().remove(materialName_);
        } catch(...)
        {
        }
        try{
        if (billboardSet_ && billboard_)
            billboardSet_->removeBillboard(billboard_);
        } catch(...)
        {
        }
        try{

        if (billboardSet_)
        {
            sceneMgr->destroyBillboardSet(billboardSet_);
        }

        } catch(...)
        {
        }
    }

    billboard_ = 0;
    billboardSet_ = 0;
    textureName_ = "";
    materialName_ = "";
}

void EC_HoveringText::SetPosition(const float3& position)
{
    if (!ViewEnabled())
        return;

    if (billboard_)
        billboard_->setPosition(Ogre::Vector3(position.x, position.y, position.z));
}

void EC_HoveringText::SetPosition(const QVector3D &position)
{
    SetPosition(float3(position.x(), position.y(), position.z()));
}

void EC_HoveringText::SetFont(const QFont &font)
{
    font_ = font;
    Redraw();
}

void EC_HoveringText::SetTextColor(const QColor &color)
{
    textColor_ = color;
    Redraw();
}

void EC_HoveringText::SetBackgroundGradient(const QColor &start_color, const QColor &end_color)
{
    bg_grad_.setColorAt(0.0, start_color);
    bg_grad_.setColorAt(1.0, end_color);
    //using_gradient_ = true;
}

void EC_HoveringText::Show()
{
    if (!ViewEnabled())
        return;

    if (billboardSet_)
        billboardSet_->setVisible(true);
}

void EC_HoveringText::Hide()
{
    if (!ViewEnabled())
        return;

    if (billboardSet_)
        billboardSet_->setVisible(false);
}

void EC_HoveringText::SetOverlayAlpha(float alpha)
{
    Ogre::MaterialManager &mgr = Ogre::MaterialManager::getSingleton();
    Ogre::MaterialPtr material = mgr.getByName(materialName_);
    if (!material.get() || material->getNumTechniques() < 1 || material->getTechnique(0)->getNumPasses() < 1 || material->getTechnique(0)->getPass(0)->getNumTextureUnitStates() < 1)
        return;

    material->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setAlphaOperation(
        Ogre::LBX_BLEND_MANUAL, Ogre::LBS_TEXTURE, Ogre::LBS_MANUAL, 1.0, 0.0, alpha);
}

void EC_HoveringText::SetBillboardSize(float width, float height)
{
    if (billboard_)
        billboard_->setDimensions(width, height);
}

bool EC_HoveringText::IsVisible() const
{
    if (!ViewEnabled())
        return false;

    if (billboardSet_)
        return billboardSet_->isVisible();
    else
        return false;
}

void EC_HoveringText::ShowMessage(const QString &text)
{
    if (!ViewEnabled())
        return;
    if (world_.expired())
        return;

    OgreWorldPtr world = world_.lock();
    Ogre::SceneManager *scene = world->GetSceneManager();
    assert(scene);
    if (!scene)
        return;

    //Scene::Entity *entity = ParentEntity();
    Entity* entity = ParentEntity();
    assert(entity);
    if (!entity)
        return;

    EC_Placeable *node = entity->GetComponent<EC_Placeable>().get();
    if (!node)
        return;

    Ogre::SceneNode *sceneNode = node->GetSceneNode();
    assert(sceneNode);
    if (!sceneNode)
        return;

    // Create billboard if it doesn't exist.
    if (!billboardSet_)
    {
        billboardSet_ = scene->createBillboardSet(world->GetUniqueObjectName("EC_HoveringText"), 1);
        assert(billboardSet_);

        materialName_ = world->GetUniqueObjectName("EC_HoveringText_material");
        OgreRenderer::CloneMaterial("HoveringText", materialName_);
        billboardSet_->setMaterialName(materialName_);
        billboardSet_->setCastShadows(false);

        sceneNode->attachObject(billboardSet_);
    }

    if (billboardSet_ && !billboard_)
    {
        billboard_ = billboardSet_->createBillboard(Ogre::Vector3(0, 0, 0.7f));

        SetBillboardSize(width.Get(), height.Get());
        SetPosition(position.Get());
    }

    Redraw();
}

void EC_HoveringText::Redraw()
{
    if (!ViewEnabled())
        return;

    if (world_.expired() || !billboardSet_ || !billboard_)
        return;

    bool textEmpty = text.Get().isEmpty();

    try
    {
        if (texture_.get() == 0)
        {
            AssetAPI* asset = framework->Asset();

            textureName_ = asset->GenerateUniqueAssetName("tex", "EC_HoveringText_").toStdString();
            QString name(textureName_.c_str());
            texture_  = boost::dynamic_pointer_cast<TextureAsset>(asset->CreateNewAsset("Texture", name));

            assert(texture_);

            if (texture_ == 0)
            {
                LogError("Failed to create texture " + textureName_);
                return;
            }
        }

        QBrush brush(backgroundColor.Get());

        if (usingGrad.Get())
        {
            QRect rect(0,0,texWidth.Get(), texHeight.Get());
            bg_grad_.setStart(QPointF(0,rect.top()));
            bg_grad_.setFinalStop(QPointF(0,rect.bottom()));
            brush = QBrush(bg_grad_);
        }

        QColor borderCol;
        Color col = borderColor.Get();
        borderCol.setRgbF(col.r, col.g, col.b, col.a);

        QPen borderPen;
        borderPen.setColor(borderCol);
        borderPen.setWidthF(borderThickness.Get());

        float2 corners =  cornerRadius.Get();

        if (!textEmpty)
        {
            // Disable mipmapping, as Ogre seems to bug with it
            texture_->SetContentsDrawText(texWidth.Get(),
                                    texHeight.Get(),
                                    text.Get(),
                                    textColor_,
                                    font_,
                                    brush,
                                    borderPen, Qt::AlignCenter | Qt::TextWordWrap, false, corners.x, corners.y);
        }
        else
            texture_->SetContentsDrawText(texWidth.Get(), texHeight.Get(), text.Get(), textColor_, font_, QBrush(), QPen(), Qt::AlignCenter | Qt::TextWordWrap, false, 0.0f, 0.0f);
    }
    catch(Ogre::Exception &e)
    {
        LogError("Failed to create texture " + textureName_  + ": " + std::string(e.what()));
        return;
    }

    // Set new texture for the material
    assert(!materialName_.empty());
    if (!materialName_.empty())
    {
        Ogre::MaterialManager &mgr = Ogre::MaterialManager::getSingleton();
        Ogre::MaterialPtr material = mgr.getByName(materialName_);
        assert(material.get());
        OgreRenderer::SetTextureUnitOnMaterial(material, textureName_);
    }
}

void EC_HoveringText::UpdateSignals()
{
    disconnect(this, SLOT(OnAttributeUpdated(IComponent *, IAttribute *)));
    if(ParentEntity())
    {
        Scene *scene = ParentEntity()->ParentScene();
        if(scene)
        connect(scene, SIGNAL(AttributeChanged(IComponent*, IAttribute*, AttributeChange::Type)),
                this, SLOT(OnAttributeUpdated(IComponent*, IAttribute*)));
    }
}

void EC_HoveringText::OnAttributeUpdated(IComponent *component, IAttribute *attribute)
{
    if(component != this)
        return;

    if(font.Name() == attribute->Name() || fontSize.Name() == attribute->Name())
    {
        SetFont(QFont(font.Get(), fontSize.Get()));
    }
    else if(fontColor.Name() == attribute->Name())
    {
        Color col = fontColor.Get();
        textColor_.setRgbF(col.r, col.g, col.b, col.a);
    }
    else if(position.Name() == attribute->Name())
    {
        SetPosition(position.Get());
    }
    else if(gradEnd.Name() == attribute->Name() || gradStart.Name() == attribute->Name() || gradEnd.Name() == attribute->Name())
    {
        QColor colStart;
        QColor colEnd;
        Color col = gradStart.Get();
        colStart.setRgbF(col.r, col.g, col.b);
        col = gradEnd.Get();
        colEnd.setRgbF(col.r, col.g, col.b);
        SetBackgroundGradient(colStart, colEnd);
    }
    else if (attribute == &overlayAlpha)
        SetOverlayAlpha(overlayAlpha.Get());
    else if (attribute == &width || attribute == &height)
        SetBillboardSize(width.Get(), height.Get());

    // Repaint the new text with new appearance.
    ShowMessage(text.Get());
}
<|endoftext|>


<commit_before>#ifndef OSRM_CELLS_CUSTOMIZER_HPP
#define OSRM_CELLS_CUSTOMIZER_HPP

#include "partition/cell_storage.hpp"
#include "partition/multi_level_partition.hpp"
#include "util/binary_heap.hpp"

#include <boost/thread/tss.hpp>
#include <unordered_set>

namespace osrm
{
namespace customizer
{

class CellCustomizer
{

  public:
    CellCustomizer(const partition::MultiLevelPartition &partition) : partition(partition) {}

    template <typename GraphT>
    void Customize(const GraphT &graph, partition::CellStorage &cells, LevelID level, CellID id)
    {
        auto cell = cells.GetCell(level, id);
        auto destinations = cell.GetDestinationNodes();

        // for each source do forward search
        for (auto source : cell.GetSourceNodes())
        {
            std::unordered_set<NodeID> destinations_set(destinations.begin(), destinations.end());
            Heap heap(graph.GetNumberOfNodes());
            heap.Insert(source, 0, {});

            // explore search space
            while (!heap.Empty() && !destinations_set.empty())
            {
                const NodeID node = heap.DeleteMin();
                const EdgeWeight weight = heap.GetKey(node);

                if (level == 1)
                    RelaxNode<true>(graph, cells, heap, level, node, weight);
                else
                    RelaxNode<false>(graph, cells, heap, level, node, weight);

                destinations_set.erase(node);
            }

            // fill a map of destination nodes to placeholder pointers
            auto destination_iter = destinations.begin();
            for (auto &weight : cell.GetOutWeight(source))
            {
                BOOST_ASSERT(destination_iter != destinations.end());
                const auto destination = *destination_iter++;
                weight =
                    heap.WasInserted(destination) ? heap.GetKey(destination) : INVALID_EDGE_WEIGHT;
            }
        }
    }

    template <typename GraphT> void Customize(const GraphT &graph, partition::CellStorage &cells)
    {
        for (std::size_t level = 1; level < partition.GetNumberOfLevels(); ++level)
        {
            tbb::parallel_for(tbb::blocked_range<std::size_t>(0, partition.GetNumberOfCells(level)),
                              [&](const tbb::blocked_range<std::size_t> &range) {
                                  for (auto id = range.begin(), end = range.end(); id != end; ++id)
                                  {
                                      Customize(graph, cells, level, id);
                                  }
                              });
        }
    }

  private:
    struct HeapData
    {
    };
    using Heap = util::
        BinaryHeap<NodeID, NodeID, EdgeWeight, HeapData, util::UnorderedMapStorage<NodeID, int>>;
    using HeapPtr = boost::thread_specific_ptr<Heap>;

    template <bool first_level, typename GraphT>
    void RelaxNode(const GraphT &graph,
                   const partition::CellStorage &cells,
                   Heap &heap,
                   LevelID level,
                   NodeID node,
                   EdgeWeight weight) const
    {
        if (!first_level)
        {
            // Relax sub-cell nodes
            auto subcell_id = partition.GetCell(level - 1, node);
            auto subcell = cells.GetCell(level - 1, subcell_id);
            auto subcell_destination = subcell.GetDestinationNodes().begin();
            for (auto subcell_weight : subcell.GetOutWeight(node))
            {
                if (subcell_weight != INVALID_EDGE_WEIGHT)
                {
                    const NodeID to = *subcell_destination;
                    const EdgeWeight to_weight = subcell_weight + weight;
                    if (!heap.WasInserted(to))
                    {
                        heap.Insert(to, to_weight, {});
                    }
                    else if (to_weight < heap.GetKey(to))
                    {
                        heap.DecreaseKey(to, to_weight);
                    }
                }

                ++subcell_destination;
            }
        }

        // Relax base graph edges if a sub-cell border edge
        for (auto edge : graph.GetInternalEdgeRange(level, node))
        {
            const NodeID to = graph.GetTarget(edge);
            const auto &data = graph.GetEdgeData(edge);
            if (data.forward &&
                (first_level ||
                 partition.GetCell(level - 1, node) != partition.GetCell(level - 1, to)))
            {
                const EdgeWeight to_weight = data.weight + weight;
                if (!heap.WasInserted(to))
                {
                    heap.Insert(to, to_weight, {});
                }
                else if (to_weight < heap.GetKey(to))
                {
                    heap.DecreaseKey(to, to_weight);
                }
            }
        }
    }

    const partition::MultiLevelPartition &partition;
};
}
}

#endif // OSRM_CELLS_CUSTOMIZER_HPP
<commit_msg>Don't scan back clique arcs<commit_after>#ifndef OSRM_CELLS_CUSTOMIZER_HPP
#define OSRM_CELLS_CUSTOMIZER_HPP

#include "partition/cell_storage.hpp"
#include "partition/multi_level_partition.hpp"
#include "util/binary_heap.hpp"

#include <boost/thread/tss.hpp>
#include <unordered_set>

namespace osrm
{
namespace customizer
{

class CellCustomizer
{

  public:
    CellCustomizer(const partition::MultiLevelPartition &partition) : partition(partition) {}

    template <typename GraphT>
    void Customize(const GraphT &graph, partition::CellStorage &cells, LevelID level, CellID id)
    {
        auto cell = cells.GetCell(level, id);
        auto destinations = cell.GetDestinationNodes();

        // for each source do forward search
        for (auto source : cell.GetSourceNodes())
        {
            std::unordered_set<NodeID> destinations_set(destinations.begin(), destinations.end());
            Heap heap(graph.GetNumberOfNodes());
            heap.Insert(source, 0, {false});

            // explore search space
            while (!heap.Empty() && !destinations_set.empty())
            {
                const NodeID node = heap.DeleteMin();
                const EdgeWeight weight = heap.GetKey(node);

                if (level == 1)
                    RelaxNode<true>(graph, cells, heap, level, node, weight);
                else
                    RelaxNode<false>(graph, cells, heap, level, node, weight);

                destinations_set.erase(node);
            }

            // fill a map of destination nodes to placeholder pointers
            auto destination_iter = destinations.begin();
            for (auto &weight : cell.GetOutWeight(source))
            {
                BOOST_ASSERT(destination_iter != destinations.end());
                const auto destination = *destination_iter++;
                weight =
                    heap.WasInserted(destination) ? heap.GetKey(destination) : INVALID_EDGE_WEIGHT;
            }
        }
    }

    template <typename GraphT> void Customize(const GraphT &graph, partition::CellStorage &cells)
    {
        for (std::size_t level = 1; level < partition.GetNumberOfLevels(); ++level)
        {
            tbb::parallel_for(tbb::blocked_range<std::size_t>(0, partition.GetNumberOfCells(level)),
                              [&](const tbb::blocked_range<std::size_t> &range) {
                                  for (auto id = range.begin(), end = range.end(); id != end; ++id)
                                  {
                                      Customize(graph, cells, level, id);
                                  }
                              });
        }
    }

  private:
    struct HeapData
    {
        bool from_clique;
    };
    using Heap = util::
        BinaryHeap<NodeID, NodeID, EdgeWeight, HeapData, util::UnorderedMapStorage<NodeID, int>>;
    using HeapPtr = boost::thread_specific_ptr<Heap>;

    template <bool first_level, typename GraphT>
    void RelaxNode(const GraphT &graph,
                   const partition::CellStorage &cells,
                   Heap &heap,
                   LevelID level,
                   NodeID node,
                   EdgeWeight weight) const
    {
        BOOST_ASSERT(heap.WasInserted(node));

        if (!first_level)
        {
            // if we reaches this node from a clique arc we don't need to scan
            // the clique arcs again because of the triangle inequality
            //
            // d(parent, node) + d(node, v) >= d(parent, v)
            //
            // And if there is a path (parent, node, v) there must also be a
            // clique arc (parent, v) with d(parent, v).
            if (!heap.GetData(node).from_clique)
            {
                // Relax sub-cell nodes
                auto subcell_id = partition.GetCell(level - 1, node);
                auto subcell = cells.GetCell(level - 1, subcell_id);
                auto subcell_destination = subcell.GetDestinationNodes().begin();
                for (auto subcell_weight : subcell.GetOutWeight(node))
                {
                    if (subcell_weight != INVALID_EDGE_WEIGHT)
                    {
                        const NodeID to = *subcell_destination;
                        const EdgeWeight to_weight = subcell_weight + weight;
                        if (!heap.WasInserted(to))
                        {
                            heap.Insert(to, to_weight, {true});
                        }
                        else if (to_weight < heap.GetKey(to))
                        {
                            heap.DecreaseKey(to, to_weight);
                            heap.GetData(to).from_clique = true;
                        }
                    }

                    ++subcell_destination;
                }
            }
        }

        // Relax base graph edges if a sub-cell border edge
        for (auto edge : graph.GetInternalEdgeRange(level, node))
        {
            const NodeID to = graph.GetTarget(edge);
            const auto &data = graph.GetEdgeData(edge);
            if (data.forward &&
                (first_level ||
                 partition.GetCell(level - 1, node) != partition.GetCell(level - 1, to)))
            {
                const EdgeWeight to_weight = data.weight + weight;
                if (!heap.WasInserted(to))
                {
                    heap.Insert(to, to_weight, {false});
                }
                else if (to_weight < heap.GetKey(to))
                {
                    heap.DecreaseKey(to, to_weight);
                    heap.GetData(to).from_clique = false;
                }
            }
        }
    }

    const partition::MultiLevelPartition &partition;
};
}
}

#endif // OSRM_CELLS_CUSTOMIZER_HPP
<|endoftext|>


<commit_before>// This file is part of gltfpack; see gltfpack.h for version/license details
#include "gltfpack.h"

#include <string.h>

static bool areTextureViewsEqual(const cgltf_texture_view& lhs, const cgltf_texture_view& rhs)
{
	if (lhs.has_transform != rhs.has_transform)
		return false;

	if (lhs.has_transform)
	{
		const cgltf_texture_transform& lt = lhs.transform;
		const cgltf_texture_transform& rt = rhs.transform;

		if (memcmp(lt.offset, rt.offset, sizeof(cgltf_float) * 2) != 0)
			return false;

		if (lt.rotation != rt.rotation)
			return false;

		if (memcmp(lt.scale, rt.scale, sizeof(cgltf_float) * 2) != 0)
			return false;

		if (lt.texcoord != rt.texcoord)
			return false;
	}

	if (lhs.texture != rhs.texture)
		return false;

	if (lhs.texcoord != rhs.texcoord)
		return false;

	if (lhs.scale != rhs.scale)
		return false;

	return true;
}

static bool areExtrasEqual(cgltf_data* data, const cgltf_extras& lhs, const cgltf_extras& rhs)
{
	if (lhs.end_offset - lhs.start_offset != rhs.end_offset - rhs.start_offset)
		return false;

	if (memcmp(data->json + lhs.start_offset, data->json + rhs.start_offset, lhs.end_offset - lhs.start_offset) != 0)
		return false;

	return true;
}

static bool areMaterialComponentsEqual(const cgltf_pbr_metallic_roughness& lhs, const cgltf_pbr_metallic_roughness& rhs)
{
	if (!areTextureViewsEqual(lhs.base_color_texture, rhs.base_color_texture))
		return false;

	if (!areTextureViewsEqual(lhs.metallic_roughness_texture, rhs.metallic_roughness_texture))
		return false;

	if (memcmp(lhs.base_color_factor, rhs.base_color_factor, sizeof(cgltf_float) * 4) != 0)
		return false;

	if (lhs.metallic_factor != rhs.metallic_factor)
		return false;

	if (lhs.roughness_factor != rhs.roughness_factor)
		return false;

	return true;
}

static bool areMaterialComponentsEqual(const cgltf_pbr_specular_glossiness& lhs, const cgltf_pbr_specular_glossiness& rhs)
{
	if (!areTextureViewsEqual(lhs.diffuse_texture, rhs.diffuse_texture))
		return false;

	if (!areTextureViewsEqual(lhs.specular_glossiness_texture, rhs.specular_glossiness_texture))
		return false;

	if (memcmp(lhs.diffuse_factor, rhs.diffuse_factor, sizeof(cgltf_float) * 4) != 0)
		return false;

	if (memcmp(lhs.specular_factor, rhs.specular_factor, sizeof(cgltf_float) * 3) != 0)
		return false;

	if (lhs.glossiness_factor != rhs.glossiness_factor)
		return false;

	return true;
}

static bool areMaterialComponentsEqual(const cgltf_clearcoat& lhs, const cgltf_clearcoat& rhs)
{
	if (!areTextureViewsEqual(lhs.clearcoat_texture, rhs.clearcoat_texture))
		return false;

	if (!areTextureViewsEqual(lhs.clearcoat_roughness_texture, rhs.clearcoat_roughness_texture))
		return false;

	if (!areTextureViewsEqual(lhs.clearcoat_normal_texture, rhs.clearcoat_normal_texture))
		return false;

	if (lhs.clearcoat_factor != rhs.clearcoat_factor)
		return false;

	if (lhs.clearcoat_roughness_factor != rhs.clearcoat_roughness_factor)
		return false;

	return true;
}

static bool areMaterialComponentsEqual(const cgltf_transmission& lhs, const cgltf_transmission& rhs)
{
	if (!areTextureViewsEqual(lhs.transmission_texture, rhs.transmission_texture))
		return false;

	if (lhs.transmission_factor != rhs.transmission_factor)
		return false;

	return true;
}

static bool areMaterialComponentsEqual(const cgltf_ior& lhs, const cgltf_ior& rhs)
{
	if (lhs.ior != rhs.ior)
		return false;

	return true;
}

static bool areMaterialComponentsEqual(const cgltf_specular& lhs, const cgltf_specular& rhs)
{
	if (!areTextureViewsEqual(lhs.specular_texture, rhs.specular_texture))
		return false;

	if (!areTextureViewsEqual(lhs.specular_color_texture, rhs.specular_color_texture))
		return false;

	if (lhs.specular_factor != rhs.specular_factor)
		return false;

	if (memcmp(lhs.specular_color_factor, rhs.specular_color_factor, sizeof(cgltf_float) * 3) != 0)
		return false;

	return true;
}

static bool areMaterialComponentsEqual(const cgltf_sheen& lhs, const cgltf_sheen& rhs)
{
	if (!areTextureViewsEqual(lhs.sheen_color_texture, rhs.sheen_color_texture))
		return false;

	if (memcmp(lhs.sheen_color_factor, rhs.sheen_color_factor, sizeof(cgltf_float) * 3) != 0)
		return false;

	if (!areTextureViewsEqual(lhs.sheen_roughness_texture, rhs.sheen_roughness_texture))
		return false;

	if (lhs.sheen_roughness_factor != rhs.sheen_roughness_factor)
		return false;

	return true;
}

static bool areMaterialsEqual(cgltf_data* data, const cgltf_material& lhs, const cgltf_material& rhs, const Settings& settings)
{
	if (lhs.has_pbr_metallic_roughness != rhs.has_pbr_metallic_roughness)
		return false;

	if (lhs.has_pbr_metallic_roughness && !areMaterialComponentsEqual(lhs.pbr_metallic_roughness, rhs.pbr_metallic_roughness))
		return false;

	if (lhs.has_pbr_specular_glossiness != rhs.has_pbr_specular_glossiness)
		return false;

	if (lhs.has_pbr_specular_glossiness && !areMaterialComponentsEqual(lhs.pbr_specular_glossiness, rhs.pbr_specular_glossiness))
		return false;

	if (lhs.has_clearcoat != rhs.has_clearcoat)
		return false;

	if (lhs.has_clearcoat && !areMaterialComponentsEqual(lhs.clearcoat, rhs.clearcoat))
		return false;

	if (lhs.has_transmission != rhs.has_transmission)
		return false;

	if (lhs.has_transmission && !areMaterialComponentsEqual(lhs.transmission, rhs.transmission))
		return false;

	if (lhs.has_ior != rhs.has_ior)
		return false;

	if (lhs.has_ior && !areMaterialComponentsEqual(lhs.ior, rhs.ior))
		return false;

	if (lhs.has_specular != rhs.has_specular)
		return false;

	if (lhs.has_specular && !areMaterialComponentsEqual(lhs.specular, rhs.specular))
		return false;

	if (lhs.has_sheen != rhs.has_sheen)
		return false;

	if (lhs.has_sheen && !areMaterialComponentsEqual(lhs.sheen, rhs.sheen))
		return false;

	if (!areTextureViewsEqual(lhs.normal_texture, rhs.normal_texture))
		return false;

	if (!areTextureViewsEqual(lhs.occlusion_texture, rhs.occlusion_texture))
		return false;

	if (!areTextureViewsEqual(lhs.emissive_texture, rhs.emissive_texture))
		return false;

	if (memcmp(lhs.emissive_factor, rhs.emissive_factor, sizeof(cgltf_float) * 3) != 0)
		return false;

	if (lhs.alpha_mode != rhs.alpha_mode)
		return false;

	if (lhs.alpha_cutoff != rhs.alpha_cutoff)
		return false;

	if (lhs.double_sided != rhs.double_sided)
		return false;

	if (lhs.unlit != rhs.unlit)
		return false;

	if (settings.keep_extras && !areExtrasEqual(data, lhs.extras, rhs.extras))
		return false;

	return true;
}

void mergeMeshMaterials(cgltf_data* data, std::vector<Mesh>& meshes, const Settings& settings)
{
	for (size_t i = 0; i < meshes.size(); ++i)
	{
		Mesh& mesh = meshes[i];

		if (!mesh.material)
			continue;

		if (settings.keep_materials && mesh.material->name && *mesh.material->name)
			continue;

		for (int j = 0; j < mesh.material - data->materials; ++j)
		{
			if (settings.keep_materials && data->materials[j].name && *data->materials[j].name)
				continue;

			if (areMaterialsEqual(data, *mesh.material, data->materials[j], settings))
			{
				mesh.material = &data->materials[j];
				break;
			}
		}
	}
}

void markNeededMaterials(cgltf_data* data, std::vector<MaterialInfo>& materials, const std::vector<Mesh>& meshes, const Settings& settings)
{
	// mark all used materials as kept
	for (size_t i = 0; i < meshes.size(); ++i)
	{
		const Mesh& mesh = meshes[i];

		if (mesh.material)
		{
			MaterialInfo& mi = materials[mesh.material - data->materials];

			mi.keep = true;
		}
	}

	// mark all named materials as kept if requested
	if (settings.keep_materials)
	{
		for (size_t i = 0; i < data->materials_count; ++i)
		{
			cgltf_material& material = data->materials[i];

			if (material.name && *material.name)
			{
				materials[i].keep = true;
			}
		}
	}
}

enum TextureKind
{
	TextureKind_Generic,
	TextureKind_Color,
	TextureKind_Normal,
};

static void analyzeMaterialTexture(const cgltf_texture_view& view, TextureKind kind, MaterialInfo& mi, cgltf_data* data, std::vector<ImageInfo>& images)
{
	mi.usesTextureTransform |= view.has_transform;

	if (view.texture && view.texture->image)
	{
		mi.textureSetMask |= 1u << view.texcoord;
		mi.needsTangents |= (kind == TextureKind_Normal);

		images[view.texture->image - data->images].srgb |= (kind == TextureKind_Color);
		images[view.texture->image - data->images].normal_map |= (kind == TextureKind_Normal);
	}
}

static void analyzeMaterial(const cgltf_material& material, MaterialInfo& mi, cgltf_data* data, std::vector<ImageInfo>& images)
{
	if (material.has_pbr_metallic_roughness)
	{
		analyzeMaterialTexture(material.pbr_metallic_roughness.base_color_texture, TextureKind_Color, mi, data, images);
		analyzeMaterialTexture(material.pbr_metallic_roughness.metallic_roughness_texture, TextureKind_Generic, mi, data, images);
	}

	if (material.has_pbr_specular_glossiness)
	{
		analyzeMaterialTexture(material.pbr_specular_glossiness.diffuse_texture, TextureKind_Color, mi, data, images);
		analyzeMaterialTexture(material.pbr_specular_glossiness.specular_glossiness_texture, TextureKind_Generic, mi, data, images);
	}

	if (material.has_clearcoat)
	{
		analyzeMaterialTexture(material.clearcoat.clearcoat_texture, TextureKind_Generic, mi, data, images);
		analyzeMaterialTexture(material.clearcoat.clearcoat_roughness_texture, TextureKind_Generic, mi, data, images);
		analyzeMaterialTexture(material.clearcoat.clearcoat_normal_texture, TextureKind_Normal, mi, data, images);
	}

	if (material.has_transmission)
	{
		analyzeMaterialTexture(material.transmission.transmission_texture, TextureKind_Generic, mi, data, images);
	}

	if (material.has_specular)
	{
		analyzeMaterialTexture(material.specular.specular_texture, TextureKind_Generic, mi, data, images);
		analyzeMaterialTexture(material.specular.specular_color_texture, TextureKind_Color, mi, data, images);
	}

	if (material.has_sheen)
	{
		analyzeMaterialTexture(material.sheen.sheen_color_texture, TextureKind_Color, mi, data, images);
		analyzeMaterialTexture(material.sheen.sheen_roughness_texture, TextureKind_Generic, mi, data, images);
	}

	analyzeMaterialTexture(material.normal_texture, TextureKind_Normal, mi, data, images);
	analyzeMaterialTexture(material.occlusion_texture, TextureKind_Generic, mi, data, images);
	analyzeMaterialTexture(material.emissive_texture, TextureKind_Color, mi, data, images);
}

void analyzeMaterials(cgltf_data* data, std::vector<MaterialInfo>& materials, std::vector<ImageInfo>& images)
{
	for (size_t i = 0; i < data->materials_count; ++i)
	{
		analyzeMaterial(data->materials[i], materials[i], data, images);
	}
}
<commit_msg>gltfpack: Fix MSVC warning<commit_after>// This file is part of gltfpack; see gltfpack.h for version/license details
#include "gltfpack.h"

#include <string.h>

static bool areTextureViewsEqual(const cgltf_texture_view& lhs, const cgltf_texture_view& rhs)
{
	if (lhs.has_transform != rhs.has_transform)
		return false;

	if (lhs.has_transform)
	{
		const cgltf_texture_transform& lt = lhs.transform;
		const cgltf_texture_transform& rt = rhs.transform;

		if (memcmp(lt.offset, rt.offset, sizeof(cgltf_float) * 2) != 0)
			return false;

		if (lt.rotation != rt.rotation)
			return false;

		if (memcmp(lt.scale, rt.scale, sizeof(cgltf_float) * 2) != 0)
			return false;

		if (lt.texcoord != rt.texcoord)
			return false;
	}

	if (lhs.texture != rhs.texture)
		return false;

	if (lhs.texcoord != rhs.texcoord)
		return false;

	if (lhs.scale != rhs.scale)
		return false;

	return true;
}

static bool areExtrasEqual(cgltf_data* data, const cgltf_extras& lhs, const cgltf_extras& rhs)
{
	if (lhs.end_offset - lhs.start_offset != rhs.end_offset - rhs.start_offset)
		return false;

	if (memcmp(data->json + lhs.start_offset, data->json + rhs.start_offset, lhs.end_offset - lhs.start_offset) != 0)
		return false;

	return true;
}

static bool areMaterialComponentsEqual(const cgltf_pbr_metallic_roughness& lhs, const cgltf_pbr_metallic_roughness& rhs)
{
	if (!areTextureViewsEqual(lhs.base_color_texture, rhs.base_color_texture))
		return false;

	if (!areTextureViewsEqual(lhs.metallic_roughness_texture, rhs.metallic_roughness_texture))
		return false;

	if (memcmp(lhs.base_color_factor, rhs.base_color_factor, sizeof(cgltf_float) * 4) != 0)
		return false;

	if (lhs.metallic_factor != rhs.metallic_factor)
		return false;

	if (lhs.roughness_factor != rhs.roughness_factor)
		return false;

	return true;
}

static bool areMaterialComponentsEqual(const cgltf_pbr_specular_glossiness& lhs, const cgltf_pbr_specular_glossiness& rhs)
{
	if (!areTextureViewsEqual(lhs.diffuse_texture, rhs.diffuse_texture))
		return false;

	if (!areTextureViewsEqual(lhs.specular_glossiness_texture, rhs.specular_glossiness_texture))
		return false;

	if (memcmp(lhs.diffuse_factor, rhs.diffuse_factor, sizeof(cgltf_float) * 4) != 0)
		return false;

	if (memcmp(lhs.specular_factor, rhs.specular_factor, sizeof(cgltf_float) * 3) != 0)
		return false;

	if (lhs.glossiness_factor != rhs.glossiness_factor)
		return false;

	return true;
}

static bool areMaterialComponentsEqual(const cgltf_clearcoat& lhs, const cgltf_clearcoat& rhs)
{
	if (!areTextureViewsEqual(lhs.clearcoat_texture, rhs.clearcoat_texture))
		return false;

	if (!areTextureViewsEqual(lhs.clearcoat_roughness_texture, rhs.clearcoat_roughness_texture))
		return false;

	if (!areTextureViewsEqual(lhs.clearcoat_normal_texture, rhs.clearcoat_normal_texture))
		return false;

	if (lhs.clearcoat_factor != rhs.clearcoat_factor)
		return false;

	if (lhs.clearcoat_roughness_factor != rhs.clearcoat_roughness_factor)
		return false;

	return true;
}

static bool areMaterialComponentsEqual(const cgltf_transmission& lhs, const cgltf_transmission& rhs)
{
	if (!areTextureViewsEqual(lhs.transmission_texture, rhs.transmission_texture))
		return false;

	if (lhs.transmission_factor != rhs.transmission_factor)
		return false;

	return true;
}

static bool areMaterialComponentsEqual(const cgltf_ior& lhs, const cgltf_ior& rhs)
{
	if (lhs.ior != rhs.ior)
		return false;

	return true;
}

static bool areMaterialComponentsEqual(const cgltf_specular& lhs, const cgltf_specular& rhs)
{
	if (!areTextureViewsEqual(lhs.specular_texture, rhs.specular_texture))
		return false;

	if (!areTextureViewsEqual(lhs.specular_color_texture, rhs.specular_color_texture))
		return false;

	if (lhs.specular_factor != rhs.specular_factor)
		return false;

	if (memcmp(lhs.specular_color_factor, rhs.specular_color_factor, sizeof(cgltf_float) * 3) != 0)
		return false;

	return true;
}

static bool areMaterialComponentsEqual(const cgltf_sheen& lhs, const cgltf_sheen& rhs)
{
	if (!areTextureViewsEqual(lhs.sheen_color_texture, rhs.sheen_color_texture))
		return false;

	if (memcmp(lhs.sheen_color_factor, rhs.sheen_color_factor, sizeof(cgltf_float) * 3) != 0)
		return false;

	if (!areTextureViewsEqual(lhs.sheen_roughness_texture, rhs.sheen_roughness_texture))
		return false;

	if (lhs.sheen_roughness_factor != rhs.sheen_roughness_factor)
		return false;

	return true;
}

static bool areMaterialsEqual(cgltf_data* data, const cgltf_material& lhs, const cgltf_material& rhs, const Settings& settings)
{
	if (lhs.has_pbr_metallic_roughness != rhs.has_pbr_metallic_roughness)
		return false;

	if (lhs.has_pbr_metallic_roughness && !areMaterialComponentsEqual(lhs.pbr_metallic_roughness, rhs.pbr_metallic_roughness))
		return false;

	if (lhs.has_pbr_specular_glossiness != rhs.has_pbr_specular_glossiness)
		return false;

	if (lhs.has_pbr_specular_glossiness && !areMaterialComponentsEqual(lhs.pbr_specular_glossiness, rhs.pbr_specular_glossiness))
		return false;

	if (lhs.has_clearcoat != rhs.has_clearcoat)
		return false;

	if (lhs.has_clearcoat && !areMaterialComponentsEqual(lhs.clearcoat, rhs.clearcoat))
		return false;

	if (lhs.has_transmission != rhs.has_transmission)
		return false;

	if (lhs.has_transmission && !areMaterialComponentsEqual(lhs.transmission, rhs.transmission))
		return false;

	if (lhs.has_ior != rhs.has_ior)
		return false;

	if (lhs.has_ior && !areMaterialComponentsEqual(lhs.ior, rhs.ior))
		return false;

	if (lhs.has_specular != rhs.has_specular)
		return false;

	if (lhs.has_specular && !areMaterialComponentsEqual(lhs.specular, rhs.specular))
		return false;

	if (lhs.has_sheen != rhs.has_sheen)
		return false;

	if (lhs.has_sheen && !areMaterialComponentsEqual(lhs.sheen, rhs.sheen))
		return false;

	if (!areTextureViewsEqual(lhs.normal_texture, rhs.normal_texture))
		return false;

	if (!areTextureViewsEqual(lhs.occlusion_texture, rhs.occlusion_texture))
		return false;

	if (!areTextureViewsEqual(lhs.emissive_texture, rhs.emissive_texture))
		return false;

	if (memcmp(lhs.emissive_factor, rhs.emissive_factor, sizeof(cgltf_float) * 3) != 0)
		return false;

	if (lhs.alpha_mode != rhs.alpha_mode)
		return false;

	if (lhs.alpha_cutoff != rhs.alpha_cutoff)
		return false;

	if (lhs.double_sided != rhs.double_sided)
		return false;

	if (lhs.unlit != rhs.unlit)
		return false;

	if (settings.keep_extras && !areExtrasEqual(data, lhs.extras, rhs.extras))
		return false;

	return true;
}

void mergeMeshMaterials(cgltf_data* data, std::vector<Mesh>& meshes, const Settings& settings)
{
	for (size_t i = 0; i < meshes.size(); ++i)
	{
		Mesh& mesh = meshes[i];

		if (!mesh.material)
			continue;

		if (settings.keep_materials && mesh.material->name && *mesh.material->name)
			continue;

		for (int j = 0; j < mesh.material - data->materials; ++j)
		{
			if (settings.keep_materials && data->materials[j].name && *data->materials[j].name)
				continue;

			if (areMaterialsEqual(data, *mesh.material, data->materials[j], settings))
			{
				mesh.material = &data->materials[j];
				break;
			}
		}
	}
}

void markNeededMaterials(cgltf_data* data, std::vector<MaterialInfo>& materials, const std::vector<Mesh>& meshes, const Settings& settings)
{
	// mark all used materials as kept
	for (size_t i = 0; i < meshes.size(); ++i)
	{
		const Mesh& mesh = meshes[i];

		if (mesh.material)
		{
			MaterialInfo& mi = materials[mesh.material - data->materials];

			mi.keep = true;
		}
	}

	// mark all named materials as kept if requested
	if (settings.keep_materials)
	{
		for (size_t i = 0; i < data->materials_count; ++i)
		{
			cgltf_material& material = data->materials[i];

			if (material.name && *material.name)
			{
				materials[i].keep = true;
			}
		}
	}
}

enum TextureKind
{
	TextureKind_Generic,
	TextureKind_Color,
	TextureKind_Normal,
};

static void analyzeMaterialTexture(const cgltf_texture_view& view, TextureKind kind, MaterialInfo& mi, cgltf_data* data, std::vector<ImageInfo>& images)
{
	mi.usesTextureTransform |= bool(view.has_transform);

	if (view.texture && view.texture->image)
	{
		mi.textureSetMask |= 1u << view.texcoord;
		mi.needsTangents |= (kind == TextureKind_Normal);

		images[view.texture->image - data->images].srgb |= (kind == TextureKind_Color);
		images[view.texture->image - data->images].normal_map |= (kind == TextureKind_Normal);
	}
}

static void analyzeMaterial(const cgltf_material& material, MaterialInfo& mi, cgltf_data* data, std::vector<ImageInfo>& images)
{
	if (material.has_pbr_metallic_roughness)
	{
		analyzeMaterialTexture(material.pbr_metallic_roughness.base_color_texture, TextureKind_Color, mi, data, images);
		analyzeMaterialTexture(material.pbr_metallic_roughness.metallic_roughness_texture, TextureKind_Generic, mi, data, images);
	}

	if (material.has_pbr_specular_glossiness)
	{
		analyzeMaterialTexture(material.pbr_specular_glossiness.diffuse_texture, TextureKind_Color, mi, data, images);
		analyzeMaterialTexture(material.pbr_specular_glossiness.specular_glossiness_texture, TextureKind_Generic, mi, data, images);
	}

	if (material.has_clearcoat)
	{
		analyzeMaterialTexture(material.clearcoat.clearcoat_texture, TextureKind_Generic, mi, data, images);
		analyzeMaterialTexture(material.clearcoat.clearcoat_roughness_texture, TextureKind_Generic, mi, data, images);
		analyzeMaterialTexture(material.clearcoat.clearcoat_normal_texture, TextureKind_Normal, mi, data, images);
	}

	if (material.has_transmission)
	{
		analyzeMaterialTexture(material.transmission.transmission_texture, TextureKind_Generic, mi, data, images);
	}

	if (material.has_specular)
	{
		analyzeMaterialTexture(material.specular.specular_texture, TextureKind_Generic, mi, data, images);
		analyzeMaterialTexture(material.specular.specular_color_texture, TextureKind_Color, mi, data, images);
	}

	if (material.has_sheen)
	{
		analyzeMaterialTexture(material.sheen.sheen_color_texture, TextureKind_Color, mi, data, images);
		analyzeMaterialTexture(material.sheen.sheen_roughness_texture, TextureKind_Generic, mi, data, images);
	}

	analyzeMaterialTexture(material.normal_texture, TextureKind_Normal, mi, data, images);
	analyzeMaterialTexture(material.occlusion_texture, TextureKind_Generic, mi, data, images);
	analyzeMaterialTexture(material.emissive_texture, TextureKind_Color, mi, data, images);
}

void analyzeMaterials(cgltf_data* data, std::vector<MaterialInfo>& materials, std::vector<ImageInfo>& images)
{
	for (size_t i = 0; i < data->materials_count; ++i)
	{
		analyzeMaterial(data->materials[i], materials[i], data, images);
	}
}
<|endoftext|>


<commit_before>/**
 *  @copyright Copyright 2018 The J-PET Framework Authors. All rights reserved.
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may find a copy of the License in the LICENCE file.
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *  @file JPetStatistics.cpp
 */

#include "JPetStatistics/JPetStatistics.h"

ClassImp(JPetStatistics);

JPetStatistics::JPetStatistics() { ; }
JPetStatistics::JPetStatistics(const JPetStatistics& old)
{
  fStats.AddAll(old.getStatsTable());
  fCounters = old.fCounters;
}

JPetStatistics::~JPetStatistics() { fStats.Clear("nodelete"); }

void JPetStatistics::createHistogram(TObject* object) { fStats.Add(object); }

void JPetStatistics::createObject(TObject* object) { fStats.Add(object); }

void JPetStatistics::createHistogramWithAxes(TObject* object, TString xAxisName, TString yAxisName, TString zAxisName)
{
  TClass *cl = object->IsA();
  if( cl->InheritsFrom("TH1D") || cl->InheritsFrom("TH1F") )
  {
    TH1D* tempHisto;
    if( cl->InheritsFrom("TH1F") )
    {
      INFO("TH1F given, casting to TH1D");
      TH1F* floatTemp = dynamic_cast<TH1F*>(object);
      floatTemp->Copy(*tempHisto);
    }
    else
      tempHisto = dynamic_cast<TH1D*>(object);
    tempHisto->GetXaxis()->SetTitle(xAxisName);
    tempHisto->GetYaxis()->SetTitle(yAxisName);
  }
  else if( cl->InheritsFrom("TH2D") || cl->InheritsFrom("TH2F") )
  {
    TH2D* tempHisto;
    if( cl->InheritsFrom("TH2F") )
    {
      INFO("TH2F given, casting to TH2D");
      TH2F* floatTemp = dynamic_cast<TH2F*>(object);
      floatTemp->Copy(*tempHisto);
    }
    else
      tempHisto = dynamic_cast<TH2D*>(object);
    tempHisto->GetXaxis()->SetTitle(xAxisName);
    tempHisto->GetYaxis()->SetTitle(yAxisName);
  }
  else if( cl->InheritsFrom("TH3D") || cl->InheritsFrom("TH3F") )
  {
    TH3D* tempHisto;
    if( cl->InheritsFrom("TH3F") )
    {
      INFO("TH3F given, casting to TH3D");
      TH3F* floatTemp = dynamic_cast<TH3F*>(object);
      floatTemp->Copy(*tempHisto);
    }
    else
      tempHisto = dynamic_cast<TH3D*>(object);
    tempHisto->GetXaxis()->SetTitle(xAxisName);
    tempHisto->GetYaxis()->SetTitle(yAxisName);
    tempHisto->GetZaxis()->SetTitle(zAxisName);
  }
  fStats.Add(object);
}

void JPetStatistics::setHistogramBinLabel(const char* name, AxisLabel axis, std::vector<std::pair<unsigned, std::string>> binLabels)
{
  TAxis *customAxis;
  TObject *tempObject = getObject<TObject>(name);
  if( !tempObject )
  {
    writeError(name, " does not exist" );
    return;
  }
  TClass *cl = tempObject->IsA();
  if( cl->InheritsFrom("TH1D") )
  {
    TH1D* tempHisto = dynamic_cast<TH1D*>(tempObject);
    if( binLabels.size() > 0 )
    {
      if( axis == AxisLabel::kXaxis )
      {
        customAxis = tempHisto->GetXaxis();
        for( unsigned i=0; i<binLabels.size(); i++ )
          customAxis->SetBinLabel(binLabels[i].first,(binLabels[i].second).c_str());
      }
      else
        writeError(name, " can not have custom Y or Z axis" );
    }
    else
      writeError(name, " had empty custom labels of bins" );
  }
  else if( cl->InheritsFrom("TH2D") )
  {
    TH2D* tempHisto = dynamic_cast<TH2D*>(tempObject);
    if( binLabels.size() > 0 )
    {
      if( axis != AxisLabel::kZaxis )
      {
        customAxis = ( axis == AxisLabel::kXaxis ? tempHisto->GetXaxis() : tempHisto->GetYaxis());
        for( unsigned i=0; i<binLabels.size(); i++ )
          customAxis->SetBinLabel(binLabels[i].first,(binLabels[i].second).c_str());
      }
      else
        writeError(name, " can not have custom Z axis" );
    }
    else
      writeError(name, " had empty custom labels of bins" );
  }
  else if( cl->InheritsFrom("TH3D") )
  {
    TH3D* tempHisto = dynamic_cast<TH3D*>(tempObject);
    if( binLabels.size() > 0 )
    {
      customAxis = ( axis == AxisLabel::kXaxis ? tempHisto->GetXaxis() :
                                        ( axis == AxisLabel::kYaxis ? tempHisto->GetYaxis() : tempHisto->GetZaxis()));
      for( unsigned i=0; i<binLabels.size(); i++ )
        customAxis->SetBinLabel(binLabels[i].first,(binLabels[i].second).c_str());
    }
    else
      writeError(name, " had empty custom labels of bins" );
  }
}

void JPetStatistics::createGraph(TObject* object) { fStats.Add(object); }

void JPetStatistics::createCanvas(TObject* object) { fStats.Add(object); }

void JPetStatistics::fillHistogram(const char* name, double xValue, doubleCheck yValue, doubleCheck zValue)
{
  TObject *tempObject = getObject<TObject>(name);
  if( !tempObject )
  {
    writeError(name, " does not exist" );
    return;
  }
  TClass *cl = tempObject->IsA();
  if( cl->InheritsFrom("TH1D") )
  {
    TH1D* tempHisto = dynamic_cast<TH1D*>(tempObject);
    tempHisto->Fill(xValue);
  }
  else if( cl->InheritsFrom("TH2D") )
  {
    TH2D* tempHisto = dynamic_cast<TH2D*>(tempObject);
    if(yValue.isChanged)
        tempHisto->Fill(xValue, yValue.value);
    else
        writeError(name, " does not received argument for Y axis" );
  }
  else if( cl->InheritsFrom("TH3D") )
  {
    TH3D* tempHisto = dynamic_cast<TH3D*>(tempObject);
    if(zValue.isChanged)
        tempHisto->Fill(xValue, yValue.value, zValue.value);
    else if(yValue.isChanged)
        writeError(name, " does not received argument for Y and Z axis" );
    else
        writeError(name, " does not received argument for Z axis" );
  }
}

TEfficiency* JPetStatistics::getEffiHisto(const char* name) { return getObject<TEfficiency>(name); }

TH1F* JPetStatistics::getHisto1D(const char* name) { return getObject<TH1F>(name); }

TH2F* JPetStatistics::getHisto2D(const char* name) { return getObject<TH2F>(name); }

TH3F* JPetStatistics::getHisto3D(const char* name) { return getObject<TH3F>(name); }

TGraph* JPetStatistics::getGraph(const char* name) { return getObject<TGraph>(name); }

TCanvas* JPetStatistics::getCanvas(const char* name) { return getObject<TCanvas>(name); }

void JPetStatistics::createCounter(const char* name) { fCounters[name] = 0.0; }

double& JPetStatistics::getCounter(const char* name) { return fCounters[name]; }

const THashTable* JPetStatistics::getStatsTable() const { return &fStats; }

void JPetStatistics::writeError(const char* nameOfHistogram, const char* messageEnd )
{
  ERROR(std::string("Histogram with name ") + std::string(nameOfHistogram) + std::string(messageEnd) );
}
<commit_msg>Optimization of the code, removing temporary pointer for TH*F, initialization to nullptr<commit_after>/**
 *  @copyright Copyright 2018 The J-PET Framework Authors. All rights reserved.
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may find a copy of the License in the LICENCE file.
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *  @file JPetStatistics.cpp
 */

#include "JPetStatistics/JPetStatistics.h"

ClassImp(JPetStatistics);

JPetStatistics::JPetStatistics() { ; }
JPetStatistics::JPetStatistics(const JPetStatistics& old)
{
  fStats.AddAll(old.getStatsTable());
  fCounters = old.fCounters;
}

JPetStatistics::~JPetStatistics() { fStats.Clear("nodelete"); }

void JPetStatistics::createHistogram(TObject* object) { fStats.Add(object); }

void JPetStatistics::createObject(TObject* object) { fStats.Add(object); }

void JPetStatistics::createHistogramWithAxes(TObject* object, TString xAxisName, TString yAxisName, TString zAxisName)
{
  TClass *cl = object->IsA();
  if( cl->InheritsFrom("TH1D") || cl->InheritsFrom("TH1F") )
  {
    TH1D* tempHisto = nullptr;
    if( cl->InheritsFrom("TH1F") ) {
      INFO("TH1F given, casting to TH1D");
    }
    object->Copy(*tempHisto);
    tempHisto->GetXaxis()->SetTitle(xAxisName);
    tempHisto->GetYaxis()->SetTitle(yAxisName);
  }
  else if( cl->InheritsFrom("TH2D") || cl->InheritsFrom("TH2F") )
  {
    TH2D* tempHisto = nullptr;
    if( cl->InheritsFrom("TH2F") )
    {
      INFO("TH2F given, casting to TH2D");
    }
    object->Copy(*tempHisto);
    tempHisto->GetXaxis()->SetTitle(xAxisName);
    tempHisto->GetYaxis()->SetTitle(yAxisName);
  }
  else if( cl->InheritsFrom("TH3D") || cl->InheritsFrom("TH3F") )
  {
    TH3D* tempHisto = nullptr;
    if( cl->InheritsFrom("TH3F") )
    {
      INFO("TH3F given, casting to TH3D");
    }
    object->Copy(*tempHisto);
    tempHisto->GetXaxis()->SetTitle(xAxisName);
    tempHisto->GetYaxis()->SetTitle(yAxisName);
    tempHisto->GetZaxis()->SetTitle(zAxisName);
  }
  fStats.Add(object);
}

void JPetStatistics::setHistogramBinLabel(const char* name, AxisLabel axis, std::vector<std::pair<unsigned, std::string>> binLabels)
{
  TAxis *customAxis;
  TObject *tempObject = getObject<TObject>(name);
  if( !tempObject )
  {
    writeError(name, " does not exist" );
    return;
  }
  TClass *cl = tempObject->IsA();
  if( cl->InheritsFrom("TH1D") )
  {
    TH1D* tempHisto = dynamic_cast<TH1D*>(tempObject);
    if( binLabels.size() > 0 )
    {
      if( axis == AxisLabel::kXaxis )
      {
        customAxis = tempHisto->GetXaxis();
        for( unsigned i=0; i<binLabels.size(); i++ )
          customAxis->SetBinLabel(binLabels[i].first,(binLabels[i].second).c_str());
      }
      else
        writeError(name, " can not have custom Y or Z axis" );
    }
    else
      writeError(name, " had empty custom labels of bins" );
  }
  else if( cl->InheritsFrom("TH2D") )
  {
    TH2D* tempHisto = dynamic_cast<TH2D*>(tempObject);
    if( binLabels.size() > 0 )
    {
      if( axis != AxisLabel::kZaxis )
      {
        customAxis = ( axis == AxisLabel::kXaxis ? tempHisto->GetXaxis() : tempHisto->GetYaxis());
        for( unsigned i=0; i<binLabels.size(); i++ )
          customAxis->SetBinLabel(binLabels[i].first,(binLabels[i].second).c_str());
      }
      else
        writeError(name, " can not have custom Z axis" );
    }
    else
      writeError(name, " had empty custom labels of bins" );
  }
  else if( cl->InheritsFrom("TH3D") )
  {
    TH3D* tempHisto = dynamic_cast<TH3D*>(tempObject);
    if( binLabels.size() > 0 )
    {
      customAxis = ( axis == AxisLabel::kXaxis ? tempHisto->GetXaxis() :
                                        ( axis == AxisLabel::kYaxis ? tempHisto->GetYaxis() : tempHisto->GetZaxis()));
      for( unsigned i=0; i<binLabels.size(); i++ )
        customAxis->SetBinLabel(binLabels[i].first,(binLabels[i].second).c_str());
    }
    else
      writeError(name, " had empty custom labels of bins" );
  }
}

void JPetStatistics::createGraph(TObject* object) { fStats.Add(object); }

void JPetStatistics::createCanvas(TObject* object) { fStats.Add(object); }

void JPetStatistics::fillHistogram(const char* name, double xValue, doubleCheck yValue, doubleCheck zValue)
{
  TObject *tempObject = getObject<TObject>(name);
  if( !tempObject )
  {
    writeError(name, " does not exist" );
    return;
  }
  TClass *cl = tempObject->IsA();
  if( cl->InheritsFrom("TH1D") )
  {
    TH1D* tempHisto = dynamic_cast<TH1D*>(tempObject);
    tempHisto->Fill(xValue);
  }
  else if( cl->InheritsFrom("TH2D") )
  {
    TH2D* tempHisto = dynamic_cast<TH2D*>(tempObject);
    if(yValue.isChanged)
        tempHisto->Fill(xValue, yValue.value);
    else
        writeError(name, " does not received argument for Y axis" );
  }
  else if( cl->InheritsFrom("TH3D") )
  {
    TH3D* tempHisto = dynamic_cast<TH3D*>(tempObject);
    if(zValue.isChanged)
        tempHisto->Fill(xValue, yValue.value, zValue.value);
    else if(yValue.isChanged)
        writeError(name, " does not received argument for Y and Z axis" );
    else
        writeError(name, " does not received argument for Z axis" );
  }
}

TEfficiency* JPetStatistics::getEffiHisto(const char* name) { return getObject<TEfficiency>(name); }

TH1F* JPetStatistics::getHisto1D(const char* name) { return getObject<TH1F>(name); }

TH2F* JPetStatistics::getHisto2D(const char* name) { return getObject<TH2F>(name); }

TH3F* JPetStatistics::getHisto3D(const char* name) { return getObject<TH3F>(name); }

TGraph* JPetStatistics::getGraph(const char* name) { return getObject<TGraph>(name); }

TCanvas* JPetStatistics::getCanvas(const char* name) { return getObject<TCanvas>(name); }

void JPetStatistics::createCounter(const char* name) { fCounters[name] = 0.0; }

double& JPetStatistics::getCounter(const char* name) { return fCounters[name]; }

const THashTable* JPetStatistics::getStatsTable() const { return &fStats; }

void JPetStatistics::writeError(const char* nameOfHistogram, const char* messageEnd )
{
  ERROR(std::string("Histogram with name ") + std::string(nameOfHistogram) + std::string(messageEnd) );
}
<|endoftext|>


<commit_before>//
// Created by rick on 5/14/17.
//

#include <cfloat>
#include "Rectangle.h"
#include "AABB.h"

bool XYRectangle::hit(const Ray &r_in, double t0, double t1, HitRecord &rec) const
{
    double t = (k - r_in.origin().z()) / r_in.direction().z();
    if (t < t0 || t > t1) return false;
    double x = r_in.origin().x() + t * r_in.direction().x();
    double y = r_in.origin().y() + t * r_in.direction().y();
    if (x < x0 || x > x1 || y < y0 || y > y1) return false;

    rec.u = (x-x0)/(x1-x0);
    rec.v = (y-y0)/(y1-y0);
    rec.t = t;
    rec.material = material;
    rec.p = r_in.pointAt(t);
    rec.normal = Vector3(0, 0, 1);

    return true;
}

bool XYRectangle::bounds(double t0, double t1, AABB &bbox) const
{
    bbox = AABB(Vector3(x0, y0, k-0.0001), Vector3(x1, y1, k+0.0001));
    return true;
}

bool XZRectangle::hit(const Ray &r_in, double t0, double t1, HitRecord &rec) const
{
    double t = (k - r_in.origin().y()) / r_in.direction().y();
    if (t < t0 || t > t1) return false;
    double x = r_in.origin().x() + t * r_in.direction().x();
    double z = r_in.origin().z() + t * r_in.direction().z();
    if (x < x0 || x > x1 || z < z0 || z > z1) return false;

    rec.u = (x-x0)/(x1-x0);
    rec.v = (z-z0)/(z1-z0);
    rec.t = t;
    rec.material = material;
    rec.p = r_in.pointAt(t);
    rec.normal = Vector3(0, 1, 0);

    return true;
}

bool XZRectangle::bounds(double t0, double t1, AABB &bbox) const
{
    bbox = AABB(Vector3(x0, k-0.0001, z0), Vector3(x1, k+0.0001, z1));
    return true;
}

bool YZRectangle::hit(const Ray &r_in, double t0, double t1, HitRecord &rec) const
{
    double t = (k - r_in.origin().x()) / r_in.direction().x();
    if (t < t0 || t > t1) return false;
    double y = r_in.origin().y() + t * r_in.direction().y();
    double z = r_in.origin().z() + t * r_in.direction().z();
    if (y < y0 || y > y1 || z < z0 || z > z1) return false;

    rec.u = (y-y0)/(y1-y0);
    rec.v = (z-z0)/(z1-z0);
    rec.t = t;
    rec.material = material;
    rec.p = r_in.pointAt(t);
    rec.normal = Vector3(1, 0, 0);

    return true;
}

bool YZRectangle::bounds(double t0, double t1, AABB &bbox) const
{
    bbox = AABB(Vector3(k-0.0001, y0, z0), Vector3(k+0.0001, y1, z1));
    return true;
}

Box::Box(const Vector3 &p0, const Vector3 &p1, Material *mat) :
    pmin(p0),
    pmax(p1)
{
    std::vector<Hitable*> hl;
    hl.push_back(new XYRectangle(p0.x(), p1.x(), p0.y(), p1.y(), p1.z(), mat));
    hl.push_back(new FlipNormals(new XYRectangle(p0.x(), p1.x(), p0.y(), p1.y(), p0.z(), mat)));
    hl.push_back(new XZRectangle(p0.x(), p1.x(), p0.z(), p1.z(), p1.y(), mat));
    hl.push_back(new FlipNormals(new XZRectangle(p0.x(), p1.x(), p0.z(), p1.z(), p0.y(), mat)));
    hl.push_back(new YZRectangle(p0.y(), p1.y(), p0.z(), p1.z(), p1.x(), mat));
    hl.push_back(new FlipNormals(new YZRectangle(p0.y(), p1.y(), p0.z(), p1.z(), p0.x(), mat)));
    child = new HitableList(hl);
}

bool Box::hit(const Ray &r_in, double t0, double t1, HitRecord &rec) const
{
    return child->hit(r_in, t0, t1, rec);
}

bool Box::bounds(double t0, double t1, AABB &bbox) const
{
    bbox = AABB(pmin, pmax);
    return true;
}

RotateY::RotateY(Hitable *p, double angle)
{
    hitable = p;
    double radians = (M_PI / 180.0) * angle;
    sinTheta = sin(radians);
    cosTheta = cos(radians);
    hasBox = p->bounds(0, 1, bbox);
    Vector3 min(DBL_MAX, DBL_MAX, DBL_MAX);
    Vector3 max(-DBL_MAX, -DBL_MAX, -DBL_MAX);
    for (int i = 0; i < 2; i++)
    {
        for (int j = 0; j < 2; j++)
        {
            for (int k = 0; k < 2; k++)
            {
                double x = i * bbox.max().x() + (1-i)*bbox.min().x();
                double y = j * bbox.max().y() + (1-j)*bbox.min().y();
                double z = k * bbox.max().z() + (1-k)*bbox.min().z();
                double newx = cosTheta * x + sinTheta * z;
                double newz = -sinTheta * x + cosTheta * z;
                Vector3 tester(newx, y, newz);
                for (int c = 0; c < 3; c++)
                {
                    if (tester[c] > max[c])
                        max[c] = tester[c];
                    if (tester[c] < min[c])
                        min[c] = tester[c];
                }
            }
        }
    }
    bbox = AABB(min, max);
}

bool RotateY::hit(const Ray &r_in, double t0, double t1, HitRecord &rec) const
{
    Vector3 origin = r_in.origin();
    Vector3 direction = r_in.direction();

    origin[0] = cosTheta*r_in.origin()[0] - sinTheta*r_in.origin()[2];
    origin[2] = sinTheta*r_in.origin()[0] + cosTheta*r_in.origin()[2];

    direction[0] = cosTheta*r_in.direction()[0] - sinTheta*r_in.direction()[2];
    direction[2] = sinTheta*r_in.direction()[0] + cosTheta*r_in.direction()[2];

    Ray rotatedR(origin, direction, r_in.time());
    if (hitable->hit(rotatedR, t0, t1, rec))
    {
        Vector3 p = rec.p;
        Vector3 normal = rec.normal;
        p[0] = cosTheta*rec.p[0] + sinTheta*rec.p[2];
        p[2] = -sinTheta*rec.p[0] + cosTheta*rec.p[2];
        normal[0] = cosTheta*rec.normal[0] + sinTheta*rec.normal[2];
        normal[2] = -sinTheta*rec.normal[2] + cosTheta*rec.normal[2];
        rec.p = p;
        rec.normal = normal;
        return true;
    }
    return false;
}
<commit_msg>Fixed rotateY bug.<commit_after>//
// Created by rick on 5/14/17.
//

#include <cfloat>
#include "Rectangle.h"
#include "AABB.h"

bool XYRectangle::hit(const Ray &r_in, double t0, double t1, HitRecord &rec) const
{
    double t = (k - r_in.origin().z()) / r_in.direction().z();
    if (t < t0 || t > t1) return false;
    double x = r_in.origin().x() + t * r_in.direction().x();
    double y = r_in.origin().y() + t * r_in.direction().y();
    if (x < x0 || x > x1 || y < y0 || y > y1) return false;

    rec.u = (x-x0)/(x1-x0);
    rec.v = (y-y0)/(y1-y0);
    rec.t = t;
    rec.material = material;
    rec.p = r_in.pointAt(t);
    rec.normal = Vector3(0, 0, 1);

    return true;
}

bool XYRectangle::bounds(double t0, double t1, AABB &bbox) const
{
    bbox = AABB(Vector3(x0, y0, k-0.0001), Vector3(x1, y1, k+0.0001));
    return true;
}

bool XZRectangle::hit(const Ray &r_in, double t0, double t1, HitRecord &rec) const
{
    double t = (k - r_in.origin().y()) / r_in.direction().y();
    if (t < t0 || t > t1) return false;
    double x = r_in.origin().x() + t * r_in.direction().x();
    double z = r_in.origin().z() + t * r_in.direction().z();
    if (x < x0 || x > x1 || z < z0 || z > z1) return false;

    rec.u = (x-x0)/(x1-x0);
    rec.v = (z-z0)/(z1-z0);
    rec.t = t;
    rec.material = material;
    rec.p = r_in.pointAt(t);
    rec.normal = Vector3(0, 1, 0);

    return true;
}

bool XZRectangle::bounds(double t0, double t1, AABB &bbox) const
{
    bbox = AABB(Vector3(x0, k-0.0001, z0), Vector3(x1, k+0.0001, z1));
    return true;
}

bool YZRectangle::hit(const Ray &r_in, double t0, double t1, HitRecord &rec) const
{
    double t = (k - r_in.origin().x()) / r_in.direction().x();
    if (t < t0 || t > t1) return false;
    double y = r_in.origin().y() + t * r_in.direction().y();
    double z = r_in.origin().z() + t * r_in.direction().z();
    if (y < y0 || y > y1 || z < z0 || z > z1) return false;

    rec.u = (y-y0)/(y1-y0);
    rec.v = (z-z0)/(z1-z0);
    rec.t = t;
    rec.material = material;
    rec.p = r_in.pointAt(t);
    rec.normal = Vector3(1, 0, 0);

    return true;
}

bool YZRectangle::bounds(double t0, double t1, AABB &bbox) const
{
    bbox = AABB(Vector3(k-0.0001, y0, z0), Vector3(k+0.0001, y1, z1));
    return true;
}

Box::Box(const Vector3 &p0, const Vector3 &p1, Material *mat) :
    pmin(p0),
    pmax(p1)
{
    std::vector<Hitable*> hl;
    hl.push_back(new XYRectangle(p0.x(), p1.x(), p0.y(), p1.y(), p1.z(), mat));
    hl.push_back(new FlipNormals(new XYRectangle(p0.x(), p1.x(), p0.y(), p1.y(), p0.z(), mat)));
    hl.push_back(new XZRectangle(p0.x(), p1.x(), p0.z(), p1.z(), p1.y(), mat));
    hl.push_back(new FlipNormals(new XZRectangle(p0.x(), p1.x(), p0.z(), p1.z(), p0.y(), mat)));
    hl.push_back(new YZRectangle(p0.y(), p1.y(), p0.z(), p1.z(), p1.x(), mat));
    hl.push_back(new FlipNormals(new YZRectangle(p0.y(), p1.y(), p0.z(), p1.z(), p0.x(), mat)));
    child = new HitableList(hl);
}

bool Box::hit(const Ray &r_in, double t0, double t1, HitRecord &rec) const
{
    return child->hit(r_in, t0, t1, rec);
}

bool Box::bounds(double t0, double t1, AABB &bbox) const
{
    bbox = AABB(pmin, pmax);
    return true;
}

RotateY::RotateY(Hitable *p, double angle)
{
    hitable = p;
    double radians = (M_PI / 180.0) * angle;
    sinTheta = sin(radians);
    cosTheta = cos(radians);
    hasBox = p->bounds(0, 1, bbox);
    Vector3 min(DBL_MAX, DBL_MAX, DBL_MAX);
    Vector3 max(-DBL_MAX, -DBL_MAX, -DBL_MAX);
    for (int i = 0; i < 2; i++)
    {
        for (int j = 0; j < 2; j++)
        {
            for (int k = 0; k < 2; k++)
            {
                double x = i * bbox.max().x() + (1-i)*bbox.min().x();
                double y = j * bbox.max().y() + (1-j)*bbox.min().y();
                double z = k * bbox.max().z() + (1-k)*bbox.min().z();
                double newx = cosTheta * x + sinTheta * z;
                double newz = -sinTheta * x + cosTheta * z;
                Vector3 tester(newx, y, newz);
                for (int c = 0; c < 3; c++)
                {
                    if (tester[c] > max[c])
                        max[c] = tester[c];
                    if (tester[c] < min[c])
                        min[c] = tester[c];
                }
            }
        }
    }
    bbox = AABB(min, max);
}

bool RotateY::hit(const Ray &r_in, double t0, double t1, HitRecord &rec) const
{
    Vector3 origin = r_in.origin();
    Vector3 direction = r_in.direction();

    origin[0] = cosTheta*r_in.origin()[0] - sinTheta*r_in.origin()[2];
    origin[2] = sinTheta*r_in.origin()[0] + cosTheta*r_in.origin()[2];

    direction[0] = cosTheta*r_in.direction()[0] - sinTheta*r_in.direction()[2];
    direction[2] = sinTheta*r_in.direction()[0] + cosTheta*r_in.direction()[2];

    Ray rotatedR(origin, direction, r_in.time());
    if (hitable->hit(rotatedR, t0, t1, rec))
    {
        Vector3 p = rec.p;
        Vector3 normal = rec.normal;
        p[0] = cosTheta*rec.p[0] + sinTheta*rec.p[2];
        p[2] = -sinTheta*rec.p[0] + cosTheta*rec.p[2];
        normal[0] = cosTheta*rec.normal[0] + sinTheta*rec.normal[2];
        normal[2] = -sinTheta*rec.normal[0] + cosTheta*rec.normal[2];
        rec.p = p;
        rec.normal = normal;
        return true;
    }
    return false;
}
<|endoftext|>


<commit_before>#include "BlendSpace2D.h"
#include <Animation/AnimationController.h>
#include <Animation/SkeletonInfo.h>
#include <Math/DelaunayTriangulation.hpp>

namespace DEM::Math
{

template<>
struct CDelaunayInputTraits<DEM::Anim::CBlendSpace2D::CSample>
{
	static vector2 GetPoint(const DEM::Anim::CBlendSpace2D::CSample& Value) { return { Value.XValue, Value.YValue }; }
};

}

namespace DEM::Anim
{

CBlendSpace2D::CBlendSpace2D(CStrID XParamID, CStrID YParamID)
	: _XParamID(XParamID)
	, _YParamID(YParamID)
{
}
//---------------------------------------------------------------------

void CBlendSpace2D::Init(CAnimationControllerInitContext& Context)
{
	EParamType XType, YType;
	if (!Context.Controller.FindParam(_XParamID, &XType, &_XParamIndex) || XType != EParamType::Float)
		_XParamIndex = INVALID_INDEX;
	if (!Context.Controller.FindParam(_YParamID, &YType, &_YParamIndex) || YType != EParamType::Float)
		_YParamIndex = INVALID_INDEX;

	if (!_Samples.empty())
	{
		// TODO: build triangles and acceleration structure
		// AABB inflate by epsilon? or mb not needed?
		std::vector<std::tuple<uint32_t, uint32_t, uint32_t>> Triangles;
		DEM::Math::Delaunay2D(_Samples.cbegin(), _Samples.cend(), Triangles);

		for (auto& Sample : _Samples)
			Sample.Source->Init(Context);

		// Call after initializing sources, letting them to contribute to SkeletonInfo
		if (Context.SkeletonInfo)
			_Blender.Initialize(3, Context.SkeletonInfo->GetNodeCount());
	}
}
//---------------------------------------------------------------------

//!!!DUPLICATED CODE! See 1D!
static inline void AdvanceNormalizedTime(float dt, float AnimLength, float& NormalizedTime)
{
	if (AnimLength)
	{
		NormalizedTime += (dt / AnimLength);
		if (NormalizedTime < 0.f) NormalizedTime += (1.f - std::truncf(NormalizedTime));
		else if (NormalizedTime > 1.f) NormalizedTime -= std::truncf(NormalizedTime);
	}
	else NormalizedTime = 0.f;
}
//---------------------------------------------------------------------

void CBlendSpace2D::UpdateSingleSample(CAnimGraphNode& Node, CAnimationController& Controller, float dt, CSyncContext* pSyncContext)
{
	const ESyncMethod SyncMethod = pSyncContext ? pSyncContext->Method : ESyncMethod::None;
	switch (SyncMethod)
	{
		case ESyncMethod::None:
		{
			AdvanceNormalizedTime(dt, Node.GetAnimationLengthScaled(), _NormalizedTime);
			break;
		}
		case ESyncMethod::NormalizedTime:
		case ESyncMethod::PhaseMatching: //???or get _NormalizedTime from _pFirst current phase?
		{
			_NormalizedTime = pSyncContext->NormalizedTime;
			break;
		}
		default: NOT_IMPLEMENTED; break;
	}

	_pFirst = &Node;
	_pSecond = nullptr;
	_pFirst->Update(Controller, dt, pSyncContext);
}
//---------------------------------------------------------------------

void CBlendSpace2D::Update(CAnimationController& Controller, float dt, CSyncContext* pSyncContext)
{
	if (_Samples.empty()) return;

	const float XInput = Controller.GetFloat(_XParamIndex);
	const float YInput = Controller.GetFloat(_YParamIndex);

	//???use cache if input parameter didn't change?

	//???TODO: optionally filter input to make transitions smoother?
	// then dt *= (BeforeFilter / AfterFilter);

	// Scale animation speed for values outside the sample range
	const float ClampedInput = std::clamp(XInput, _Samples.front().XValue, _Samples.back().XValue);
	if (ClampedInput && ClampedInput != XInput) dt *= (XInput / ClampedInput);

	if (_Samples.size() == 1)
	{
		UpdateSingleSample(*_Samples[0].Source, Controller, dt, pSyncContext);
		return;
	}

	const auto It = std::lower_bound(_Samples.cbegin(), _Samples.cend(), XInput,
		[](const auto& Elm, float Val) { return Elm.XValue < Val; });

	if (It == _Samples.cbegin() || (It != _Samples.cend() && n_fequal(It->XValue, XInput, SAMPLE_MATCH_TOLERANCE)))
	{
		UpdateSingleSample(*It->Source, Controller, dt, pSyncContext);
		return;
	}

	const auto PrevIt = std::prev(It);
	if (n_fequal(PrevIt->XValue, XInput, SAMPLE_MATCH_TOLERANCE) || (It == _Samples.cend() && PrevIt->XValue < XInput))
	{
		UpdateSingleSample(*PrevIt->Source, Controller, dt, pSyncContext);
		return;
	}

	// For convenience make sure _pFirst is always the one with greater weight
	const float BlendFactor = (XInput - PrevIt->XValue) / (It->XValue - PrevIt->XValue);
	if (BlendFactor <= 0.5f)
	{
		_pFirst = PrevIt->Source.get();
		_pSecond = It->Source.get();
		_Blender.SetWeight(0, 1.f - BlendFactor);
		_Blender.SetWeight(1, BlendFactor);
	}
	else
	{
		_pFirst = It->Source.get();
		_pSecond = PrevIt->Source.get();
		_Blender.SetWeight(0, BlendFactor);
		_Blender.SetWeight(1, 1.f - BlendFactor);
	}

	const ESyncMethod SyncMethod = pSyncContext ? pSyncContext->Method : ESyncMethod::None;
	switch (SyncMethod)
	{
		case ESyncMethod::None:
		{
			const float AnimLength = It->Source->GetAnimationLengthScaled() * BlendFactor +
				PrevIt->Source->GetAnimationLengthScaled() * (1.f - BlendFactor);
			AdvanceNormalizedTime(dt, AnimLength, _NormalizedTime);

			CSyncContext LocalSyncContext{ ESyncMethod::NormalizedTime, _NormalizedTime };

			_pFirst->Update(Controller, dt, &LocalSyncContext);

			// Try to synchronize next animations by a locomotion phase
			const float Phase = _pFirst->GetLocomotionPhase();
			if (Phase >= 0.f)
			{
				LocalSyncContext.Method = ESyncMethod::PhaseMatching;
				LocalSyncContext.LocomotionPhase = Phase;
			}

			_pSecond->Update(Controller, dt, &LocalSyncContext);

			break;
		}
		case ESyncMethod::NormalizedTime:
		case ESyncMethod::PhaseMatching:
		{
			_NormalizedTime = pSyncContext->NormalizedTime;
			//???if phase matching, get _NormalizedTime from curr _pFirst phase?

			_pFirst->Update(Controller, dt, pSyncContext);
			_pSecond->Update(Controller, dt, pSyncContext);

			break;
		}
		default: NOT_IMPLEMENTED; break;
	}

	//::Sys::DbgOut("***CBlendSpace2D: time %lf, input %lf, ipol (%d-%d) %lf\n", _NormalizedTime, Input,
	//	std::distance(_Samples.cbegin(), PrevIt), std::distance(_Samples.cbegin(), It), BlendFactor);
}
//---------------------------------------------------------------------

void CBlendSpace2D::EvaluatePose(IPoseOutput& Output)
{
	if (_pSecond)
	{
		//???TODO: try inplace blending in Output instead of preallocated Blender? helps to save
		// memory and may be faster! We don't use priority here anyway, and weights always sum to 1.f.
		//Can use special wrapper output CPoseScaleBiasOutput / CPoseWeightedOutput to apply weights on the fly!
		//!!!can even have single and per-bone weigh variations!
		//???cache locality may suffer if blending in place? scene nodes are scattered around the heap.
		_pFirst->EvaluatePose(*_Blender.GetInput(0));
		_pSecond->EvaluatePose(*_Blender.GetInput(1));

		_Blender.EvaluatePose(Output);
	}
	else if (_pFirst) _pFirst->EvaluatePose(Output);
}
//---------------------------------------------------------------------

float CBlendSpace2D::GetAnimationLengthScaled() const
{
	if (!_pFirst) return 0.f;
	if (!_pSecond) return _pFirst->GetAnimationLengthScaled();
	return _pFirst->GetAnimationLengthScaled() * _Blender.GetWeight(0) +
		_pSecond->GetAnimationLengthScaled() * _Blender.GetWeight(1);
}
//---------------------------------------------------------------------

float CBlendSpace2D::GetLocomotionPhase() const
{
	// Always from the most weighted animation, others must be synchronized
	return _pFirst ? _pFirst->GetLocomotionPhase() : std::numeric_limits<float>().lowest();
}
//---------------------------------------------------------------------

// NB: sample value must be different from neighbours at least by SAMPLE_MATCH_TOLERANCE
bool CBlendSpace2D::AddSample(PAnimGraphNode&& Source, float XValue, float YValue)
{
	if (!Source) return false;

	auto It = _Samples.cend();
	if (!_Samples.empty())
	{
		It = std::lower_bound(_Samples.cbegin(), _Samples.cend(), XValue,
			[](const auto& Elm, float Val) { return Elm.XValue < Val; });

		// Check if the value is the same as upper
		if (It != _Samples.cend() && n_fequal(It->XValue, XValue, SAMPLE_MATCH_TOLERANCE)) return false;

		// Check if the value is the same as lower
		if (It != _Samples.cbegin() && n_fequal(std::prev(It)->XValue, XValue, SAMPLE_MATCH_TOLERANCE)) return false;
	}

	_Samples.insert(It, { std::move(Source), XValue });
	return true;
}
//---------------------------------------------------------------------

}
<commit_msg>Blend space 2D sample adding<commit_after>#include "BlendSpace2D.h"
#include <Animation/AnimationController.h>
#include <Animation/SkeletonInfo.h>
#include <Math/DelaunayTriangulation.hpp>

namespace DEM::Math
{

template<>
struct CDelaunayInputTraits<DEM::Anim::CBlendSpace2D::CSample>
{
	static vector2 GetPoint(const DEM::Anim::CBlendSpace2D::CSample& Value) { return { Value.XValue, Value.YValue }; }
};

}

namespace DEM::Anim
{

CBlendSpace2D::CBlendSpace2D(CStrID XParamID, CStrID YParamID)
	: _XParamID(XParamID)
	, _YParamID(YParamID)
{
}
//---------------------------------------------------------------------

void CBlendSpace2D::Init(CAnimationControllerInitContext& Context)
{
	EParamType XType, YType;
	if (!Context.Controller.FindParam(_XParamID, &XType, &_XParamIndex) || XType != EParamType::Float)
		_XParamIndex = INVALID_INDEX;
	if (!Context.Controller.FindParam(_YParamID, &YType, &_YParamIndex) || YType != EParamType::Float)
		_YParamIndex = INVALID_INDEX;

	if (!_Samples.empty())
	{
		// TODO: build acceleration structure
		//!!!when sampling, too low weight must be discarded with renormalization! E.g. 0.499+0.499+0.002->0.5+0.5+0.0
		std::vector<Math::CDelaunayTriangle> Triangles;
		Math::Delaunay2D(_Samples.cbegin(), _Samples.cend(), Triangles);

		for (auto& Sample : _Samples)
			Sample.Source->Init(Context);

		// Call after initializing sources, letting them to contribute to SkeletonInfo
		if (Context.SkeletonInfo)
			_Blender.Initialize(3, Context.SkeletonInfo->GetNodeCount());
	}
}
//---------------------------------------------------------------------

//!!!DUPLICATED CODE! See 1D!
static inline void AdvanceNormalizedTime(float dt, float AnimLength, float& NormalizedTime)
{
	if (AnimLength)
	{
		NormalizedTime += (dt / AnimLength);
		if (NormalizedTime < 0.f) NormalizedTime += (1.f - std::truncf(NormalizedTime));
		else if (NormalizedTime > 1.f) NormalizedTime -= std::truncf(NormalizedTime);
	}
	else NormalizedTime = 0.f;
}
//---------------------------------------------------------------------

//!!!DUPLICATED CODE! See 1D!
void CBlendSpace2D::UpdateSingleSample(CAnimGraphNode& Node, CAnimationController& Controller, float dt, CSyncContext* pSyncContext)
{
	const ESyncMethod SyncMethod = pSyncContext ? pSyncContext->Method : ESyncMethod::None;
	switch (SyncMethod)
	{
		case ESyncMethod::None:
		{
			AdvanceNormalizedTime(dt, Node.GetAnimationLengthScaled(), _NormalizedTime);
			break;
		}
		case ESyncMethod::NormalizedTime:
		case ESyncMethod::PhaseMatching: //???or get _NormalizedTime from _pFirst current phase?
		{
			_NormalizedTime = pSyncContext->NormalizedTime;
			break;
		}
		default: NOT_IMPLEMENTED; break;
	}

	_pFirst = &Node;
	_pSecond = nullptr;
	_pFirst->Update(Controller, dt, pSyncContext);
}
//---------------------------------------------------------------------

void CBlendSpace2D::Update(CAnimationController& Controller, float dt, CSyncContext* pSyncContext)
{
	if (_Samples.empty()) return;

	const float XInput = Controller.GetFloat(_XParamIndex);
	const float YInput = Controller.GetFloat(_YParamIndex);

	//???use cache if input parameter didn't change?

	//???TODO: optionally filter input to make transitions smoother?
	// then dt *= (BeforeFilter / AfterFilter);

	// Scale animation speed for values outside the sample range
	const float ClampedInput = std::clamp(XInput, _Samples.front().XValue, _Samples.back().XValue);
	if (ClampedInput && ClampedInput != XInput) dt *= (XInput / ClampedInput);

	if (_Samples.size() == 1)
	{
		UpdateSingleSample(*_Samples[0].Source, Controller, dt, pSyncContext);
		return;
	}

	const auto It = std::lower_bound(_Samples.cbegin(), _Samples.cend(), XInput,
		[](const auto& Elm, float Val) { return Elm.XValue < Val; });

	if (It == _Samples.cbegin() || (It != _Samples.cend() && n_fequal(It->XValue, XInput, SAMPLE_MATCH_TOLERANCE)))
	{
		UpdateSingleSample(*It->Source, Controller, dt, pSyncContext);
		return;
	}

	const auto PrevIt = std::prev(It);
	if (n_fequal(PrevIt->XValue, XInput, SAMPLE_MATCH_TOLERANCE) || (It == _Samples.cend() && PrevIt->XValue < XInput))
	{
		UpdateSingleSample(*PrevIt->Source, Controller, dt, pSyncContext);
		return;
	}

	// For convenience make sure _pFirst is always the one with greater weight
	const float BlendFactor = (XInput - PrevIt->XValue) / (It->XValue - PrevIt->XValue);
	if (BlendFactor <= 0.5f)
	{
		_pFirst = PrevIt->Source.get();
		_pSecond = It->Source.get();
		_Blender.SetWeight(0, 1.f - BlendFactor);
		_Blender.SetWeight(1, BlendFactor);
	}
	else
	{
		_pFirst = It->Source.get();
		_pSecond = PrevIt->Source.get();
		_Blender.SetWeight(0, BlendFactor);
		_Blender.SetWeight(1, 1.f - BlendFactor);
	}

	const ESyncMethod SyncMethod = pSyncContext ? pSyncContext->Method : ESyncMethod::None;
	switch (SyncMethod)
	{
		case ESyncMethod::None:
		{
			const float AnimLength = It->Source->GetAnimationLengthScaled() * BlendFactor +
				PrevIt->Source->GetAnimationLengthScaled() * (1.f - BlendFactor);
			AdvanceNormalizedTime(dt, AnimLength, _NormalizedTime);

			CSyncContext LocalSyncContext{ ESyncMethod::NormalizedTime, _NormalizedTime };

			_pFirst->Update(Controller, dt, &LocalSyncContext);

			// Try to synchronize next animations by a locomotion phase
			const float Phase = _pFirst->GetLocomotionPhase();
			if (Phase >= 0.f)
			{
				LocalSyncContext.Method = ESyncMethod::PhaseMatching;
				LocalSyncContext.LocomotionPhase = Phase;
			}

			_pSecond->Update(Controller, dt, &LocalSyncContext);

			break;
		}
		case ESyncMethod::NormalizedTime:
		case ESyncMethod::PhaseMatching:
		{
			_NormalizedTime = pSyncContext->NormalizedTime;
			//???if phase matching, get _NormalizedTime from curr _pFirst phase?

			_pFirst->Update(Controller, dt, pSyncContext);
			_pSecond->Update(Controller, dt, pSyncContext);

			break;
		}
		default: NOT_IMPLEMENTED; break;
	}

	//::Sys::DbgOut("***CBlendSpace2D: time %lf, input %lf, ipol (%d-%d) %lf\n", _NormalizedTime, Input,
	//	std::distance(_Samples.cbegin(), PrevIt), std::distance(_Samples.cbegin(), It), BlendFactor);
}
//---------------------------------------------------------------------

void CBlendSpace2D::EvaluatePose(IPoseOutput& Output)
{
	if (_pSecond)
	{
		//???TODO: try inplace blending in Output instead of preallocated Blender? helps to save
		// memory and may be faster! We don't use priority here anyway, and weights always sum to 1.f.
		//Can use special wrapper output CPoseScaleBiasOutput / CPoseWeightedOutput to apply weights on the fly!
		//!!!can even have single and per-bone weigh variations!
		//???cache locality may suffer if blending in place? scene nodes are scattered around the heap.
		_pFirst->EvaluatePose(*_Blender.GetInput(0));
		_pSecond->EvaluatePose(*_Blender.GetInput(1));
		if (_pThird) _pThird->EvaluatePose(*_Blender.GetInput(2));

		_Blender.EvaluatePose(Output);
	}
	else if (_pFirst) _pFirst->EvaluatePose(Output);
}
//---------------------------------------------------------------------

float CBlendSpace2D::GetAnimationLengthScaled() const
{
	if (!_pFirst) return 0.f;
	if (!_pSecond) return _pFirst->GetAnimationLengthScaled();
	return _pFirst->GetAnimationLengthScaled() * _Blender.GetWeight(0) +
		_pSecond->GetAnimationLengthScaled() * _Blender.GetWeight(1) +
		(_pThird ? _pThird->GetAnimationLengthScaled() * _Blender.GetWeight(2) : 0.f);
}
//---------------------------------------------------------------------

float CBlendSpace2D::GetLocomotionPhase() const
{
	// Always from the most weighted animation, others must be synchronized
	return _pFirst ? _pFirst->GetLocomotionPhase() : std::numeric_limits<float>().lowest();
}
//---------------------------------------------------------------------

// NB: sample value must be different from neighbours at least by SAMPLE_MATCH_TOLERANCE
bool CBlendSpace2D::AddSample(PAnimGraphNode&& Source, float XValue, float YValue)
{
	// TODO: fail if acceleration structure/triangles are not empty, or invalidate them and refresh on next Init/sampling!
	if (!Source) return false;

	for (const auto& Sample : _Samples)
		if (n_fequal(Sample.XValue, XValue, SAMPLE_MATCH_TOLERANCE) && n_fequal(Sample.YValue, YValue, SAMPLE_MATCH_TOLERANCE))
			return false;

	_Samples.push_back({ std::move(Source), XValue, YValue });
	return true;
}
//---------------------------------------------------------------------

}
<|endoftext|>