<commit_before>#include "extractor/guidance/turn_lane_augmentation.hpp"
#include "extractor/guidance/turn_lane_types.hpp"
#include "util/log.hpp"

#include <algorithm>
#include <boost/assert.hpp>
#include <cstddef>
#include <utility>

namespace osrm
{
namespace extractor
{
namespace guidance
{
namespace lanes
{

namespace
{

const constexpr TurnLaneType::Mask tag_by_modifier[] = {TurnLaneType::uturn,
                                                        TurnLaneType::sharp_right,
                                                        TurnLaneType::right,
                                                        TurnLaneType::slight_right,
                                                        TurnLaneType::straight,
                                                        TurnLaneType::slight_left,
                                                        TurnLaneType::left,
                                                        TurnLaneType::sharp_left};

std::size_t getNumberOfTurns(const Intersection &intersection)
{
    return std::count_if(intersection.begin(), intersection.end(), [](const ConnectedRoad &road) {
        return road.entry_allowed;
    });
}

LaneDataVector augmentMultiple(const std::size_t none_index,
                               const std::size_t connection_count,
                               LaneDataVector lane_data,
                               const Intersection &intersection)
{
    // a none-turn is allowing multiple turns. we have to add a lane-data entry for
    // every possible turn. This should, hopefully, only be the case for single lane
    // entries?

    // looking at the left side first
    const auto range = [&]() {
        if (none_index == 0)
        {
            // find first connection_count - lane_data.size() valid turns
            std::size_t count = 0;
            for (std::size_t intersection_index = 1; intersection_index < intersection.size();
                 ++intersection_index)
            {

                count += static_cast<int>(intersection[intersection_index].entry_allowed);
                if (count > connection_count - lane_data.size())
                    return std::make_pair(std::size_t{1}, intersection_index + 1);
            }
        }
        else if (none_index + 1 == lane_data.size())
        {
            BOOST_ASSERT(!lane_data.empty());
            // find last connection-count - last_data.size() valid turns
            std::size_t count = 0;
            for (std::size_t intersection_index = intersection.size() - 1; intersection_index > 0;
                 --intersection_index)
            {
                count += static_cast<int>(intersection[intersection_index].entry_allowed);
                if (count > connection_count - lane_data.size())
                    return std::make_pair(intersection_index, intersection.size());
            }
        }
        else
        {
            // skip the first #index valid turns, find next connection_count -
            // lane_data.size() valid ones

            std::size_t begin = 1, count = 0, intersection_index;
            for (intersection_index = 1; intersection_index < intersection.size();
                 ++intersection_index)
            {
                count += static_cast<int>(intersection[intersection_index].entry_allowed);
                // if we reach the amount of
                if (count >= none_index)
                {
                    begin = intersection_index + 1;
                    break;
                }
            }

            // reset count to find the number of necessary entries
            count = 0;
            for (intersection_index = begin; intersection_index < intersection.size();
                 ++intersection_index)
            {
                count += static_cast<int>(intersection[intersection_index].entry_allowed);
                if (count > connection_count - lane_data.size())
                {
                    return std::make_pair(begin, intersection_index + 1);
                }
            }
        }
        // this should, theoretically, never be reached
        util::Log(logWARNING) << "Failed lane assignment. Reached bad situation.";
        return std::make_pair(std::size_t{0}, std::size_t{0});
    }();

    const auto allowed_in_range =
        std::count_if(intersection.begin() + range.first,
                      intersection.begin() + range.second,
                      [](const auto &road) { return road.entry_allowed; });

    if (allowed_in_range > 1 && lane_data[none_index].to - lane_data[none_index].from >= 1)
    {
        // check if there is a straight turn
        auto straight_itr = std::find_if(intersection.begin() + range.first,
                                         intersection.begin() + range.second,
                                         [](const auto &road) {
                                             return road.instruction.direction_modifier ==
                                                    DirectionModifier::Straight;
                                         });

        // we have a straight turn?
        if (straight_itr != intersection.begin() + range.second)
        {
            for (auto itr = intersection.begin() + range.first; itr != straight_itr; ++itr)
            {
                lane_data.push_back({tag_by_modifier[itr->instruction.direction_modifier],
                                     lane_data[none_index].from,
                                     lane_data[none_index].from,
                                     false});
            }
            lane_data.push_back({tag_by_modifier[straight_itr->instruction.direction_modifier],
                                 lane_data[none_index].from,
                                 lane_data[none_index].to,
                                 false});
            for (auto itr = straight_itr + 1; itr != intersection.begin() + range.second; ++itr)
            {
                lane_data.push_back({tag_by_modifier[itr->instruction.direction_modifier],
                                     lane_data[none_index].to,
                                     lane_data[none_index].to,
                                     false});
            }

            lane_data.erase(lane_data.begin() + none_index);
        }
        return lane_data;
    }
    else
    {
        for (auto intersection_index = range.first; intersection_index < range.second;
             ++intersection_index)
        {
            if (intersection[intersection_index].entry_allowed)
            {
                // FIXME this probably can be only a subset of these turns here?
                lane_data.push_back({tag_by_modifier[intersection[intersection_index]
                                                         .instruction.direction_modifier],
                                     lane_data[none_index].from,
                                     lane_data[none_index].to,
                                     false});
            }
        }
        lane_data.erase(lane_data.begin() + none_index);
    }
    return lane_data;
}

// Merging none-tag into its neighboring fields
// This handles situations like "left | | | right".
LaneDataVector mergeNoneTag(const std::size_t none_index, LaneDataVector lane_data)
{
    if (none_index == 0 || none_index + 1 == lane_data.size())
    {
        if (none_index == 0)
        {
            lane_data[1].from = lane_data[0].from;
        }
        else
        {
            lane_data[none_index - 1].to = lane_data[none_index].to;
        }
        lane_data.erase(lane_data.begin() + none_index);
    }
    else if (lane_data[none_index].to - lane_data[none_index].from <= 1)
    {
        lane_data[none_index - 1].to = lane_data[none_index].from;
        lane_data[none_index + 1].from = lane_data[none_index].to;

        lane_data.erase(lane_data.begin() + none_index);
    }
    return lane_data;
}

LaneDataVector handleRenamingSituations(const std::size_t none_index,
                                        LaneDataVector lane_data,
                                        const Intersection &intersection)
{
    bool has_right = false;
    bool has_through = false;
    bool has_left = false;
    for (const auto &road : intersection)
    {
        if (!road.entry_allowed)
            continue;

        const auto modifier = road.instruction.direction_modifier;
        has_right |= modifier == DirectionModifier::Right;
        has_right |= modifier == DirectionModifier::SlightRight;
        has_right |= modifier == DirectionModifier::SharpRight;
        has_through |= modifier == DirectionModifier::Straight;
        has_left |= modifier == DirectionModifier::Left;
        has_left |= modifier == DirectionModifier::SlightLeft;
        has_left |= modifier == DirectionModifier::SharpLeft;
    }

    // find missing tag and augment neighboring, if possible
    if (none_index == 0)
    {
        if (has_right &&
            (lane_data.size() == 1 || (lane_data[none_index + 1].tag != TurnLaneType::sharp_right &&
                                       lane_data[none_index + 1].tag != TurnLaneType::right)))
        {
            lane_data[none_index].tag = TurnLaneType::right;
            if (lane_data.size() > 1 && lane_data[none_index + 1].tag == TurnLaneType::straight)
            {
                lane_data[none_index + 1].from = lane_data[none_index].from;
                // turning right through a possible through lane is not possible
                lane_data[none_index].to = lane_data[none_index].from;
            }
        }
        else if (has_through &&
                 (lane_data.size() == 1 || lane_data[none_index + 1].tag != TurnLaneType::straight))
        {
            lane_data[none_index].tag = TurnLaneType::straight;
        }
    }
    else if (none_index + 1 == lane_data.size())
    {
        if (has_left && ((lane_data[none_index - 1].tag != TurnLaneType::sharp_left &&
                          lane_data[none_index - 1].tag != TurnLaneType::left)))
        {
            lane_data[none_index].tag = TurnLaneType::left;
            if (lane_data[none_index - 1].tag == TurnLaneType::straight)
            {
                lane_data[none_index - 1].to = lane_data[none_index].to;
                // turning left through a possible through lane is not possible
                lane_data[none_index].from = lane_data[none_index].to;
            }
        }
        else if (has_through && lane_data[none_index - 1].tag != TurnLaneType::straight)
        {
            lane_data[none_index].tag = TurnLaneType::straight;
        }
    }
    else
    {
        if ((lane_data[none_index + 1].tag == TurnLaneType::left ||
             lane_data[none_index + 1].tag == TurnLaneType::slight_left ||
             lane_data[none_index + 1].tag == TurnLaneType::sharp_left) &&
            (lane_data[none_index - 1].tag == TurnLaneType::right ||
             lane_data[none_index - 1].tag == TurnLaneType::slight_right ||
             lane_data[none_index - 1].tag == TurnLaneType::sharp_right))
        {
            lane_data[none_index].tag = TurnLaneType::straight;
        }
    }
    return lane_data;
}

} // namespace

/*
   Lanes can have the tag none. While a nice feature for visibility, it is a terrible feature
   for parsing. None can be part of neighboring turns, or not. We have to look at both the
   intersection and the lane data to see what turns we have to augment by the none-lanes
 */
LaneDataVector handleNoneValueAtSimpleTurn(LaneDataVector lane_data,
                                           const Intersection &intersection)
{
    const bool needs_no_processing =
        (intersection.empty() || lane_data.empty() || !hasTag(TurnLaneType::none, lane_data));

    if (needs_no_processing)
        return lane_data;

    // FIXME all this needs to consider the number of lanes at the target to ensure that we
    // augment lanes correctly, if the target lane allows for more turns
    //
    // -----------------
    //
    // -----        ----
    //  -v          |
    // -----        |
    //      |   |   |
    //
    // A situation like this would allow a right turn from the through lane.
    //
    // -----------------
    //
    // -----    --------
    //  -v      |
    // -----    |
    //      |   |
    //
    // Here, the number of lanes in the right road would not allow turns from both lanes, but
    // only from the right one.

    const std::size_t connection_count =
        getNumberOfTurns(intersection) -
        ((intersection[0].entry_allowed && lane_data.back().tag != TurnLaneType::uturn) ? 1 : 0);

    // TODO check for impossible turns to see whether the turn lane is at the correct place
    const std::size_t none_index =
        std::distance(lane_data.begin(), findTag(TurnLaneType::none, lane_data));
    BOOST_ASSERT(none_index != lane_data.size());
    // we have to create multiple turns
    if (connection_count > lane_data.size())
    {
        lane_data =
            augmentMultiple(none_index, connection_count, std::move(lane_data), intersection);
    }
    // we have to reduce it, assigning it to neighboring turns
    else if (connection_count < lane_data.size())
    {
        // a pgerequisite is simple turns. Larger differences should not end up here
        // an additional line at the side is only reasonable if it is targeting public
        // service vehicles. Otherwise, we should not have it
        if (connection_count + 1 == lane_data.size())
        {
            lane_data = mergeNoneTag(none_index, std::move(lane_data));
        }
        else
        {
            // This represents a currently unhandled case. It should not even get here, but to be
            // sure we return nevertheless.
            return lane_data;
        }
    }
    // we have to rename and possibly augment existing ones. The pure count remains the
    // same.
    else
    {
        lane_data = handleRenamingSituations(none_index, std::move(lane_data), intersection);
    }

    // finally make sure we are still sorted
    std::sort(lane_data.begin(), lane_data.end());
    return lane_data;
}

} // namespace lanes
} // namespace guidance
} // namespace extractor
} // namespace osrm
<commit_msg>comments<commit_after>#include "extractor/guidance/turn_lane_augmentation.hpp"
#include "extractor/guidance/turn_lane_types.hpp"
#include "util/log.hpp"

