<commit_before>#include <cassert>
#include <iostream>
#include <map>
#include <random>

#include <GLFW/glfw3.h>

#include <glbinding/Binding.h>
#include <glbinding/callbacks.h>
#include <glbinding/gl/gl.h>
#include <globjects/globjects.h>

#include <glm/gtc/matrix_transform.hpp>

#include <openll/GlyphRenderer.h>
#include <openll/FontLoader.h>
#include <openll/Typesetter.h>
#include <openll/stages/GlyphPreparationStage.h>

#include <openll/FontFace.h>
#include <openll/GlyphSequence.h>
#include <openll/Alignment.h>
#include <openll/LineAnchor.h>
#include <openll/SuperSampling.h>
#include <openll/layout/layoutbase.h>
#include <openll/layout/algorithm.h>

#include <cpplocate/cpplocate.h>
#include <cpplocate/ModuleInfo.h>

#include "PointDrawable.h"
#include "RectangleDrawable.h"
#include "benchmark.h"

using namespace gl;

glm::uvec2 g_viewport{640, 480};
bool g_config_changed = true;
size_t g_algorithmID = 0;
bool g_frames_visible = true;
long int g_seed = 0;
int g_numLabels = 64;

struct Algorithm
{
    std::string name;
    std::function<void(std::vector<gloperate_text::Label>&)> function;
};

using namespace std::placeholders;

std::vector<Algorithm> layoutAlgorithms
{
    {"constant",                          gloperate_text::layout::constant},
    {"random",                            gloperate_text::layout::random},
    {"greedy with area",                  std::bind(gloperate_text::layout::greedy, _1, gloperate_text::layout::overlapArea)},
    {"discreteGradientDescent with area", std::bind(gloperate_text::layout::discreteGradientDescent, _1, gloperate_text::layout::overlapArea)},
    {"simulatedAnnealing with area",      std::bind(gloperate_text::layout::simulatedAnnealing, _1, gloperate_text::layout::overlapArea, false, glm::vec2(0.f))},
    {"simulatedAnnealing",                std::bind(gloperate_text::layout::simulatedAnnealing, _1, gloperate_text::layout::standard, false, glm::vec2(0.f))},
    {"simulatedAnnealing with padding",   std::bind(gloperate_text::layout::simulatedAnnealing, _1, gloperate_text::layout::standard, false, glm::vec2(0.2f))},
    {"simulatedAnnealing with selection", std::bind(gloperate_text::layout::simulatedAnnealing, _1, gloperate_text::layout::standard, true, glm::vec2(0.f))},
    {"simulatedAnnealing (everything)",   std::bind(gloperate_text::layout::simulatedAnnealing, _1, gloperate_text::layout::standard, true, glm::vec2(0.2f))},
};

void onResize(GLFWwindow*, int width, int height)
{
    g_viewport = {width, height};
    g_config_changed = true;
}

void onKeyPress(GLFWwindow * window, int key, int, int action, int mods)
{
    if (key == 'Q' && action == GLFW_PRESS && mods == GLFW_MOD_CONTROL)
    {
        glfwSetWindowShouldClose(window, 1);
    }
    else if (key == 'F' && action == GLFW_PRESS)
    {
        g_frames_visible = !g_frames_visible;
    }
    else if (key == 'R' && action == GLFW_PRESS)
    {
        g_seed = std::chrono::high_resolution_clock::now().time_since_epoch().count();
        g_config_changed = true;
    }
    else if (key == '-' && action == GLFW_PRESS)
    {
        g_numLabels = std::max(g_numLabels - 8, 8);
        g_config_changed = true;
    }
    else if ((key == '+' || key == '=') && action == GLFW_PRESS)
    {
        g_numLabels = std::min(g_numLabels + 8, 1024);
        g_config_changed = true;
    }
    else if ('1' <= key && key <= '9' && action == GLFW_PRESS)
    {
        g_algorithmID = std::min(static_cast<size_t>(key - '1'), layoutAlgorithms.size() - 1);
        g_config_changed = true;
    }
}

void glInitialize()
{
    glbinding::Binding::initialize(false);
    glbinding::setCallbackMaskExcept(
        glbinding::CallbackMask::After | glbinding::CallbackMask::Parameters,
        {"glGetError"});
    glbinding::setAfterCallback([](const glbinding::FunctionCall& call) {
        const auto error = glGetError();
        if (error != GL_NO_ERROR)
        {
            std::cout << error << " in " << call.function->name()
                      << " with parameters:" << std::endl;
            for (const auto& parameter : call.parameters)
            {
                std::cout << "    " << parameter->asString() << std::endl;
            }
        }
    });
    globjects::init();
    std::cout << "OpenGL version: " << glGetString(GL_VERSION) << std::endl;
}

std::string random_name(std::default_random_engine engine)
{
    std::uniform_int_distribution<int> charDistribution(32, 126);
    std::uniform_int_distribution<int> lengthDistribution(3, 15);
    const auto length = lengthDistribution(engine);
    std::vector<char> characters;
    for (int i = 0; i < length; ++i)
    {
        characters.push_back(static_cast<char>(charDistribution(engine)));
    }
    return {characters.begin(), characters.end()};
}

std::vector<gloperate_text::Label> prepareLabels(gloperate_text::FontFace * font, glm::uvec2 viewport)
{
    std::vector<gloperate_text::Label> labels;

    std::default_random_engine generator;
    generator.seed(g_seed);
    std::uniform_real_distribution<float> distribution(-1.f, 1.f);
    std::uniform_int_distribution<unsigned int> priorityDistribution(1, 10);

    for (int i = 0; i < g_numLabels; ++i)
    {
        const auto string = random_name(generator);
        const auto priority = priorityDistribution(generator);
        gloperate_text::GlyphSequence sequence;
        std::u32string unicode_string(string.begin(), string.end());
        sequence.setString(unicode_string);
        sequence.setWordWrap(true);
        sequence.setLineWidth(400.f);
        sequence.setAlignment(gloperate_text::Alignment::LeftAligned);
        sequence.setLineAnchor(gloperate_text::LineAnchor::Ascent);
        sequence.setFontSize(10.f + priority);
        sequence.setFontFace(font);
        sequence.setFontColor(glm::vec4(glm::vec3(0.5f - priority * 0.05f), 1.f));
        sequence.setSuperSampling(gloperate_text::SuperSampling::Quincunx);

        const auto origin = glm::vec2{distribution(generator), distribution(generator)};
        // compute  transform matrix
        glm::mat4 transform;
        transform = glm::translate(transform, glm::vec3(origin, 0.f));
        transform = glm::scale(transform, glm::vec3(1.f,
            static_cast<float>(viewport.x) / viewport.y, 1.f));
        transform = glm::scale(transform, glm::vec3(1 / 300.f));

        const auto placement = gloperate_text::LabelPlacement{ glm::vec2{ 0.f, 0.f }
            , gloperate_text::Alignment::LeftAligned, gloperate_text::LineAnchor::Baseline, true };

        sequence.setAdditionalTransform(transform);
        labels.push_back({sequence, origin, priority, placement});
    }
    return labels;
}

gloperate_text::GlyphVertexCloud prepareCloud(const std::vector<gloperate_text::Label> & labels)
{
    std::vector<gloperate_text::GlyphSequence> sequences;
    for (const auto & label : labels)
    {
        if (label.placement.display)
        {
            sequences.push_back(gloperate_text::applyPlacement(label));
        }
    }
    return gloperate_text::prepareGlyphs(sequences, true);
}

void preparePointDrawable(const std::vector<gloperate_text::Label> & labels, PointDrawable& pointDrawable)
{
    std::vector<Point> points;
    for (const auto & label : labels)
    {
        points.push_back({
            label.pointLocation,
            label.placement.display ? glm::vec3(.7f, .0f, .0f) : glm::vec3(.5f, .5f, .5f),
            (label.placement.display ? 4.f : 2.f) * g_viewport.x / 640
        });
    }
    pointDrawable.initialize(points);
}

void prepareRectangleDrawable(const std::vector<gloperate_text::Label> & labels, RectangleDrawable& rectangleDrawable)
{
    std::vector<glm::vec2> rectangles;
    for (const auto & label : labels)
    {
        auto sequence = gloperate_text::applyPlacement(label);
        auto extent = gloperate_text::Typesetter::rectangle(sequence, glm::vec3(label.pointLocation, 0.f));
        rectangles.push_back(extent.first);
        rectangles.push_back(extent.first + extent.second);
    }
    rectangleDrawable.initialize(rectangles);
}

void runAndBenchmark(std::vector<gloperate_text::Label> & labels, Algorithm algorithm)
{
    auto start = std::chrono::steady_clock::now();
    algorithm.function(labels);
    auto end = std::chrono::steady_clock::now();
    std::chrono::duration<double> diff = end - start;
    auto positions = labelPositionDesirability(labels);
    std::cout << "Evaluation results for " << algorithm.name << ":" << std::endl
        << "Runtime:                 " << diff.count() << "s" << std::endl
        << "Labels hidden:           " << labelsHidden(labels) << "/" << labels.size() << std::endl
        << "Overlaps:                " << labelOverlaps(labels) << std::endl
        << "Overlap area:            " << labelOverlapArea(labels) << std::endl
        << "Padding Violations:      " << labelOverlaps(labels, {0.2f, 0.2f}) << std::endl
        << "Padding Overlap:         " << labelOverlapArea(labels, {0.2f, 0.2f}) << std::endl
        << "Relative label positions:" << std::endl
        << "  Upper Right:           " << positions[gloperate_text::RelativeLabelPosition::UpperRight] << std::endl
        << "  Upper Left:            " << positions[gloperate_text::RelativeLabelPosition::UpperLeft]  << std::endl
        << "  Lower Left:            " << positions[gloperate_text::RelativeLabelPosition::LowerLeft]  << std::endl
        << "  Lower Right:           " << positions[gloperate_text::RelativeLabelPosition::LowerRight] << std::endl;
    std::cout << std::endl;
}

int main()
{
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);

    auto window = glfwCreateWindow(g_viewport.x, g_viewport.y, "Text-Demo", 0, nullptr);

    if (!window)
    {
        glfwTerminate();
        return -1;
    }

    glfwMakeContextCurrent(window);

    glInitialize();

    glfwSetWindowSizeCallback(window, onResize);
    glfwSetKeyCallback(window, onKeyPress);

    cpplocate::ModuleInfo moduleInfo = cpplocate::findModule("openll");
    std::string dataPath = moduleInfo.value("dataPath");

    gloperate_text::FontLoader loader;
    auto font = loader.load(dataPath + "/fonts/opensansr36/opensansr36.fnt");
    gloperate_text::GlyphRenderer renderer;
    gloperate_text::GlyphVertexCloud cloud;
    std::vector<gloperate_text::Label> labels;
    PointDrawable pointDrawable {dataPath};
    RectangleDrawable rectangleDrawable {dataPath};
    glClearColor(1.f, 1.f, 1.f, 1.f);

    while (!glfwWindowShouldClose(window))
    {
        if (g_config_changed)
        {
            glViewport(0, 0, g_viewport.x, g_viewport.y);
            labels = prepareLabels(font, g_viewport);
            runAndBenchmark(labels, layoutAlgorithms[g_algorithmID]);
            cloud = prepareCloud(labels);
            preparePointDrawable(labels, pointDrawable);
            prepareRectangleDrawable(labels, rectangleDrawable);
        }

        gl::glClear(gl::GL_COLOR_BUFFER_BIT | gl::GL_DEPTH_BUFFER_BIT);

        gl::glDepthMask(gl::GL_FALSE);
        gl::glEnable(gl::GL_CULL_FACE);
        gl::glEnable(gl::GL_BLEND);
        gl::glBlendFunc(gl::GL_SRC_ALPHA, gl::GL_ONE_MINUS_SRC_ALPHA);

        renderer.render(cloud);
        pointDrawable.render();
        if (g_frames_visible)
            rectangleDrawable.render();

        gl::glDepthMask(gl::GL_TRUE);
        gl::glDisable(gl::GL_CULL_FACE);
        gl::glDisable(gl::GL_BLEND);

        g_config_changed = false;

        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    glfwTerminate();
}
<commit_msg>Improve layout example<commit_after>#include <cassert>
#include <iostream>
#include <map>
#include <random>