#include <algorithm>
#include <boost/assert.hpp>
#include <cstddef>
#include <utility>

namespace osrm
{
namespace extractor
{
namespace guidance
{
namespace lanes
{

namespace
{

const constexpr TurnLaneType::Mask tag_by_modifier[] = {TurnLaneType::uturn,
                                                        TurnLaneType::sharp_right,
                                                        TurnLaneType::right,
                                                        TurnLaneType::slight_right,
                                                        TurnLaneType::straight,
                                                        TurnLaneType::slight_left,
                                                        TurnLaneType::left,
                                                        TurnLaneType::sharp_left};

std::size_t getNumberOfTurns(const Intersection &intersection)
{
    return std::count_if(intersection.begin(), intersection.end(), [](const ConnectedRoad &road) {
        return road.entry_allowed;
    });
}

LaneDataVector augmentMultiple(const std::size_t none_index,
                               const std::size_t connection_count,
                               LaneDataVector lane_data,
                               const Intersection &intersection)
{
    // a none-turn is allowing multiple turns. we have to add a lane-data entry for
    // every possible turn. This should, hopefully, only be the case for single lane
    // entries?

    // looking at the left side first
    const auto range = [&]() {
        if (none_index == 0)
        {
            // find first connection_count - lane_data.size() valid turns
            std::size_t count = 0;
            for (std::size_t intersection_index = 1; intersection_index < intersection.size();
                 ++intersection_index)
            {

                count += static_cast<int>(intersection[intersection_index].entry_allowed);
                if (count > connection_count - lane_data.size())
                    return std::make_pair(std::size_t{1}, intersection_index + 1);
            }
        }
        else if (none_index + 1 == lane_data.size())
        {
            BOOST_ASSERT(!lane_data.empty());
            // find last connection-count - last_data.size() valid turns
            std::size_t count = 0;
            for (std::size_t intersection_index = intersection.size() - 1; intersection_index > 0;
                 --intersection_index)
            {
                count += static_cast<int>(intersection[intersection_index].entry_allowed);
                if (count > connection_count - lane_data.size())
                    return std::make_pair(intersection_index, intersection.size());
            }
        }
        else
        {
            // skip the first #index valid turns, find next connection_count -
            // lane_data.size() valid ones

            std::size_t begin = 1, count = 0, intersection_index;
            for (intersection_index = 1; intersection_index < intersection.size();
                 ++intersection_index)
            {
                count += static_cast<int>(intersection[intersection_index].entry_allowed);
                // if we reach the amount of
                if (count >= none_index)
                {
                    begin = intersection_index + 1;
                    break;
                }
            }

            // reset count to find the number of necessary entries
            count = 0;
            for (intersection_index = begin; intersection_index < intersection.size();
                 ++intersection_index)
            {
                count += static_cast<int>(intersection[intersection_index].entry_allowed);
                if (count > connection_count - lane_data.size())
                {
                    return std::make_pair(begin, intersection_index + 1);
                }
            }
        }
        // this should, theoretically, never be reached
        util::Log(logWARNING) << "Failed lane assignment. Reached bad situation.";
        return std::make_pair(std::size_t{0}, std::size_t{0});
    }();

    const auto intersection_range_first = intersection.begin() + range.first;
    const auto intersection_range_end = intersection.begin() + range.second;
    const auto allowed_in_range =
        std::count_if(intersection_range_first, intersection_range_end, [](const auto &road) {
            return road.entry_allowed;
        });

    if (allowed_in_range > 1 && lane_data[none_index].to - lane_data[none_index].from >= 1)
    {
        // check if there is a straight turn
        auto straight_itr =
            std::find_if(intersection_range_first, intersection_range_end, [](const auto &road) {
                return road.instruction.direction_modifier == DirectionModifier::Straight;
            });

        // we have a straight turn?
        if (straight_itr != intersection_range_end)
        {
            for (auto itr = intersection_range_first; itr != straight_itr; ++itr)
            {
                lane_data.push_back({tag_by_modifier[itr->instruction.direction_modifier],
                                     lane_data[none_index].from,
                                     lane_data[none_index].from,
                                     false});
            }
            lane_data.push_back({tag_by_modifier[straight_itr->instruction.direction_modifier],
                                 lane_data[none_index].from,
                                 lane_data[none_index].to,
                                 false});
            for (auto itr = straight_itr + 1; itr != intersection_range_end; ++itr)
            {
                lane_data.push_back({tag_by_modifier[itr->instruction.direction_modifier],
                                     lane_data[none_index].to,
                                     lane_data[none_index].to,
                                     false});
            }

            lane_data.erase(lane_data.begin() + none_index);
        }
        return lane_data;
    }
    else
    {
        for (auto intersection_index = range.first; intersection_index < range.second;
             ++intersection_index)
        {
            if (intersection[intersection_index].entry_allowed)
            {
                lane_data.push_back({tag_by_modifier[intersection[intersection_index]
                                                         .instruction.direction_modifier],
                                     lane_data[none_index].from,
                                     lane_data[none_index].to,
                                     false});
            }
        }
        lane_data.erase(lane_data.begin() + none_index);
    }
    return lane_data;
}

// Merging none-tag into its neighboring fields
// This handles situations like "left | | | right".
LaneDataVector mergeNoneTag(const std::size_t none_index, LaneDataVector lane_data)
{
    if (none_index == 0 || none_index + 1 == lane_data.size())
    {
        if (none_index == 0)
        {
            lane_data[1].from = lane_data[0].from;
        }
        else
        {
            lane_data[none_index - 1].to = lane_data[none_index].to;
        }
        lane_data.erase(lane_data.begin() + none_index);
    }
    else if (lane_data[none_index].to - lane_data[none_index].from <= 1)
    {
        lane_data[none_index - 1].to = lane_data[none_index].from;
        lane_data[none_index + 1].from = lane_data[none_index].to;

        lane_data.erase(lane_data.begin() + none_index);
    }
    return lane_data;
}

LaneDataVector handleRenamingSituations(const std::size_t none_index,
                                        LaneDataVector lane_data,
                                        const Intersection &intersection)
{
    bool has_right = false;
    bool has_through = false;
    bool has_left = false;
    for (const auto &road : intersection)
    {
        if (!road.entry_allowed)
            continue;

        const auto modifier = road.instruction.direction_modifier;
        has_right |= modifier == DirectionModifier::Right;
        has_right |= modifier == DirectionModifier::SlightRight;
        has_right |= modifier == DirectionModifier::SharpRight;
        has_through |= modifier == DirectionModifier::Straight;
        has_left |= modifier == DirectionModifier::Left;
        has_left |= modifier == DirectionModifier::SlightLeft;
        has_left |= modifier == DirectionModifier::SharpLeft;
    }

    // find missing tag and augment neighboring, if possible
    if (none_index == 0)
    {
        if (has_right &&
            (lane_data.size() == 1 || (lane_data[none_index + 1].tag != TurnLaneType::sharp_right &&
                                       lane_data[none_index + 1].tag != TurnLaneType::right)))
        {
            lane_data[none_index].tag = TurnLaneType::right;
            if (lane_data.size() > 1 && lane_data[none_index + 1].tag == TurnLaneType::straight)
            {
                lane_data[none_index + 1].from = lane_data[none_index].from;
                // turning right through a possible through lane is not possible
                lane_data[none_index].to = lane_data[none_index].from;
            }
        }
        else if (has_through &&
                 (lane_data.size() == 1 || lane_data[none_index + 1].tag != TurnLaneType::straight))
        {
            lane_data[none_index].tag = TurnLaneType::straight;
        }
    }
    else if (none_index + 1 == lane_data.size())
    {
        if (has_left && ((lane_data[none_index - 1].tag != TurnLaneType::sharp_left &&
                          lane_data[none_index - 1].tag != TurnLaneType::left)))
        {
            lane_data[none_index].tag = TurnLaneType::left;
            if (lane_data[none_index - 1].tag == TurnLaneType::straight)
            {
                lane_data[none_index - 1].to = lane_data[none_index].to;
                // turning left through a possible through lane is not possible
                lane_data[none_index].from = lane_data[none_index].to;
            }
        }
        else if (has_through && lane_data[none_index - 1].tag != TurnLaneType::straight)
        {
            lane_data[none_index].tag = TurnLaneType::straight;
        }
    }
    else
    {
        if ((lane_data[none_index + 1].tag == TurnLaneType::left ||
             lane_data[none_index + 1].tag == TurnLaneType::slight_left ||
             lane_data[none_index + 1].tag == TurnLaneType::sharp_left) &&
            (lane_data[none_index - 1].tag == TurnLaneType::right ||
             lane_data[none_index - 1].tag == TurnLaneType::slight_right ||
             lane_data[none_index - 1].tag == TurnLaneType::sharp_right))
        {
            lane_data[none_index].tag = TurnLaneType::straight;
        }
    }
    return lane_data;
}

} // namespace

/*
   Lanes can have the tag none. While a nice feature for visibility, it is a terrible feature
   for parsing. None can be part of neighboring turns, or not. We have to look at both the
   intersection and the lane data to see what turns we have to augment by the none-lanes
 */
LaneDataVector handleNoneValueAtSimpleTurn(LaneDataVector lane_data,
                                           const Intersection &intersection)
{
    const bool needs_no_processing =
        (intersection.empty() || lane_data.empty() || !hasTag(TurnLaneType::none, lane_data));

    if (needs_no_processing)
        return lane_data;

    // FIXME all this needs to consider the number of lanes at the target to ensure that we
    // augment lanes correctly, if the target lane allows for more turns
    //
    // -----------------
    //
    // -----        ----
    //  -v          |
    // -----        |
    //      |   |   |
    //
    // A situation like this would allow a right turn from the through lane.
    //
    // -----------------
    //
    // -----    --------
    //  -v      |
    // -----    |
    //      |   |
    //
    // Here, the number of lanes in the right road would not allow turns from both lanes, but
    // only from the right one.

    const std::size_t connection_count =
        getNumberOfTurns(intersection) -
        ((intersection[0].entry_allowed && lane_data.back().tag != TurnLaneType::uturn) ? 1 : 0);

    // TODO check for impossible turns to see whether the turn lane is at the correct place
    const std::size_t none_index =
        std::distance(lane_data.begin(), findTag(TurnLaneType::none, lane_data));
    BOOST_ASSERT(none_index != lane_data.size());
    // we have to create multiple turns
    if (connection_count > lane_data.size())
    {
        lane_data =
            augmentMultiple(none_index, connection_count, std::move(lane_data), intersection);
    }
    // we have to reduce it, assigning it to neighboring turns
    else if (connection_count < lane_data.size())
    {
        // a pgerequisite is simple turns. Larger differences should not end up here
        // an additional line at the side is only reasonable if it is targeting public
        // service vehicles. Otherwise, we should not have it
        if (connection_count + 1 == lane_data.size())
        {
            lane_data = mergeNoneTag(none_index, std::move(lane_data));
        }
        else
        {
            // This represents a currently unhandled case. It should not even get here, but to be
            // sure we return nevertheless.
            return lane_data;
        }
    }
    // we have to rename and possibly augment existing ones. The pure count remains the
    // same.
    else
    {
        lane_data = handleRenamingSituations(none_index, std::move(lane_data), intersection);
    }

    // finally make sure we are still sorted
    std::sort(lane_data.begin(), lane_data.end());
    return lane_data;
}

} // namespace lanes
} // namespace guidance
} // namespace extractor
} // namespace osrm
<|endoftext|>


<commit_before>// Copyright (c) 2012 Dougrist Productions
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//
// Author: Benjamin Crist
//         Josh Douglas
// File: main.cc

#ifdef DEBUG

//#pragma comment (lib, "sfml-main-d.lib")
#pragma comment (lib, "sfml-system-d.lib")
#pragma comment (lib, "sfml-window-d.lib")
#pragma comment(lib, "opengl32.lib")

#else

//#pragma comment (lib, "sfml-main.lib")
#pragma comment (lib, "sfml-system.lib")
#pragma comment (lib, "sfml-window.lib")
#pragma comment(lib, "opengl32.lib")

#endif
#define PI 3.14159
#include <SFML/Window.hpp>
#include <SFML/System.hpp>
#include <SFML/OpenGL.hpp>
#include <glm\glm.hpp>
#include <glm\gtc\matrix_transform.hpp>
#include <glm\gtc\type_ptr.hpp>

#include <iostream>

void testCamera();
void display();
void keyboard();

float dist = sqrt(1/3.0);
const int FPS = 27;
int bound = 100;
glm::vec3 eye = glm::vec3 (1, 0, 0);
glm::vec3 center = glm::vec3 (0, 0, 0);
glm::vec3 up = glm::vec3 (0, 1, 0);
float fovY = 60;
float aspectX = 1;
float zNear = 0.1;
float zFar = 5000;

void rotateCameraY(double rSpeed)
{
	glm::vec3 v = center - eye;

	center[0] = eye[0] + v[0] * cos(rSpeed) + v[2] * sin(rSpeed);
	center[2] = eye[2] + -v[0] * sin(rSpeed) + v[2] * cos(rSpeed);
}

//causes the camera to slide left or right without turning
void strafeCameraXZ(double mSpeed)
{
	glm::vec3 v = center - eye;

	glm::vec3 oV(-v.z, 0, v.x);

	eye[0] = eye[0] + oV[0] * mSpeed;
	eye[2] = eye[2] + oV[2] * mSpeed;

	center[0] = center[0] + oV[0] * mSpeed;
	center[2] = center[2] + oV[2] * mSpeed;
}

//moves the camera in a straight line in forward or reverse
void moveCameraXZ(double mSpeed)
{
	glm::vec3 v = center - eye;

	eye[0] = eye[0] + v[0] * mSpeed;
	eye[2] = eye[2] + v[2] * mSpeed;

	center[0] = center[0] + v[0] * mSpeed;
	center[2] = center[2] + v[2] * mSpeed;
}

void testCamera()
{
	glEnable(GL_DEPTH_TEST);
	glMatrixMode(GL_PROJECTION);
	glm::mat4 perspective = glm::perspective(fovY, aspectX, zNear, zFar);
	glLoadMatrixf(glm::value_ptr(perspective));
	//glLoadIdentity();
	//glOrtho(-bound, bound,-bound, bound,-bound, bound);

	//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

	glMatrixMode(GL_MODELVIEW);
	std::cerr << eye.x << ',' << eye.z << '\t';
	std::cerr << center.x << ',' << center.z << std::endl;
	glm::mat4 isoViewMatrix = glm::lookAt(eye, center, up);
	glLoadMatrixf(glm::value_ptr(isoViewMatrix));

	float points[8][3] =
	{
		{-1, -1, 1}, {1, -1, 1}, {1, -1, -1}, {-1, -1, -1},
		{-1, 1, 1}, {1, 1, 1}, {1, 1, -1}, {-1, 1, -1}
	};

	float size = 10;
	glColor3f(1,0,1);
	glPushMatrix();
	glScalef(size,(size/8),size);

		glColor3f(1,1,1);
		//front
		glBegin(GL_QUADS);
			glVertex3fv(points[2]);
			glVertex3fv(points[3]);
			glVertex3fv(points[7]);
			glVertex3fv(points[6]);
		glEnd();

		glColor3f(1,1,0);
		//back
		glBegin(GL_QUADS);
			glVertex3fv(points[0]);
			glVertex3fv(points[1]);
			glVertex3fv(points[5]);
			glVertex3fv(points[4]);
		glEnd();

		glColor3f(1,0,1);
		//right
		glBegin(GL_QUADS);
			glVertex3fv(points[1]);
			glVertex3fv(points[2]);
			glVertex3fv(points[6]);
			glVertex3fv(points[5]);
		glEnd();

		glColor3f(0.5,1,0.5);
		//left
		glBegin(GL_QUADS);
			glVertex3fv(points[0]);
			glVertex3fv(points[3]);
			glVertex3fv(points[7]);
			glVertex3fv(points[4]);
		glEnd();

		glColor3f(0,1,1);
		//bottom
		glBegin(GL_QUADS);
			glVertex3fv(points[0]);
			glVertex3fv(points[1]);
			glVertex3fv(points[2]);
			glVertex3fv(points[3]);
		glEnd();

		glColor3f(0,1,0);
		//top
		glBegin(GL_QUADS);
			glVertex3fv(points[4]);
			glVertex3fv(points[5]);
			glVertex3fv(points[6]);
			glVertex3fv(points[7]);
		glEnd();

	glPopMatrix();
	glDisable(GL_DEPTH_TEST);

}

void keyboard()
{
	const double M_SPEED = 1.25;
	const double R_SPEED = 0.05;

	if (sf::Keyboard::isKeyPressed(sf::Keyboard::W))
	{
		moveCameraXZ(M_SPEED);
	}

	else if (sf::Keyboard::isKeyPressed(sf::Keyboard::S))
	{
		 moveCameraXZ(-M_SPEED);
	}

	else if (sf::Keyboard::isKeyPressed(sf::Keyboard::D))
	{
		rotateCameraY(-R_SPEED);
	}

	else if (sf::Keyboard::isKeyPressed(sf::Keyboard::A))
	{
		rotateCameraY(R_SPEED);
	}

	else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Q))
	{
		 strafeCameraXZ(-M_SPEED);
	}

	else if (sf::Keyboard::isKeyPressed(sf::Keyboard::E))
	{
		strafeCameraXZ(M_SPEED);
	}

	else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Escape))
	{
		exit(0);
	}

}

void display()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	testCamera();
}

int main()
{
  // create the window
    sf::Window window(sf::VideoMode(600, 600), "OpenGL", sf::Style::Default, sf::ContextSettings(32));
    window.setVerticalSyncEnabled(true);

	glClearColor(0,0,0,0);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glm::mat4 perspective(float fovy, float aspect, float zNear,float zFar);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

    // load resources, initialize the OpenGL states, ...

    // run the main loop
    bool running = true;
    while (running)
    {
        // handle events
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
            {
                // end the program
                running = false;
            }
            else if (event.type == sf::Event::Resized)
            {
                // adjust the viewport when the window is resized
                glViewport(0, 0, event.size.width, event.size.height);
            }
        }

        // clear the buffers
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        // draw...
		display();
		keyboard();
        // end the current frame (internally swaps the front and back buffers)
        window.display();
    }

    // release resources...

    return 0;
}<commit_msg>Possible 3d mouse detection done<commit_after>// Copyright (c) 2012 Dougrist Productions
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//
// Author: Benjamin Crist
//         Josh Douglas
// File: main.cc

#ifdef DEBUG

//#pragma comment (lib, "sfml-main-d.lib")
#pragma comment (lib, "sfml-system-d.lib")
#pragma comment (lib, "sfml-window-d.lib")
#pragma comment(lib, "opengl32.lib")

#else

//#pragma comment (lib, "sfml-main.lib")
#pragma comment (lib, "sfml-system.lib")
#pragma comment (lib, "sfml-window.lib")
#pragma comment(lib, "opengl32.lib")

#endif
#define PI 3.14159
#include <SFML/Window.hpp>
#include <SFML/System.hpp>
#include <SFML/OpenGL.hpp>
#include <glm\glm.hpp>
#include <glm\gtc\matrix_transform.hpp>
#include <glm\gtc\type_ptr.hpp>

#include <iostream>

void testCamera();
void display();
void keyboard();
void onMouse();
void initGL();

float wHeight = 600;
float wWidth  = 600;
float dist = sqrt(1/3.0);
const int FPS = 27;
int bound = 100;
glm::vec3 eye = glm::vec3 (250, 50, 0);
glm::vec3 center = glm::vec3 (0, 0, 0);
glm::vec3 up = glm::vec3 (0, 1, 0);
float fovY = 60;
float aspectX = 1;
float zNear = 0.1;
float zFar = 5000;
bool isSelected = false;
sf::Window window(sf::VideoMode(wWidth, wHeight), "OpenGL", sf::Style::Default, sf::ContextSettings(32));

void rotateCameraY(double rSpeed)
{
	glm::vec3 v = center - eye;

	center[0] = eye[0] + v[0] * cos(rSpeed) + v[2] * sin(rSpeed);
	center[2] = eye[2] + -v[0] * sin(rSpeed) + v[2] * cos(rSpeed);
}

//causes the camera to slide left or right without turning
void strafeCameraXZ(double mSpeed)
{
	glm::vec3 v = center - eye;

	glm::vec3 oV(-v.z, 0, v.x);

	eye[0] = eye[0] + oV[0] * mSpeed;
	eye[2] = eye[2] + oV[2] * mSpeed;

	center[0] = center[0] + oV[0] * mSpeed;
	center[2] = center[2] + oV[2] * mSpeed;
}

//moves the camera in a straight line in forward or reverse
void moveCameraXZ(double mSpeed)
{
	glm::vec3 v = center - eye;

	eye[0] = eye[0] + v[0] * mSpeed;
	eye[2] = eye[2] + v[2] * mSpeed;

	center[0] = center[0] + v[0] * mSpeed;
	center[2] = center[2] + v[2] * mSpeed;
}

void testCamera()
{
	glEnable(GL_DEPTH_TEST);
	glMatrixMode(GL_PROJECTION);
	glm::mat4 perspective = glm::perspective(fovY, aspectX, zNear, zFar);
	glLoadMatrixf(glm::value_ptr(perspective));
	//glLoadIdentity();
	//glOrtho(-bound, bound,-bound, bound,-bound, bound);

	//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

	glMatrixMode(GL_MODELVIEW);
	//std::cerr << eye.x << ',' << eye.z << '\t';
	//std::cerr << center.x << ',' << center.z << std::endl;
	glm::mat4 isoViewMatrix = glm::lookAt(eye, center, up);
	glLoadMatrixf(glm::value_ptr(isoViewMatrix));

	float points[8][3] =
	{
		{-1, -1, 1}, {1, -1, 1}, {1, -1, -1}, {-1, -1, -1},
		{-1, 1, 1}, {1, 1, 1}, {1, 1, -1}, {-1, 1, -1}
	};

	float size = 10;
	glColor3f(1,0,1);
	glPushMatrix();
	glScalef(size,(size/8),size);

		glColor3f(1,1,1);
		//front
		glBegin(GL_QUADS);
			glVertex3fv(points[2]);
			glVertex3fv(points[3]);
			glVertex3fv(points[7]);
			glVertex3fv(points[6]);
		glEnd();

		glColor3f(1,1,0);
		//back
		glBegin(GL_QUADS);
			glVertex3fv(points[0]);
			glVertex3fv(points[1]);
			glVertex3fv(points[5]);
			glVertex3fv(points[4]);
		glEnd();

		glColor3f(1,0,1);
		//right
		glBegin(GL_QUADS);
			glVertex3fv(points[1]);
			glVertex3fv(points[2]);
			glVertex3fv(points[6]);
			glVertex3fv(points[5]);
		glEnd();

		glColor3f(0.5,1,0.5);
		//left
		glBegin(GL_QUADS);
			glVertex3fv(points[0]);
			glVertex3fv(points[3]);
			glVertex3fv(points[7]);
			glVertex3fv(points[4]);
		glEnd();

		glColor3f(0,1,1);
		//bottom
		glBegin(GL_QUADS);
			glVertex3fv(points[0]);
			glVertex3fv(points[1]);
			glVertex3fv(points[2]);
			glVertex3fv(points[3]);
		glEnd();

		if(isSelected)
		{
			glColor3f(0,1,0);
			//top
			glBegin(GL_QUADS);
				glVertex3fv(points[4]);
				glVertex3fv(points[5]);
				glVertex3fv(points[6]);
				glVertex3fv(points[7]);
			glEnd();
		}

		else
		{
			glColor3f(1,1,0);
			//top
			glBegin(GL_QUADS);
				glVertex3fv(points[4]);
				glVertex3fv(points[5]);
				glVertex3fv(points[6]);
				glVertex3fv(points[7]);
			glEnd();

		}

	glPopMatrix();
	glDisable(GL_DEPTH_TEST);

}