#include <GLFW/glfw3.h>

#include <glbinding/Binding.h>
#include <glbinding/callbacks.h>
#include <glbinding/gl/gl.h>
#include <globjects/globjects.h>

#include <glm/gtc/matrix_transform.hpp>

#include <openll/GlyphRenderer.h>
#include <openll/FontLoader.h>
#include <openll/Typesetter.h>
#include <openll/stages/GlyphPreparationStage.h>

#include <openll/FontFace.h>
#include <openll/GlyphSequence.h>
#include <openll/Alignment.h>
#include <openll/LineAnchor.h>
#include <openll/SuperSampling.h>
#include <openll/layout/layoutbase.h>
#include <openll/layout/algorithm.h>

#include <cpplocate/cpplocate.h>
#include <cpplocate/ModuleInfo.h>

#include "PointDrawable.h"
#include "RectangleDrawable.h"
#include "benchmark.h"

using namespace gl;

glm::uvec2 g_viewport{640, 480};
bool g_config_changed = true;
size_t g_algorithmID = 0;
bool g_frames_visible = true;
long int g_seed = 0;
int g_numLabels = 64;

struct Algorithm
{
    std::string name;
    std::function<void(std::vector<gloperate_text::Label>&)> function;
};

using namespace std::placeholders;

std::vector<Algorithm> layoutAlgorithms
{
    {"Constant",                                 gloperate_text::layout::constant},
    {"Random",                                   gloperate_text::layout::random},
    {"Greedy",                                   std::bind(gloperate_text::layout::greedy, _1, gloperate_text::layout::standard)},
    {"Discrete Gradient Descent with area",      std::bind(gloperate_text::layout::discreteGradientDescent, _1, gloperate_text::layout::standard)},
    {"Simulated Annealing",                      std::bind(gloperate_text::layout::simulatedAnnealing, _1, gloperate_text::layout::standard, false, glm::vec2(0.f))},
    {"Simulated Annealing with padding",         std::bind(gloperate_text::layout::simulatedAnnealing, _1, gloperate_text::layout::standard, false, glm::vec2(0.2f))},
    {"Simulated Annealing with selection",       std::bind(gloperate_text::layout::simulatedAnnealing, _1, gloperate_text::layout::standard, true, glm::vec2(0.f))},
    {"Simulated Annealing (padding, selection)", std::bind(gloperate_text::layout::simulatedAnnealing, _1, gloperate_text::layout::standard, true, glm::vec2(0.2f))},
};

void onResize(GLFWwindow*, int width, int height)
{
    g_viewport = {width, height};
    g_config_changed = true;
}

void onKeyPress(GLFWwindow * window, int key, int, int action, int mods)
{
    if (key == 'Q' && action == GLFW_PRESS && mods == GLFW_MOD_CONTROL)
    {
        glfwSetWindowShouldClose(window, 1);
    }
    else if (key == 'F' && action == GLFW_PRESS)
    {
        g_frames_visible = !g_frames_visible;
    }
    else if (key == 'R' && action == GLFW_PRESS)
    {
        g_seed = std::chrono::high_resolution_clock::now().time_since_epoch().count();
        g_config_changed = true;
    }
    else if (key == '-' && action == GLFW_PRESS)
    {
        g_numLabels = std::max(g_numLabels - 8, 8);
        g_config_changed = true;
    }
    else if ((key == '+' || key == '=') && action == GLFW_PRESS)
    {
        g_numLabels = std::min(g_numLabels + 8, 1024);
        g_config_changed = true;
    }
    else if ('1' <= key && key <= '9' && action == GLFW_PRESS)
    {
        g_algorithmID = std::min(static_cast<size_t>(key - '1'), layoutAlgorithms.size() - 1);
        g_config_changed = true;
    }
}

void glInitialize()
{
    glbinding::Binding::initialize(false);
    glbinding::setCallbackMaskExcept(
        glbinding::CallbackMask::After | glbinding::CallbackMask::Parameters,
        {"glGetError"});
    glbinding::setAfterCallback([](const glbinding::FunctionCall& call) {
        const auto error = glGetError();
        if (error != GL_NO_ERROR)
        {
            std::cout << error << " in " << call.function->name()
                      << " with parameters:" << std::endl;
            for (const auto& parameter : call.parameters)
            {
                std::cout << "    " << parameter->asString() << std::endl;
            }
        }
    });
    globjects::init();
    std::cout << "OpenGL version: " << glGetString(GL_VERSION) << std::endl;
}

std::string random_name(std::default_random_engine engine)
{
    std::uniform_int_distribution<int> upperDistribution(65, 90);
    std::uniform_int_distribution<int> lowerDistribution(97, 122);
    std::uniform_int_distribution<int> lengthDistribution(3, 14);
    const auto length = lengthDistribution(engine);
    std::vector<char> characters;
    characters.push_back(static_cast<char>(upperDistribution(engine)));
    for (int i = 0; i < length; ++i)
    {
        characters.push_back(static_cast<char>(lowerDistribution(engine)));
    }
    return {characters.begin(), characters.end()};
}

std::vector<gloperate_text::Label> prepareLabels(gloperate_text::FontFace * font, glm::uvec2 viewport)
{
    std::vector<gloperate_text::Label> labels;

    std::default_random_engine generator;
    generator.seed(g_seed);
    std::uniform_real_distribution<float> y_distribution(-.8f, .6f);
    std::uniform_real_distribution<float> x_distribution(-.8f, .8f);
    std::uniform_int_distribution<unsigned int> priorityDistribution(1, 10);

    for (int i = 0; i < g_numLabels; ++i)
    {
        const auto string = random_name(generator);
        const std::u32string unicode_string {string.begin(), string.end()};
        const auto priority = priorityDistribution(generator);
        const auto origin = glm::vec2{x_distribution(generator), y_distribution(generator)};

        gloperate_text::GlyphSequence sequence;
        sequence.setString(unicode_string);
        sequence.setWordWrap(true);
        sequence.setLineWidth(400.f);
        sequence.setAlignment(gloperate_text::Alignment::LeftAligned);
        sequence.setLineAnchor(gloperate_text::LineAnchor::Ascent);
        sequence.setFontSize(10.f + priority);
        sequence.setFontFace(font);
        sequence.setFontColor(glm::vec4(glm::vec3(0.5f - priority * 0.05f), 1.f));
        sequence.setSuperSampling(gloperate_text::SuperSampling::Quincunx);

        // compute  transform matrix
        glm::mat4 transform;
        transform = glm::translate(transform, glm::vec3(origin, 0.f));
        transform = glm::scale(transform, glm::vec3(1.f,
            static_cast<float>(viewport.x) / viewport.y, 1.f));
        transform = glm::scale(transform, glm::vec3(1 / 300.f));

        const auto placement = gloperate_text::LabelPlacement{ glm::vec2{ 0.f, 0.f }
            , gloperate_text::Alignment::LeftAligned, gloperate_text::LineAnchor::Baseline, true };

        sequence.setAdditionalTransform(transform);
        labels.push_back({sequence, origin, priority, placement});
    }
    return labels;
}

gloperate_text::GlyphSequence prepareHeadline(gloperate_text::FontFace * font, glm::uvec2 viewport, const std::string & name)
{

    const std::u32string unicode_string {name.begin(), name.end()};
    const auto origin = glm::vec2{-0.9f, 0.75f};

    gloperate_text::GlyphSequence sequence;
    sequence.setString(unicode_string);
    sequence.setWordWrap(false);
    sequence.setLineWidth(800.f);
    sequence.setAlignment(gloperate_text::Alignment::LeftAligned);
    sequence.setLineAnchor(gloperate_text::LineAnchor::Descent);
    sequence.setFontSize(30.f);
    sequence.setFontFace(font);
    sequence.setFontColor(glm::vec4(0.4f, 0.4f, 1.0f, 1.f));

    // compute  transform matrix
    glm::mat4 transform;
    transform = glm::translate(transform, glm::vec3(origin, 0.f));
    transform = glm::scale(transform, glm::vec3(1.f,
        static_cast<float>(viewport.x) / viewport.y, 1.f));
    transform = glm::scale(transform, glm::vec3(1 / 300.f));

    sequence.setAdditionalTransform(transform);

    return sequence;
}

std::vector<gloperate_text::GlyphSequence> getSequences(const std::vector<gloperate_text::Label> & labels)
{
    std::vector<gloperate_text::GlyphSequence> sequences;
    for (const auto & label : labels)
    {
        if (label.placement.display)
        {
            sequences.push_back(gloperate_text::applyPlacement(label));
        }
    }
    return sequences;
}

void preparePointDrawable(const std::vector<gloperate_text::Label> & labels, PointDrawable& pointDrawable)
{
    std::vector<Point> points;
    for (const auto & label : labels)
    {
        points.push_back({
            label.pointLocation,
            label.placement.display ? glm::vec3(.7f, .0f, .0f) : glm::vec3(.5f, .5f, .5f),
            (label.placement.display ? 4.f : 2.f) * g_viewport.x / 640
        });
    }
    pointDrawable.initialize(points);
}

void prepareRectangleDrawable(const std::vector<gloperate_text::Label> & labels, RectangleDrawable& rectangleDrawable)
{
    std::vector<glm::vec2> rectangles;
    for (const auto & label : labels)
    {
        auto sequence = gloperate_text::applyPlacement(label);
        auto extent = gloperate_text::Typesetter::rectangle(sequence, glm::vec3(label.pointLocation, 0.f));
        rectangles.push_back(extent.first);
        rectangles.push_back(extent.first + extent.second);
    }
    rectangleDrawable.initialize(rectangles);
}

void runAndBenchmark(std::vector<gloperate_text::Label> & labels, Algorithm algorithm)
{
    auto start = std::chrono::steady_clock::now();
    algorithm.function(labels);
    auto end = std::chrono::steady_clock::now();
    std::chrono::duration<double> diff = end - start;
    auto positions = labelPositionDesirability(labels);
    std::cout << "Evaluation results for " << algorithm.name << ":" << std::endl
        << "Runtime:                 " << diff.count() << "s" << std::endl
        << "Labels hidden:           " << labelsHidden(labels) << "/" << labels.size() << std::endl
        << "Overlaps:                " << labelOverlaps(labels) << std::endl
        << "Overlap area:            " << labelOverlapArea(labels) << std::endl
        << "Padding Violations:      " << labelOverlaps(labels, {0.2f, 0.2f}) << std::endl
        << "Padding Overlap:         " << labelOverlapArea(labels, {0.2f, 0.2f}) << std::endl
        << "Relative label positions:" << std::endl
        << "  Upper Right:           " << positions[gloperate_text::RelativeLabelPosition::UpperRight] << std::endl
        << "  Upper Left:            " << positions[gloperate_text::RelativeLabelPosition::UpperLeft]  << std::endl
        << "  Lower Left:            " << positions[gloperate_text::RelativeLabelPosition::LowerLeft]  << std::endl
        << "  Lower Right:           " << positions[gloperate_text::RelativeLabelPosition::LowerRight] << std::endl;
    std::cout << std::endl;
}