void keyboard()
{
	const double M_SPEED = 0.01;
	const double R_SPEED = 0.05;

	if (sf::Keyboard::isKeyPressed(sf::Keyboard::W))
	{
		moveCameraXZ(M_SPEED);
	}

	else if (sf::Keyboard::isKeyPressed(sf::Keyboard::S))
	{
		 moveCameraXZ(-M_SPEED);
	}

	else if (sf::Keyboard::isKeyPressed(sf::Keyboard::D))
	{
		rotateCameraY(-R_SPEED);
	}

	else if (sf::Keyboard::isKeyPressed(sf::Keyboard::A))
	{
		rotateCameraY(R_SPEED);
	}

	else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Q))
	{
		 strafeCameraXZ(-M_SPEED);
	}

	else if (sf::Keyboard::isKeyPressed(sf::Keyboard::E))
	{
		strafeCameraXZ(M_SPEED);
	}

	else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Escape))
	{
		exit(0);
	}

}

void onMouse()
{

	glMatrixMode(GL_PROJECTION);
	glm::mat4 perspective = glm::perspective(fovY, aspectX, zNear, zFar);
	glLoadMatrixf(glm::value_ptr(perspective));
	//glLoadIdentity();
	//glOrtho(-bound, bound,-bound, bound,-bound, bound);

	//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

	glMatrixMode(GL_MODELVIEW);
	//std::cerr << eye.x << ',' << eye.z << '\t';
	//std::cerr << center.x << ',' << center.z << std::endl;
	glm::mat4 isoViewMatrix = glm::lookAt(eye, center, up);
	glLoadMatrixf(glm::value_ptr(isoViewMatrix));

  //if(state != GLUT_DOWN)
  //  return;

	double window_width = wWidth;
	double window_height = wHeight;
	//glm::vec2 window_width = glm::vec2(window);
	//glm::vec2 window_height = glm::vec2(window);

  GLbyte color[4];
  GLfloat depth;
  GLuint index;

  glReadPixels(sf::Mouse::getPosition(window).x, window_height - sf::Mouse::getPosition(window).y - 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, color);
  glReadPixels(sf::Mouse::getPosition(window).x, window_height - sf::Mouse::getPosition(window).y - 1, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth);
  glReadPixels(sf::Mouse::getPosition(window).x, window_height - sf::Mouse::getPosition(window).y - 1, 1, 1, GL_STENCIL_INDEX, GL_UNSIGNED_INT, &index);

  //std::cerr<<"Clicked on pixel %d, %d, color %02hhx%02hhx%02hhx%02hhx, depth %f, stencil index %u\n"<<
  //       sf::Mouse::getPosition(window).x<< sf::Mouse::getPosition(window).y<< color[0]<< color[1]<< color[2]<< color[3]<< depth<< index<<std::endl;

	glm::vec4 viewport = glm::vec4(0, 0, window_width, window_height);
	glm::vec3 wincoord = glm::vec3(window.getPosition().x, window_height - window.getPosition().y - 1, depth);
	glm::vec3 objcoord = glm::unProject(wincoord, isoViewMatrix, perspective, viewport);
 if (sf::Mouse::isButtonPressed(sf::Mouse::Left))
 {
     // left click...
	 std::cerr<<"Coordinates in object space: %f, %f, %f\n"<<
         objcoord.x<< " "<< objcoord.y<<" "<< objcoord.z<<std::endl;
 }

}

void display()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	testCamera();
}

int main()
{
  // create the window

    window.setVerticalSyncEnabled(true);

	glClearColor(0,0,0,0);

	glMatrixMode(GL_PROJECTION);
	glm::mat4 perspective = glm::perspective(fovY, aspectX, zNear, zFar);
	glLoadMatrixf(glm::value_ptr(perspective));
	//glLoadIdentity();
	//glOrtho(-bound, bound,-bound, bound,-bound, bound);

	//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

	glMatrixMode(GL_MODELVIEW);
	std::cerr << eye.x << ',' << eye.z << '\t';
	std::cerr << center.x << ',' << center.z << std::endl;
	glm::mat4 isoViewMatrix = glm::lookAt(eye, center, up);
	glLoadMatrixf(glm::value_ptr(isoViewMatrix));

    // load resources, initialize the OpenGL states, ...

    // run the main loop
    bool running = true;
    while (running)
    {
        // handle events
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
            {
                // end the program
                running = false;
            }
            else if (event.type == sf::Event::Resized)
            {
                // adjust the viewport when the window is resized
                glViewport(0, 0, event.size.width, event.size.height);
            }
        }
        // clear the buffers
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        // draw...
		display();
		keyboard();
		onMouse();
        // end the current frame (internally swaps the front and back buffers)
        window.display();
    }

    // release resources...
	/*glDisable(GL_DEPTH);*/

    return 0;
}<|endoftext|>


<commit_before>#include "stdafx.h"
#include <dukat/camera2.h>
#include <dukat/log.h>
#include <dukat/renderer2.h>
#include <dukat/renderlayer2.h>
#include <dukat/shadercache.h>
#include <dukat/sprite.h>
#include <dukat/vertextypes2.h>
#include <dukat/sysutil.h>
#include <dukat/meshbuilder2.h>
#include <dukat/meshdata.h>
#include <dukat/textureutil.h>

namespace dukat
{
	Renderer2::Renderer2(Window* window, ShaderCache* shader_cache) : Renderer(window, shader_cache),
		composite_program(nullptr), composite_binder(nullptr), render_flags(RenderFx | RenderSprites | RenderParticles | RenderText | ForceClear)
	{
		// Enable transparency
		set_blending(true);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		// Disable depth test - we'll have to take care of rendering order ourselves
		glDisable(GL_DEPTH_TEST);
#ifdef OPENGL_CORE
        // Enable gl_PointSize instruction in shader
		glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
#endif

		initialize_sprite_buffers();
		initialize_particle_buffers();
		initialize_frame_buffers();
		initialize_render_stages();

		composite_program = shader_cache->get_program("fx_default.vsh", "fx_default.fsh");

		MeshBuilder2 builder;
		quad = builder.build_textured_quad();

		for (auto& l : lights)
		{
			l.position = Vector2{ 0, 0 };
			l.k1 = l.k2 = 0.0f;
		}

		gl_check_error();
	}

	void Renderer2::initialize_sprite_buffers(void)
	{
		// Vertex order:
		//	0 2
		//	1 3
		Vertex2PT vertices[] = {
			{ -0.5f, -0.5f, 0.0f, 0.0f },	// top-left
			{ -0.5f,  0.5f, 0.0f, 1.0f },	// bottom-left
			{  0.5f, -0.5f, 1.0f, 0.0f },	// top-right
			{  0.5f,  0.5f, 1.0f, 1.0f }	// bottom-right
		};
		// Create buffer for sprite rendering
		sprite_buffer = std::make_unique<VertexBuffer>(1);
		sprite_buffer->load_data(0, GL_ARRAY_BUFFER, 4, sizeof(Vertex2PT), vertices, GL_STATIC_DRAW);
	}

	void Renderer2::initialize_particle_buffers(void)
	{
		// Create buffer for particle rendering
		particle_buffer = std::make_unique<VertexBuffer>(1);
		particle_buffer->load_data(0, GL_ARRAY_BUFFER, max_particles, sizeof(Vertex2PSC), nullptr, GL_STREAM_DRAW);
	}

	void Renderer2::initialize_frame_buffers(void)
	{
		if (camera == nullptr)
		{
			const auto width = window->get_width();
			const auto height = window->get_height();
			log->trace("Initializing frame buffers: {}x{}", width, height);
			frame_buffer = std::make_unique<FrameBuffer>(width, height, true, false, TextureFilterProfile::ProfileNearest);
			screen_buffer = std::make_unique<FrameBuffer>(width, height, true, false, TextureFilterProfile::ProfileNearest);
		}
		else
		{
			const auto& dim = camera->transform.dimension;
			log->trace("Resizing frame buffers: {}x{}", static_cast<int>(dim.x), static_cast<int>(dim.y));
			frame_buffer->resize(static_cast<int>(dim.x), static_cast<int>(dim.y));

			// recreate intermediate render targets
			for (auto& stage : stages)
			{
				if (stage->frame_buffer != nullptr)
					stage->frame_buffer->resize(static_cast<int>(dim.x), static_cast<int>(dim.y));

				for (auto& layer : stage->layers)
				{
					if (layer->get_render_target() != nullptr)
					{
						auto texture = std::make_unique<Texture>(frame_buffer->width, frame_buffer->height);
						frame_buffer->initialize_draw_buffer(texture.get());
						layer->set_render_target(std::move(texture));
					}
				}
			}
		}
	}

	void Renderer2::initialize_render_stages(void)
	{
		// For now, we'll just have a hard-coded list of 2 stages.
		stages.push_back(std::make_unique<RenderStage2>(static_cast<int>(RenderStage::Scene)));
		stages.push_back(std::make_unique<RenderStage2>(static_cast<int>(RenderStage::Overlay)));
	}

	RenderLayer2* Renderer2::create_layer(const std::string& id, RenderStage2* stage, float priority, float parallax)
	{
		log->debug("Creating layer: {} [{} {}]", id, priority, parallax);
		auto layer = std::make_unique<RenderLayer2>(shader_cache,
			sprite_buffer.get(), particle_buffer.get(), id, priority, parallax);
		auto res = layer.get();
		bool inserted = false;
		layer_map.emplace(id, res);
		// find position to insert based on priority
		for (auto it = stage->layers.begin(); it != stage->layers.end(); ++it)
		{
			if ((*it)->priority > layer->priority)
			{
				stage->layers.insert(it, std::move(layer));
				inserted = true;
				break;
			}
		}
		// insert at the end
		if (!inserted)
			stage->layers.push_back(std::move(layer));
		return res;
	}

	RenderLayer2* Renderer2::create_composite_layer(const std::string& id, float priority, float parallax, bool has_render_target)
	{
		auto stage = stages.front().get(); // ok for now, since we're only supporting 2 stages
		auto layer = create_layer(id, stage, priority, parallax);
		if (has_render_target)
		{
			// create render target compatible with current frame buffer
			auto texture = std::make_unique<Texture>(frame_buffer->width, frame_buffer->height);
			frame_buffer->initialize_draw_buffer(texture.get());
			layer->set_render_target(std::move(texture));
		}
		return layer;
	}

	RenderLayer2* Renderer2::create_direct_layer(const std::string& id, float priority)
	{
		auto stage = stages.back().get(); // ok for now, since we're only supporting 2 stages
		auto layer = create_layer(id, stage, priority, 1.0f);
		layer->set_composite_program(nullptr); // by default overlay doesn't use compositing
		return layer;
	}

	void Renderer2::destroy_layer(const std::string& id)
	{
		if (!layer_map.erase(id))
			return; // layer not found

		for (auto& stage : stages)
		{
			auto it = std::find_if(stage->layers.begin(), stage->layers.end(), [&id](const std::unique_ptr<RenderLayer2>& layer) {
				return layer->id == id;
			});
			if (it != stage->layers.end())
			{
				stage->layers.erase(it);
				break;
			}
		}
	}

	void Renderer2::destroy_layers(void)
	{
		layer_map.clear();
		for (auto& stage : stages)
			stage->layers.clear();
	}

	RenderLayer2* Renderer2::get_layer(const std::string& id) const
	{
		if (layer_map.count(id))
			return layer_map.at(id);
		else
			return nullptr;
	}

	void Renderer2::add_to_layer(const std::string& id, Sprite* sprite)
	{
		auto layer = get_layer(id);
		if (layer == nullptr)
			log->warn("Invalid layer: {}", id);
		else
			layer->add(sprite);
	}

	void Renderer2::remove_from_layer(const std::string& id, Sprite* sprite)
	{
		auto layer = get_layer(id);
		if (layer == nullptr)
			log->warn("Invalid layer: {}", id);
		else
			layer->remove(sprite);
	}

	void Renderer2::set_camera(std::unique_ptr<Camera2> camera)
	{
		this->camera = std::move(camera);
		if (frame_buffer == nullptr || frame_buffer->texture->w != this->camera->transform.dimension.x
			|| frame_buffer->texture->h != this->camera->transform.dimension.y)
		{
			initialize_frame_buffers();
		}
	}

	void Renderer2::resize_window(void)
	{
		Renderer::resize_window();
		log->debug("Resizing screen buffer to {}x{}", window->get_width(), window->get_height());
		screen_buffer->resize(window->get_width(), window->get_height());
	}

	void Renderer2::render_layer(RenderLayer2& layer, FrameBuffer* target_buffer)
	{
		// Each layer can either render directly to the global screen buffer,
		// or alternatively render to a dedicated frame buffer followed by
		// a composite pass to merge the frame buffer into the screen buffer
		auto comp_program = layer.get_composite_program();
		auto render_target = layer.get_render_target();
		if (comp_program != nullptr)
		{
			frame_buffer->bind();
			if (render_target != nullptr)
				frame_buffer->attach_draw_buffer(render_target);
			clear();
		}

		layer.render(this);

		// Composite pass
		if (comp_program != nullptr)
		{
			if (render_target != nullptr)
				frame_buffer->detach_draw_buffer();
			render_composite(target_buffer, comp_program, layer.get_composite_binder(),
				render_target ? render_target : frame_buffer->texture.get());
		}
	}

	void Renderer2::render_composite(FrameBuffer* target_buffer, ShaderProgram* comp_program, ShaderBinder comp_binder, Texture* source_tex)
	{
		// Swith to screen buffer
		target_buffer->bind();
		switch_shader(comp_program);
		auto id = comp_program->attr("u_aspect");
		if (id != -1)
			comp_program->set(id, camera->get_aspect_ratio());
		source_tex->bind(0, comp_program);
		if (comp_binder)
			comp_binder(comp_program);
		// Finally, render composite image to screen buffer
		quad->render(comp_program);
	}

	void Renderer2::render(void)
	{
		// Call glFinish to avoid buffer updates on older Intel GPU.
		// The goal is to wait until all pending render operations of
		// the previous frame have finished.
		if (check_flag(render_flags, ForceSync) && window->is_fullscreen())
			glFinish();

		// Bind and clear screenbuffer.
		screen_buffer->bind();
		clear();

#if OPENGL_VERSION >= 30
		// This will pick up the change we made to camera transform. Since we're
		// using uniform buffers, this will only be done once each frame.
		update_uniforms();
#endif

		// Rendering of layers supports the following use case:
		// 1. Direct layer in a stage without composite program
		//	  Will render the layer directly to the screen buffer.
		// 2. Composite layer in a stage without composite program
		//    Will render the layer to the internal frame buffer before
		//    using the layer's program to composite it onto the screen buffer.
		// 3. Composite layer in a stage with a composite program.
		//	  Similar to #2, but will render all layers within the same stage
		//    onto a shared frame buffer. Then, will use the stage's program
		//    to composite the buffer onto the screen buffer.

		for (auto& stage : stages)
		{
			FrameBuffer* target_buffer;
			if (stage->composite_program != nullptr)
			{
				target_buffer = stage->frame_buffer.get();
				target_buffer->bind();
				clear();
			}
			else
			{
				target_buffer = screen_buffer.get();
			}

			for (auto& layer : stage->layers)
			{
				if (!layer->visible())
					continue;
				render_layer(*layer, target_buffer);
			}

			if (stage->composite_program != nullptr)
			{
				// render from stage fbo to screen buffer
				render_composite(screen_buffer.get(), stage->composite_program, stage->composite_binder,
					stage->frame_buffer->texture.get());
			}
		}

		render_screenbuffer();

#if OPENGL_VERSION < 30
		// invalidate active program to force uniforms rebind during
		// next frame
		active_program = 0;
#endif
	}

	void Renderer2::render_screenbuffer(void)
	{
		screen_buffer->unbind();
		if (check_flag(render_flags, ForceClear))
			clear(); // clean actual screen
		if (check_flag(render_flags, GammaCorrect))
			glEnable(GL_FRAMEBUFFER_SRGB);
		switch_shader(composite_program);
		screen_buffer->texture->bind(0, composite_program);
		if (composite_binder != nullptr)
			composite_binder(composite_program);
		quad->render(composite_program);
		if (check_flag(render_flags, GammaCorrect))
			glDisable(GL_FRAMEBUFFER_SRGB);
		window->present();
	}

	RenderStage2* Renderer2::get_stage(RenderStage id) const
	{
		for (const auto& stage : stages)
		{
			if (static_cast<int>(id) == stage->id)
				return stage.get();
		}
		return nullptr;
	}

	void Renderer2::update_uniforms(void)
	{
#if OPENGL_VERSION >= 30
		// Update uniform buffers
		glBindBufferBase(GL_UNIFORM_BUFFER, static_cast<GLuint>(UniformBuffer::Camera), uniform_buffers->buffers[static_cast<int>(UniformBuffer::Camera)]);
		glBufferData(GL_UNIFORM_BUFFER, sizeof(CameraTransform2), &camera->transform, GL_STREAM_DRAW);
		glBindBufferBase(GL_UNIFORM_BUFFER, static_cast<GLuint>(UniformBuffer::Light), uniform_buffers->buffers[static_cast<int>(UniformBuffer::Light)]);
		glBufferData(GL_UNIFORM_BUFFER, max_lights * sizeof(Light2), &lights, GL_STREAM_DRAW);
#else
		// Update uniform variables
		glUniformMatrix4fv(active_program->attr(Renderer2::u_cam_proj_orth), 1, false, camera->transform.mat_proj_orth.m);
		glUniformMatrix4fv(active_program->attr(Renderer2::u_cam_view), 1, false, camera->transform.mat_view.m);
		glUniform2fv(active_program->attr(Renderer2::u_cam_position), 1, (GLfloat*)(&camera->transform.position));
		glUniform2fv(active_program->attr(Renderer2::u_cam_dimension), 1, (GLfloat*)(&camera->transform.dimension));
#endif
	}
	void Renderer2::set_composite_program(ShaderProgram* composite_program, std::function<void(ShaderProgram*)> composite_binder)
	{
		this->composite_program = composite_program;
		this->composite_binder = composite_binder;
	}
}
<commit_msg>Clear after swap to improve latency on Intel cards<commit_after>#include "stdafx.h"
#include <dukat/camera2.h>
#include <dukat/log.h>
#include <dukat/renderer2.h>
#include <dukat/renderlayer2.h>
#include <dukat/shadercache.h>
#include <dukat/sprite.h>
#include <dukat/vertextypes2.h>
#include <dukat/sysutil.h>
#include <dukat/meshbuilder2.h>
#include <dukat/meshdata.h>
#include <dukat/textureutil.h>

namespace dukat
{
	Renderer2::Renderer2(Window* window, ShaderCache* shader_cache) : Renderer(window, shader_cache),
		composite_program(nullptr), composite_binder(nullptr), render_flags(RenderFx | RenderSprites | RenderParticles | RenderText | ForceClear)
	{
		// Enable transparency
		set_blending(true);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		// Disable depth test - we'll have to take care of rendering order ourselves
		glDisable(GL_DEPTH_TEST);
#ifdef OPENGL_CORE
        // Enable gl_PointSize instruction in shader
		glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
#endif

		initialize_sprite_buffers();
		initialize_particle_buffers();
		initialize_frame_buffers();
		initialize_render_stages();

		composite_program = shader_cache->get_program("fx_default.vsh", "fx_default.fsh");

		MeshBuilder2 builder;
		quad = builder.build_textured_quad();

		for (auto& l : lights)
		{
			l.position = Vector2{ 0, 0 };
			l.k1 = l.k2 = 0.0f;
		}

		gl_check_error();
	}

	void Renderer2::initialize_sprite_buffers(void)
	{
		// Vertex order:
		//	0 2
		//	1 3
		Vertex2PT vertices[] = {
			{ -0.5f, -0.5f, 0.0f, 0.0f },	// top-left
			{ -0.5f,  0.5f, 0.0f, 1.0f },	// bottom-left
			{  0.5f, -0.5f, 1.0f, 0.0f },	// top-right
			{  0.5f,  0.5f, 1.0f, 1.0f }	// bottom-right
		};
		// Create buffer for sprite rendering
		sprite_buffer = std::make_unique<VertexBuffer>(1);
		sprite_buffer->load_data(0, GL_ARRAY_BUFFER, 4, sizeof(Vertex2PT), vertices, GL_STATIC_DRAW);
	}

	void Renderer2::initialize_particle_buffers(void)
	{
		// Create buffer for particle rendering
		particle_buffer = std::make_unique<VertexBuffer>(1);
		particle_buffer->load_data(0, GL_ARRAY_BUFFER, max_particles, sizeof(Vertex2PSC), nullptr, GL_STREAM_DRAW);
	}

	void Renderer2::initialize_frame_buffers(void)
	{
		if (camera == nullptr)
		{
			const auto width = window->get_width();
			const auto height = window->get_height();
			log->trace("Initializing frame buffers: {}x{}", width, height);
			frame_buffer = std::make_unique<FrameBuffer>(width, height, true, false, TextureFilterProfile::ProfileNearest);
			screen_buffer = std::make_unique<FrameBuffer>(width, height, true, false, TextureFilterProfile::ProfileNearest);
		}
		else
		{
			const auto& dim = camera->transform.dimension;
			log->trace("Resizing frame buffers: {}x{}", static_cast<int>(dim.x), static_cast<int>(dim.y));
			frame_buffer->resize(static_cast<int>(dim.x), static_cast<int>(dim.y));

			// recreate intermediate render targets
			for (auto& stage : stages)
			{
				if (stage->frame_buffer != nullptr)
					stage->frame_buffer->resize(static_cast<int>(dim.x), static_cast<int>(dim.y));

				for (auto& layer : stage->layers)
				{
					if (layer->get_render_target() != nullptr)
					{
						auto texture = std::make_unique<Texture>(frame_buffer->width, frame_buffer->height);
						frame_buffer->initialize_draw_buffer(texture.get());
						layer->set_render_target(std::move(texture));
					}
				}
			}
		}
	}

	void Renderer2::initialize_render_stages(void)
	{
		// For now, we'll just have a hard-coded list of 2 stages.
		stages.push_back(std::make_unique<RenderStage2>(static_cast<int>(RenderStage::Scene)));
		stages.push_back(std::make_unique<RenderStage2>(static_cast<int>(RenderStage::Overlay)));
	}