int main()
{
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);

    auto window = glfwCreateWindow(g_viewport.x, g_viewport.y, "Text-Demo", 0, nullptr);

    if (!window)
    {
        glfwTerminate();
        return -1;
    }

    glfwMakeContextCurrent(window);

    glInitialize();

    glfwSetWindowSizeCallback(window, onResize);
    glfwSetKeyCallback(window, onKeyPress);

    cpplocate::ModuleInfo moduleInfo = cpplocate::findModule("openll");
    std::string dataPath = moduleInfo.value("dataPath");

    gloperate_text::FontLoader loader;
    const auto font = loader.load(dataPath + "/fonts/opensansr36/opensansr36.fnt");
    gloperate_text::GlyphRenderer renderer;
    gloperate_text::GlyphVertexCloud cloud;
    std::vector<gloperate_text::Label> labels;
    PointDrawable pointDrawable {dataPath};
    RectangleDrawable rectangleDrawable {dataPath};
    glClearColor(1.f, 1.f, 1.f, 1.f);

    while (!glfwWindowShouldClose(window))
    {
        if (g_config_changed)
        {
            glViewport(0, 0, g_viewport.x, g_viewport.y);
            labels = prepareLabels(font, g_viewport);
            runAndBenchmark(labels, layoutAlgorithms[g_algorithmID]);
            auto sequences = getSequences(labels);
            sequences.push_back(prepareHeadline(font, g_viewport, layoutAlgorithms[g_algorithmID].name));
            cloud = gloperate_text::prepareGlyphs(sequences, true);
            preparePointDrawable(labels, pointDrawable);
            prepareRectangleDrawable(labels, rectangleDrawable);
        }

        gl::glClear(gl::GL_COLOR_BUFFER_BIT | gl::GL_DEPTH_BUFFER_BIT);

        gl::glDepthMask(gl::GL_FALSE);
        gl::glEnable(gl::GL_CULL_FACE);
        gl::glEnable(gl::GL_BLEND);
        gl::glBlendFunc(gl::GL_SRC_ALPHA, gl::GL_ONE_MINUS_SRC_ALPHA);

        renderer.render(cloud);
        pointDrawable.render();
        if (g_frames_visible)
            rectangleDrawable.render();

        gl::glDepthMask(gl::GL_TRUE);
        gl::glDisable(gl::GL_CULL_FACE);
        gl::glDisable(gl::GL_BLEND);

        g_config_changed = false;

        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    glfwTerminate();
}
<|endoftext|>


<commit_before>#include "fileoperations/coperationperformer.h"
#include "cfolderenumeratorrecursive.h"
#include "ctestfoldergenerator.h"
#include "container/set_operations.hpp"
#include "system/processfilepath.hpp"
#include "system/ctimeelapsed.h"
#include "filecomparator/cfilecomparator.h"

// test_utils
#include "foldercomparator.h"
#include "qt_helpers.hpp"
#include "catch2_utils.hpp"

DISABLE_COMPILER_WARNINGS
#include <QDateTime>
#include <QDir>
#include <QStringBuilder>
#include <QTemporaryDir>
RESTORE_COMPILER_WARNINGS

#include <algorithm>
#include <iostream>
#include <string>

#define CATCH_CONFIG_RUNNER
#include "../catch2/catch.hpp"

static uint32_t g_randomSeed = 0;

static constexpr QFileDevice::FileTime supportedFileTimeTypes[] {
	QFileDevice::FileAccessTime,
#ifndef __linux__
	QFileDevice::FileBirthTime,
#endif
#if !defined __linux__ && !defined _WIN32
	QFileDevice::FileMetadataChangeTime,
#endif
	QFileDevice::FileModificationTime,
};

static bool timesAlmostMatch(const QDateTime& t1, const QDateTime& t2, const QFileDevice::FileTime type)
{
	const auto diff = ::abs(t1.toMSecsSinceEpoch() - t2.toMSecsSinceEpoch());

	int allowedTimeDiffMs = 0;
	switch (type)
	{
	// These margins are generally too long, but necessary for CI systems (sloooow cloud virtualized hardware)
	case QFileDevice::FileAccessTime:
		allowedTimeDiffMs = 3000;
		break;
	case QFileDevice::FileBirthTime:
		allowedTimeDiffMs = 100;
		break;
	case QFileDevice::FileMetadataChangeTime:
		allowedTimeDiffMs = 2500;
		break;
	case QFileDevice::FileModificationTime:
		allowedTimeDiffMs = 100;
		break;
	default:
		assert_and_return_unconditional_r("Unknown QFileDevice::FileTime", false);
	}

#ifdef __APPLE__
	static constexpr int multiplier = 10;
#else
	static constexpr int multiplier = 1;
#endif

	if (diff <= allowedTimeDiffMs * multiplier)
		return true;
	else
	{
		std::cerr << "Time mismatch for type " << type << ": diff = " << diff << ", allowed = " << allowedTimeDiffMs * multiplier << '\n';
		return false;
	}
}

struct ProgressObserver final : public CFileOperationObserver {
	inline void onProgressChanged(float totalPercentage, size_t /*numFilesProcessed*/, size_t /*totalNumFiles*/, float filePercentage, uint64_t /*speed*/ /* B/s*/, uint32_t /*secondsRemaining*/) override {
		CHECK(totalPercentage <= 100.0f);
		CHECK(filePercentage <= 100.0f);
	}
	inline void onProcessHalted(HaltReason /*reason*/, CFileSystemObject /*source*/, CFileSystemObject /*dest*/, QString /*errorMessage*/) override { // User decision required (file exists, file is read-only etc.)
		FAIL("onProcessHalted called");
	}
	inline void onProcessFinished(QString /*message*/ = QString()) override {} // Done or canceled
	inline void onCurrentFileChanged(QString /*file*/) override {} // Starting to process a new file
};

TEST_CASE((std::string("Copy test #") + std::to_string((srand(time(nullptr)), rand()))).c_str(), "[operationperformer-copy]")
{
	QTemporaryDir sourceDirectory(QDir::tempPath() + "/" + CURRENT_TEST_NAME.c_str() + "_SOURCE_XXXXXX");
	QTemporaryDir targetDirectory(QDir::tempPath() + "/" + CURRENT_TEST_NAME.c_str() + "_TARGET_XXXXXX");
	REQUIRE(sourceDirectory.isValid());
	REQUIRE(targetDirectory.isValid());

	TRACE_LOG << "Source: " << sourceDirectory.path();
	TRACE_LOG << "Target: " << targetDirectory.path();

	REQUIRE(QFileInfo::exists(sourceDirectory.path()));
	REQUIRE(CFileSystemObject(sourceDirectory.path()).isEmptyDir());

	REQUIRE(QFileInfo::exists(targetDirectory.path()));
	REQUIRE(CFileSystemObject(targetDirectory.path()).isEmptyDir());

	CTestFolderGenerator generator;
	generator.setSeed(g_randomSeed);
	TRACE_LOG << "std::random seed: " << g_randomSeed;
	REQUIRE(generator.generateRandomTree(sourceDirectory.path(), 1000, 200));

	COperationPerformer p(operationCopy, CFileSystemObject(sourceDirectory.path()), targetDirectory.path());
	CTimeElapsed timer(true);

	ProgressObserver progressObserver;
	p.setObserver(&progressObserver);
	p.start();
	while (!p.done())
	{
		progressObserver.processEvents();
#ifndef _DEBUG
		// Reasonable timeout
		if (timer.elapsed<std::chrono::seconds>() > 2 * 60)
		{
			FAIL("File operation timeout reached.");
			return;
		}
#endif
	}

	std::vector<CFileSystemObject> sourceTree, destTree;
	CFolderEnumeratorRecursive::enumerateFolder(sourceDirectory.path(), sourceTree);
	CFolderEnumeratorRecursive::enumerateFolder(targetDirectory.path() % '/' % QFileInfo(sourceDirectory.path()).completeBaseName(), destTree);

	CFileComparator comparator;
	REQUIRE(sourceTree.size() == destTree.size());

	for (size_t i = 0, n = sourceTree.size(); i < n; ++i)
	{
		const auto& sourceItem = sourceTree[i];
		const auto& destItem = destTree[i];
		if (!sourceItem.isFile())
			continue;

		QFile fileA(sourceItem.fullAbsolutePath());
		REQUIRE(fileA.open(QFile::ReadOnly));

		QFile fileB(destItem.fullAbsolutePath());
		REQUIRE(fileB.open(QFile::ReadOnly));

		comparator.compareFiles(fileA, fileB, [](int) {}, [](const CFileComparator::ComparisonResult result) {
			CHECK(result == CFileComparator::Equal);
		});

		for (const auto fileTimeType: supportedFileTimeTypes)
		{
			if (!timesAlmostMatch(fileA.fileTime(fileTimeType), fileB.fileTime(fileTimeType), fileTimeType))
				FAIL_CHECK();
		}
	}

	REQUIRE(compareFolderContents(sourceTree, destTree));
}

TEST_CASE((std::string("Move test #") + std::to_string((srand(time(nullptr)), rand()))).c_str(), "[operationperformer-move]")
{
	QTemporaryDir sourceDirectory(QDir::tempPath() + "/" + CURRENT_TEST_NAME.c_str() + "_SOURCE_XXXXXX");
	QTemporaryDir targetDirectory(QDir::tempPath() + "/" + CURRENT_TEST_NAME.c_str() + "_TARGET_XXXXXX");
	REQUIRE(sourceDirectory.isValid());
	REQUIRE(targetDirectory.isValid());

	TRACE_LOG << "Source: " << sourceDirectory.path();
	TRACE_LOG << "Target: " << targetDirectory.path();

	REQUIRE(QFileInfo::exists(sourceDirectory.path()));
	REQUIRE(CFileSystemObject(sourceDirectory.path()).isEmptyDir());

	REQUIRE(QFileInfo::exists(targetDirectory.path()));
	REQUIRE(CFileSystemObject(targetDirectory.path()).isEmptyDir());

	CTestFolderGenerator generator;
	generator.setSeed(g_randomSeed);
	TRACE_LOG << "std::random seed: " << g_randomSeed;
	REQUIRE(generator.generateRandomTree(sourceDirectory.path(), 1000, 200));

	std::vector<CFileSystemObject> sourceTree;
	CFolderEnumeratorRecursive::enumerateFolder(sourceDirectory.path(), sourceTree);

	COperationPerformer p(operationMove, CFileSystemObject(sourceDirectory.path()), targetDirectory.path());
	ProgressObserver progressObserver;
	p.setObserver(&progressObserver);

	CTimeElapsed timer(true);
	p.start();
	while (!p.done())
	{
		progressObserver.processEvents();
#ifndef _DEBUG
		// Reasonable timeout
		if (timer.elapsed<std::chrono::seconds>() > 2 * 60)
		{
			FAIL("File operation timeout reached.");
			return;
		}
#endif
	}

	REQUIRE(!CFileSystemObject(sourceDirectory.path()).exists());

	std::vector<CFileSystemObject> destTree;
	CFolderEnumeratorRecursive::enumerateFolder(targetDirectory.path() % '/' % QFileInfo(sourceDirectory.path()).completeBaseName(), destTree);

	REQUIRE(compareFolderContents(sourceTree, destTree));
}

TEST_CASE((std::string("Delete test #") + std::to_string((srand(time(nullptr)), rand()))).c_str(), "[operationperformer-delete]")
{
	QTemporaryDir sourceDirectory(QDir::tempPath() + "/" + CURRENT_TEST_NAME.c_str() + "_SOURCE_XXXXXX");
	REQUIRE(sourceDirectory.isValid());

	TRACE_LOG << "Source: " << sourceDirectory.path();

	REQUIRE(QFileInfo::exists(sourceDirectory.path()));
	REQUIRE(CFileSystemObject(sourceDirectory.path()).isEmptyDir());

	CTestFolderGenerator generator;
	generator.setSeed(g_randomSeed);
	TRACE_LOG << "std::random seed: " << g_randomSeed;
	REQUIRE(generator.generateRandomTree(sourceDirectory.path(), 1000, 200));
	REQUIRE(!QDir(sourceDirectory.path()).entryList(QDir::NoDotAndDotDot | QDir::AllEntries).empty());

	COperationPerformer p(operationDelete, CFileSystemObject(sourceDirectory.path()));
	ProgressObserver progressObserver;
	p.setObserver(&progressObserver);

	CTimeElapsed timer(true);
	p.start();
	while (!p.done())
	{
		progressObserver.processEvents();
#ifndef _DEBUG
		// Reasonable timeout
		if (timer.elapsed<std::chrono::seconds>() > 2 * 60)
		{
			FAIL("File operation timeout reached.");
			return;
		}
#endif
	}

	REQUIRE(!CFileSystemObject(sourceDirectory.path()).exists());
}