	RenderLayer2* Renderer2::create_layer(const std::string& id, RenderStage2* stage, float priority, float parallax)
	{
		log->debug("Creating layer: {} [{} {}]", id, priority, parallax);
		auto layer = std::make_unique<RenderLayer2>(shader_cache,
			sprite_buffer.get(), particle_buffer.get(), id, priority, parallax);
		auto res = layer.get();
		bool inserted = false;
		layer_map.emplace(id, res);
		// find position to insert based on priority
		for (auto it = stage->layers.begin(); it != stage->layers.end(); ++it)
		{
			if ((*it)->priority > layer->priority)
			{
				stage->layers.insert(it, std::move(layer));
				inserted = true;
				break;
			}
		}
		// insert at the end
		if (!inserted)
			stage->layers.push_back(std::move(layer));
		return res;
	}

	RenderLayer2* Renderer2::create_composite_layer(const std::string& id, float priority, float parallax, bool has_render_target)
	{
		auto stage = stages.front().get(); // ok for now, since we're only supporting 2 stages
		auto layer = create_layer(id, stage, priority, parallax);
		if (has_render_target)
		{
			// create render target compatible with current frame buffer
			auto texture = std::make_unique<Texture>(frame_buffer->width, frame_buffer->height);
			frame_buffer->initialize_draw_buffer(texture.get());
			layer->set_render_target(std::move(texture));
		}
		return layer;
	}

	RenderLayer2* Renderer2::create_direct_layer(const std::string& id, float priority)
	{
		auto stage = stages.back().get(); // ok for now, since we're only supporting 2 stages
		auto layer = create_layer(id, stage, priority, 1.0f);
		layer->set_composite_program(nullptr); // by default overlay doesn't use compositing
		return layer;
	}

	void Renderer2::destroy_layer(const std::string& id)
	{
		if (!layer_map.erase(id))
			return; // layer not found

		for (auto& stage : stages)
		{
			auto it = std::find_if(stage->layers.begin(), stage->layers.end(), [&id](const std::unique_ptr<RenderLayer2>& layer) {
				return layer->id == id;
			});
			if (it != stage->layers.end())
			{
				stage->layers.erase(it);
				break;
			}
		}
	}

	void Renderer2::destroy_layers(void)
	{
		layer_map.clear();
		for (auto& stage : stages)
			stage->layers.clear();
	}

	RenderLayer2* Renderer2::get_layer(const std::string& id) const
	{
		if (layer_map.count(id))
			return layer_map.at(id);
		else
			return nullptr;
	}

	void Renderer2::add_to_layer(const std::string& id, Sprite* sprite)
	{
		auto layer = get_layer(id);
		if (layer == nullptr)
			log->warn("Invalid layer: {}", id);
		else
			layer->add(sprite);
	}

	void Renderer2::remove_from_layer(const std::string& id, Sprite* sprite)
	{
		auto layer = get_layer(id);
		if (layer == nullptr)
			log->warn("Invalid layer: {}", id);
		else
			layer->remove(sprite);
	}

	void Renderer2::set_camera(std::unique_ptr<Camera2> camera)
	{
		this->camera = std::move(camera);
		if (frame_buffer == nullptr || frame_buffer->texture->w != this->camera->transform.dimension.x
			|| frame_buffer->texture->h != this->camera->transform.dimension.y)
		{
			initialize_frame_buffers();
		}
	}

	void Renderer2::resize_window(void)
	{
		Renderer::resize_window();
		log->debug("Resizing screen buffer to {}x{}", window->get_width(), window->get_height());
		screen_buffer->resize(window->get_width(), window->get_height());
	}

	void Renderer2::render_layer(RenderLayer2& layer, FrameBuffer* target_buffer)
	{
		// Each layer can either render directly to the global screen buffer,
		// or alternatively render to a dedicated frame buffer followed by
		// a composite pass to merge the frame buffer into the screen buffer
		auto comp_program = layer.get_composite_program();
		auto render_target = layer.get_render_target();
		if (comp_program != nullptr)
		{
			frame_buffer->bind();
			if (render_target != nullptr)
				frame_buffer->attach_draw_buffer(render_target);
			clear();
		}

		layer.render(this);

		// Composite pass
		if (comp_program != nullptr)
		{
			if (render_target != nullptr)
				frame_buffer->detach_draw_buffer();
			render_composite(target_buffer, comp_program, layer.get_composite_binder(),
				render_target ? render_target : frame_buffer->texture.get());
		}
	}

	void Renderer2::render_composite(FrameBuffer* target_buffer, ShaderProgram* comp_program, ShaderBinder comp_binder, Texture* source_tex)
	{
		// Swith to screen buffer
		target_buffer->bind();
		switch_shader(comp_program);
		auto id = comp_program->attr("u_aspect");
		if (id != -1)
			comp_program->set(id, camera->get_aspect_ratio());
		source_tex->bind(0, comp_program);
		if (comp_binder)
			comp_binder(comp_program);
		// Finally, render composite image to screen buffer
		quad->render(comp_program);
	}

	void Renderer2::render(void)
	{
		// Call glFinish to avoid buffer updates on older Intel GPU.
		// The goal is to wait until all pending render operations of
		// the previous frame have finished.
		if (check_flag(render_flags, ForceSync) && window->is_fullscreen())
			glFinish();

		// Bind and clear screenbuffer.
		screen_buffer->bind();
		clear();

#if OPENGL_VERSION >= 30
		// This will pick up the change we made to camera transform. Since we're
		// using uniform buffers, this will only be done once each frame.
		update_uniforms();
#endif

		// Rendering of layers supports the following use case:
		// 1. Direct layer in a stage without composite program
		//	  Will render the layer directly to the screen buffer.
		// 2. Composite layer in a stage without composite program
		//    Will render the layer to the internal frame buffer before
		//    using the layer's program to composite it onto the screen buffer.
		// 3. Composite layer in a stage with a composite program.
		//	  Similar to #2, but will render all layers within the same stage
		//    onto a shared frame buffer. Then, will use the stage's program
		//    to composite the buffer onto the screen buffer.

		for (auto& stage : stages)
		{
			FrameBuffer* target_buffer;
			if (stage->composite_program != nullptr)
			{
				target_buffer = stage->frame_buffer.get();
				target_buffer->bind();
				clear();
			}
			else
			{
				target_buffer = screen_buffer.get();
			}

			for (auto& layer : stage->layers)
			{
				if (!layer->visible())
					continue;
				render_layer(*layer, target_buffer);
			}

			if (stage->composite_program != nullptr)
			{
				// render from stage fbo to screen buffer
				render_composite(screen_buffer.get(), stage->composite_program, stage->composite_binder,
					stage->frame_buffer->texture.get());
			}
		}

		render_screenbuffer();

#if OPENGL_VERSION < 30
		// invalidate active program to force uniforms rebind during
		// next frame
		active_program = 0;
#endif
	}

	void Renderer2::render_screenbuffer(void)
	{
		screen_buffer->unbind();
		if (check_flag(render_flags, GammaCorrect))
			glEnable(GL_FRAMEBUFFER_SRGB);
		switch_shader(composite_program);
		screen_buffer->texture->bind(0, composite_program);
		if (composite_binder != nullptr)
			composite_binder(composite_program);
		quad->render(composite_program);
		if (check_flag(render_flags, GammaCorrect))
			glDisable(GL_FRAMEBUFFER_SRGB);
		window->present();
		if (check_flag(render_flags, ForceClear))
			clear(); // clean actual screen
	}

	RenderStage2* Renderer2::get_stage(RenderStage id) const
	{
		for (const auto& stage : stages)
		{
			if (static_cast<int>(id) == stage->id)
				return stage.get();
		}
		return nullptr;
	}

	void Renderer2::update_uniforms(void)
	{
#if OPENGL_VERSION >= 30
		// Update uniform buffers
		glBindBufferBase(GL_UNIFORM_BUFFER, static_cast<GLuint>(UniformBuffer::Camera), uniform_buffers->buffers[static_cast<int>(UniformBuffer::Camera)]);
		glBufferData(GL_UNIFORM_BUFFER, sizeof(CameraTransform2), &camera->transform, GL_STREAM_DRAW);
		glBindBufferBase(GL_UNIFORM_BUFFER, static_cast<GLuint>(UniformBuffer::Light), uniform_buffers->buffers[static_cast<int>(UniformBuffer::Light)]);
		glBufferData(GL_UNIFORM_BUFFER, max_lights * sizeof(Light2), &lights, GL_STREAM_DRAW);
#else
		// Update uniform variables
		glUniformMatrix4fv(active_program->attr(Renderer2::u_cam_proj_orth), 1, false, camera->transform.mat_proj_orth.m);
		glUniformMatrix4fv(active_program->attr(Renderer2::u_cam_view), 1, false, camera->transform.mat_view.m);
		glUniform2fv(active_program->attr(Renderer2::u_cam_position), 1, (GLfloat*)(&camera->transform.position));
		glUniform2fv(active_program->attr(Renderer2::u_cam_dimension), 1, (GLfloat*)(&camera->transform.dimension));
#endif
	}
	void Renderer2::set_composite_program(ShaderProgram* composite_program, std::function<void(ShaderProgram*)> composite_binder)
	{
		this->composite_program = composite_program;
		this->composite_binder = composite_binder;
	}
}
<|endoftext|>


<commit_before>#include <string>
#include <vector>
#include <time.h>
#include "../glsl_optimizer.h"

#ifdef _MSC_VER
#include <windows.h>
#include <gl/GL.h>
extern "C" {
typedef char GLcharARB;		/* native character */
typedef unsigned int GLhandleARB;	/* shader object handle */
#define GL_VERTEX_SHADER_ARB              0x8B31
#define GL_FRAGMENT_SHADER_ARB            0x8B30
#define GL_OBJECT_COMPILE_STATUS_ARB      0x8B81
typedef void (WINAPI * PFNGLDELETEOBJECTARBPROC) (GLhandleARB obj);
typedef GLhandleARB (WINAPI * PFNGLCREATESHADEROBJECTARBPROC) (GLenum shaderType);
typedef void (WINAPI * PFNGLSHADERSOURCEARBPROC) (GLhandleARB shaderObj, GLsizei count, const GLcharARB* *string, const GLint *length);
typedef void (WINAPI * PFNGLCOMPILESHADERARBPROC) (GLhandleARB shaderObj);
typedef void (WINAPI * PFNGLGETINFOLOGARBPROC) (GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *infoLog);
typedef void (WINAPI * PFNGLGETOBJECTPARAMETERIVARBPROC) (GLhandleARB obj, GLenum pname, GLint *params);
static PFNGLDELETEOBJECTARBPROC glDeleteObjectARB;
static PFNGLCREATESHADEROBJECTARBPROC glCreateShaderObjectARB;
static PFNGLSHADERSOURCEARBPROC glShaderSourceARB;
static PFNGLCOMPILESHADERARBPROC glCompileShaderARB;
static PFNGLGETINFOLOGARBPROC glGetInfoLogARB;
static PFNGLGETOBJECTPARAMETERIVARBPROC glGetObjectParameterivARB;
}
#else
#include <OpenGL/OpenGL.h>
#include <AGL/agl.h>
#include <dirent.h>
#endif

static bool InitializeOpenGL ()
{
	bool hasGLSL = false;

#ifdef _MSC_VER
	// setup minimal required GL
	HWND wnd = CreateWindowA(
		"STATIC",
		"GL",
		WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS |	WS_CLIPCHILDREN,
		0, 0, 16, 16,
		NULL, NULL,
		GetModuleHandle(NULL), NULL );
	HDC dc = GetDC( wnd );

	PIXELFORMATDESCRIPTOR pfd = {
		sizeof(PIXELFORMATDESCRIPTOR), 1,
		PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL,
		PFD_TYPE_RGBA, 32,
		0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0,
		16, 0,
		0, PFD_MAIN_PLANE, 0, 0, 0, 0
	};

	int fmt = ChoosePixelFormat( dc, &pfd );
	SetPixelFormat( dc, fmt, &pfd );

	HGLRC rc = wglCreateContext( dc );
	wglMakeCurrent( dc, rc );

#else
	GLint attributes[16];
	int i = 0;
	attributes[i++]=AGL_RGBA;
	attributes[i++]=AGL_PIXEL_SIZE;
	attributes[i++]=32;
	attributes[i++]=AGL_NO_RECOVERY;
	attributes[i++]=AGL_NONE;

	AGLPixelFormat pixelFormat = aglChoosePixelFormat(NULL,0,attributes);
	AGLContext agl = aglCreateContext(pixelFormat, NULL);
	aglSetCurrentContext (agl);

#endif

	// check if we have GLSL
	const char* extensions = (const char*)glGetString(GL_EXTENSIONS);
	hasGLSL = strstr(extensions, "GL_ARB_shader_objects") && strstr(extensions, "GL_ARB_vertex_shader") && strstr(extensions, "GL_ARB_fragment_shader");

#ifdef _MSC_VER
	if (hasGLSL)
	{
		glDeleteObjectARB = (PFNGLDELETEOBJECTARBPROC)wglGetProcAddress("glDeleteObjectARB");
		glCreateShaderObjectARB = (PFNGLCREATESHADEROBJECTARBPROC)wglGetProcAddress("glCreateShaderObjectARB");
		glShaderSourceARB = (PFNGLSHADERSOURCEARBPROC)wglGetProcAddress("glShaderSourceARB");
		glCompileShaderARB = (PFNGLCOMPILESHADERARBPROC)wglGetProcAddress("glCompileShaderARB");
		glGetInfoLogARB = (PFNGLGETINFOLOGARBPROC)wglGetProcAddress("glGetInfoLogARB");
		glGetObjectParameterivARB = (PFNGLGETOBJECTPARAMETERIVARBPROC)wglGetProcAddress("glGetObjectParameterivARB");
	}
#endif

	return hasGLSL;
}

static bool CheckGLSL (bool vertex, bool gles, const char* prefix, const std::string& source)
{
	std::string src;
	if (gles)
	{
		src += "#define lowp\n";
		src += "#define mediump\n";
		src += "#define highp\n";
	}
	src += source;
	const char* sourcePtr = src.c_str();

	GLhandleARB shader = glCreateShaderObjectARB (vertex ? GL_VERTEX_SHADER_ARB : GL_FRAGMENT_SHADER_ARB);
	glShaderSourceARB (shader, 1, &sourcePtr, NULL);
	glCompileShaderARB (shader);
	GLint status;
	glGetObjectParameterivARB (shader, GL_OBJECT_COMPILE_STATUS_ARB, &status);
	bool res = true;
	if (status == 0)
	{
		char log[4096];
		GLsizei logLength;
		glGetInfoLogARB (shader, sizeof(log), &logLength, log);
		printf ("  glsl compile error on %s:\n%s\n", prefix, log);
		res = false;
	}
	glDeleteObjectARB (shader);
	return res;
}

static bool ReadStringFromFile (const char* pathName, std::string& output)
{
	FILE* file = fopen( pathName, "rb" );
	if (file == NULL)
		return false;
	fseek(file, 0, SEEK_END);
	int length = ftell(file);
	fseek(file, 0, SEEK_SET);
	if (length < 0)
	{
		fclose( file );
		return false;
	}
	output.resize(length);
	int readLength = fread(&*output.begin(), 1, length, file);
	fclose(file);
	if (readLength != length)
	{
		output.clear();
		return false;
	}
	return true;
}

bool EndsWith (const std::string& str, const std::string& sub)
{
	return (str.size() >= sub.size()) && (strncmp (str.c_str()+str.size()-sub.size(), sub.c_str(), sub.size())==0);
}

typedef std::vector<std::string> StringVector;

static StringVector GetFiles (const std::string& folder, const std::string& endsWith)
{
	StringVector res;

	#ifdef _MSC_VER
	WIN32_FIND_DATAA FindFileData;
	HANDLE hFind = FindFirstFileA ((folder+"/*"+endsWith).c_str(), &FindFileData);
	if (hFind == INVALID_HANDLE_VALUE)
		return res;

	do {
		res.push_back (FindFileData.cFileName);
	} while (FindNextFileA (hFind, &FindFileData));

	FindClose (hFind);

	#else

	DIR *dirp;
	struct dirent *dp;

	if ((dirp = opendir(folder.c_str())) == NULL)
		return res;

	while ( (dp = readdir(dirp)) )
	{
		std::string fname = dp->d_name;
		if (fname == "." || fname == "..")
			continue;
		if (!EndsWith (fname, endsWith))
			continue;
		res.push_back (fname);
	}
	closedir(dirp);

	#endif

	return res;
}

static void DeleteFile (const std::string& path)
{
	#ifdef _MSC_VER
	DeleteFileA (path.c_str());
	#else
	unlink (path.c_str());
	#endif
}

static void MassageVertexForGLES (std::string& s)
{
	std::string pre;
	pre += "#define gl_Vertex _glesVertex\nattribute highp vec4 _glesVertex;\n";
	pre += "#define gl_Normal _glesNormal\nattribute mediump vec3 _glesNormal;\n";
	pre += "#define gl_MultiTexCoord0 _glesMultiTexCoord0\nattribute highp vec4 _glesMultiTexCoord0;\n";
	pre += "#define gl_MultiTexCoord1 _glesMultiTexCoord1\nattribute highp vec4 _glesMultiTexCoord1;\n";
	pre += "#define gl_Color _glesColor\nattribute lowp vec4 _glesColor;\n";
	s = pre + s;
}

static void MassageFragmentForGLES (std::string& s)
{
	std::string pre;
	s = pre + s;
}

static bool TestFile (glslopt_ctx* ctx, bool vertex,
	const std::string& inputPath,
	const std::string& hirPath,
	const std::string& outputPath,
	bool gles,
	bool doCheckGLSL)
{
	std::string input;
	if (!ReadStringFromFile (inputPath.c_str(), input))
	{
		printf ("  failed to read input file\n");
		return false;
	}
	if (doCheckGLSL)
	{
		if (!CheckGLSL (vertex, gles, "input", input.c_str()))
			return false;
	}

	if (gles)
	{
		if (vertex)
			MassageVertexForGLES (input);
		else
			MassageFragmentForGLES (input);
	}

	bool res = true;

	glslopt_shader_type type = vertex ? kGlslOptShaderVertex : kGlslOptShaderFragment;
	glslopt_shader* shader = glslopt_optimize (ctx, type, input.c_str(), 0);

	bool optimizeOk = glslopt_get_status(shader);
	if (optimizeOk)
	{
		std::string textHir = glslopt_get_raw_output (shader);
		std::string textOpt = glslopt_get_output (shader);
		std::string outputHir;
		ReadStringFromFile (hirPath.c_str(), outputHir);
		std::string outputOpt;
		ReadStringFromFile (outputPath.c_str(), outputOpt);

		if (textHir != outputHir)
		{
			// write output
			FILE* f = fopen (hirPath.c_str(), "wb");
			fwrite (textHir.c_str(), 1, textHir.size(), f);
			fclose (f);
			printf ("  does not match raw output\n");
			res = false;
		}

		if (textOpt != outputOpt)
		{
			// write output
			FILE* f = fopen (outputPath.c_str(), "wb");
			fwrite (textOpt.c_str(), 1, textOpt.size(), f);
			fclose (f);
			printf ("  does not match optimized output\n");
			res = false;
		}
		if (res && doCheckGLSL && !CheckGLSL (vertex, gles, "raw", textHir.c_str()))
			res = false;
		if (res && doCheckGLSL && !CheckGLSL (vertex, gles, "optimized", textOpt.c_str()))
			res = false;
	}
	else
	{
		printf ("  optimize error: %s\n", glslopt_get_log(shader));
		res = false;
	}

	glslopt_shader_delete (shader);

	return res;
}

int main (int argc, const char** argv)
{
	if (argc < 2)
	{
		printf ("USAGE: glsloptimizer testfolder\n");
		return 1;
	}

	bool hasOpenGL = InitializeOpenGL ();
	glslopt_ctx* ctx[2] = {
		glslopt_initialize(true),
		glslopt_initialize(false),
	};

	std::string baseFolder = argv[1];

	clock_t time0 = clock();

	static const char* kTypeName[2] = { "vertex", "fragment" };
	size_t tests = 0;
	size_t errors = 0;
	for (int type = 0; type < 2; ++type)
	{
		std::string testFolder = baseFolder + "/" + kTypeName[type];

		static const char* kAPIName[2] = { "OpenGL ES 2.0", "OpenGL" };
		static const char* kApiIn [2] = {"-inES.txt", "-in.txt"};
		static const char* kApiIR [2] = {"-irES.txt", "-ir.txt"};
		static const char* kApiOut[2] = {"-outES.txt", "-out.txt"};
		for (int api = 0; api < 2; ++api)
		{
			printf ("** running %s tests for %s...\n", kTypeName[type], kAPIName[api]);
			StringVector inputFiles = GetFiles (testFolder, kApiIn[api]);

			size_t n = inputFiles.size();
			for (size_t i = 0; i < n; ++i)
			{
				std::string inname = inputFiles[i];
				printf ("test %s\n", inname.c_str());
				std::string hirname = inname.substr (0,inname.size()-strlen(kApiIn[api])) + kApiIR[api];
				std::string outname = inname.substr (0,inname.size()-strlen(kApiIn[api])) + kApiOut[api];
				bool ok = TestFile (ctx[api], type==0, testFolder + "/" + inname, testFolder + "/" + hirname, testFolder + "/" + outname, api==0, hasOpenGL);
				if (!ok)
				{
					++errors;
				}
				++tests;
			}
		}
	}
	clock_t time1 = clock();
	float timeDelta = float(time1-time0)/CLOCKS_PER_SEC;

	if (errors != 0)
		printf ("**** %i tests (%.2fsec), %i !!!FAILED!!!\n", tests, timeDelta, errors);
	else
		printf ("**** %i tests (%.2fsec) succeeded\n", tests, timeDelta);

	for (int i = 0; i < 2; ++i)
		glslopt_cleanup (ctx[i]);

	return errors ? 1 : 0;
}
<commit_msg>tests: more resistant to file write failures<commit_after>#include <string>
#include <vector>
#include <time.h>
#include "../glsl_optimizer.h"

#ifdef _MSC_VER
#include <windows.h>
#include <gl/GL.h>
extern "C" {
typedef char GLcharARB;		/* native character */
typedef unsigned int GLhandleARB;	/* shader object handle */
#define GL_VERTEX_SHADER_ARB              0x8B31
#define GL_FRAGMENT_SHADER_ARB            0x8B30
#define GL_OBJECT_COMPILE_STATUS_ARB      0x8B81
typedef void (WINAPI * PFNGLDELETEOBJECTARBPROC) (GLhandleARB obj);
typedef GLhandleARB (WINAPI * PFNGLCREATESHADEROBJECTARBPROC) (GLenum shaderType);
typedef void (WINAPI * PFNGLSHADERSOURCEARBPROC) (GLhandleARB shaderObj, GLsizei count, const GLcharARB* *string, const GLint *length);
typedef void (WINAPI * PFNGLCOMPILESHADERARBPROC) (GLhandleARB shaderObj);
typedef void (WINAPI * PFNGLGETINFOLOGARBPROC) (GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *infoLog);
typedef void (WINAPI * PFNGLGETOBJECTPARAMETERIVARBPROC) (GLhandleARB obj, GLenum pname, GLint *params);
static PFNGLDELETEOBJECTARBPROC glDeleteObjectARB;
static PFNGLCREATESHADEROBJECTARBPROC glCreateShaderObjectARB;
static PFNGLSHADERSOURCEARBPROC glShaderSourceARB;
static PFNGLCOMPILESHADERARBPROC glCompileShaderARB;
static PFNGLGETINFOLOGARBPROC glGetInfoLogARB;
static PFNGLGETOBJECTPARAMETERIVARBPROC glGetObjectParameterivARB;
}
#else
#include <OpenGL/OpenGL.h>
#include <AGL/agl.h>
#include <dirent.h>
#endif