int main(int argc, char* argv[])
{
	Catch::Session session; // There must be exactly one instance

	// Build a new parser on top of Catch's
	using namespace Catch::clara;
	auto cli
		= session.cli() // Get Catch's composite command line parser
		| Opt(g_randomSeed, "std::random seed") // bind variable to a new option, with a hint string
		["--std-seed"]        // the option names it will respond to
		("std::random seed"); // description string for the help output

	// Now pass the new composite back to Catch so it uses that
	session.cli(cli);

	// Let Catch (using Clara) parse the command line
	const int returnCode = session.applyCommandLine(argc, argv);
	if (returnCode != 0) // Indicates a command line error
		return returnCode;

	return session.run();
}
<commit_msg>Failing tests on Mac in Github workflow fixed<commit_after>#include "fileoperations/coperationperformer.h"
#include "cfolderenumeratorrecursive.h"
#include "ctestfoldergenerator.h"
#include "container/set_operations.hpp"
#include "system/processfilepath.hpp"
#include "system/ctimeelapsed.h"
#include "filecomparator/cfilecomparator.h"

// test_utils
#include "foldercomparator.h"
#include "qt_helpers.hpp"
#include "catch2_utils.hpp"

DISABLE_COMPILER_WARNINGS
#include <QDateTime>
#include <QDir>
#include <QStringBuilder>
#include <QTemporaryDir>
RESTORE_COMPILER_WARNINGS

#include <algorithm>
#include <iostream>
#include <string>

#define CATCH_CONFIG_RUNNER
#include "../catch2/catch.hpp"

static uint32_t g_randomSeed = 0;

static constexpr QFileDevice::FileTime supportedFileTimeTypes[] {
	QFileDevice::FileAccessTime,
#ifndef __linux__
	QFileDevice::FileBirthTime,
#endif
#if !defined __linux__ && !defined _WIN32
	QFileDevice::FileMetadataChangeTime,
#endif
	QFileDevice::FileModificationTime,
};

static bool timesAlmostMatch(const QDateTime& t1, const QDateTime& t2, const QFileDevice::FileTime type)
{
	const auto diff = ::abs(t1.toMSecsSinceEpoch() - t2.toMSecsSinceEpoch());

	int allowedTimeDiffMs = 0;
	switch (type)
	{
	// These margins are generally too long, but necessary for CI systems (sloooow cloud virtualized hardware)
	case QFileDevice::FileAccessTime:
		allowedTimeDiffMs = 3000;
		break;
	case QFileDevice::FileBirthTime:
		allowedTimeDiffMs = 100;
		break;
	case QFileDevice::FileMetadataChangeTime:
		allowedTimeDiffMs = 2500;
		break;
	case QFileDevice::FileModificationTime:
		allowedTimeDiffMs = 100;
		break;
	default:
		assert_and_return_unconditional_r("Unknown QFileDevice::FileTime", false);
	}

#ifdef __APPLE__
	static constexpr int multiplier = 20;
#else
	static constexpr int multiplier = 1;
#endif

	if (diff <= allowedTimeDiffMs * multiplier)
		return true;
	else
	{
		std::cerr << "Time mismatch for type " << type << ": diff = " << diff << ", allowed = " << allowedTimeDiffMs * multiplier << '\n';
		return false;
	}
}

struct ProgressObserver final : public CFileOperationObserver {
	inline void onProgressChanged(float totalPercentage, size_t /*numFilesProcessed*/, size_t /*totalNumFiles*/, float filePercentage, uint64_t /*speed*/ /* B/s*/, uint32_t /*secondsRemaining*/) override {
		CHECK(totalPercentage <= 100.0f);
		CHECK(filePercentage <= 100.0f);
	}
	inline void onProcessHalted(HaltReason /*reason*/, CFileSystemObject /*source*/, CFileSystemObject /*dest*/, QString /*errorMessage*/) override { // User decision required (file exists, file is read-only etc.)
		FAIL("onProcessHalted called");
	}
	inline void onProcessFinished(QString /*message*/ = QString()) override {} // Done or canceled
	inline void onCurrentFileChanged(QString /*file*/) override {} // Starting to process a new file
};

TEST_CASE((std::string("Copy test #") + std::to_string((srand(time(nullptr)), rand()))).c_str(), "[operationperformer-copy]")
{
	QTemporaryDir sourceDirectory(QDir::tempPath() + "/" + CURRENT_TEST_NAME.c_str() + "_SOURCE_XXXXXX");
	QTemporaryDir targetDirectory(QDir::tempPath() + "/" + CURRENT_TEST_NAME.c_str() + "_TARGET_XXXXXX");
	REQUIRE(sourceDirectory.isValid());
	REQUIRE(targetDirectory.isValid());

	TRACE_LOG << "Source: " << sourceDirectory.path();
	TRACE_LOG << "Target: " << targetDirectory.path();

	REQUIRE(QFileInfo::exists(sourceDirectory.path()));
	REQUIRE(CFileSystemObject(sourceDirectory.path()).isEmptyDir());

	REQUIRE(QFileInfo::exists(targetDirectory.path()));
	REQUIRE(CFileSystemObject(targetDirectory.path()).isEmptyDir());

	CTestFolderGenerator generator;
	generator.setSeed(g_randomSeed);
	TRACE_LOG << "std::random seed: " << g_randomSeed;
	REQUIRE(generator.generateRandomTree(sourceDirectory.path(), 1000, 200));

	COperationPerformer p(operationCopy, CFileSystemObject(sourceDirectory.path()), targetDirectory.path());
	CTimeElapsed timer(true);

	ProgressObserver progressObserver;
	p.setObserver(&progressObserver);
	p.start();
	while (!p.done())
	{
		progressObserver.processEvents();
#ifndef _DEBUG
		// Reasonable timeout
		if (timer.elapsed<std::chrono::seconds>() > 2 * 60)
		{
			FAIL("File operation timeout reached.");
			return;
		}
#endif
	}

	std::vector<CFileSystemObject> sourceTree, destTree;
	CFolderEnumeratorRecursive::enumerateFolder(sourceDirectory.path(), sourceTree);
	CFolderEnumeratorRecursive::enumerateFolder(targetDirectory.path() % '/' % QFileInfo(sourceDirectory.path()).completeBaseName(), destTree);

	CFileComparator comparator;
	REQUIRE(sourceTree.size() == destTree.size());

	for (size_t i = 0, n = sourceTree.size(); i < n; ++i)
	{
		const auto& sourceItem = sourceTree[i];
		const auto& destItem = destTree[i];
		if (!sourceItem.isFile())
			continue;

		QFile fileA(sourceItem.fullAbsolutePath());
		REQUIRE(fileA.open(QFile::ReadOnly));

		QFile fileB(destItem.fullAbsolutePath());
		REQUIRE(fileB.open(QFile::ReadOnly));

		comparator.compareFiles(fileA, fileB, [](int) {}, [](const CFileComparator::ComparisonResult result) {
			CHECK(result == CFileComparator::Equal);
		});

		for (const auto fileTimeType: supportedFileTimeTypes)
		{
			if (!timesAlmostMatch(fileA.fileTime(fileTimeType), fileB.fileTime(fileTimeType), fileTimeType))
				FAIL_CHECK();
		}
	}

	REQUIRE(compareFolderContents(sourceTree, destTree));
}

TEST_CASE((std::string("Move test #") + std::to_string((srand(time(nullptr)), rand()))).c_str(), "[operationperformer-move]")
{
	QTemporaryDir sourceDirectory(QDir::tempPath() + "/" + CURRENT_TEST_NAME.c_str() + "_SOURCE_XXXXXX");
	QTemporaryDir targetDirectory(QDir::tempPath() + "/" + CURRENT_TEST_NAME.c_str() + "_TARGET_XXXXXX");
	REQUIRE(sourceDirectory.isValid());
	REQUIRE(targetDirectory.isValid());

	TRACE_LOG << "Source: " << sourceDirectory.path();
	TRACE_LOG << "Target: " << targetDirectory.path();

	REQUIRE(QFileInfo::exists(sourceDirectory.path()));
	REQUIRE(CFileSystemObject(sourceDirectory.path()).isEmptyDir());

	REQUIRE(QFileInfo::exists(targetDirectory.path()));
	REQUIRE(CFileSystemObject(targetDirectory.path()).isEmptyDir());

	CTestFolderGenerator generator;
	generator.setSeed(g_randomSeed);
	TRACE_LOG << "std::random seed: " << g_randomSeed;
	REQUIRE(generator.generateRandomTree(sourceDirectory.path(), 1000, 200));

	std::vector<CFileSystemObject> sourceTree;
	CFolderEnumeratorRecursive::enumerateFolder(sourceDirectory.path(), sourceTree);

	COperationPerformer p(operationMove, CFileSystemObject(sourceDirectory.path()), targetDirectory.path());
	ProgressObserver progressObserver;
	p.setObserver(&progressObserver);

	CTimeElapsed timer(true);
	p.start();
	while (!p.done())
	{
		progressObserver.processEvents();
#ifndef _DEBUG
		// Reasonable timeout
		if (timer.elapsed<std::chrono::seconds>() > 2 * 60)
		{
			FAIL("File operation timeout reached.");
			return;
		}
#endif
	}

	REQUIRE(!CFileSystemObject(sourceDirectory.path()).exists());

	std::vector<CFileSystemObject> destTree;
	CFolderEnumeratorRecursive::enumerateFolder(targetDirectory.path() % '/' % QFileInfo(sourceDirectory.path()).completeBaseName(), destTree);

	REQUIRE(compareFolderContents(sourceTree, destTree));
}

TEST_CASE((std::string("Delete test #") + std::to_string((srand(time(nullptr)), rand()))).c_str(), "[operationperformer-delete]")
{
	QTemporaryDir sourceDirectory(QDir::tempPath() + "/" + CURRENT_TEST_NAME.c_str() + "_SOURCE_XXXXXX");
	REQUIRE(sourceDirectory.isValid());

	TRACE_LOG << "Source: " << sourceDirectory.path();

	REQUIRE(QFileInfo::exists(sourceDirectory.path()));
	REQUIRE(CFileSystemObject(sourceDirectory.path()).isEmptyDir());

	CTestFolderGenerator generator;
	generator.setSeed(g_randomSeed);
	TRACE_LOG << "std::random seed: " << g_randomSeed;
	REQUIRE(generator.generateRandomTree(sourceDirectory.path(), 1000, 200));
	REQUIRE(!QDir(sourceDirectory.path()).entryList(QDir::NoDotAndDotDot | QDir::AllEntries).empty());

	COperationPerformer p(operationDelete, CFileSystemObject(sourceDirectory.path()));
	ProgressObserver progressObserver;
	p.setObserver(&progressObserver);

	CTimeElapsed timer(true);
	p.start();
	while (!p.done())
	{
		progressObserver.processEvents();
#ifndef _DEBUG
		// Reasonable timeout
		if (timer.elapsed<std::chrono::seconds>() > 2 * 60)
		{
			FAIL("File operation timeout reached.");
			return;
		}
#endif
	}

	REQUIRE(!CFileSystemObject(sourceDirectory.path()).exists());
}

int main(int argc, char* argv[])
{
	Catch::Session session; // There must be exactly one instance

	// Build a new parser on top of Catch's
	using namespace Catch::clara;
	auto cli
		= session.cli() // Get Catch's composite command line parser
		| Opt(g_randomSeed, "std::random seed") // bind variable to a new option, with a hint string
		["--std-seed"]        // the option names it will respond to
		("std::random seed"); // description string for the help output

	// Now pass the new composite back to Catch so it uses that
	session.cli(cli);

	// Let Catch (using Clara) parse the command line
	const int returnCode = session.applyCommandLine(argc, argv);
	if (returnCode != 0) // Indicates a command line error
		return returnCode;

	return session.run();
}
<|endoftext|>