static bool InitializeOpenGL ()
{
	bool hasGLSL = false;

#ifdef _MSC_VER
	// setup minimal required GL
	HWND wnd = CreateWindowA(
		"STATIC",
		"GL",
		WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS |	WS_CLIPCHILDREN,
		0, 0, 16, 16,
		NULL, NULL,
		GetModuleHandle(NULL), NULL );
	HDC dc = GetDC( wnd );

	PIXELFORMATDESCRIPTOR pfd = {
		sizeof(PIXELFORMATDESCRIPTOR), 1,
		PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL,
		PFD_TYPE_RGBA, 32,
		0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0,
		16, 0,
		0, PFD_MAIN_PLANE, 0, 0, 0, 0
	};

	int fmt = ChoosePixelFormat( dc, &pfd );
	SetPixelFormat( dc, fmt, &pfd );

	HGLRC rc = wglCreateContext( dc );
	wglMakeCurrent( dc, rc );

#else
	GLint attributes[16];
	int i = 0;
	attributes[i++]=AGL_RGBA;
	attributes[i++]=AGL_PIXEL_SIZE;
	attributes[i++]=32;
	attributes[i++]=AGL_NO_RECOVERY;
	attributes[i++]=AGL_NONE;

	AGLPixelFormat pixelFormat = aglChoosePixelFormat(NULL,0,attributes);
	AGLContext agl = aglCreateContext(pixelFormat, NULL);
	aglSetCurrentContext (agl);

#endif

	// check if we have GLSL
	const char* extensions = (const char*)glGetString(GL_EXTENSIONS);
	hasGLSL = strstr(extensions, "GL_ARB_shader_objects") && strstr(extensions, "GL_ARB_vertex_shader") && strstr(extensions, "GL_ARB_fragment_shader");

#ifdef _MSC_VER
	if (hasGLSL)
	{
		glDeleteObjectARB = (PFNGLDELETEOBJECTARBPROC)wglGetProcAddress("glDeleteObjectARB");
		glCreateShaderObjectARB = (PFNGLCREATESHADEROBJECTARBPROC)wglGetProcAddress("glCreateShaderObjectARB");
		glShaderSourceARB = (PFNGLSHADERSOURCEARBPROC)wglGetProcAddress("glShaderSourceARB");
		glCompileShaderARB = (PFNGLCOMPILESHADERARBPROC)wglGetProcAddress("glCompileShaderARB");
		glGetInfoLogARB = (PFNGLGETINFOLOGARBPROC)wglGetProcAddress("glGetInfoLogARB");
		glGetObjectParameterivARB = (PFNGLGETOBJECTPARAMETERIVARBPROC)wglGetProcAddress("glGetObjectParameterivARB");
	}
#endif

	return hasGLSL;
}

static bool CheckGLSL (bool vertex, bool gles, const char* prefix, const std::string& source)
{
	std::string src;
	if (gles)
	{
		src += "#define lowp\n";
		src += "#define mediump\n";
		src += "#define highp\n";
	}
	src += source;
	const char* sourcePtr = src.c_str();

	GLhandleARB shader = glCreateShaderObjectARB (vertex ? GL_VERTEX_SHADER_ARB : GL_FRAGMENT_SHADER_ARB);
	glShaderSourceARB (shader, 1, &sourcePtr, NULL);
	glCompileShaderARB (shader);
	GLint status;
	glGetObjectParameterivARB (shader, GL_OBJECT_COMPILE_STATUS_ARB, &status);
	bool res = true;
	if (status == 0)
	{
		char log[4096];
		GLsizei logLength;
		glGetInfoLogARB (shader, sizeof(log), &logLength, log);
		printf ("  glsl compile error on %s:\n%s\n", prefix, log);
		res = false;
	}
	glDeleteObjectARB (shader);
	return res;
}

static bool ReadStringFromFile (const char* pathName, std::string& output)
{
	FILE* file = fopen( pathName, "rb" );
	if (file == NULL)
		return false;
	fseek(file, 0, SEEK_END);
	int length = ftell(file);
	fseek(file, 0, SEEK_SET);
	if (length < 0)
	{
		fclose( file );
		return false;
	}
	output.resize(length);
	int readLength = fread(&*output.begin(), 1, length, file);
	fclose(file);
	if (readLength != length)
	{
		output.clear();
		return false;
	}
	return true;
}

bool EndsWith (const std::string& str, const std::string& sub)
{
	return (str.size() >= sub.size()) && (strncmp (str.c_str()+str.size()-sub.size(), sub.c_str(), sub.size())==0);
}

typedef std::vector<std::string> StringVector;

static StringVector GetFiles (const std::string& folder, const std::string& endsWith)
{
	StringVector res;

	#ifdef _MSC_VER
	WIN32_FIND_DATAA FindFileData;
	HANDLE hFind = FindFirstFileA ((folder+"/*"+endsWith).c_str(), &FindFileData);
	if (hFind == INVALID_HANDLE_VALUE)
		return res;

	do {
		res.push_back (FindFileData.cFileName);
	} while (FindNextFileA (hFind, &FindFileData));

	FindClose (hFind);

	#else

	DIR *dirp;
	struct dirent *dp;

	if ((dirp = opendir(folder.c_str())) == NULL)
		return res;

	while ( (dp = readdir(dirp)) )
	{
		std::string fname = dp->d_name;
		if (fname == "." || fname == "..")
			continue;
		if (!EndsWith (fname, endsWith))
			continue;
		res.push_back (fname);
	}
	closedir(dirp);

	#endif

	return res;
}

static void DeleteFile (const std::string& path)
{
	#ifdef _MSC_VER
	DeleteFileA (path.c_str());
	#else
	unlink (path.c_str());
	#endif
}

static void MassageVertexForGLES (std::string& s)
{
	std::string pre;
	pre += "#define gl_Vertex _glesVertex\nattribute highp vec4 _glesVertex;\n";
	pre += "#define gl_Normal _glesNormal\nattribute mediump vec3 _glesNormal;\n";
	pre += "#define gl_MultiTexCoord0 _glesMultiTexCoord0\nattribute highp vec4 _glesMultiTexCoord0;\n";
	pre += "#define gl_MultiTexCoord1 _glesMultiTexCoord1\nattribute highp vec4 _glesMultiTexCoord1;\n";
	pre += "#define gl_Color _glesColor\nattribute lowp vec4 _glesColor;\n";
	s = pre + s;
}

static void MassageFragmentForGLES (std::string& s)
{
	std::string pre;
	s = pre + s;
}

static bool TestFile (glslopt_ctx* ctx, bool vertex,
	const std::string& inputPath,
	const std::string& hirPath,
	const std::string& outputPath,
	bool gles,
	bool doCheckGLSL)
{
	std::string input;
	if (!ReadStringFromFile (inputPath.c_str(), input))
	{
		printf ("  failed to read input file\n");
		return false;
	}
	if (doCheckGLSL)
	{
		if (!CheckGLSL (vertex, gles, "input", input.c_str()))
			return false;
	}

	if (gles)
	{
		if (vertex)
			MassageVertexForGLES (input);
		else
			MassageFragmentForGLES (input);
	}

	bool res = true;

	glslopt_shader_type type = vertex ? kGlslOptShaderVertex : kGlslOptShaderFragment;
	glslopt_shader* shader = glslopt_optimize (ctx, type, input.c_str(), 0);

	bool optimizeOk = glslopt_get_status(shader);
	if (optimizeOk)
	{
		std::string textHir = glslopt_get_raw_output (shader);
		std::string textOpt = glslopt_get_output (shader);
		std::string outputHir;
		ReadStringFromFile (hirPath.c_str(), outputHir);
		std::string outputOpt;
		ReadStringFromFile (outputPath.c_str(), outputOpt);

		if (textHir != outputHir)
		{
			// write output
			FILE* f = fopen (hirPath.c_str(), "wb");
			if (!f)
			{
				printf ("  can't write to IR file!\n");
			}
			else
			{
				fwrite (textHir.c_str(), 1, textHir.size(), f);
				fclose (f);
			}
			printf ("  does not match raw output\n");
			res = false;
		}

		if (textOpt != outputOpt)
		{
			// write output
			FILE* f = fopen (outputPath.c_str(), "wb");
			if (!f)
			{
				printf ("  can't write to optimized file!\n");
			}
			else
			{
				fwrite (textOpt.c_str(), 1, textOpt.size(), f);
				fclose (f);
			}
			printf ("  does not match optimized output\n");
			res = false;
		}
		if (res && doCheckGLSL && !CheckGLSL (vertex, gles, "raw", textHir.c_str()))
			res = false;
		if (res && doCheckGLSL && !CheckGLSL (vertex, gles, "optimized", textOpt.c_str()))
			res = false;
	}
	else
	{
		printf ("  optimize error: %s\n", glslopt_get_log(shader));
		res = false;
	}

	glslopt_shader_delete (shader);

	return res;
}

int main (int argc, const char** argv)
{
	if (argc < 2)
	{
		printf ("USAGE: glsloptimizer testfolder\n");
		return 1;
	}

	bool hasOpenGL = InitializeOpenGL ();
	glslopt_ctx* ctx[2] = {
		glslopt_initialize(true),
		glslopt_initialize(false),
	};

	std::string baseFolder = argv[1];

	clock_t time0 = clock();

	static const char* kTypeName[2] = { "vertex", "fragment" };
	size_t tests = 0;
	size_t errors = 0;
	for (int type = 0; type < 2; ++type)
	{
		std::string testFolder = baseFolder + "/" + kTypeName[type];

		static const char* kAPIName[2] = { "OpenGL ES 2.0", "OpenGL" };
		static const char* kApiIn [2] = {"-inES.txt", "-in.txt"};
		static const char* kApiIR [2] = {"-irES.txt", "-ir.txt"};
		static const char* kApiOut[2] = {"-outES.txt", "-out.txt"};
		for (int api = 0; api < 2; ++api)
		{
			printf ("** running %s tests for %s...\n", kTypeName[type], kAPIName[api]);
			StringVector inputFiles = GetFiles (testFolder, kApiIn[api]);

			size_t n = inputFiles.size();
			for (size_t i = 0; i < n; ++i)
			{
				std::string inname = inputFiles[i];
				printf ("test %s\n", inname.c_str());
				std::string hirname = inname.substr (0,inname.size()-strlen(kApiIn[api])) + kApiIR[api];
				std::string outname = inname.substr (0,inname.size()-strlen(kApiIn[api])) + kApiOut[api];
				bool ok = TestFile (ctx[api], type==0, testFolder + "/" + inname, testFolder + "/" + hirname, testFolder + "/" + outname, api==0, hasOpenGL);
				if (!ok)
				{
					++errors;
				}
				++tests;
			}
		}
	}
	clock_t time1 = clock();
	float timeDelta = float(time1-time0)/CLOCKS_PER_SEC;

	if (errors != 0)
		printf ("**** %i tests (%.2fsec), %i !!!FAILED!!!\n", tests, timeDelta, errors);
	else
		printf ("**** %i tests (%.2fsec) succeeded\n", tests, timeDelta);

	for (int i = 0; i < 2; ++i)
		glslopt_cleanup (ctx[i]);

	return errors ? 1 : 0;
}
<|endoftext|>