<commit_before>/******************************************************************************
 * This file is part of the Gluon Development Platform
 * Copyright (c) 2010 Arjen Hiemstra <ahiemstra@heimr.nl>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "spriterenderercomponent.h"

#include <texture.h>
#include <game.h>

#include "graphics/item.h"
#include "graphics/engine.h"
#include "graphics/material.h"
#include "graphics/mesh.h"
#include "graphics/materialinstance.h"
#include "engine/gameobject.h"
#include "engine/asset.h"

#include <QtGui/QMatrix4x4>
#include <QtGui/QColor>
#include <QtCore/QMimeData>
#include <QtCore/QVariant>

REGISTER_OBJECTTYPE( GluonEngine, SpriteRendererComponent )

using namespace GluonEngine;

class SpriteRendererComponent::SpriteRendererComponentPrivate
{
    public:
        SpriteRendererComponentPrivate()
        {
            item = 0;
            texture = 0;
            material = 0;
            size = QSizeF( 1.0f, 1.0f );
            color.setRgb( 255, 255, 255 );
        }

        GluonGraphics::Item* item;
        GluonEngine::Asset* texture;
        GluonGraphics::MaterialInstance* material;

        QColor color;
        QSizeF size;
};

SpriteRendererComponent::SpriteRendererComponent( QObject* parent )
    : Component( parent )
    , d( new SpriteRendererComponentPrivate )
{

}

SpriteRendererComponent::SpriteRendererComponent( const SpriteRendererComponent& other )
    : Component( other )
    , d( other.d )
{
}

SpriteRendererComponent::~SpriteRendererComponent()
{
    delete d;
}

QString SpriteRendererComponent::category() const
{
    return QString( "Graphics Rendering" );
}

void SpriteRendererComponent::initialize()
{
    if( !d->item )
    {
        d->item = GluonGraphics::Engine::instance()->createItem( "default" );
    }

    if( d->material )
    {
        Asset* materialAsset = qobject_cast<Asset*>( d->material->parent() );
        if( materialAsset )
            materialAsset->load();

        Asset* texture = 0;
        if( d->material->property( "texture0" ).type() == QVariant::String )
            texture = gameProject()->findChild<Asset*>( d->material->property( "texture0" ).toString() );
        else
            texture = qobject_cast<Asset*>( GluonCore::GluonObjectFactory::instance()->wrappedObject( d->material->property( "texture0" ) ) );

        if( texture )
            texture->load();
        d->item->setMaterialInstance( d->material );
    }
}

void SpriteRendererComponent::start()
{
}

void SpriteRendererComponent::draw( int timeLapse )
{
    Q_UNUSED( timeLapse )

    if( d->item )
    {
        QMatrix4x4 transform = gameObject()->transform();
        transform.scale( d->size.width() / 2, d->size.height() / 2 );
        d->item->setTransform( transform );
    }
}

void SpriteRendererComponent::cleanup()
{
    if( d->item )
    {
        GluonGraphics::Engine::instance()->destroyItem( d->item );
        d->item = 0;
    }
}

void SpriteRendererComponent::setSize( const QSizeF& size )
{
    d->size = size;
}

QSizeF SpriteRendererComponent::size()
{
    return d->size;
}

GluonGraphics::MaterialInstance*
SpriteRendererComponent::material()
{
    return d->material;
}

void SpriteRendererComponent::setMaterial( GluonGraphics::MaterialInstance* material )
{
    if( !material )
        return;

    d->material = material;

    if( d->item )
        d->item->setMaterialInstance( material );
}

void SpriteRendererComponent::setMaterial( const QString& path )
{
    setMaterial( qobject_cast<GluonGraphics::MaterialInstance*>( Game::instance()->gameProject()->findItemByName( path ) ) );
}

Q_EXPORT_PLUGIN2( gluon_component_spriterenderer, GluonEngine::SpriteRendererComponent );

#include "spriterenderercomponent.moc"
<commit_msg>Allow the materialInstance property to be set to 0.<commit_after>/******************************************************************************
 * This file is part of the Gluon Development Platform
 * Copyright (c) 2010 Arjen Hiemstra <ahiemstra@heimr.nl>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "spriterenderercomponent.h"

#include <texture.h>
#include <game.h>

#include "graphics/item.h"
#include "graphics/engine.h"
#include "graphics/material.h"
#include "graphics/mesh.h"
#include "graphics/materialinstance.h"
#include "engine/gameobject.h"
#include "engine/asset.h"

#include <QtGui/QMatrix4x4>
#include <QtGui/QColor>
#include <QtCore/QMimeData>
#include <QtCore/QVariant>

REGISTER_OBJECTTYPE( GluonEngine, SpriteRendererComponent )

using namespace GluonEngine;

class SpriteRendererComponent::SpriteRendererComponentPrivate
{
    public:
        SpriteRendererComponentPrivate()
        {
            item = 0;
            texture = 0;
            material = 0;
            size = QSizeF( 1.0f, 1.0f );
            color.setRgb( 255, 255, 255 );
        }

        GluonGraphics::Item* item;
        GluonEngine::Asset* texture;
        GluonGraphics::MaterialInstance* material;

        QColor color;
        QSizeF size;
};

SpriteRendererComponent::SpriteRendererComponent( QObject* parent )
    : Component( parent )
    , d( new SpriteRendererComponentPrivate )
{

}

SpriteRendererComponent::SpriteRendererComponent( const SpriteRendererComponent& other )
    : Component( other )
    , d( other.d )
{
}

SpriteRendererComponent::~SpriteRendererComponent()
{
    delete d;
}

QString SpriteRendererComponent::category() const
{
    return QString( "Graphics Rendering" );
}

void SpriteRendererComponent::initialize()
{
    if( !d->item )
    {
        d->item = GluonGraphics::Engine::instance()->createItem( "default" );
    }

    if( d->material )
    {
        Asset* materialAsset = qobject_cast<Asset*>( d->material->parent() );
        if( materialAsset )
            materialAsset->load();

        Asset* texture = 0;
        if( d->material->property( "texture0" ).type() == QVariant::String )
            texture = gameProject()->findChild<Asset*>( d->material->property( "texture0" ).toString() );
        else
            texture = qobject_cast<Asset*>( GluonCore::GluonObjectFactory::instance()->wrappedObject( d->material->property( "texture0" ) ) );

        if( texture )
            texture->load();
        d->item->setMaterialInstance( d->material );
    }
}

void SpriteRendererComponent::start()
{
}

void SpriteRendererComponent::draw( int timeLapse )
{
    Q_UNUSED( timeLapse )

    if( d->item )
    {
        QMatrix4x4 transform = gameObject()->transform();
        transform.scale( d->size.width() / 2, d->size.height() / 2 );
        d->item->setTransform( transform );
    }
}

void SpriteRendererComponent::cleanup()
{
    if( d->item )
    {
        GluonGraphics::Engine::instance()->destroyItem( d->item );
        d->item = 0;
    }
}

void SpriteRendererComponent::setSize( const QSizeF& size )
{
    d->size = size;
}

QSizeF SpriteRendererComponent::size()
{
    return d->size;
}

GluonGraphics::MaterialInstance*
SpriteRendererComponent::material()
{
    return d->material;
}

void SpriteRendererComponent::setMaterial( GluonGraphics::MaterialInstance* material )
{
    d->material = material;

    if( d->item )
    {
        if(material)
        {
            d->item->setMaterialInstance( material );
        }
        else
        {
            d->item->setMaterialInstance(GluonGraphics::Engine::instance()->material("default")->instance("default"));
        }
    }
}

void SpriteRendererComponent::setMaterial( const QString& path )
{
    setMaterial( qobject_cast<GluonGraphics::MaterialInstance*>( Game::instance()->gameProject()->findItemByName( path ) ) );
}

Q_EXPORT_PLUGIN2( gluon_component_spriterenderer, GluonEngine::SpriteRendererComponent );

#include "spriterenderercomponent.moc"
<|endoftext|>


<commit_before>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/component_updater/swiftshader_component_installer.h"

#include "base/base_paths.h"
#include "base/bind.h"
#include "base/compiler_specific.h"
#include "base/cpu.h"
#include "base/file_util.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "base/strings/string_util.h"
#include "base/values.h"
#include "chrome/browser/component_updater/component_updater_service.h"
#include "chrome/common/chrome_paths.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/gpu_data_manager.h"
#include "content/public/browser/gpu_data_manager_observer.h"
#include "gpu/config/gpu_feature_type.h"

using content::BrowserThread;
using content::GpuDataManager;

namespace component_updater {

namespace {

// CRX hash. The extension id is: nhfgdggnnopgbfdlpeoalgcjdgfafocg.
const uint8 kSha2Hash[] = {0xd7, 0x56, 0x36, 0x6d, 0xde, 0xf6, 0x15, 0x3b,
                           0xf4, 0xe0, 0xb6, 0x29, 0x36, 0x50, 0x5e, 0x26,
                           0xbd, 0x77, 0x8b, 0x8e, 0x35, 0xc2, 0x7e, 0x43,
                           0x52, 0x47, 0x62, 0xed, 0x12, 0xca, 0xcc, 0x6a};

// File name of the internal SwiftShader plugin on different platforms.
const base::FilePath::CharType kSwiftShaderEglName[] =
    FILE_PATH_LITERAL("libegl.dll");
const base::FilePath::CharType kSwiftShaderGlesName[] =
    FILE_PATH_LITERAL("libglesv2.dll");

const char kSwiftShaderManifestName[] = "SwiftShader";

const base::FilePath::CharType kSwiftShaderBaseDirectory[] =
    FILE_PATH_LITERAL("SwiftShader");

// If we don't have a SwiftShader component, this is the version we claim.
const char kNullVersion[] = "0.0.0.0";

// The base directory on windows looks like:
// <profile>\AppData\Local\Google\Chrome\User Data\SwiftShader\.
base::FilePath GetSwiftShaderBaseDirectory() {
  base::FilePath result;
  PathService::Get(chrome::DIR_USER_DATA, &result);
  return result.Append(kSwiftShaderBaseDirectory);
}

// SwiftShader has version encoded in the path itself
// so we need to enumerate the directories to find the full path.
// On success it returns something like:
// <profile>\AppData\Local\Google\Chrome\User Data\SwiftShader\10.3.44.555\.
bool GetLatestSwiftShaderDirectory(base::FilePath* result,
                                   Version* latest,
                                   std::vector<base::FilePath>* older_dirs) {
  base::FilePath base_dir = GetSwiftShaderBaseDirectory();
  bool found = false;
  base::FileEnumerator
      file_enumerator(base_dir, false, base::FileEnumerator::DIRECTORIES);
  for (base::FilePath path = file_enumerator.Next(); !path.value().empty();
       path = file_enumerator.Next()) {
    Version version(path.BaseName().MaybeAsASCII());
    if (!version.IsValid())
      continue;
    if (version.CompareTo(*latest) > 0 &&
        base::PathExists(path.Append(kSwiftShaderEglName)) &&
        base::PathExists(path.Append(kSwiftShaderGlesName))) {
      if (found && older_dirs)
          older_dirs->push_back(*result);
      *latest = version;
      *result = path;
      found = true;
    } else {
      if (older_dirs)
        older_dirs->push_back(path);
    }
  }
  return found;
}

void RegisterSwiftShaderWithChrome(const base::FilePath& path) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  GpuDataManager::GetInstance()->RegisterSwiftShaderPath(path);
}

}  // namespace

class SwiftShaderComponentInstaller : public ComponentInstaller {
 public:
  explicit SwiftShaderComponentInstaller(const Version& version);

  virtual ~SwiftShaderComponentInstaller() {}

  virtual void OnUpdateError(int error) OVERRIDE;

  virtual bool Install(const base::DictionaryValue& manifest,
                       const base::FilePath& unpack_path) OVERRIDE;

  virtual bool GetInstalledFile(const std::string& file,
                                base::FilePath* installed_file) OVERRIDE;

 private:
  Version current_version_;
};

SwiftShaderComponentInstaller::SwiftShaderComponentInstaller(
    const Version& version) : current_version_(version) {
  DCHECK(version.IsValid());
}

void SwiftShaderComponentInstaller::OnUpdateError(int error) {
  NOTREACHED() << "SwiftShader update error: " << error;
}

bool SwiftShaderComponentInstaller::Install(
    const base::DictionaryValue& manifest,
    const base::FilePath& unpack_path) {
  std::string name;
  manifest.GetStringASCII("name", &name);
  if (name != kSwiftShaderManifestName)
    return false;
  std::string proposed_version;
  manifest.GetStringASCII("version", &proposed_version);
  Version version(proposed_version.c_str());
  if (!version.IsValid())
    return false;
  if (current_version_.CompareTo(version) >= 0)
    return false;
  if (!base::PathExists(unpack_path.Append(kSwiftShaderEglName)) ||
      !base::PathExists(unpack_path.Append(kSwiftShaderGlesName)))
    return false;
  // Passed the basic tests. Time to install it.
  base::FilePath path =
      GetSwiftShaderBaseDirectory().AppendASCII(version.GetString());
  if (base::PathExists(path))
    return false;
  if (!base::Move(unpack_path, path))
    return false;
  // Installation is done. Now tell the rest of chrome.
  current_version_ = version;
  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
      base::Bind(&RegisterSwiftShaderWithChrome, path));
  return true;
}

bool SwiftShaderComponentInstaller::GetInstalledFile(
    const std::string& file, base::FilePath* installed_file) {
  return false;
}

void FinishSwiftShaderUpdateRegistration(ComponentUpdateService* cus,
                                         const Version& version) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  CrxComponent swiftshader;
  swiftshader.name = "Swift Shader";
  swiftshader.installer = new SwiftShaderComponentInstaller(version);
  swiftshader.version = version;
  swiftshader.pk_hash.assign(kSha2Hash, &kSha2Hash[sizeof(kSha2Hash)]);
  if (cus->RegisterComponent(swiftshader) != ComponentUpdateService::kOk) {
    NOTREACHED() << "SwiftShader component registration fail";
  }
}

class UpdateChecker : public content::GpuDataManagerObserver {
 public:
  explicit UpdateChecker(ComponentUpdateService* cus);

  virtual void OnGpuInfoUpdate() OVERRIDE;

 private:
  ComponentUpdateService* cus_;
};

UpdateChecker::UpdateChecker(ComponentUpdateService* cus)
  : cus_(cus) {
}

void UpdateChecker::OnGpuInfoUpdate() {
  GpuDataManager *gpu_data_manager = GpuDataManager::GetInstance();

  if (!gpu_data_manager->GpuAccessAllowed(NULL) ||
      gpu_data_manager->IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_WEBGL) ||
      gpu_data_manager->ShouldUseSwiftShader()) {
    gpu_data_manager->RemoveObserver(this);
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
    base::FilePath path = GetSwiftShaderBaseDirectory();

    Version version(kNullVersion);
    GetLatestSwiftShaderDirectory(&path, &version, NULL);

    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
        base::Bind(&FinishSwiftShaderUpdateRegistration, cus_, version));
  }
}

// Check if there already is a version of swiftshader installed,
// and if so register it.
void RegisterSwiftShaderPath(ComponentUpdateService* cus) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  base::FilePath path = GetSwiftShaderBaseDirectory();
  if (!base::PathExists(path)) {
    if (!base::CreateDirectory(path)) {
      NOTREACHED() << "Could not create SwiftShader directory.";
      return;
    }
  }

  Version version(kNullVersion);
  std::vector<base::FilePath> older_dirs;
  if (GetLatestSwiftShaderDirectory(&path, &version, &older_dirs))
    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
        base::Bind(&RegisterSwiftShaderWithChrome, path));

  UpdateChecker *update_checker = new UpdateChecker(cus);
  GpuDataManager::GetInstance()->AddObserver(update_checker);
  update_checker->OnGpuInfoUpdate();
  // We leak update_checker here, because it has to stick around for the life
  // of the GpuDataManager.

  // Remove older versions of SwiftShader.
  for (std::vector<base::FilePath>::iterator iter = older_dirs.begin();
       iter != older_dirs.end(); ++iter) {
    base::DeleteFile(*iter, true);
  }
}

void RegisterSwiftShaderComponent(ComponentUpdateService* cus) {
#if defined(ENABLE_SWIFTSHADER)
  base::CPU cpu;

  if (!cpu.has_sse2())
    return;
  BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
      base::Bind(&RegisterSwiftShaderPath, cus));
#endif
}

}  // namespace component_updater

<commit_msg>Fixes an ODR for the UpdateChecker class in the component updater.<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/component_updater/swiftshader_component_installer.h"

#include "base/base_paths.h"
#include "base/bind.h"
#include "base/compiler_specific.h"
#include "base/cpu.h"
#include "base/file_util.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "base/strings/string_util.h"
#include "base/values.h"
#include "chrome/browser/component_updater/component_updater_service.h"
#include "chrome/common/chrome_paths.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/gpu_data_manager.h"
#include "content/public/browser/gpu_data_manager_observer.h"
#include "gpu/config/gpu_feature_type.h"

using content::BrowserThread;
using content::GpuDataManager;

namespace component_updater {

namespace {

// CRX hash. The extension id is: nhfgdggnnopgbfdlpeoalgcjdgfafocg.
const uint8 kSha2Hash[] = {0xd7, 0x56, 0x36, 0x6d, 0xde, 0xf6, 0x15, 0x3b,
                           0xf4, 0xe0, 0xb6, 0x29, 0x36, 0x50, 0x5e, 0x26,
                           0xbd, 0x77, 0x8b, 0x8e, 0x35, 0xc2, 0x7e, 0x43,
                           0x52, 0x47, 0x62, 0xed, 0x12, 0xca, 0xcc, 0x6a};

// File name of the internal SwiftShader plugin on different platforms.
const base::FilePath::CharType kSwiftShaderEglName[] =
    FILE_PATH_LITERAL("libegl.dll");
const base::FilePath::CharType kSwiftShaderGlesName[] =
    FILE_PATH_LITERAL("libglesv2.dll");

const char kSwiftShaderManifestName[] = "SwiftShader";

const base::FilePath::CharType kSwiftShaderBaseDirectory[] =
    FILE_PATH_LITERAL("SwiftShader");

// If we don't have a SwiftShader component, this is the version we claim.
const char kNullVersion[] = "0.0.0.0";

// The base directory on windows looks like:
// <profile>\AppData\Local\Google\Chrome\User Data\SwiftShader\.
base::FilePath GetSwiftShaderBaseDirectory() {
  base::FilePath result;
  PathService::Get(chrome::DIR_USER_DATA, &result);
  return result.Append(kSwiftShaderBaseDirectory);
}

// SwiftShader has version encoded in the path itself
// so we need to enumerate the directories to find the full path.
// On success it returns something like:
// <profile>\AppData\Local\Google\Chrome\User Data\SwiftShader\10.3.44.555\.
bool GetLatestSwiftShaderDirectory(base::FilePath* result,
                                   Version* latest,
                                   std::vector<base::FilePath>* older_dirs) {
  base::FilePath base_dir = GetSwiftShaderBaseDirectory();
  bool found = false;
  base::FileEnumerator
      file_enumerator(base_dir, false, base::FileEnumerator::DIRECTORIES);
  for (base::FilePath path = file_enumerator.Next(); !path.value().empty();
       path = file_enumerator.Next()) {
    Version version(path.BaseName().MaybeAsASCII());
    if (!version.IsValid())
      continue;
    if (version.CompareTo(*latest) > 0 &&
        base::PathExists(path.Append(kSwiftShaderEglName)) &&
        base::PathExists(path.Append(kSwiftShaderGlesName))) {
      if (found && older_dirs)
          older_dirs->push_back(*result);
      *latest = version;
      *result = path;
      found = true;
    } else {
      if (older_dirs)
        older_dirs->push_back(path);
    }
  }
  return found;
}

void RegisterSwiftShaderWithChrome(const base::FilePath& path) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  GpuDataManager::GetInstance()->RegisterSwiftShaderPath(path);
}

class SwiftShaderComponentInstaller : public ComponentInstaller {
 public:
  explicit SwiftShaderComponentInstaller(const Version& version);

  virtual ~SwiftShaderComponentInstaller() {}

  virtual void OnUpdateError(int error) OVERRIDE;

  virtual bool Install(const base::DictionaryValue& manifest,
                       const base::FilePath& unpack_path) OVERRIDE;

  virtual bool GetInstalledFile(const std::string& file,
                                base::FilePath* installed_file) OVERRIDE;

 private:
  Version current_version_;
};

SwiftShaderComponentInstaller::SwiftShaderComponentInstaller(
    const Version& version) : current_version_(version) {
  DCHECK(version.IsValid());
}

void SwiftShaderComponentInstaller::OnUpdateError(int error) {
  NOTREACHED() << "SwiftShader update error: " << error;
}

bool SwiftShaderComponentInstaller::Install(
    const base::DictionaryValue& manifest,
    const base::FilePath& unpack_path) {
  std::string name;
  manifest.GetStringASCII("name", &name);
  if (name != kSwiftShaderManifestName)
    return false;
  std::string proposed_version;
  manifest.GetStringASCII("version", &proposed_version);
  Version version(proposed_version.c_str());
  if (!version.IsValid())
    return false;
  if (current_version_.CompareTo(version) >= 0)
    return false;
  if (!base::PathExists(unpack_path.Append(kSwiftShaderEglName)) ||
      !base::PathExists(unpack_path.Append(kSwiftShaderGlesName)))
    return false;
  // Passed the basic tests. Time to install it.
  base::FilePath path =
      GetSwiftShaderBaseDirectory().AppendASCII(version.GetString());
  if (base::PathExists(path))
    return false;
  if (!base::Move(unpack_path, path))
    return false;
  // Installation is done. Now tell the rest of chrome.
  current_version_ = version;
  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
      base::Bind(&RegisterSwiftShaderWithChrome, path));
  return true;
}

bool SwiftShaderComponentInstaller::GetInstalledFile(
    const std::string& file, base::FilePath* installed_file) {
  return false;
}

void FinishSwiftShaderUpdateRegistration(ComponentUpdateService* cus,
                                         const Version& version) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  CrxComponent swiftshader;
  swiftshader.name = "Swift Shader";
  swiftshader.installer = new SwiftShaderComponentInstaller(version);
  swiftshader.version = version;
  swiftshader.pk_hash.assign(kSha2Hash, &kSha2Hash[sizeof(kSha2Hash)]);
  if (cus->RegisterComponent(swiftshader) != ComponentUpdateService::kOk) {
    NOTREACHED() << "SwiftShader component registration fail";
  }
}

class UpdateChecker : public content::GpuDataManagerObserver {
 public:
  explicit UpdateChecker(ComponentUpdateService* cus);

  virtual void OnGpuInfoUpdate() OVERRIDE;

 private:
  ComponentUpdateService* cus_;
};

UpdateChecker::UpdateChecker(ComponentUpdateService* cus)
  : cus_(cus) {
}

void UpdateChecker::OnGpuInfoUpdate() {
  GpuDataManager *gpu_data_manager = GpuDataManager::GetInstance();

  if (!gpu_data_manager->GpuAccessAllowed(NULL) ||
      gpu_data_manager->IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_WEBGL) ||
      gpu_data_manager->ShouldUseSwiftShader()) {
    gpu_data_manager->RemoveObserver(this);
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
    base::FilePath path = GetSwiftShaderBaseDirectory();

    Version version(kNullVersion);
    GetLatestSwiftShaderDirectory(&path, &version, NULL);

    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
        base::Bind(&FinishSwiftShaderUpdateRegistration, cus_, version));
  }
}

#if defined(ENABLE_SWIFTSHADER)

// Check if there already is a version of swiftshader installed,
// and if so register it.
void RegisterSwiftShaderPath(ComponentUpdateService* cus) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  base::FilePath path = GetSwiftShaderBaseDirectory();
  if (!base::PathExists(path)) {
    if (!base::CreateDirectory(path)) {
      NOTREACHED() << "Could not create SwiftShader directory.";
      return;
    }
  }

  Version version(kNullVersion);
  std::vector<base::FilePath> older_dirs;
  if (GetLatestSwiftShaderDirectory(&path, &version, &older_dirs))
    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
        base::Bind(&RegisterSwiftShaderWithChrome, path));

  UpdateChecker *update_checker = new UpdateChecker(cus);
  GpuDataManager::GetInstance()->AddObserver(update_checker);
  update_checker->OnGpuInfoUpdate();
  // We leak update_checker here, because it has to stick around for the life
  // of the GpuDataManager.

  // Remove older versions of SwiftShader.
  for (std::vector<base::FilePath>::iterator iter = older_dirs.begin();
       iter != older_dirs.end(); ++iter) {
    base::DeleteFile(*iter, true);
  }
}

#endif  // ENABLE_SWIFTSHADER

}  // namespace

void RegisterSwiftShaderComponent(ComponentUpdateService* cus) {
#if defined(ENABLE_SWIFTSHADER)
  base::CPU cpu;

  if (!cpu.has_sse2())
    return;
  BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
      base::Bind(&RegisterSwiftShaderPath, cus));
#endif
}

}  // namespace component_updater

<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: confeventhelpers.hxx,v $
 *
 *  $Revision: 1.11 $
 *
 *  last change: $Author: ihi $ $Date: 2007-11-23 14:01:04 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/
#ifndef CONFIGMGR_API_EVENTHELPERS_HXX_
#define CONFIGMGR_API_EVENTHELPERS_HXX_

#ifndef CONFIGMGR_API_EVENTS_HXX_
#include "confevents.hxx"
#endif

#ifndef CONFIGMGR_CONFIGPATH_HXX_
#include "configpath.hxx"
#endif

#ifndef _COM_SUN_STAR_UNO_RUNTIMEEXCEPTION_HPP_
#include <com/sun/star/uno/RuntimeException.hpp>
#endif

#ifndef _OSL_DIAGNOSE_H_
#include <osl/diagnose.h>
#endif
#ifndef _OSL_MUTEX_HXX_
#include <osl/mutex.hxx>
#endif

#ifndef INCLUDED_MAP
#include <map>
#define INCLUDED_MAP
#endif
#ifndef INCLUDED_FUNCTIONAL
#include <functional>
#define INCLUDED_FUNCTIONAL
#endif
#include <hash_set>
#ifndef INCLUDED_SET
#include <set>
#define INCLUDED_SET
#endif

namespace configmgr
{
    namespace internal
    {

        using namespace configuration;
    ////////////////////////////////////////////////////////////////////////
        template <class ListenerRef>
        class BroadcastImplHelper
        {
        public:
            osl::Mutex mutex;

        public:
            BroadcastImplHelper()
            {}

            ~BroadcastImplHelper()
            {
                OSL_ENSURE(m_aInterfaces.empty(), "Configuration Broadcaster was not disposed properly");
            }

        public:
            typedef std::set<ListenerRef> Interfaces;
            typedef typename Interfaces::iterator FullIterator;
            typedef typename Interfaces::const_iterator Iterator;

        public:
            FullIterator addListener(ListenerRef aListener)
            {
                return m_aInterfaces.insert(aListener).first;
            }
            void removeListener(ListenerRef aListener)
            {
                m_aInterfaces.erase(aListener);
            }

            void disposing(IConfigBroadcaster* pSource);

        public:
            Iterator begin() const { return m_aInterfaces.begin(); }
            Iterator end() const { return m_aInterfaces.end(); }

            Iterator find(ListenerRef aListener) const   { return m_aInterfaces.find(aListener); }
            FullIterator findFull(ListenerRef aListener) { return m_aInterfaces.find(aListener); }
        private:
            Interfaces m_aInterfaces;

            // no implementation - not copyable
            BroadcastImplHelper(BroadcastImplHelper&);
            void operator=(BroadcastImplHelper&);
        };

        ////////////////////////////////////////////////////////////////////////
        template <class Listener>
        void BroadcastImplHelper<Listener>::disposing(IConfigBroadcaster* pSource)
        {
            osl::ClearableMutexGuard aGuard(this->mutex);   // ensure that no notifications are running

            Interfaces aTargets;
            aTargets.swap(m_aInterfaces);

            aGuard.clear();
            for(FullIterator it = aTargets.begin(); it != aTargets.end(); )
            {
                FullIterator cur = it++;
                if (*cur)
                    (*cur)->disposing(pSource);
            }
        }

    /////////////////////////////////////////////////////////////////////////

        class NodeListenerInfo
        {
        public:
            typedef std::hash_set<AbsolutePath, Path::Hash, Path::Equiv> Pathes;

        public:
            NodeListenerInfo(INodeListenerRef const&    pListener)
                : m_pListener(pListener)
            {
            }

        // path handling
            Pathes const& pathList() const { return m_aPathes; }

            void addPath(AbsolutePath const& sPath) const { m_aPathes.insert(sPath); }
            void removePath(AbsolutePath const& sPath) const { m_aPathes.erase(sPath); }
            //void removeChildPathes(OUString const& sPath);

        // behave as pointer for use as a 'reference' class
            INodeListenerRef get() const { return m_pListener; }
            INodeListenerRef operator->() const { return get(); }
            INodeListener& operator*() const { return *m_pListener; }
        // needed to allow if (info) ...
            struct HasListener;
            operator HasListener const*() const { return reinterpret_cast<HasListener*>(m_pListener.get()); }

            bool operator < (NodeListenerInfo const& aInfo) const
            { return std::less<INodeListener*>()(m_pListener.get(), aInfo.m_pListener.get()); }

            bool operator == (NodeListenerInfo const& aInfo) const
            { return !!( m_pListener == aInfo.m_pListener); }

            bool operator > (NodeListenerInfo const& aInfo) const
            { return aInfo.operator < (*this); }
            bool operator >= (NodeListenerInfo const& aInfo) const
            { return !operator<(aInfo); }
            bool operator <= (NodeListenerInfo const& aInfo) const
            { return !operator>(aInfo); }

            bool operator != (NodeListenerInfo const& aInfo) const
            { return !operator==(aInfo); }

        private:
            INodeListenerRef m_pListener;
            mutable Pathes m_aPathes; // hack to be mutable even as set element
        };
        class ConfigChangesBroadcasterImpl
        {
        public:
            ConfigChangesBroadcasterImpl();
            ~ConfigChangesBroadcasterImpl();

            void add(AbsolutePath const& aPath, INodeListenerRef const& pListener);
            void remove(INodeListenerRef const& pListener);

//          void removed(OUString const& aPath, bool bRemovedFromModel, IConfigBroadcaster* pSource);

            void dispatch(Change const& rBaseChange, AbsolutePath const& sChangeLocation, sal_Bool _bError, IConfigBroadcaster* pSource);
            void dispatch(TreeChangeList const& rList_, sal_Bool _bError, IConfigBroadcaster* pSource);
            void disposing(IConfigBroadcaster* pSource);
        private:
            typedef BroadcastImplHelper<NodeListenerInfo> Listeners;
            typedef Listeners::FullIterator InfoRef;
            typedef std::multimap<AbsolutePath, InfoRef, Path::Before> PathMap;
            Listeners m_aListeners;
            PathMap m_aPathMap;
        private:
            void dispatchInner(INodeListenerRef const& pTarget, AbsolutePath const& sTargetPath, Change const& rBaseChange, AbsolutePath const& sChangeLocation, sal_Bool _bError, IConfigBroadcaster* pSource);
            void dispatchOuter(INodeListenerRef const& pTarget, AbsolutePath const& sTargetPath, Change const& rBaseChange, AbsolutePath const& sChangeLocation, sal_Bool _bError, IConfigBroadcaster* pSource);
        };

    /////////////////////////////////////////////////////////////////////////
    } // namespace
} // namespace

#endif // CONFIGMGR_API_EVENTHELPERS_HXX_

<commit_msg>INTEGRATION: CWS changefileheader (1.11.16); FILE MERGED 2008/04/01 15:06:29 thb 1.11.16.3: #i85898# Stripping all external header guards 2008/04/01 12:27:08 thb 1.11.16.2: #i85898# Stripping all external header guards 2008/03/31 12:22:34 rt 1.11.16.1: #i87441# Change license header to LPGL v3.<commit_after>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: confeventhelpers.hxx,v $
 * $Revision: 1.12 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/
#ifndef CONFIGMGR_API_EVENTHELPERS_HXX_
#define CONFIGMGR_API_EVENTHELPERS_HXX_

#include "confevents.hxx"
#include "configpath.hxx"
#include <com/sun/star/uno/RuntimeException.hpp>
#include <osl/diagnose.h>
#include <osl/mutex.hxx>

#ifndef INCLUDED_MAP
#include <map>
#define INCLUDED_MAP
#endif
#ifndef INCLUDED_FUNCTIONAL
#include <functional>
#define INCLUDED_FUNCTIONAL
#endif
#include <hash_set>
#ifndef INCLUDED_SET
#include <set>
#define INCLUDED_SET
#endif

namespace configmgr
{
    namespace internal
    {

        using namespace configuration;
    ////////////////////////////////////////////////////////////////////////
        template <class ListenerRef>
        class BroadcastImplHelper
        {
        public:
            osl::Mutex mutex;

        public:
            BroadcastImplHelper()
            {}

            ~BroadcastImplHelper()
            {
                OSL_ENSURE(m_aInterfaces.empty(), "Configuration Broadcaster was not disposed properly");
            }

        public:
            typedef std::set<ListenerRef> Interfaces;
            typedef typename Interfaces::iterator FullIterator;
            typedef typename Interfaces::const_iterator Iterator;

        public:
            FullIterator addListener(ListenerRef aListener)
            {
                return m_aInterfaces.insert(aListener).first;
            }
            void removeListener(ListenerRef aListener)
            {
                m_aInterfaces.erase(aListener);
            }

            void disposing(IConfigBroadcaster* pSource);

        public:
            Iterator begin() const { return m_aInterfaces.begin(); }
            Iterator end() const { return m_aInterfaces.end(); }

            Iterator find(ListenerRef aListener) const   { return m_aInterfaces.find(aListener); }
            FullIterator findFull(ListenerRef aListener) { return m_aInterfaces.find(aListener); }
        private:
            Interfaces m_aInterfaces;

            // no implementation - not copyable
            BroadcastImplHelper(BroadcastImplHelper&);
            void operator=(BroadcastImplHelper&);
        };

        ////////////////////////////////////////////////////////////////////////
        template <class Listener>
        void BroadcastImplHelper<Listener>::disposing(IConfigBroadcaster* pSource)
        {
            osl::ClearableMutexGuard aGuard(this->mutex);   // ensure that no notifications are running

            Interfaces aTargets;
            aTargets.swap(m_aInterfaces);

            aGuard.clear();
            for(FullIterator it = aTargets.begin(); it != aTargets.end(); )
            {
                FullIterator cur = it++;
                if (*cur)
                    (*cur)->disposing(pSource);
            }
        }

    /////////////////////////////////////////////////////////////////////////

        class NodeListenerInfo
        {
        public:
            typedef std::hash_set<AbsolutePath, Path::Hash, Path::Equiv> Pathes;

        public:
            NodeListenerInfo(INodeListenerRef const&    pListener)
                : m_pListener(pListener)
            {
            }

        // path handling
            Pathes const& pathList() const { return m_aPathes; }

            void addPath(AbsolutePath const& sPath) const { m_aPathes.insert(sPath); }
            void removePath(AbsolutePath const& sPath) const { m_aPathes.erase(sPath); }
            //void removeChildPathes(OUString const& sPath);

        // behave as pointer for use as a 'reference' class
            INodeListenerRef get() const { return m_pListener; }
            INodeListenerRef operator->() const { return get(); }
            INodeListener& operator*() const { return *m_pListener; }
        // needed to allow if (info) ...
            struct HasListener;
            operator HasListener const*() const { return reinterpret_cast<HasListener*>(m_pListener.get()); }

            bool operator < (NodeListenerInfo const& aInfo) const
            { return std::less<INodeListener*>()(m_pListener.get(), aInfo.m_pListener.get()); }

            bool operator == (NodeListenerInfo const& aInfo) const
            { return !!( m_pListener == aInfo.m_pListener); }

            bool operator > (NodeListenerInfo const& aInfo) const
            { return aInfo.operator < (*this); }
            bool operator >= (NodeListenerInfo const& aInfo) const
            { return !operator<(aInfo); }
            bool operator <= (NodeListenerInfo const& aInfo) const
            { return !operator>(aInfo); }

            bool operator != (NodeListenerInfo const& aInfo) const
            { return !operator==(aInfo); }

        private:
            INodeListenerRef m_pListener;
            mutable Pathes m_aPathes; // hack to be mutable even as set element
        };
        class ConfigChangesBroadcasterImpl
        {
        public:
            ConfigChangesBroadcasterImpl();
            ~ConfigChangesBroadcasterImpl();

            void add(AbsolutePath const& aPath, INodeListenerRef const& pListener);
            void remove(INodeListenerRef const& pListener);

//          void removed(OUString const& aPath, bool bRemovedFromModel, IConfigBroadcaster* pSource);

            void dispatch(Change const& rBaseChange, AbsolutePath const& sChangeLocation, sal_Bool _bError, IConfigBroadcaster* pSource);
            void dispatch(TreeChangeList const& rList_, sal_Bool _bError, IConfigBroadcaster* pSource);
            void disposing(IConfigBroadcaster* pSource);
        private:
            typedef BroadcastImplHelper<NodeListenerInfo> Listeners;
            typedef Listeners::FullIterator InfoRef;
            typedef std::multimap<AbsolutePath, InfoRef, Path::Before> PathMap;
            Listeners m_aListeners;
            PathMap m_aPathMap;
        private:
            void dispatchInner(INodeListenerRef const& pTarget, AbsolutePath const& sTargetPath, Change const& rBaseChange, AbsolutePath const& sChangeLocation, sal_Bool _bError, IConfigBroadcaster* pSource);
            void dispatchOuter(INodeListenerRef const& pTarget, AbsolutePath const& sTargetPath, Change const& rBaseChange, AbsolutePath const& sChangeLocation, sal_Bool _bError, IConfigBroadcaster* pSource);
        };

    /////////////////////////////////////////////////////////////////////////
    } // namespace
} // namespace

#endif // CONFIGMGR_API_EVENTHELPERS_HXX_

<|endoftext|>


<commit_before>//////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2007-2011, Image Engine Design Inc. All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions are
//  met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
//     * Neither the name of Image Engine Design nor the names of any
//       other contributors to this software may be used to endorse or
//       promote products derived from this software without specific prior
//       written permission.
//
//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
//  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
//  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
//  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
//  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
//  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
//  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
//  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
//  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
//  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
//  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////

#include "IECoreMaya/FromMayaCameraConverter.h"
#include "IECoreMaya/Convert.h"

#include "IECore/CompoundParameter.h"
#include "IECore/Camera.h"
#include "IECore/MatrixTransform.h"
#include "IECore/AngleConversion.h"

#include "maya/MFnCamera.h"
#include "maya/MString.h"
#include "maya/MRenderUtil.h"
#include "maya/MCommonRenderSettingsData.h"
#include "maya/MDagPath.h"

#include "OpenEXR/ImathMath.h"

using namespace IECoreMaya;
using namespace IECore;
using namespace Imath;

IE_CORE_DEFINERUNTIMETYPED( FromMayaCameraConverter );

FromMayaDagNodeConverter::Description<FromMayaCameraConverter> FromMayaCameraConverter::m_description( MFn::kCamera, CameraTypeId, true );

FromMayaCameraConverter::FromMayaCameraConverter( const MDagPath &dagPath )
	:	FromMayaDagNodeConverter( "Converts maya camera shape nodes into IECore::Camera objects.", dagPath )
{

	IntParameter::PresetsContainer resolutionModePresets;
	resolutionModePresets.push_back( IntParameter::Preset( "renderGlobals", RenderGlobals ) );
	resolutionModePresets.push_back( IntParameter::Preset( "specified", Specified ) );

	m_resolutionMode = new IntParameter(
		"resolutionMode",
		"Determines how the resolution of the camera is decided.",
		RenderGlobals,
		RenderGlobals,
		Specified,
		resolutionModePresets,
		true
	);

	parameters()->addParameter( m_resolutionMode );

	V2iParameter::PresetsContainer resolutionPresets;
	resolutionPresets.push_back( V2iParameter::Preset( "2K", Imath::V2i( 2048, 1556 ) ) );
	resolutionPresets.push_back( V2iParameter::Preset( "1K", Imath::V2i( 1024, 778 ) ) );

	m_resolution = new V2iParameter(
		"resolution",
		"Specifies the resolution of the camera when mode is set to \"Specified\".",
		Imath::V2i( 2048, 1556 ),
		resolutionPresets
	);

	parameters()->addParameter( m_resolution );

}

IECore::ObjectPtr FromMayaCameraConverter::doConversion( const MDagPath &dagPath, IECore::ConstCompoundObjectPtr operands ) const
{
	MFnCamera fnCamera( dagPath );

	// convert things that are required by the IECore::Renderer specification

	CameraPtr result = new Camera;
	result->setName( IECore::convert<std::string>( fnCamera.name() ) );

	result->setTransform( new MatrixTransform( IECore::convert<Imath::M44f>( dagPath.inclusiveMatrix() ) ) );

	V2i resolution;
	if( operands->member<IntData>( "resolutionMode" )->readable()==RenderGlobals )
	{
		MCommonRenderSettingsData renderSettings;
		MRenderUtil::getCommonRenderSettings( renderSettings );
		resolution = Imath::V2i( renderSettings.width, renderSettings.height );
	}
	else
	{
		resolution = operands->member<V2iData>( "resolution" )->readable();
	}
	result->parameters()["resolution"] = new V2iData( resolution );

	Imath::V2f clippingPlanes = Imath::V2f( fnCamera.nearClippingPlane(), fnCamera.farClippingPlane() );
	result->parameters()["clippingPlanes"] = new V2fData( clippingPlanes );

	Imath::Box2d frustum;
	fnCamera.getRenderingFrustum( (float)resolution.x / (float)resolution.y, frustum.min.x, frustum.max.x, frustum.min.y, frustum.max.y );

	if( fnCamera.isOrtho() )
	{
		// orthographic
		result->parameters()["projection"] = new StringData( "orthographic" );
		result->parameters()["screenWindow"] = new Box2fData( Box2f( frustum.min, frustum.max ) );
	}
	else
	{
		// perspective
		result->parameters()["projection"] = new StringData( "perspective" );

		// derive horizontal field of view from the viewing frustum
		float fov = Math<double>::atan( frustum.max.x / clippingPlanes[0] ) * 2.0f;
		fov = radiansToDegrees( fov );
		result->parameters()["projection:fov"] = new FloatData( fov );

		// scale the frustum so that it's -1,1 in x and that gives us the screen window
		float frustumScale = 2.0f/(frustum.max.x - frustum.min.x);
		Box2f screenWindow( V2f( -1, frustum.min.y * frustumScale ), V2f( 1, frustum.max.y * frustumScale ) );
		result->parameters()["screenWindow"] = new Box2fData( screenWindow );
	}

	// and add on other bits and bobs from maya attributes as blind data
	CompoundDataPtr maya = new CompoundData;
	result->blindData()->writable()["maya"] = maya;
	maya->writable()["aperture"] = new V2fData( Imath::V2f( fnCamera.horizontalFilmAperture(), fnCamera.verticalFilmAperture() ) );

	return result;
}
<commit_msg>Fixed FromMayaCameraConverter to account for non-zero film offsets.<commit_after>//////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2007-2015, Image Engine Design Inc. All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions are
//  met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
//     * Neither the name of Image Engine Design nor the names of any
//       other contributors to this software may be used to endorse or
//       promote products derived from this software without specific prior
//       written permission.
//
//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
//  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
//  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
//  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
//  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
//  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
//  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
//  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
//  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
//  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
//  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////

#include "IECoreMaya/FromMayaCameraConverter.h"
#include "IECoreMaya/Convert.h"

#include "IECore/CompoundParameter.h"
#include "IECore/Camera.h"
#include "IECore/MatrixTransform.h"
#include "IECore/AngleConversion.h"

#include "maya/MFnCamera.h"
#include "maya/MString.h"
#include "maya/MRenderUtil.h"
#include "maya/MCommonRenderSettingsData.h"
#include "maya/MDagPath.h"

#include "OpenEXR/ImathMath.h"

using namespace IECoreMaya;
using namespace IECore;
using namespace Imath;

IE_CORE_DEFINERUNTIMETYPED( FromMayaCameraConverter );

FromMayaDagNodeConverter::Description<FromMayaCameraConverter> FromMayaCameraConverter::m_description( MFn::kCamera, CameraTypeId, true );

FromMayaCameraConverter::FromMayaCameraConverter( const MDagPath &dagPath )
	:	FromMayaDagNodeConverter( "Converts maya camera shape nodes into IECore::Camera objects.", dagPath )
{

	IntParameter::PresetsContainer resolutionModePresets;
	resolutionModePresets.push_back( IntParameter::Preset( "renderGlobals", RenderGlobals ) );
	resolutionModePresets.push_back( IntParameter::Preset( "specified", Specified ) );

	m_resolutionMode = new IntParameter(
		"resolutionMode",
		"Determines how the resolution of the camera is decided.",
		RenderGlobals,
		RenderGlobals,
		Specified,
		resolutionModePresets,
		true
	);

	parameters()->addParameter( m_resolutionMode );

	V2iParameter::PresetsContainer resolutionPresets;
	resolutionPresets.push_back( V2iParameter::Preset( "2K", Imath::V2i( 2048, 1556 ) ) );
	resolutionPresets.push_back( V2iParameter::Preset( "1K", Imath::V2i( 1024, 778 ) ) );

	m_resolution = new V2iParameter(
		"resolution",
		"Specifies the resolution of the camera when mode is set to \"Specified\".",
		Imath::V2i( 2048, 1556 ),
		resolutionPresets
	);

	parameters()->addParameter( m_resolution );

}

IECore::ObjectPtr FromMayaCameraConverter::doConversion( const MDagPath &dagPath, IECore::ConstCompoundObjectPtr operands ) const
{
	MFnCamera fnCamera( dagPath );

	// convert things that are required by the IECore::Renderer specification

	CameraPtr result = new Camera;
	result->setName( IECore::convert<std::string>( fnCamera.name() ) );

	result->setTransform( new MatrixTransform( IECore::convert<Imath::M44f>( dagPath.inclusiveMatrix() ) ) );

	V2i resolution;
	if( operands->member<IntData>( "resolutionMode" )->readable()==RenderGlobals )
	{
		MCommonRenderSettingsData renderSettings;
		MRenderUtil::getCommonRenderSettings( renderSettings );
		resolution = Imath::V2i( renderSettings.width, renderSettings.height );
	}
	else
	{
		resolution = operands->member<V2iData>( "resolution" )->readable();
	}
	result->parameters()["resolution"] = new V2iData( resolution );

	Imath::V2f clippingPlanes = Imath::V2f( fnCamera.nearClippingPlane(), fnCamera.farClippingPlane() );
	result->parameters()["clippingPlanes"] = new V2fData( clippingPlanes );

	Imath::Box2d frustum;
	fnCamera.getRenderingFrustum( (float)resolution.x / (float)resolution.y, frustum.min.x, frustum.max.x, frustum.min.y, frustum.max.y );

	if( fnCamera.isOrtho() )
	{
		// orthographic
		result->parameters()["projection"] = new StringData( "orthographic" );
		result->parameters()["screenWindow"] = new Box2fData( Box2f( frustum.min, frustum.max ) );
	}
	else
	{
		// perspective
		result->parameters()["projection"] = new StringData( "perspective" );

		// derive horizontal field of view from the viewing frustum
		float horizontalFrustumOffset = frustum.max.x - (frustum.max.x - frustum.min.x) / 2.0f;
		float fov = Math<double>::atan( (frustum.max.x - horizontalFrustumOffset) / ( clippingPlanes[0] ) ) * 2.0f;
		fov = radiansToDegrees( fov );
		result->parameters()["projection:fov"] = new FloatData( fov );

		// scale the frustum so that it's -1,1 in x and that gives us the screen window
		float frustumScale = 2.0f/(frustum.max.x - frustum.min.x);
		Box2f screenWindow( V2f( -1 + (horizontalFrustumOffset * frustumScale), frustum.min.y * frustumScale ), V2f( 1 + (horizontalFrustumOffset * frustumScale), frustum.max.y * frustumScale ) );
		result->parameters()["screenWindow"] = new Box2fData( screenWindow );
	}

	// and add on other bits and bobs from maya attributes as blind data
	CompoundDataPtr maya = new CompoundData;
	result->blindData()->writable()["maya"] = maya;
	maya->writable()["aperture"] = new V2fData( Imath::V2f( fnCamera.horizontalFilmAperture(), fnCamera.verticalFilmAperture() ) );

	return result;
}
<|endoftext|>