<commit_before>//------------------------------------------------------------------------------
//  winURLLoader.cc
//------------------------------------------------------------------------------
#include "Pre.h"
#include "winURLLoader.h"
#include "Core/String/StringConverter.h"
#include "IO/Stream/MemoryStream.h"
#define VC_EXTRALEAN (1)
#define WIN32_LEAN_AND_MEAN (1)
#include <Windows.h>
#include <WinHttp.h>

namespace Oryol {
namespace _priv {

const std::chrono::seconds winURLLoader::connectionMaxAge{10};

//------------------------------------------------------------------------------
winURLLoader::winURLLoader() :
contentTypeString("Content-Type")
{
    this->hSession = WinHttpOpen(NULL,      // pwszUserAgent
                                 WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,     // dwAccessType
                                 WINHTTP_NO_PROXY_NAME, // pwszProxyName
                                 WINHTTP_NO_PROXY_BYPASS,   // pwszProxyByPass
                                 0);        // dwFlags
    o_assert(NULL != this->hSession);
}

//------------------------------------------------------------------------------
winURLLoader::~winURLLoader() {
    o_assert(NULL != this->hSession);
    // close remaining connections
    for (const auto& kvp : this->connections) {
        WinHttpCloseHandle(kvp.Value().hConnection);
    }
    this->connections.Clear();
    WinHttpCloseHandle(this->hSession);
    this->hSession = NULL;
}

//------------------------------------------------------------------------------
void
winURLLoader::doWork() {
    while (!this->requestQueue.Empty()) {
        Ptr<HTTPProtocol::HTTPRequest> req = this->requestQueue.Dequeue();
        if (!baseURLLoader::handleCancelled(req)) {
            this->doOneRequest(req);

            // transfer result to embedded ioRequest and set to handled
            auto ioReq = req->GetIoRequest();
            if (ioReq) {
                auto httpResponse = req->GetResponse();
                ioReq->SetStatus(httpResponse->GetStatus());
                ioReq->SetStream(httpResponse->GetBody());
                ioReq->SetErrorDesc(httpResponse->GetErrorDesc());
                ioReq->SetHandled();
            }
            req->SetHandled();
        }
    }
    this->garbageCollectConnections();
}

//------------------------------------------------------------------------------
void
winURLLoader::doOneRequest(const Ptr<HTTPProtocol::HTTPRequest>& req) {
    // obtain a connection
    HINTERNET hConn = this->obtainConnection(req->GetURL());
    if (NULL != hConn) {
        WideString method(HTTPMethod::ToWideString(req->GetMethod()));
        WideString path(StringConverter::UTF8ToWide(req->GetURL().PathToEnd()));

        // setup an HTTP request
        HINTERNET hRequest = WinHttpOpenRequest(
            hConn,              // hConnect
            method.AsCStr(),    // pwszVerb
            path.AsCStr(),      // pwszObjectName
            NULL,               // pwszVersion (NULL == HTTP/1.1)
            WINHTTP_NO_REFERER, // pwszReferrer
            WINHTTP_DEFAULT_ACCEPT_TYPES,   // pwszAcceptTypes
            WINHTTP_FLAG_ESCAPE_PERCENT);   // dwFlags
        if (NULL != hRequest) {

            // add request headers to the request
            if (!req->GetRequestHeaders().Empty()) {
                for (const auto& kvp : req->GetRequestHeaders()) {
                    this->stringBuilder.Clear();
                    this->stringBuilder.Append(kvp.Key());
                    this->stringBuilder.Append(": ");
                    this->stringBuilder.Append(kvp.Value());
                    this->stringBuilder.Append("\r\n");
                }

                // check if we need to add a Content-Type field:
                if (req->GetBody().isValid() && req->GetBody()->GetContentType().IsValid()) {
                    this->stringBuilder.Append("Content-Type: ");
                    this->stringBuilder.Append(req->GetBody()->GetContentType().AsCStr());
                    this->stringBuilder.Append("\r\n");
                }

                // remove last CRLF
                this->stringBuilder.PopBack();
                this->stringBuilder.PopBack();
                WideString reqHeaders(StringConverter::UTF8ToWide(this->stringBuilder.GetString()));
                BOOL headerResult = WinHttpAddRequestHeaders(
                    hRequest,
                    reqHeaders.AsCStr(),
                    reqHeaders.Length(),
                    WINHTTP_ADDREQ_FLAG_ADD|WINHTTP_ADDREQ_FLAG_REPLACE);
                o_assert(headerResult);
            }

            // do we have body-data?
            const uint8* bodyDataPtr = WINHTTP_NO_REQUEST_DATA;
            int32 bodySize = 0;
            if (req->GetBody().isValid()) {
                const Ptr<Stream>& bodyStream = req->GetBody();
                bodySize = bodyStream->Size();
                o_assert(bodySize > 0);
                bodyStream->Open(OpenMode::ReadOnly);
                bodyDataPtr = bodyStream->MapRead(nullptr);
            }

            // create a response object, no matter whether the
            // send fails or not
            Ptr<HTTPProtocol::HTTPResponse> httpResponse = HTTPProtocol::HTTPResponse::Create();
            req->SetResponse(httpResponse);

            // send the request
            BOOL sendResult = WinHttpSendRequest(
                hRequest,                           // hRequest
                WINHTTP_NO_ADDITIONAL_HEADERS,      // pwszHeaders
                0,                                  // dwHeadersLength
                (LPVOID) bodyDataPtr,               // lpOptional
                bodySize,                           // dwOptionalLength
                0,                                  // dwTotoalLength
                0);                                 // dwContext
            if (sendResult) {
                // receive and process response
                BOOL recvResult = WinHttpReceiveResponse(hRequest, NULL);
                if (recvResult) {
                    // query the HTTP status code
                    DWORD dwStatusCode = 0;
                    DWORD dwTemp = sizeof(dwStatusCode);
                    WinHttpQueryHeaders(
                        hRequest,
                        WINHTTP_QUERY_STATUS_CODE|WINHTTP_QUERY_FLAG_NUMBER,
                        NULL,
                        &dwStatusCode,
                        &dwTemp,
                        NULL);
                    httpResponse->SetStatus((IOStatus::Code) dwStatusCode);

                    // query the response headers required data size
                    DWORD dwSize = 0;
                    WinHttpQueryHeaders(hRequest,
                                        WINHTTP_QUERY_RAW_HEADERS_CRLF,
                                        WINHTTP_HEADER_NAME_BY_INDEX,
                                        NULL,
                                        &dwSize,
                                        WINHTTP_NO_HEADER_INDEX);
                    if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
                        // and get the response headers
                        LPVOID headerBuffer = Memory::Alloc(dwSize);
                        BOOL headerResult = WinHttpQueryHeaders(
                            hRequest,
                            WINHTTP_QUERY_RAW_HEADERS_CRLF,
                            WINHTTP_HEADER_NAME_BY_INDEX,
                            headerBuffer,
                            &dwSize,
                            WINHTTP_NO_HEADER_INDEX);
                        o_assert(headerResult);

                        // convert from wide and split the header fields
                        this->stringBuilder.Set(StringConverter::WideToUTF8((const wchar_t*) headerBuffer, dwSize / sizeof(wchar_t)));
                        Memory::Free(headerBuffer);
                        Array<String> tokens;
                        this->stringBuilder.Tokenize("\r\n", tokens);
                        Map<String, String> fields;
                        fields.Reserve(tokens.Size());
                        for (const String& str : tokens) {
                            const int32 colonIndex = StringBuilder::FindFirstOf(str.AsCStr(), 0, EndOfString, ":");
                            if (colonIndex != InvalidIndex) {
                                String key(str.AsCStr(), 0, colonIndex);
                                String value(str.AsCStr(), colonIndex+2, EndOfString);
                                fields.Add(key, value);
                            }
                        }
                        httpResponse->SetResponseHeaders(fields);
                    }
                    else {
                        Log::Warn("winURLLoader: failed to extract response header fields!\n");
                    }

                    // extract body data
                    Ptr<MemoryStream> responseBodyStream = MemoryStream::Create();
                    responseBodyStream->SetURL(req->GetURL());
                    responseBodyStream->Open(OpenMode::WriteOnly);
                    DWORD bytesToRead = 0;
                    do {
                        // how much data available?
                        BOOL queryDataResult = WinHttpQueryDataAvailable(hRequest, &bytesToRead);
                        o_assert(queryDataResult);
                        if (dwSize > 0) {
                            uint8* dstPtr = responseBodyStream->MapWrite(bytesToRead);
                            o_assert(nullptr != dstPtr);
                            DWORD bytesRead = 0;
                            BOOL readDataResult = WinHttpReadData(hRequest, dstPtr, bytesToRead, &bytesRead);
                            o_assert(readDataResult);
                            o_assert(bytesRead == bytesToRead);
                            responseBodyStream->UnmapWrite();
                        }
                    }
                    while (bytesToRead > 0);
                    responseBodyStream->Close();

                    // extract the Content-Type response header field and set on responseBodyStream
                    if (httpResponse->GetResponseHeaders().Contains(this->contentTypeString)) {
                        ContentType contentType(httpResponse->GetResponseHeaders()[this->contentTypeString]);
                        responseBodyStream->SetContentType(contentType);
                    }

                    // ...and finally set the responseBodyStream on the httpResponse
                    httpResponse->SetBody(responseBodyStream);

                    // @todo: write error desc to httpResponse if something went wrong
                }
                else {
                    Log::Warn("winURLLoader: WinHttpReceiveResponse() failed for '%s'!\n", req->GetURL().AsCStr());
                }
            }
            else {
                /// @todo: better error reporting!
                Log::Warn("winURLLoader: WinHttpSendRequest() failed for '%s'\n", req->GetURL().AsCStr());
            }

            // unmap the body data if necessary
            if (req->GetBody().isValid()) {
                req->GetBody()->Close();
            }

            // close the request object
            WinHttpCloseHandle(hRequest);
        }
        else {
            Log::Warn("winURLLoader: WinHttpOpenRequest() failed for '%s'\n", req->GetURL().AsCStr());
        }
    }
}

//------------------------------------------------------------------------------
HINTERNET
winURLLoader::obtainConnection(const URL& url) {
    o_assert(NULL != this->hSession);
    const String hostAndPort = url.HostAndPort();
    if (this->connections.Contains(hostAndPort)) {
        // connection was already established, refresh the timestamp
        // and return the connection
        connection& con = this->connections[hostAndPort];
        con.timeStamp = std::chrono::steady_clock::now();
        return con.hConnection;
    }
    else {
        // new connection
        connection con;
        const WideString host = StringConverter::UTF8ToWide(url.Host());
        const String portString = url.Port();
        INTERNET_PORT port = INTERNET_DEFAULT_HTTP_PORT;
        if (!portString.Empty()) {
            port = StringConverter::FromString<int16>(portString);
        }
        con.hConnection = WinHttpConnect(this->hSession,        // hSession
                                          host.AsCStr(),        // pswzServerName
                                          port,                 // nServerPort
                                          0);                   // dwReserved
        if (NULL != con.hConnection) {
            con.timeStamp = std::chrono::steady_clock::now();
            this->connections.Add(hostAndPort, con);
            return con.hConnection;
        }
        else {
            Log::Warn("winURLLoader: failed to connect to '%s'\n", hostAndPort.AsCStr());
            return NULL;
        }
    }
}

//------------------------------------------------------------------------------
void
winURLLoader::garbageCollectConnections() {
    std::chrono::steady_clock::time_point curTime = std::chrono::steady_clock::now();
    for (int32 i = this->connections.Size() - 1; i >= 0; i--) {
        const String key = this->connections.KeyAtIndex(i);
        const connection con = this->connections.ValueAtIndex(i);
        std::chrono::seconds age = std::chrono::duration_cast<std::chrono::seconds>(curTime - con.timeStamp);
        if (age > connectionMaxAge) {
            o_assert(NULL != con.hConnection);
            WinHttpCloseHandle(con.hConnection);
            this->connections.EraseIndex(i);
        }
    }
}

} // namespace _priv
} // namespace Oryol
<commit_msg>Added some status output to winURLLoader<commit_after>//------------------------------------------------------------------------------
//  winURLLoader.cc
//------------------------------------------------------------------------------
#include "Pre.h"
#include "winURLLoader.h"
#include "Core/String/StringConverter.h"
#include "IO/Stream/MemoryStream.h"
#define VC_EXTRALEAN (1)
#define WIN32_LEAN_AND_MEAN (1)
#include <Windows.h>
#include <WinHttp.h>

namespace Oryol {
namespace _priv {

const std::chrono::seconds winURLLoader::connectionMaxAge{10};

//------------------------------------------------------------------------------
winURLLoader::winURLLoader() :
contentTypeString("Content-Type")
{
    this->hSession = WinHttpOpen(NULL,      // pwszUserAgent
                                 WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,     // dwAccessType
                                 WINHTTP_NO_PROXY_NAME, // pwszProxyName
                                 WINHTTP_NO_PROXY_BYPASS,   // pwszProxyByPass
                                 0);        // dwFlags
    o_assert(NULL != this->hSession);
}

//------------------------------------------------------------------------------
winURLLoader::~winURLLoader() {
    o_assert(NULL != this->hSession);
    // close remaining connections
    for (const auto& kvp : this->connections) {
        WinHttpCloseHandle(kvp.Value().hConnection);
    }
    this->connections.Clear();
    WinHttpCloseHandle(this->hSession);
    this->hSession = NULL;
}

//------------------------------------------------------------------------------
void
winURLLoader::doWork() {
    while (!this->requestQueue.Empty()) {
        Ptr<HTTPProtocol::HTTPRequest> req = this->requestQueue.Dequeue();
        if (!baseURLLoader::handleCancelled(req)) {
            this->doOneRequest(req);

            // transfer result to embedded ioRequest and set to handled
            auto ioReq = req->GetIoRequest();
            if (ioReq) {
                auto httpResponse = req->GetResponse();
                ioReq->SetStatus(httpResponse->GetStatus());
                ioReq->SetStream(httpResponse->GetBody());
                ioReq->SetErrorDesc(httpResponse->GetErrorDesc());
                ioReq->SetHandled();
            }
            req->SetHandled();
        }
    }
    this->garbageCollectConnections();
}

//------------------------------------------------------------------------------
void
winURLLoader::doOneRequest(const Ptr<HTTPProtocol::HTTPRequest>& req) {
    Log::Info("winURLLoader::doOneRequest() start: %s\n", req->GetURL().AsCStr());

    // obtain a connection
    HINTERNET hConn = this->obtainConnection(req->GetURL());
    if (NULL != hConn) {
        WideString method(HTTPMethod::ToWideString(req->GetMethod()));
        WideString path(StringConverter::UTF8ToWide(req->GetURL().PathToEnd()));

        // setup an HTTP request
        HINTERNET hRequest = WinHttpOpenRequest(
            hConn,              // hConnect
            method.AsCStr(),    // pwszVerb
            path.AsCStr(),      // pwszObjectName
            NULL,               // pwszVersion (NULL == HTTP/1.1)
            WINHTTP_NO_REFERER, // pwszReferrer
            WINHTTP_DEFAULT_ACCEPT_TYPES,   // pwszAcceptTypes
            WINHTTP_FLAG_ESCAPE_PERCENT);   // dwFlags
        if (NULL != hRequest) {

            // add request headers to the request
            if (!req->GetRequestHeaders().Empty()) {
                for (const auto& kvp : req->GetRequestHeaders()) {
                    this->stringBuilder.Clear();
                    this->stringBuilder.Append(kvp.Key());
                    this->stringBuilder.Append(": ");
                    this->stringBuilder.Append(kvp.Value());
                    this->stringBuilder.Append("\r\n");
                }

                // check if we need to add a Content-Type field:
                if (req->GetBody().isValid() && req->GetBody()->GetContentType().IsValid()) {
                    this->stringBuilder.Append("Content-Type: ");
                    this->stringBuilder.Append(req->GetBody()->GetContentType().AsCStr());
                    this->stringBuilder.Append("\r\n");
                }

                // remove last CRLF
                this->stringBuilder.PopBack();
                this->stringBuilder.PopBack();
                WideString reqHeaders(StringConverter::UTF8ToWide(this->stringBuilder.GetString()));
                BOOL headerResult = WinHttpAddRequestHeaders(
                    hRequest,
                    reqHeaders.AsCStr(),
                    reqHeaders.Length(),
                    WINHTTP_ADDREQ_FLAG_ADD|WINHTTP_ADDREQ_FLAG_REPLACE);
                o_assert(headerResult);
            }

            // do we have body-data?
            const uint8* bodyDataPtr = WINHTTP_NO_REQUEST_DATA;
            int32 bodySize = 0;
            if (req->GetBody().isValid()) {
                const Ptr<Stream>& bodyStream = req->GetBody();
                bodySize = bodyStream->Size();
                o_assert(bodySize > 0);
                bodyStream->Open(OpenMode::ReadOnly);
                bodyDataPtr = bodyStream->MapRead(nullptr);
            }

            // create a response object, no matter whether the
            // send fails or not
            Ptr<HTTPProtocol::HTTPResponse> httpResponse = HTTPProtocol::HTTPResponse::Create();
            req->SetResponse(httpResponse);

            // send the request
            BOOL sendResult = WinHttpSendRequest(
                hRequest,                           // hRequest
                WINHTTP_NO_ADDITIONAL_HEADERS,      // pwszHeaders
                0,                                  // dwHeadersLength
                (LPVOID) bodyDataPtr,               // lpOptional
                bodySize,                           // dwOptionalLength
                0,                                  // dwTotoalLength
                0);                                 // dwContext
            if (sendResult) {
                // receive and process response
                BOOL recvResult = WinHttpReceiveResponse(hRequest, NULL);
                if (recvResult) {
                    // query the HTTP status code
                    DWORD dwStatusCode = 0;
                    DWORD dwTemp = sizeof(dwStatusCode);
                    WinHttpQueryHeaders(
                        hRequest,
                        WINHTTP_QUERY_STATUS_CODE|WINHTTP_QUERY_FLAG_NUMBER,
                        NULL,
                        &dwStatusCode,
                        &dwTemp,
                        NULL);
                    httpResponse->SetStatus((IOStatus::Code) dwStatusCode);

                    // query the response headers required data size
                    DWORD dwSize = 0;
                    WinHttpQueryHeaders(hRequest,
                                        WINHTTP_QUERY_RAW_HEADERS_CRLF,
                                        WINHTTP_HEADER_NAME_BY_INDEX,
                                        NULL,
                                        &dwSize,
                                        WINHTTP_NO_HEADER_INDEX);
                    if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
                        // and get the response headers
                        LPVOID headerBuffer = Memory::Alloc(dwSize);
                        BOOL headerResult = WinHttpQueryHeaders(
                            hRequest,
                            WINHTTP_QUERY_RAW_HEADERS_CRLF,
                            WINHTTP_HEADER_NAME_BY_INDEX,
                            headerBuffer,
                            &dwSize,
                            WINHTTP_NO_HEADER_INDEX);
                        o_assert(headerResult);

                        // convert from wide and split the header fields
                        this->stringBuilder.Set(StringConverter::WideToUTF8((const wchar_t*) headerBuffer, dwSize / sizeof(wchar_t)));
                        Memory::Free(headerBuffer);
                        Array<String> tokens;
                        this->stringBuilder.Tokenize("\r\n", tokens);
                        Map<String, String> fields;
                        fields.Reserve(tokens.Size());
                        for (const String& str : tokens) {
                            const int32 colonIndex = StringBuilder::FindFirstOf(str.AsCStr(), 0, EndOfString, ":");
                            if (colonIndex != InvalidIndex) {
                                String key(str.AsCStr(), 0, colonIndex);
                                String value(str.AsCStr(), colonIndex+2, EndOfString);
                                fields.Add(key, value);
                            }
                        }
                        httpResponse->SetResponseHeaders(fields);
                    }
                    else {
                        Log::Warn("winURLLoader: failed to extract response header fields!\n");
                    }

                    // extract body data
                    Ptr<MemoryStream> responseBodyStream = MemoryStream::Create();
                    responseBodyStream->SetURL(req->GetURL());
                    responseBodyStream->Open(OpenMode::WriteOnly);
                    DWORD bytesToRead = 0;
                    do {
                        // how much data available?
                        BOOL queryDataResult = WinHttpQueryDataAvailable(hRequest, &bytesToRead);
                        o_assert(queryDataResult);
                        if (dwSize > 0) {
                            uint8* dstPtr = responseBodyStream->MapWrite(bytesToRead);
                            o_assert(nullptr != dstPtr);
                            DWORD bytesRead = 0;
                            BOOL readDataResult = WinHttpReadData(hRequest, dstPtr, bytesToRead, &bytesRead);
                            o_assert(readDataResult);
                            o_assert(bytesRead == bytesToRead);
                            responseBodyStream->UnmapWrite();
                        }
                    }
                    while (bytesToRead > 0);
                    responseBodyStream->Close();

                    // extract the Content-Type response header field and set on responseBodyStream
                    if (httpResponse->GetResponseHeaders().Contains(this->contentTypeString)) {
                        ContentType contentType(httpResponse->GetResponseHeaders()[this->contentTypeString]);
                        responseBodyStream->SetContentType(contentType);
                    }

                    // ...and finally set the responseBodyStream on the httpResponse
                    httpResponse->SetBody(responseBodyStream);

                    // @todo: write error desc to httpResponse if something went wrong
                }
                else {
                    Log::Warn("winURLLoader: WinHttpReceiveResponse() failed for '%s'!\n", req->GetURL().AsCStr());
                }
            }
            else {
                /// @todo: better error reporting!
                Log::Warn("winURLLoader: WinHttpSendRequest() failed for '%s'\n", req->GetURL().AsCStr());
            }

            // unmap the body data if necessary
            if (req->GetBody().isValid()) {
                req->GetBody()->Close();
            }

            // close the request object
            WinHttpCloseHandle(hRequest);
        }
        else {
            Log::Warn("winURLLoader: WinHttpOpenRequest() failed for '%s'\n", req->GetURL().AsCStr());
        }
    }
    Log::Info("winURLLoader::doOneRequest() end: %s\n", req->GetURL().AsCStr());
}

//------------------------------------------------------------------------------
HINTERNET
winURLLoader::obtainConnection(const URL& url) {
    o_assert(NULL != this->hSession);
    const String hostAndPort = url.HostAndPort();
    if (this->connections.Contains(hostAndPort)) {
        // connection was already established, refresh the timestamp
        // and return the connection
        connection& con = this->connections[hostAndPort];
        con.timeStamp = std::chrono::steady_clock::now();
        return con.hConnection;
    }
    else {
        // new connection
        connection con;
        const WideString host = StringConverter::UTF8ToWide(url.Host());
        const String portString = url.Port();
        INTERNET_PORT port = INTERNET_DEFAULT_HTTP_PORT;
        if (!portString.Empty()) {
            port = StringConverter::FromString<int16>(portString);
        }
        con.hConnection = WinHttpConnect(this->hSession,        // hSession
                                          host.AsCStr(),        // pswzServerName
                                          port,                 // nServerPort
                                          0);                   // dwReserved
        if (NULL != con.hConnection) {
            con.timeStamp = std::chrono::steady_clock::now();
            this->connections.Add(hostAndPort, con);
            return con.hConnection;
        }
        else {
            Log::Warn("winURLLoader: failed to connect to '%s'\n", hostAndPort.AsCStr());
            return NULL;
        }
    }
}

//------------------------------------------------------------------------------
void
winURLLoader::garbageCollectConnections() {
    std::chrono::steady_clock::time_point curTime = std::chrono::steady_clock::now();
    for (int32 i = this->connections.Size() - 1; i >= 0; i--) {
        const String key = this->connections.KeyAtIndex(i);
        const connection con = this->connections.ValueAtIndex(i);
        std::chrono::seconds age = std::chrono::duration_cast<std::chrono::seconds>(curTime - con.timeStamp);
        if (age > connectionMaxAge) {
            o_assert(NULL != con.hConnection);
            WinHttpCloseHandle(con.hConnection);
            this->connections.EraseIndex(i);
        }
    }
}

} // namespace _priv
} // namespace Oryol
<|endoftext|>


<commit_before>#include "gamelib/components/rendering/SpriteComponent.hpp"
#include "gamelib/core/res/ResourceManager.hpp"
#include "gamelib/core/rendering/RenderSystem.hpp"
#include "gamelib/properties/PropResource.hpp"
#include <SFML/Graphics/Vertex.hpp>

namespace gamelib
{
    SpriteComponent::SpriteComponent() :
        _ani(this)
    {
        registerResourceProperty(_props, "sprite", _sprite, PROP_METHOD(_sprite, change), this);

        // Don't register these properties, because it can't be guaranteed after serializaion
        // that these are loaded _after_ sprite is loaded and therefore overwrite the default
        // values.
        // That means these properties are unreliable and need extra code to work properly.
        // TODO: Fix this
        // _props.registerProperty("offset", _ani.ani.offset, 0, _ani.ani.length);
        // _props.registerProperty("speed", _ani.ani.speed);

        // props->registerProperty("length", ani.length);
    }

    bool SpriteComponent::_init()
    {
        if (!RenderComponent::_init())
            return false;
        if (!_ani._init())
            return false;

        _system->createNodeMesh(_handle, 4, sf::TriangleStrip);
        _system->setNodeMeshSize(_handle, 0);   // Don't render anything when no sprite is set
        return true;
    }

    void SpriteComponent::_quit()
    {
        RenderComponent::_quit();
        _ani._quit();
    }

    void SpriteComponent::setIndex(int index)
    {
        _ani.ani.setIndex(index);
        _updateUV();
    }

    void SpriteComponent::change(const std::string& fname)
    {
        change(getSubsystem<ResourceManager>()->get(fname).as<SpriteResource>());
    }

    void SpriteComponent::change(SpriteResource::Handle sprite)
    {
        _sprite = sprite;

        if (!_sprite)
        {
            _system->setNodeMeshSize(_handle, 0);
            return;
        }

        _ani.ani = _sprite->ani;
        if (_ani.ani.offset < 0)
            _ani.ani.randomize();

        sf::Vector2f vertices[] = {
            sf::Vector2f(0, _sprite->rect.h),
            sf::Vector2f(_sprite->rect.w, 0),
            sf::Vector2f(_sprite->rect.w, _sprite->rect.h),
        };

        _system->setNodeOptions(_handle, nullptr, nullptr, nullptr, &_sprite->tex);
        _system->updateNodeMesh(_handle, 3, 1, vertices);
        _updateUV();
        setOrigin(sprite->origin);
    }

    SpriteResource::Handle SpriteComponent::getSprite() const
    {
        return _sprite;
    }

    const std::string& SpriteComponent::getSpriteName() const
    {
        return _sprite.getResource()->getPath();
    }

    void SpriteComponent::_updateUV()
    {
        if (!_sprite)
            return;

        // Adding this to the tex coords will prevent the 1px border glitch (hopefully)
        // https://gamedev.stackexchange.com/a/75244
        // https://stackoverflow.com/questions/19611745/opengl-black-lines-in-between-tiles
        constexpr float magic = 0.375;

        const auto& rect = _sprite->rect;
        auto tsize = _sprite->tex->getSize();

        int x = rect.x + (int)_ani.ani.offset * rect.w;
        int y = (rect.y + (int)(x / tsize.x) * rect.h) % tsize.y;
        x = x % tsize.x;

        sf::Vector2f uv[] = {
            sf::Vector2f(x + magic, y + magic),
            sf::Vector2f(x + magic, y + rect.h - magic),
            sf::Vector2f(x + rect.w - magic, y + magic),
            sf::Vector2f(x + rect.w - magic, y + rect.h - magic),
        };
        _system->updateNodeMesh(_handle, 4, 0, nullptr, uv);
    }
}
<commit_msg>Implement missing functions<commit_after>#include "gamelib/components/rendering/SpriteComponent.hpp"
#include "gamelib/core/res/ResourceManager.hpp"
#include "gamelib/core/rendering/RenderSystem.hpp"
#include "gamelib/properties/PropResource.hpp"
#include <SFML/Graphics/Vertex.hpp>

namespace gamelib
{
    SpriteComponent::SpriteComponent() :
        _ani(this)
    {
        registerResourceProperty(_props, "sprite", _sprite, PROP_METHOD(_sprite, change), this);

        // Don't register these properties, because it can't be guaranteed after serializaion
        // that these are loaded _after_ sprite is loaded and therefore overwrite the default
        // values.
        // That means these properties are unreliable and need extra code to work properly.
        // TODO: Fix this
        // _props.registerProperty("offset", _ani.ani.offset, 0, _ani.ani.length);
        // _props.registerProperty("speed", _ani.ani.speed);

        // props->registerProperty("length", ani.length);
    }

    bool SpriteComponent::_init()
    {
        if (!RenderComponent::_init())
            return false;
        if (!_ani._init())
            return false;

        _system->createNodeMesh(_handle, 4, sf::TriangleStrip);
        _system->setNodeMeshSize(_handle, 0);   // Don't render anything when no sprite is set
        return true;
    }

    void SpriteComponent::_quit()
    {
        RenderComponent::_quit();
        _ani._quit();
    }

    void SpriteComponent::setIndex(int index)
    {
        _ani.ani.setIndex(index);
        _updateUV();
    }

    void SpriteComponent::change(const std::string& fname)
    {
        change(getSubsystem<ResourceManager>()->get(fname).as<SpriteResource>());
    }

    void SpriteComponent::change(SpriteResource::Handle sprite)
    {
        _sprite = sprite;

        if (!_sprite)
        {
            _system->setNodeMeshSize(_handle, 0);
            return;
        }

        _ani.ani = _sprite->ani;
        if (_ani.ani.offset < 0)
            _ani.ani.randomize();

        sf::Vector2f vertices[] = {
            sf::Vector2f(0, _sprite->rect.h),
            sf::Vector2f(_sprite->rect.w, 0),
            sf::Vector2f(_sprite->rect.w, _sprite->rect.h),
        };

        _system->setNodeOptions(_handle, nullptr, nullptr, nullptr, &_sprite->tex);
        _system->updateNodeMesh(_handle, 3, 1, vertices);
        _updateUV();
        setOrigin(sprite->origin);
    }

    SpriteResource::Handle SpriteComponent::getSprite() const
    {
        return _sprite;
    }

    const std::string& SpriteComponent::getSpriteName() const
    {
        return _sprite.getResource()->getPath();
    }

    void SpriteComponent::_updateUV()
    {
        if (!_sprite)
            return;

        // Adding this to the tex coords will prevent the 1px border glitch (hopefully)
        // https://gamedev.stackexchange.com/a/75244
        // https://stackoverflow.com/questions/19611745/opengl-black-lines-in-between-tiles
        constexpr float magic = 0.375;

        const auto& rect = _sprite->rect;
        auto tsize = _sprite->tex->getSize();

        int x = rect.x + (int)_ani.ani.offset * rect.w;
        int y = (rect.y + (int)(x / tsize.x) * rect.h) % tsize.y;
        x = x % tsize.x;

        sf::Vector2f uv[] = {
            sf::Vector2f(x + magic, y + magic),
            sf::Vector2f(x + magic, y + rect.h - magic),
            sf::Vector2f(x + rect.w - magic, y + magic),
            sf::Vector2f(x + rect.w - magic, y + rect.h - magic),
        };
        _system->updateNodeMesh(_handle, 4, 0, nullptr, uv);
    }

    auto SpriteComponent::getAnimation() const -> const AnimationComponent&
    {
        return _ani;
    }

    auto SpriteComponent::getTexture() const -> TextureResource::Handle
    {
        return _sprite->tex;
    }
}
<|endoftext|>


<commit_before>#include "randomized.hpp"
#include "parallel.hpp"
#include "parallel.cpp"
#include <vector>
#include <cstdlib>
#include <cmath>
#include <iostream>
#include <atomic>

//TODO! usa una libreria migliore per random

/**
 * Flip a coin until a head is found, then return the number of tails
 */
unsigned long int flipCoinsUntilHeads() {
	unsigned long int tails = 0;
	while (rand() % 2 == 0) {
		tails++;
	}
	return tails;
}

unsigned long int produceAnEstimate(std::vector<unsigned long long int> const &addends) {

	unsigned long int max_tails = 0;
	unsigned int n = addends.size();
	#pragma omp parallel
	{
		#pragma omp for
		for (unsigned int i=0; i < n; i++) {
			if (addends[i] != 0){
				unsigned long int new_tails = flipCoinsUntilHeads();

				// gcc version
				#ifdef __GNUC__
				{
					// keep trying to write on max_tails if the new result is higher
					while (1) {
						unsigned long int curr_tails = max_tails;
						if (new_tails > curr_tails) {
							if (__sync_bool_compare_and_swap(&max_tails, curr_tails, new_tails)) {
								break;    // swap successful
							};
						} else {
							break;
						}
					}
				}
				// not gcc/windows version -> "http://en.cppreference.com/w/cpp/atomic/atomic_compare_exchange"
				#else
				{
					// TODO: compare_and_swap su windows?
					#pragma omp critical (max_tails)
					{
						if (new_tails > max_tails) {
							max_tails = new_tails;
						}
					}
				}
				#endif
			}
		}
	}
	return max_tails;
}

unsigned long long int randomizedSum(std::vector<unsigned long long int> &addends, unsigned int k, unsigned int a, unsigned int d) {

	// Estimation

	// run produce-an-estimate k times
	std::vector<unsigned long int> estimates (k);
	for (unsigned int i=0; i < k; i++) {
		estimates[i] = produceAnEstimate(addends);
	}

	// apply formula to find m'
	unsigned long int sum_of_estimates = 0;
	for (unsigned long int e: estimates) {
		sum_of_estimates += e;
	}
	double E = log(2) * ((double) sum_of_estimates / k);
	unsigned long int m = exp(2) * exp(E) + d;

	#ifdef DEBUG
		// Alternative estimate (using approximate fit)
		unsigned long int mm = pow(2.0, (double) sum_of_estimates / k -1 + 0.6667) + d;

		unsigned long int actual = 0;
		for (unsigned long long int addend: addends) {
			if (addend != 0) {
				actual++;
			}
		}
		std::cout << "Estimate: " << m << std::endl;
		std::cout << "Alternative: " << mm << std::endl;
		std::cout << "Actual: " << actual << std::endl;
	#endif

	// Addition

	// initialize space
	unsigned long int red_size = m * a;
	std::vector<unsigned long long int> reduced_vector (red_size, 0);

	// memory marking
	unsigned int n = addends.size();
	#pragma omp parallel
	{
		#pragma omp for
		for (unsigned int i=0; i < n; i++) {
			if (addends[i] != 0) {
				// keep trying to write on new vector
				bool done = false;
				while (!done) {
					unsigned int index = rand() % red_size;
					#ifdef __GNUC__
						if (reduced_vector[index] == 0) {
							if (__sync_bool_compare_and_swap(&reduced_vector[index], 0, addends[i])) {
								done = true;
							}
						}
					#else
						// TODO: compare_and_swap su windows?
						#pragma omp critical(reduced_vector)
						{
							if (reduced_vector[index] == 0) {
								reduced_vector[index] = addends[i];
								done = true;
							}
						}
					#endif
				}
			}
		}
	}

	// parallel sum
	unsigned long long int sum = parallelSum(reduced_vector);

	return sum;
}
<commit_msg>Added TRNG headers<commit_after>#include "randomized.hpp"
#include "parallel.hpp"
#include "parallel.cpp"
#include <vector>
#include <cstdlib>
#include <cmath>
#include <iostream>
#include <trng/yarn2.hpp>
#include <trng/uniform01_dist.hpp>

/**
 * Flip a coin until a head is found, then return the number of tails
 */
unsigned long int flipCoinsUntilHeads() {
	unsigned long int tails = 0;
	while (rand() % 2 == 0) {
		tails++;
	}
	return tails;
}

unsigned long int produceAnEstimate(std::vector<unsigned long long int> const &addends) {

	unsigned long int max_tails = 0;
	unsigned int n = addends.size();
	#pragma omp parallel
	{
		#pragma omp for
		for (unsigned int i=0; i < n; i++) {
			if (addends[i] != 0){
				unsigned long int new_tails = flipCoinsUntilHeads();

				// gcc version
				#ifdef __GNUC__
				{
					// keep trying to write on max_tails if the new result is higher
					while (1) {
						unsigned long int curr_tails = max_tails;
						if (new_tails > curr_tails) {
							if (__sync_bool_compare_and_swap(&max_tails, curr_tails, new_tails)) {
								break;    // swap successful
							};
						} else {
							break;
						}
					}
				}
				// without gcc
				#else
				{
					#pragma omp critical (max_tails)
					{
						if (new_tails > max_tails) {
							max_tails = new_tails;
						}
					}
				}
				#endif
			}
		}
	}
	return max_tails;
}

unsigned long long int randomizedSum(std::vector<unsigned long long int> &addends, unsigned int k, unsigned int a, unsigned int d) {

	// Estimation

	// run produce-an-estimate k times
	std::vector<unsigned long int> estimates (k);
	for (unsigned int i=0; i < k; i++) {
		estimates[i] = produceAnEstimate(addends);
	}

	// apply formula to find m'
	unsigned long int sum_of_estimates = 0;
	for (unsigned long int e: estimates) {
		sum_of_estimates += e;
	}
	double E = log(2) * ((double) sum_of_estimates / k);
	unsigned long int m = exp(2) * exp(E) + d;

	#ifdef DEBUG
		// Alternative estimate (using approximate fit)
		unsigned long int mm = pow(2.0, (double) sum_of_estimates / k -1 + 0.6667) + d;

		unsigned long int actual = 0;
		for (unsigned long long int addend: addends) {
			if (addend != 0) {
				actual++;
			}
		}
		std::cout << "Estimate: " << m << std::endl;
		std::cout << "Alternative: " << mm << std::endl;
		std::cout << "Actual: " << actual << std::endl;
	#endif

	// Addition

	// initialize space
	unsigned long int red_size = m * a;
	std::vector<unsigned long long int> reduced_vector (red_size, 0);

	// memory marking
	unsigned int n = addends.size();
	#pragma omp parallel
	{
		#pragma omp for
		for (unsigned int i=0; i < n; i++) {
			if (addends[i] != 0) {
				// keep trying to write on new vector
				bool done = false;
				while (!done) {
					unsigned int index = rand() % red_size;
					// gcc version
					#ifdef __GNUC__
						if (reduced_vector[index] == 0) {
							if (__sync_bool_compare_and_swap(&reduced_vector[index], 0, addends[i])) {
								done = true;
							}
						}
					// without gcc
					#else
						#pragma omp critical(reduced_vector)
						{
							if (reduced_vector[index] == 0) {
								reduced_vector[index] = addends[i];
								done = true;
							}
						}
					#endif
				}
			}
		}
	}

	// parallel sum
	unsigned long long int sum = parallelSum(reduced_vector);

	return sum;
}
<|endoftext|>


<commit_before>#include <stdio.h>
#include <float.h>
#ifdef WIN32
#include <windows.h>
#endif
#ifdef __APPLE__
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif

#include "raytracing.h"

//temporary variables
//these are only used to illustrate
//a simple debug drawing. A ray
Vec3Df testRayOrigin;
Vec3Df testRayDestination;
Vec3Df testColor;

//use this function for any preprocessing of the mesh.
void init()
{
	//load the mesh file
	//please realize that not all OBJ files will successfully load.
	//Nonetheless, if they come from Blender, they should, if they
	//are exported as WavefrontOBJ.
	//PLEASE ADAPT THE LINE BELOW TO THE FULL PATH OF THE dodgeColorTest.obj
	//model, e.g., "C:/temp/myData/GraphicsIsFun/dodgeColorTest.obj",
	//otherwise the application will not load properly

	MyMesh.loadMesh(FILE_LOCATION, true);
	MyMesh.computeVertexNormals();

	//one first move: initialize the first light source
	//at least ONE light source has to be in the scene!!!
	//here, we set it to the current location of the camera
	MyLightPositions.push_back(MyCameraPosition);
}

//return the color of your pixel.
Vec3Df performRayTracing(const Vec3Df & origin, const Vec3Df & dest)
{
	int level = 0;
	return trace(origin, dest, level);

	//  if(intersect(level, ray, max, &hit)) {
	//    Shade(level, hit, &color);
	//  }
	//  else
	//    color=BackgroundColor

	//return Vec3Df(dest[0],dest[1],dest[2]);
}

Vec3Df trace(const Vec3Df & origin, const Vec3Df & dir, int level){
	float depth = FLT_MAX;
	Vec3Df color = Vec3Df(0, 0, 0);
	for (int i = 0; i < MyMesh.triangles.size(); i++){
		Triangle triangle = MyMesh.triangles.at(i);
		Vertex v0 = MyMesh.vertices.at(triangle.v[0]);
		Vertex v1 = MyMesh.vertices.at(triangle.v[1]);
		Vertex v2 = MyMesh.vertices.at(triangle.v[2]);

		Vec3Df N = Vec3Df(0, 0, 0);
		Vec3Df intersection = rayTriangleIntersect(origin, dir, v0.p, v1.p, v2.p, depth, N);
		if (!isNulVector(intersection)){
			// save color and depth
			color = shade(dir, intersection, level, i, N);
		}

	}
	return color;
}

Vec3Df shade(const Vec3Df dir, const Vec3Df intersection, int level, int triangleIndex, const Vec3Df N){
	Vec3Df color = Vec3Df(0, 0, 0);
	Vec3Df lightDirection = Vec3Df(0, 0, 0);
	//lightDirection = lightVector(intersection, origin);
	color += diffuse(dir, N, triangleIndex);
	color += ambient(dir, intersection, level, triangleIndex);
	color += speculair(dir, intersection, level, triangleIndex);
	return color;
}

Vec3Df diffuse(const Vec3Df lightSource, const Vec3Df normal, int triangleIndex){
	Vec3Df color = Vec3Df(0, 0, 0);
	unsigned int triMat = MyMesh.triangleMaterials.at(triangleIndex);
	color = MyMesh.materials.at(triMat).Kd();

	// diffuser = Kd * dot(lightsource, normal) * Od * Ld
	// Od = object color
	// Ld = lightSource color
	//Vec3Df diffuser = color * (Vec3Df::dotProduct(lightSource, normal)) / pow(normal.getLength(), 2) * 1 * 1;
	return color;
}

Vec3Df ambient(const Vec3Df dir, const Vec3Df intersection, int level, int triangleIndex){
	Vec3Df color = Vec3Df(0, 0, 0);
	unsigned int triMat = MyMesh.triangleMaterials.at(triangleIndex);
	color = MyMesh.materials.at(triMat).Ka();
	return color;
}

Vec3Df speculair(const Vec3Df dir, const Vec3Df intersection, int level, int triangleIndex){
	Vec3Df color = Vec3Df(0, 0, 0);
	unsigned int triMat = MyMesh.triangleMaterials.at(triangleIndex);
	color = MyMesh.materials.at(triMat).Ks();
	return color;
}

Vec3Df lightVector(const Vec3Df point, const Vec3Df lightPoint){
	Vec3Df lightDir = Vec3Df(0, 0, 0);
	lightDir = lightPoint - point;
	return lightDir;
}

Vec3Df reflectionVector(const Vec3Df lightDirection, const Vec3Df normalVector) {
	Vec3Df reflection = Vec3Df(0, 0, 0);
	reflection = lightDirection - 2 * (Vec3Df::dotProduct(lightDirection, normalVector) / pow(normalVector.getLength(), 2))*normalVector;
	return reflection;
}
// We can also add textures!

// The source of this function is:
// http://www.scratchapixel.com/lessons/3d-basic-rendering/ray-tracing-rendering-a-triangle/ray-triangle-intersection-geometric-solution
// Returns the point of intersection
Vec3Df rayTriangleIntersect(const Vec3Df &orig, const Vec3Df &dir, const Vec3Df v0, const Vec3Df v1, const Vec3Df v2, float &depth, Vec3Df &N)
{
	Vec3Df nulVector = Vec3Df(0, 0, 0);
	// compute plane's normal
	Vec3Df v0v1 = v1 - v0;
	Vec3Df v0v2 = v2 - v0;
	// no need to normalize
	N = Vec3Df::crossProduct(v0v1, v0v2); // N

	// Step 1: finding P (the point where the ray intersects the plane)

	// check if ray and plane are parallel ?
	float NdotRayDirection = Vec3Df::dotProduct(N, dir);
	if (fabs(NdotRayDirection) < 0.000000001) // almost 0
		return nulVector; // they are parallel so they don't intersect !

	// compute d parameter using equation 2 (d is the distance from the origin (0, 0, 0) to the plane)
	float d = Vec3Df::dotProduct(N, v0);

	// compute t (equation 3) (t is distance from the ray origin to P)
	float t = (-Vec3Df::dotProduct(N, orig) + d) / NdotRayDirection;
	// check if the triangle is in behind the ray
	if (t < 0) return nulVector; // the triangle is behind
	if (t > depth) return nulVector; // already have something closerby

	// compute the intersection point P using equation 1
	Vec3Df P = orig + t * dir;

	// Step 2: inside-outside test
	Vec3Df C; // vector perpendicular to triangle's plane

	// edge 0
	Vec3Df edge0 = v1 - v0;
	Vec3Df vp0 = P - v0;
	C = Vec3Df::crossProduct(edge0, vp0);
	if (Vec3Df::dotProduct(N, C) < 0) return nulVector; // P is on the right side

	// edge 1
	Vec3Df edge1 = v2 - v1;
	Vec3Df vp1 = P - v1;
	C = Vec3Df::crossProduct(edge1, vp1);
	if (Vec3Df::dotProduct(N, C) < 0) return nulVector; // P is on the right side

	// edge 2
	Vec3Df edge2 = v0 - v2;
	Vec3Df vp2 = P - v2;
	C = Vec3Df::crossProduct(edge2, vp2);
	if (Vec3Df::dotProduct(N, C) < 0) return nulVector; // P is on the right side;

	depth = t;
	return P; // this is the intersectionpoint
}

//Shade(level, hit, &color){
//  for each lightsource
//    ComputeDirectLight(hit, &directColor);
//  if(material reflects && (level < maxLevel))
//    computeReflectedRay(hit, &reflectedray);
//    Trace(level+1, reflectedRay, &reflectedColor);
//  color = directColor + reflection * reflectedcolor + transmission * refractedColor;
//}

void yourDebugDraw()
{
	//draw open gl debug stuff
	//this function is called every frame

	//let's draw the mesh
	MyMesh.draw();

	//let's draw the lights in the scene as points
	glPushAttrib(GL_ALL_ATTRIB_BITS); //store all GL attributes
	glDisable(GL_LIGHTING);
	glColor3f(1, 1, 1);
	glPointSize(10);
	glBegin(GL_POINTS);
	for (int i = 0; i<MyLightPositions.size(); ++i)
		glVertex3fv(MyLightPositions[i].pointer());
	glEnd();
	glPopAttrib();//restore all GL attributes
	//The Attrib commands maintain the state.
	//e.g., even though inside the two calls, we set
	//the color to white, it will be reset to the previous
	//state after the pop.

	//as an example: we draw the test ray, which is set by the keyboard function
	glPushAttrib(GL_ALL_ATTRIB_BITS);
	glDisable(GL_LIGHTING);
	glBegin(GL_LINES);
	glColor3f(testColor[0], testColor[1], testColor[2]);
	glVertex3f(testRayOrigin[0], testRayOrigin[1], testRayOrigin[2]);
	glColor3f(testColor[0], testColor[1], testColor[2]);
	glVertex3f(testRayDestination[0], testRayDestination[1], testRayDestination[2]);
	glEnd();
	glPointSize(10);
	glBegin(GL_POINTS);
	glVertex3fv(MyLightPositions[0].pointer());
	glEnd();
	glPopAttrib();

	//draw whatever else you want...
	////glutSolidSphere(1,10,10);
	////allows you to draw a sphere at the origin.
	////using a glTranslate, it can be shifted to whereever you want
	////if you produce a sphere renderer, this
	////triangulated sphere is nice for the preview
}

//yourKeyboardFunc is used to deal with keyboard input.
//t is the character that was pressed
//x,y is the mouse position in pixels
//rayOrigin, rayDestination is the ray that is going in the view direction UNDERNEATH your mouse position.
//
//A few keys are already reserved:
//'L' adds a light positioned at the camera location to the MyLightPositions vector
//'l' modifies the last added light to the current
//    camera position (by default, there is only one light, so move it with l)
//    ATTENTION These lights do NOT affect the real-time rendering.
//    You should use them for the raytracing.
//'r' calls the function performRaytracing on EVERY pixel, using the correct associated ray.
//    It then stores the result in an image "result.ppm".
//    Initially, this function is fast (performRaytracing simply returns
//    the target of the ray - see the code above), but once you replaced
//    this function and raytracing is in place, it might take a
//    while to complete...
void yourKeyboardFunc(char t, int x, int y, const Vec3Df & rayOrigin, const Vec3Df & rayDestination)
{

	//here, as an example, I use the ray to fill in the values for my upper global ray variable
	//I use these variables in the debugDraw function to draw the corresponding ray.
	//try it: Press a key, move the camera, see the ray that was launched as a line.
	testRayOrigin = rayOrigin;
	testRayDestination = rayDestination;
	testColor = performRayTracing(rayOrigin, rayDestination);

	std::cout << " The color from the ray is " << testColor[0] << "," << testColor[1] << "," << testColor[2] << std::endl;
	// do here, whatever you want with the keyboard input t.

	//      Trace(0, ray, &color);
	//      PutPixel(x, y, color);

	std::cout << t << " pressed! The mouse was in location " << x << "," << y << "!" << std::endl;
}

bool isNulVector(Vec3Df vector){
	Vec3Df nullVector = Vec3Df(0, 0, 0);
	return vector == nullVector;
}

// Pseudocode From slides

//RayTrace (view)
//{
//  for (y=0; y<view.yres; ++y){
//    for(x=0; x<view.xres; ++x){
//      ComputeRay(x, y, view, &ray);
//      Trace(0, ray, &color);
//      PutPixel(x, y, color);
//    }
//  }
//}
//
//Trace( level, ray, &color){
//  if(intersect(level, ray, max, &hit)) {
//    Shade(level, hit, &color);
//  }
//  else
//    color=BackgroundColor
//}
//
//Shade(level, hit, &color){
//  for each lightsource
//    ComputeDirectLight(hit, &directColor);
//  if(material reflects && (level < maxLevel))
//    computeReflectedRay(hit, &reflectedray);
//    Trace(level+1, reflectedRay, &reflectedColor);
//  color = directColor + reflection * reflectedcolor + transmission * refractedColor;
//}
<commit_msg>Added debug and formula of ambient shading<commit_after>#include <stdio.h>
#include <float.h>
#ifdef WIN32
#include <windows.h>
#endif
#ifdef __APPLE__
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif

#include "raytracing.h"

//temporary variables
//these are only used to illustrate
//a simple debug drawing. A ray
Vec3Df testRayOrigin;
Vec3Df testRayDestination;
Vec3Df testColor;

//use this function for any preprocessing of the mesh.
void init()
{
	//load the mesh file
	//please realize that not all OBJ files will successfully load.
	//Nonetheless, if they come from Blender, they should, if they
	//are exported as WavefrontOBJ.
	//PLEASE ADAPT THE LINE BELOW TO THE FULL PATH OF THE dodgeColorTest.obj
	//model, e.g., "C:/temp/myData/GraphicsIsFun/dodgeColorTest.obj",
	//otherwise the application will not load properly

	MyMesh.loadMesh(FILE_LOCATION, true);
	MyMesh.computeVertexNormals();

	//one first move: initialize the first light source
	//at least ONE light source has to be in the scene!!!
	//here, we set it to the current location of the camera
	MyLightPositions.push_back(MyCameraPosition);
}

//return the color of your pixel.
Vec3Df performRayTracing(const Vec3Df & origin, const Vec3Df & dest)
{
	int level = 0;
	return trace(origin, dest, level);

	//  if(intersect(level, ray, max, &hit)) {
	//    Shade(level, hit, &color);
	//  }
	//  else
	//    color=BackgroundColor

	//return Vec3Df(dest[0],dest[1],dest[2]);
}

Vec3Df trace(const Vec3Df & origin, const Vec3Df & dir, int level){
	float depth = FLT_MAX;
	Vec3Df color = Vec3Df(0, 0, 0);
	for (int i = 0; i < MyMesh.triangles.size(); i++){
		Triangle triangle = MyMesh.triangles.at(i);
		Vertex v0 = MyMesh.vertices.at(triangle.v[0]);
		Vertex v1 = MyMesh.vertices.at(triangle.v[1]);
		Vertex v2 = MyMesh.vertices.at(triangle.v[2]);

		Vec3Df N = Vec3Df(0, 0, 0);
		Vec3Df intersection = rayTriangleIntersect(origin, dir, v0.p, v1.p, v2.p, depth, N);
		if (!isNulVector(intersection)){
			// save color and depth
			color = shade(dir, intersection, level, i, N);
		}

	}
	return color;
}

Vec3Df shade(const Vec3Df dir, const Vec3Df intersection, int level, int triangleIndex, const Vec3Df N){
	Vec3Df color = Vec3Df(0, 0, 0);
	Vec3Df lightDirection = Vec3Df(0, 0, 0);
	//lightDirection = lightVector(intersection, origin);
	color += diffuse(dir, N, triangleIndex);
	color += ambient(dir, intersection, level, triangleIndex);
	color += speculair(dir, intersection, level, triangleIndex);
	return color;
}

Vec3Df diffuse(const Vec3Df lightSource, const Vec3Df normal, int triangleIndex){
	Vec3Df color = Vec3Df(0, 0, 0);
	unsigned int triMat = MyMesh.triangleMaterials.at(triangleIndex);
	color = MyMesh.materials.at(triMat).Kd();

	// diffuser = Kd * dot(lightsource, normal) * Od * Ld
	// Od = object color
	// Ld = lightSource color
	//Vec3Df diffuser = color * (Vec3Df::dotProduct(lightSource, normal)) / pow(normal.getLength(), 2) * 1 * 1;
	return color;
}

Vec3Df ambient(const Vec3Df dir, const Vec3Df intersection, int level, int triangleIndex){
	Vec3Df color = Vec3Df(0, 0, 0);
	unsigned int triMat = MyMesh.triangleMaterials.at(triangleIndex);
	// ambient = Ka * Ia
	// where Ka is surface property, Ia is light property

	Vec3Df ka = MyMesh.materials.at(triMat).Ka();
	std::cout << "Mesh properties are : " << ka[0] << "," << ka[1] << "," << ka[2] << std::endl;
	// the Ka mesh properties of cube.obj and wollahberggeit.obj are 0?

	//color = MyMesh.materials.at(triMat).Ka();
	return color;
}

Vec3Df speculair(const Vec3Df dir, const Vec3Df intersection, int level, int triangleIndex){
	Vec3Df color = Vec3Df(0, 0, 0);
	unsigned int triMat = MyMesh.triangleMaterials.at(triangleIndex);
	color = MyMesh.materials.at(triMat).Ks();
	return color;
}

Vec3Df lightVector(const Vec3Df point, const Vec3Df lightPoint){
	Vec3Df lightDir = Vec3Df(0, 0, 0);
	lightDir = lightPoint - point;
	return lightDir;
}

Vec3Df reflectionVector(const Vec3Df lightDirection, const Vec3Df normalVector) {
	Vec3Df reflection = Vec3Df(0, 0, 0);
	reflection = lightDirection - 2 * (Vec3Df::dotProduct(lightDirection, normalVector) / pow(normalVector.getLength(), 2))*normalVector;
	return reflection;
}
// We can also add textures!

// The source of this function is:
// http://www.scratchapixel.com/lessons/3d-basic-rendering/ray-tracing-rendering-a-triangle/ray-triangle-intersection-geometric-solution
// Returns the point of intersection
Vec3Df rayTriangleIntersect(const Vec3Df &orig, const Vec3Df &dir, const Vec3Df v0, const Vec3Df v1, const Vec3Df v2, float &depth, Vec3Df &N)
{
	Vec3Df nulVector = Vec3Df(0, 0, 0);
	// compute plane's normal
	Vec3Df v0v1 = v1 - v0;
	Vec3Df v0v2 = v2 - v0;
	// no need to normalize
	N = Vec3Df::crossProduct(v0v1, v0v2); // N

	// Step 1: finding P (the point where the ray intersects the plane)

	// check if ray and plane are parallel ?
	float NdotRayDirection = Vec3Df::dotProduct(N, dir);
	if (fabs(NdotRayDirection) < 0.000000001) // almost 0
		return nulVector; // they are parallel so they don't intersect !

	// compute d parameter using equation 2 (d is the distance from the origin (0, 0, 0) to the plane)
	float d = Vec3Df::dotProduct(N, v0);

	// compute t (equation 3) (t is distance from the ray origin to P)
	float t = (-Vec3Df::dotProduct(N, orig) + d) / NdotRayDirection;
	// check if the triangle is in behind the ray
	if (t < 0) return nulVector; // the triangle is behind
	if (t > depth) return nulVector; // already have something closerby

	// compute the intersection point P using equation 1
	Vec3Df P = orig + t * dir;

	// Step 2: inside-outside test
	Vec3Df C; // vector perpendicular to triangle's plane

	// edge 0
	Vec3Df edge0 = v1 - v0;
	Vec3Df vp0 = P - v0;
	C = Vec3Df::crossProduct(edge0, vp0);
	if (Vec3Df::dotProduct(N, C) < 0) return nulVector; // P is on the right side

	// edge 1
	Vec3Df edge1 = v2 - v1;
	Vec3Df vp1 = P - v1;
	C = Vec3Df::crossProduct(edge1, vp1);
	if (Vec3Df::dotProduct(N, C) < 0) return nulVector; // P is on the right side

	// edge 2
	Vec3Df edge2 = v0 - v2;
	Vec3Df vp2 = P - v2;
	C = Vec3Df::crossProduct(edge2, vp2);
	if (Vec3Df::dotProduct(N, C) < 0) return nulVector; // P is on the right side;

	depth = t;
	return P; // this is the intersectionpoint
}

//Shade(level, hit, &color){
//  for each lightsource
//    ComputeDirectLight(hit, &directColor);
//  if(material reflects && (level < maxLevel))
//    computeReflectedRay(hit, &reflectedray);
//    Trace(level+1, reflectedRay, &reflectedColor);
//  color = directColor + reflection * reflectedcolor + transmission * refractedColor;
//}

void yourDebugDraw()
{
	//draw open gl debug stuff
	//this function is called every frame

	//let's draw the mesh
	MyMesh.draw();

	//let's draw the lights in the scene as points
	glPushAttrib(GL_ALL_ATTRIB_BITS); //store all GL attributes
	glDisable(GL_LIGHTING);
	glColor3f(1, 1, 1);
	glPointSize(10);
	glBegin(GL_POINTS);
	for (int i = 0; i<MyLightPositions.size(); ++i)
		glVertex3fv(MyLightPositions[i].pointer());
	glEnd();
	glPopAttrib();//restore all GL attributes
	//The Attrib commands maintain the state.
	//e.g., even though inside the two calls, we set
	//the color to white, it will be reset to the previous
	//state after the pop.

	//as an example: we draw the test ray, which is set by the keyboard function
	glPushAttrib(GL_ALL_ATTRIB_BITS);
	glDisable(GL_LIGHTING);
	glBegin(GL_LINES);
	glColor3f(testColor[0], testColor[1], testColor[2]);
	glVertex3f(testRayOrigin[0], testRayOrigin[1], testRayOrigin[2]);
	glColor3f(testColor[0], testColor[1], testColor[2]);
	glVertex3f(testRayDestination[0], testRayDestination[1], testRayDestination[2]);
	glEnd();
	glPointSize(10);
	glBegin(GL_POINTS);
	glVertex3fv(MyLightPositions[0].pointer());
	glEnd();
	glPopAttrib();

	//draw whatever else you want...
	////glutSolidSphere(1,10,10);
	////allows you to draw a sphere at the origin.
	////using a glTranslate, it can be shifted to whereever you want
	////if you produce a sphere renderer, this
	////triangulated sphere is nice for the preview
}

//yourKeyboardFunc is used to deal with keyboard input.
//t is the character that was pressed
//x,y is the mouse position in pixels
//rayOrigin, rayDestination is the ray that is going in the view direction UNDERNEATH your mouse position.
//
//A few keys are already reserved:
//'L' adds a light positioned at the camera location to the MyLightPositions vector
//'l' modifies the last added light to the current
//    camera position (by default, there is only one light, so move it with l)
//    ATTENTION These lights do NOT affect the real-time rendering.
//    You should use them for the raytracing.
//'r' calls the function performRaytracing on EVERY pixel, using the correct associated ray.
//    It then stores the result in an image "result.ppm".
//    Initially, this function is fast (performRaytracing simply returns
//    the target of the ray - see the code above), but once you replaced
//    this function and raytracing is in place, it might take a
//    while to complete...
void yourKeyboardFunc(char t, int x, int y, const Vec3Df & rayOrigin, const Vec3Df & rayDestination)
{

	//here, as an example, I use the ray to fill in the values for my upper global ray variable
	//I use these variables in the debugDraw function to draw the corresponding ray.
	//try it: Press a key, move the camera, see the ray that was launched as a line.
	testRayOrigin = rayOrigin;
	testRayDestination = rayDestination;
	testColor = performRayTracing(rayOrigin, rayDestination);

	std::cout << " The color from the ray is " << testColor[0] << "," << testColor[1] << "," << testColor[2] << std::endl;
	// do here, whatever you want with the keyboard input t.

	//      Trace(0, ray, &color);
	//      PutPixel(x, y, color);

	std::cout << t << " pressed! The mouse was in location " << x << "," << y << "!" << std::endl;
}

bool isNulVector(Vec3Df vector){
	Vec3Df nullVector = Vec3Df(0, 0, 0);
	return vector == nullVector;
}

// Pseudocode From slides

//RayTrace (view)
//{
//  for (y=0; y<view.yres; ++y){
//    for(x=0; x<view.xres; ++x){
//      ComputeRay(x, y, view, &ray);
//      Trace(0, ray, &color);
//      PutPixel(x, y, color);
//    }
//  }
//}
//
//Trace( level, ray, &color){
//  if(intersect(level, ray, max, &hit)) {
//    Shade(level, hit, &color);
//  }
//  else
//    color=BackgroundColor
//}
//
//Shade(level, hit, &color){
//  for each lightsource
//    ComputeDirectLight(hit, &directColor);
//  if(material reflects && (level < maxLevel))
//    computeReflectedRay(hit, &reflectedray);
//    Trace(level+1, reflectedRay, &reflectedColor);
//  color = directColor + reflection * reflectedcolor + transmission * refractedColor;
//}
<|endoftext|>


<commit_before>#include "vec3.hpp"
#include "mat4.hpp"
#include "Transform.hpp"
#include "Sphere.hpp"
#include "Plane.hpp"
#include <vector>
#include "zlib.h"
#include "png.h"
#include <stdint.h>
#include <memory>
#include "Sampling.hpp"
#include "Renderable.hpp"
#include "PerspectiveCamera.hpp"
#include "Image.hpp"
#include "Sample.hpp"

static const int SamplesPerPixel = 64;
static const int ImageWidth = 800;
static const int ImageHeight = 600;
static const int maxDepth = 5;

static int save_png_to_file(const char *path);
void readpng_version_info();
void generate_samples(std::vector<Sample>& samples, const RNG& rng);

vec3 brdf(const vec3& wo, const vec3& wi, const vec3& color)
{
	return color * float(M_1_PI);
}

int main(int argc, char* argv[])
{
	//Create the image
	Image render(ImageWidth, ImageHeight);
	//Samples
	RNG rng;
	std::vector<Sample> image_samples;
	image_samples.reserve((ImageWidth) * (ImageHeight) * SamplesPerPixel);
	//Generate image samples
	generate_samples(image_samples, rng);
	// Setup  camera
	Transform worldToCamera = Transform::lookat(vec3(0.0f, 0.0f, -200.0f), vec3(), vec3(0.0f, 1.0f, 0.0f));
	float aspectRatio = render.getAspectRatio();
	float hFOV = degreeToRadians(66.0f); //degrees
	float vFOV = 2 * atan(tan(hFOV / 2) * aspectRatio);
	float yScale = 1.0f / tan(vFOV / 2);
	float xScale = yScale / aspectRatio;
	float near = 0.1f; float far = 1000.0f;
	float right = near / xScale;
	float left = -right;
	float top = -near / yScale;
	float bottom = -top;
	std::unique_ptr<Camera> cam( new PerspectiveCamera(inv(worldToCamera),worldToCamera, left, right, top, bottom,
		near, far, float(ImageWidth), float(ImageHeight)));
	//readpng_version_info();
	// Setup scene
	std::vector<std::shared_ptr<Renderable> > objects;
	objects.reserve(10);
	//Add a sphere
	Transform transformSphere = Transform::translate(100.0, -100.0f, 150.0f);
	std::shared_ptr<Sphere> sphere(new Sphere(transformSphere, inv(transformSphere), 100.0f));
	std::shared_ptr<Material> whiteMat(new Material(vec3(1.0f, 1.0f, 1.0f)));
	std::shared_ptr<Plane> plane(new Plane(vec3(0.0, 0.0f, 200.0f), vec3(0.0f, 0.0f, -1.0f))); //Back wall
	std::shared_ptr<Plane> plane1(new Plane(vec3(0.0, 200.0f, 0.0f), vec3(0.0f, -1.0f, 0.0f))); //Ceiling
	std::shared_ptr<Plane> plane2(new Plane(vec3(0.0, -200.0f, 0.0f), vec3(0.0f, 1.0f, 0.0f))); //Floot
	std::shared_ptr<Plane> plane3(new Plane(vec3(-200.0, 0.0f, 0.0f), vec3(1.0f, 0.0f, 0.0f))); //Left Wall
	std::shared_ptr<Plane> plane4(new Plane(vec3(200.0, 0.0f, 0.0f), vec3(-1.0f, 0.0f, 0.0f))); //Right Wall
	std::shared_ptr<Material> blueMat(new Material(vec3(0.0f, 0.0f, 1.0f)));
	std::shared_ptr<Material> redMat(new Material(vec3(1.0f, 0.0f, 0.0f)));
	std::shared_ptr<Material> greenMat(new Material(vec3(0.0f, 1.0f, 0.0f)));
	objects.push_back(std::shared_ptr<Renderable>(new Renderable(sphere, whiteMat)));
	objects.push_back(std::shared_ptr<Renderable>(new Renderable(plane, whiteMat)));
	objects.push_back(std::shared_ptr<Renderable>(new Renderable(plane1, whiteMat)));
	objects.push_back(std::shared_ptr<Renderable>(new Renderable(plane2, whiteMat)));
	objects.push_back(std::shared_ptr<Renderable>(new Renderable(plane3, redMat)));
	objects.push_back(std::shared_ptr<Renderable>(new Renderable(plane4, greenMat)));
	std::shared_ptr<Intersection> intersection(new Intersection());
	Intersection trueIntersection;
	vec3 light(0.0, 190.0f, 100.0f);
	bool hit;
	unsigned int counter = 0;
	float lightIntensity = 10000.0f;
	unsigned int tnSamples = ImageWidth * ImageHeight * SamplesPerPixel;
	for each(auto& sample in image_samples)
	{
		int numHit = 0;
		Ray ray = cam->generateRay(sample.imageX, sample.imageY);
		//fprintf(stderr, "Casting ray in dir: %f,%f,%f \n", ray.dir.x, ray.dir.y, ray.dir.z);
		float tHit = std::numeric_limits<float>::infinity();
		for each(auto& surf in objects)
		{
			float tObj = 0;
			hit = surf->intersect(ray, &tObj, intersection);
			if (hit == false) continue;
			numHit += 1;
			if (tObj < tHit)
			{
				trueIntersection = *intersection;
				tHit = tObj;
			}
		}
		if (numHit == 0)
		{
			//imag
			render.addSample(sample, vec3());
			continue;
		}

		//One bounce?
		//fprintf(stderr, "Number of objects hit: %i\n", numHit);
		vec3 L = normalize(light - trueIntersection.ls->p);
		float d = (light - trueIntersection.ls->p).length(); d *= d;
		float ndl = dot(trueIntersection.ls->n, L);
		float lterm = std::max(ndl, 0.0f);
		vec3 color = brdf(vec3(), vec3(), trueIntersection.material->getColor());
		float rr = color.x * lterm * (lightIntensity / d);
		float gg = color.y * lterm * (lightIntensity / d);
		float bb = color.z * lterm * (lightIntensity / d);
		vec3 dddd(1.0, 1.0f, 1.0f);
		vec3 oldNorm = trueIntersection.ls->n;
		vec3 oldColor = color;
		for (int k = 0; k < maxDepth; ++k)
		{
			vec3 newRayDir = normalize(cosineSampleHemisphere(rng.nextFloat(), rng.nextFloat()));
			float pdf = cosineHemispherePdf(abs(newRayDir.z), 0.0f);
			vec3 n = trueIntersection.ls->n;
			vec3 dpdu = trueIntersection.ls->dpdu;
			vec3 last = normalize(cross(n, dpdu));
			Transform world2Shading = Transform::worldToShading(n, dpdu, last);
			Transform shading2World = inv(world2Shading);
			newRayDir = shading2World.transformVector(newRayDir);
			Ray newRay(trueIntersection.ls->p, newRayDir, tHit + 0.0001f);
			float v = abs(dot(oldNorm, newRayDir));
			dddd.x *= v * oldColor.x / pdf;
			dddd.y *= v * oldColor.y / pdf;
			dddd.z *= v * oldColor.z / pdf;
			numHit = 0;
			tHit = std::numeric_limits<float>::infinity();
			for each(auto& surf in objects)
			{
				float tObj = 0;
				hit = surf->intersect(newRay, &tObj, intersection);
				if (hit == false) continue;
				numHit += 1;
				if (tObj < tHit)
				{
					trueIntersection = *intersection;
					tHit = tObj;
				}
			}
			if (numHit == 0)
			{
				break;
			}
			L = normalize(light - trueIntersection.ls->p);
			d = (light - trueIntersection.ls->p).length(); d *= d;
			vec3 norm = trueIntersection.ls->n;
			ndl = dot(norm, L);
			lterm = std::max(ndl, 0.0f);
			color = brdf(vec3(), vec3(), trueIntersection.material->getColor());
			rr += color.x * color.x * lterm * (lightIntensity / d) *  dddd.x;
			gg += color.y * color.y * lterm * (lightIntensity / d) * dddd.y;
			bb += color.z * color.z * lterm * (lightIntensity / d) * dddd.z;
			oldNorm = norm;
			oldColor = color;
		}
		render.addSample(sample, vec3(rr, gg, bb));
		++counter;
		if (counter % 5000 == 0)
		{
			float progress = float(counter) / float(tnSamples);
			progress *= 100.0f;
			printf("We are  on ray number %u counter and %f percent done\n", counter, progress);
		}
	}

	render.saveImage("render.png");
}
//
//int save_png_to_file(const char *path)
//{
//	FILE * fp;
//	png_structp png_ptr = NULL;
//	png_infop info_ptr = NULL;
//	size_t x, y;
//	png_byte ** row_pointers = NULL;
//	/* "status" contains the return value of this function. At first
//	it is set to a value which means 'failure'. When the routine
//	has finished its work, it is set to a value which means
//	'success'. */
//	int status = -1;
//	/* The following number is set by trial and error only. I cannot
//	see where it it is documented in the libpng manual.
//	*/
//	int pixel_size = 3;
//	int depth = 8;
//
//	fp = fopen(path, "wb");
//	if (!fp) {
//		goto fopen_failed;
//	}
//
//	png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
//	if (png_ptr == NULL) {
//		goto png_create_write_struct_failed;
//	}
//
//	info_ptr = png_create_info_struct(png_ptr);
//	if (info_ptr == NULL) {
//		goto png_create_info_struct_failed;
//	}
//
//	/* Set up error handling. */
//
//	if (setjmp(png_jmpbuf(png_ptr))) {
//		goto png_failure;
//	}
//
//	/* Set image attributes. */
//
//	png_set_IHDR(png_ptr,
//		info_ptr,
//		IMWIDTH,
//		IMHEIGHT,
//		depth,
//		PNG_COLOR_TYPE_RGB,
//		PNG_INTERLACE_NONE,
//		PNG_COMPRESSION_TYPE_DEFAULT,
//		PNG_FILTER_TYPE_DEFAULT);
//
//	/* Initialize rows of PNG. */
//
//	row_pointers = (png_bytepp)png_malloc(png_ptr, IMHEIGHT * sizeof(png_byte *));
//	for (y = 0; y < IMHEIGHT; ++y) {
//		png_byte *row =
//			(png_bytep)png_malloc(png_ptr, sizeof(uint8_t) * IMWIDTH * pixel_size);
//		row_pointers[y] = row;
//		for (x = 0; x < IMWIDTH; ++x) {
//			*row++ = g_red[y * IMWIDTH + x];
//			*row++ = g_green[y * IMWIDTH + x];
//			*row++ = g_blue[y * IMWIDTH + x];
//		}
//	}
//
//	/* Write the image data to "fp". */
//
//	png_init_io(png_ptr, fp);
//	png_set_rows(png_ptr, info_ptr, row_pointers);
//	png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
//
//	/* The routine has successfully written the file, so we set
//	"status" to a value which indicates success. */
//
//	status = 0;
//
//	for (y = 0; y < IMHEIGHT; y++) {
//		png_free(png_ptr, row_pointers[y]);
//	}
//	png_free(png_ptr, row_pointers);
//
//png_failure:
//png_create_info_struct_failed:
//	png_destroy_write_struct(&png_ptr, &info_ptr);
//png_create_write_struct_failed:
//	fclose(fp);
//fopen_failed:
//	return status;
//}

void readpng_version_info()
{
	fprintf(stderr, "   Compiled with libpng %s; using libpng %s.\n",
		PNG_LIBPNG_VER_STRING, png_libpng_ver);
	fprintf(stderr, "   Compiled with zlib %s; using zlib %s.\n",
		ZLIB_VERSION, zlib_version);
}

void generate_samples(std::vector<Sample>& samples, const RNG& rng)
{
	std::vector<float> rawSamples;
	rawSamples.reserve(SamplesPerPixel * 2);
	int spp2 = SamplesPerPixel / 2;
	for (int y = 0; y < ImageHeight + 1; ++y)
	{
		for (int x = 0; x < ImageWidth + 1; ++x)
		{
			stratified2D(spp2, spp2, true, rawSamples, rng);
			for (int i = 0; i < SamplesPerPixel; ++i)
			{
				float tmpx = rawSamples[i * 2];
				float tmpy = rawSamples[i * 2 + 1];
				samples.push_back(Sample(tmpx + x, tmpy + y));
			}
			rawSamples.clear();
		}
	}
}<commit_msg>Commiting chagnges before branching to threading.<commit_after>#include "vec3.hpp"
#include "mat4.hpp"
#include "Transform.hpp"
#include "Sphere.hpp"
#include "Plane.hpp"
#include <vector>
#include "zlib.h"
#include "png.h"
#include <stdint.h>
#include <memory>
#include "Sampling.hpp"
#include "Renderable.hpp"
#include "PerspectiveCamera.hpp"
#include "Image.hpp"
#include "Sample.hpp"

static const int SamplesPerPixel = 4;
static const int ImageWidth = 800;
static const int ImageHeight = 600;
static const int maxDepth = 5;

static int save_png_to_file(const char *path);
void readpng_version_info();
void generate_samples(std::vector<Sample>& samples, const RNG& rng);

vec3 brdf(const vec3& wo, const vec3& wi, const vec3& color)
{
	return color * float(M_1_PI);
}

int main(int argc, char* argv[])
{
	//Create the image
	Image render(ImageWidth, ImageHeight);
	//Samples
	RNG rng;
	std::vector<Sample> image_samples;
	image_samples.reserve((ImageWidth) * (ImageHeight) * SamplesPerPixel);
	//Generate image samples
	generate_samples(image_samples, rng);
	// Setup  camera
	Transform worldToCamera = Transform::lookat(vec3(0.0f, 0.0f, -200.0f), vec3(), vec3(0.0f, 1.0f, 0.0f));
	float aspectRatio = render.getAspectRatio();
	float hFOV = degreeToRadians(66.0f); //degrees
	float vFOV = 2 * atan(tan(hFOV / 2) * aspectRatio);
	float yScale = 1.0f / tan(vFOV / 2);
	float xScale = yScale / aspectRatio;
	float near = 0.1f; float far = 1000.0f;
	float right = near / xScale;
	float left = -right;
	float top = -near / yScale;
	float bottom = -top;
	std::unique_ptr<Camera> cam( new PerspectiveCamera(inv(worldToCamera),worldToCamera, left, right, top, bottom,
		near, far, float(ImageWidth), float(ImageHeight)));
	//readpng_version_info();
	// Setup scene
	std::vector<std::shared_ptr<Renderable> > objects;
	objects.reserve(10);
	//Add a sphere
	Transform transformSphere = Transform::translate(100.0, -100.0f, 150.0f);
	std::shared_ptr<Sphere> sphere(new Sphere(transformSphere, inv(transformSphere), 100.0f));
	std::shared_ptr<Material> whiteMat(new Material(vec3(1.0f, 1.0f, 1.0f)));
	std::shared_ptr<Plane> plane(new Plane(vec3(0.0, 0.0f, 200.0f), vec3(0.0f, 0.0f, -1.0f))); //Back wall
	std::shared_ptr<Plane> plane1(new Plane(vec3(0.0, 200.0f, 0.0f), vec3(0.0f, -1.0f, 0.0f))); //Ceiling
	std::shared_ptr<Plane> plane2(new Plane(vec3(0.0, -200.0f, 0.0f), vec3(0.0f, 1.0f, 0.0f))); //Floot
	std::shared_ptr<Plane> plane3(new Plane(vec3(-200.0, 0.0f, 0.0f), vec3(1.0f, 0.0f, 0.0f))); //Left Wall
	std::shared_ptr<Plane> plane4(new Plane(vec3(200.0, 0.0f, 0.0f), vec3(-1.0f, 0.0f, 0.0f))); //Right Wall
	std::shared_ptr<Material> blueMat(new Material(vec3(0.0f, 0.0f, 1.0f)));
	std::shared_ptr<Material> redMat(new Material(vec3(1.0f, 0.0f, 0.0f)));
	std::shared_ptr<Material> greenMat(new Material(vec3(0.0f, 1.0f, 0.0f)));
	objects.push_back(std::shared_ptr<Renderable>(new Renderable(sphere, whiteMat)));
	objects.push_back(std::shared_ptr<Renderable>(new Renderable(plane, whiteMat)));
	objects.push_back(std::shared_ptr<Renderable>(new Renderable(plane1, whiteMat)));
	objects.push_back(std::shared_ptr<Renderable>(new Renderable(plane2, whiteMat)));
	objects.push_back(std::shared_ptr<Renderable>(new Renderable(plane3, redMat)));
	objects.push_back(std::shared_ptr<Renderable>(new Renderable(plane4, greenMat)));
	std::shared_ptr<Intersection> intersection(new Intersection());
	Intersection trueIntersection;
	vec3 light(0.0, 190.0f, 100.0f);
	bool hit;
	unsigned int counter = 0;
	float lightIntensity = 10000.0f;
	unsigned int tnSamples = ImageWidth * ImageHeight * SamplesPerPixel;
	for each(auto& sample in image_samples)
	{
		int numHit = 0;
		Ray ray = cam->generateRay(sample.imageX, sample.imageY);
		//fprintf(stderr, "Casting ray in dir: %f,%f,%f \n", ray.dir.x, ray.dir.y, ray.dir.z);
		float tHit = std::numeric_limits<float>::infinity();
		for each(auto& surf in objects)
		{
			float tObj = 0;
			hit = surf->intersect(ray, &tObj, intersection);
			if (hit == false) continue;
			numHit += 1;
			if (tObj < tHit)
			{
				trueIntersection = *intersection;
				tHit = tObj;
			}
		}
		if (numHit == 0)
		{
			//imag
			render.addSample(sample, vec3());
			continue;
		}

		//One bounce?
		//fprintf(stderr, "Number of objects hit: %i\n", numHit);
		vec3 L = normalize(light - trueIntersection.ls->p);
		float d = (light - trueIntersection.ls->p).length(); d *= d;
		float ndl = dot(trueIntersection.ls->n, L);
		float lterm = std::max(ndl, 0.0f);
		vec3 color = brdf(vec3(), vec3(), trueIntersection.material->getColor());
		float rr = color.x * lterm * (lightIntensity / d);
		float gg = color.y * lterm * (lightIntensity / d);
		float bb = color.z * lterm * (lightIntensity / d);
		vec3 dddd(1.0, 1.0f, 1.0f);
		vec3 oldNorm = trueIntersection.ls->n;
		vec3 oldColor = color;
		for (int k = 0; k < maxDepth; ++k)
		{
			vec3 newRayDir = normalize(cosineSampleHemisphere(rng.nextFloat(), rng.nextFloat()));
			float pdf = cosineHemispherePdf(abs(newRayDir.z), 0.0f);
			vec3 n = trueIntersection.ls->n;
			vec3 dpdu = trueIntersection.ls->dpdu;
			vec3 last = normalize(cross(n, dpdu));
			Transform world2Shading = Transform::worldToShading(n, dpdu, last);
			Transform shading2World = inv(world2Shading);
			newRayDir = shading2World.transformVector(newRayDir);
			Ray newRay(trueIntersection.ls->p, newRayDir, tHit + 0.0001f);
			float v = abs(dot(oldNorm, newRayDir));
			dddd.x *= v * oldColor.x / pdf;
			dddd.y *= v * oldColor.y / pdf;
			dddd.z *= v * oldColor.z / pdf;
			numHit = 0;
			tHit = std::numeric_limits<float>::infinity();
			for each(auto& surf in objects)
			{
				float tObj = 0;
				hit = surf->intersect(newRay, &tObj, intersection);
				if (hit == false) continue;
				numHit += 1;
				if (tObj < tHit)
				{
					trueIntersection = *intersection;
					tHit = tObj;
				}
			}
			if (numHit == 0)
			{
				break;
			}
			L = normalize(light - trueIntersection.ls->p);
			d = (light - trueIntersection.ls->p).length(); d *= d;
			vec3 norm = trueIntersection.ls->n;
			ndl = dot(norm, L);
			lterm = std::max(ndl, 0.0f);
			color = brdf(vec3(), vec3(), trueIntersection.material->getColor());
			rr += color.x * color.x * lterm * (lightIntensity / d) *  dddd.x;
			gg += color.y * color.y * lterm * (lightIntensity / d) * dddd.y;
			bb += color.z * color.z * lterm * (lightIntensity / d) * dddd.z;
			oldNorm = norm;
			oldColor = color;
		}
		render.addSample(sample, vec3(rr, gg, bb));
		++counter;
		if (counter % 5000 == 0)
		{
			float progress = float(counter) / float(tnSamples);
			progress *= 100.0f;
			printf("We are  on ray number %u counter and %f percent done\n", counter, progress);
		}
	}

	render.saveImage("render.png");
}
//
//int save_png_to_file(const char *path)
//{
//	FILE * fp;
//	png_structp png_ptr = NULL;
//	png_infop info_ptr = NULL;
//	size_t x, y;
//	png_byte ** row_pointers = NULL;
//	/* "status" contains the return value of this function. At first
//	it is set to a value which means 'failure'. When the routine
//	has finished its work, it is set to a value which means
//	'success'. */
//	int status = -1;
//	/* The following number is set by trial and error only. I cannot
//	see where it it is documented in the libpng manual.
//	*/
//	int pixel_size = 3;
//	int depth = 8;
//
//	fp = fopen(path, "wb");
//	if (!fp) {
//		goto fopen_failed;
//	}
//
//	png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
//	if (png_ptr == NULL) {
//		goto png_create_write_struct_failed;
//	}
//
//	info_ptr = png_create_info_struct(png_ptr);
//	if (info_ptr == NULL) {
//		goto png_create_info_struct_failed;
//	}
//
//	/* Set up error handling. */
//
//	if (setjmp(png_jmpbuf(png_ptr))) {
//		goto png_failure;
//	}
//
//	/* Set image attributes. */
//
//	png_set_IHDR(png_ptr,
//		info_ptr,
//		IMWIDTH,
//		IMHEIGHT,
//		depth,
//		PNG_COLOR_TYPE_RGB,
//		PNG_INTERLACE_NONE,
//		PNG_COMPRESSION_TYPE_DEFAULT,
//		PNG_FILTER_TYPE_DEFAULT);
//
//	/* Initialize rows of PNG. */
//
//	row_pointers = (png_bytepp)png_malloc(png_ptr, IMHEIGHT * sizeof(png_byte *));
//	for (y = 0; y < IMHEIGHT; ++y) {
//		png_byte *row =
//			(png_bytep)png_malloc(png_ptr, sizeof(uint8_t) * IMWIDTH * pixel_size);
//		row_pointers[y] = row;
//		for (x = 0; x < IMWIDTH; ++x) {
//			*row++ = g_red[y * IMWIDTH + x];
//			*row++ = g_green[y * IMWIDTH + x];
//			*row++ = g_blue[y * IMWIDTH + x];
//		}
//	}
//
//	/* Write the image data to "fp". */
//
//	png_init_io(png_ptr, fp);
//	png_set_rows(png_ptr, info_ptr, row_pointers);
//	png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
//
//	/* The routine has successfully written the file, so we set
//	"status" to a value which indicates success. */
//
//	status = 0;
//
//	for (y = 0; y < IMHEIGHT; y++) {
//		png_free(png_ptr, row_pointers[y]);
//	}
//	png_free(png_ptr, row_pointers);
//
//png_failure:
//png_create_info_struct_failed:
//	png_destroy_write_struct(&png_ptr, &info_ptr);
//png_create_write_struct_failed:
//	fclose(fp);
//fopen_failed:
//	return status;
//}

void readpng_version_info()
{
	fprintf(stderr, "   Compiled with libpng %s; using libpng %s.\n",
		PNG_LIBPNG_VER_STRING, png_libpng_ver);
	fprintf(stderr, "   Compiled with zlib %s; using zlib %s.\n",
		ZLIB_VERSION, zlib_version);
}

void generate_samples(std::vector<Sample>& samples, const RNG& rng)
{
	std::vector<float> rawSamples;
	rawSamples.reserve(SamplesPerPixel * 2);
	int spp2 = SamplesPerPixel / 2;
	for (int y = 0; y < ImageHeight + 1; ++y)
	{
		for (int x = 0; x < ImageWidth + 1; ++x)
		{
			stratified2D(spp2, spp2, true, rawSamples, rng);
			for (int i = 0; i < SamplesPerPixel; ++i)
			{
				float tmpx = rawSamples[i * 2];
				float tmpy = rawSamples[i * 2 + 1];
				samples.push_back(Sample(tmpx + x, tmpy + y));
			}
			rawSamples.clear();
		}
	}
}<|endoftext|>


<commit_before>#include "DualExActor_Tests.h"
#include "cryptoTools/Network/Endpoint.h"

#include "Common.h"
#include "cryptoTools/Common/Defines.h"
#include "DualEx/DualExActor.h"
#include "cryptoTools/Network/Channel.h"
#include "cryptoTools/Common/Log.h"
#include "DebugCircuits.h"

//#include "MyAssert.h"
#include <array>
using namespace osuCrypto;

void DualExActor_BitAdder_Complete_Test_Impl()
{
	u64 numExe = 32,
		bucketSize = 4,
		numOpened = 4,
		psiSecParam = 40;

	setThreadName("Actor1");
	//InitDebugPrinting("..\\test.out");

	Circuit c = AdderCircuit(4);
	//NetworkManager netMgr0("127.0.0.1", 1212, 4, true);
	//NetworkManager netMgr1("127.0.0.1", 1212, 4, false);

	std::string name("ss");
	IOService ios(0);
	Endpoint ep0(ios, "localhost", 1212, EpMode::Server, name);
	Endpoint ep1(ios, "localhost", 1212, EpMode::Server, name);
	//Channel& recvChannel = ep1.addChannel(name, name);
	//Channel& senderChannel = ep0.addChannel(name, name);
	{

		PRNG prng0(_mm_set_epi32(4253465, 3434565, 234435, 23987045));
		PRNG prng1(prng0.get<block>());
		BitVector input0(4);
		*input0.data() = 2;

		BitVector input1(4);
		*input1.data() = 3;

		BitVector expected(5);
		*expected.data() = 5;

		auto thrd = std::thread([&]() {
			setThreadName("Actor0");

			DualExActor actor0(c, Role::First, numExe, bucketSize, numOpened, psiSecParam, ep0);
			Timer timer;
			actor0.init(prng0, 4, 1, bucketSize, timer);

			for (u64 i = 0; i < numExe; ++i)
			{
				BitVector output = actor0.execute(i, prng0, input0, timer);

				for (u64 b = 0; b < expected.size(); ++b)
					if (output[b] != expected[b])
					{
						//std::cout << input0 << " " << input1 << "  " << expected << std::endl;
						//std::cout << "but got " << output << std::endl;
						throw UnitTestFail();
					}
			}
		});

		DualExActor actor1(c, Role::Second, numExe, bucketSize, numOpened, psiSecParam, ep1);
		Timer timer;
		actor1.init(prng1, 4, 1, bucketSize, timer);
		for (u64 i = 0; i < numExe; ++i)
		{

			BitVector output = actor1.execute(i, prng1, input1, timer);

			for (u64 b = 0; b < expected.size(); ++b)
				if (output[b] != expected[b])
					throw UnitTestFail();
		}

		thrd.join();
	}

	ep0.stop();
	ep1.stop();
	ios.stop();
}
void DualExActor_BitAdder_Concurrent_Test_Impl()
{
	u64 numExe = 32,
		bucketSize = 4,
		numOpened = 4,
		numConcurEvals = 4,
		psiSecParam = 40;

	setThreadName("Actor1");
	//InitDebugPrinting("..\\test.out");

	Circuit c = AdderCircuit(4);
	std::string name("ss");
	IOService ios(0);
	Endpoint ep0(ios, "localhost", 1212, EpMode::Server, name);
	Endpoint ep1(ios, "localhost", 1212, EpMode::Server, name);
	//Channel& recvChannel = ep1.addChannel(name, name);
	//Channel& senderChannel = ep0.addChannel(name, name);
	{
		PRNG prng0(_mm_set_epi32(4253465, 3434565, 234435, 23987045));
		PRNG prng1(prng0.get<block>());
		BitVector input0(4);
		*input0.data() = 2;

		BitVector input1(4);
		*input1.data() = 3;

		BitVector expected(5);
		*expected.data() = 5;

		auto thrd = std::thread([&]() {
			setThreadName("Actor0");

			DualExActor actor0(c, Role::First, numExe, bucketSize, numOpened, psiSecParam, ep0);
			Timer timer;
			actor0.init(prng0, 4, numConcurEvals, bucketSize, timer);
			std::vector<std::thread> evalThreads(numConcurEvals);

			for (u64 j = 0; j < numConcurEvals; ++j)
			{
				block seed = prng0.get<block>();
				evalThreads[j] = std::thread([&, j, seed]() {
					Timer timerj;
					PRNG prng(seed);
					for (u64 i = j; i < numExe; i += numConcurEvals)
					{

						BitVector output = actor0.execute(i, prng, input0, timerj);

						for (u64 b = 0; b < expected.size(); ++b)
							if (output[b] != expected[b])
								throw UnitTestFail();
					}
				});
			}
			for (auto& evalThrd : evalThreads)
				evalThrd.join();
		});

		DualExActor actor1(c, Role::Second, numExe, bucketSize, numOpened, psiSecParam, ep1);
		Timer timer;
		actor1.init(prng1, 4, numConcurEvals, bucketSize, timer);

		std::vector<std::thread> evalThreads(numConcurEvals);

		for (u64 j = 0; j < numConcurEvals; ++j)
		{
			block seed = prng1.get<block>();
			evalThreads[j] = std::thread([&actor1, j, seed, numExe, numConcurEvals, &input1, &expected]() {
				Timer timerj;
				PRNG prng(seed);
				for (u64 i = j; i < numExe; i += numConcurEvals)
				{
					BitVector output = actor1.execute(i, prng, input1, timerj);

					for (u64 b = 0; b < expected.size(); ++b)
						if (output[b] != expected[b])
							throw UnitTestFail();
				}
			});

		}
		for (auto& evalThrd : evalThreads)
			evalThrd.join();

		thrd.join();
	}

	ep0.stop();
	ep1.stop();
	ios.stop();

}
<commit_msg>unit test fix<commit_after>#include "DualExActor_Tests.h"
#include "cryptoTools/Network/Endpoint.h"

#include "Common.h"
#include "cryptoTools/Common/Defines.h"
#include "DualEx/DualExActor.h"
#include "cryptoTools/Network/Channel.h"
#include "cryptoTools/Common/Log.h"
#include "DebugCircuits.h"

//#include "MyAssert.h"
#include <array>
using namespace osuCrypto;

void DualExActor_BitAdder_Complete_Test_Impl()
{
	u64 numExe = 32,
		bucketSize = 4,
		numOpened = 4,
		psiSecParam = 40;

	setThreadName("Actor1");
	//InitDebugPrinting("..\\test.out");

	Circuit c = AdderCircuit(4);
	//NetworkManager netMgr0("127.0.0.1", 1212, 4, true);
	//NetworkManager netMgr1("127.0.0.1", 1212, 4, false);

	std::string name("ss");
	IOService ios(0);
	Endpoint ep0(ios, "localhost", 1212, EpMode::Server, name);
	Endpoint ep1(ios, "localhost", 1212, EpMode::Client, name);
	//Channel& recvChannel = ep1.addChannel(name, name);
	//Channel& senderChannel = ep0.addChannel(name, name);
	{

		PRNG prng0(_mm_set_epi32(4253465, 3434565, 234435, 23987045));
		PRNG prng1(prng0.get<block>());
		BitVector input0(4);
		*input0.data() = 2;

		BitVector input1(4);
		*input1.data() = 3;

		BitVector expected(5);
		*expected.data() = 5;

		auto thrd = std::thread([&]() {
			setThreadName("Actor0");

			DualExActor actor0(c, Role::First, numExe, bucketSize, numOpened, psiSecParam, ep0);
			Timer timer;
			actor0.init(prng0, 4, 1, bucketSize, timer);

			for (u64 i = 0; i < numExe; ++i)
			{
				BitVector output = actor0.execute(i, prng0, input0, timer);

				for (u64 b = 0; b < expected.size(); ++b)
					if (output[b] != expected[b])
					{
						//std::cout << input0 << " " << input1 << "  " << expected << std::endl;
						//std::cout << "but got " << output << std::endl;
						throw UnitTestFail();
					}
			}
		});

		DualExActor actor1(c, Role::Second, numExe, bucketSize, numOpened, psiSecParam, ep1);
		Timer timer;
		actor1.init(prng1, 4, 1, bucketSize, timer);
		for (u64 i = 0; i < numExe; ++i)
		{

			BitVector output = actor1.execute(i, prng1, input1, timer);

			for (u64 b = 0; b < expected.size(); ++b)
				if (output[b] != expected[b])
					throw UnitTestFail();
		}

		thrd.join();
	}

	ep0.stop();
	ep1.stop();
	ios.stop();
}
void DualExActor_BitAdder_Concurrent_Test_Impl()
{
	u64 numExe = 32,
		bucketSize = 4,
		numOpened = 4,
		numConcurEvals = 4,
		psiSecParam = 40;

	setThreadName("Actor1");
	//InitDebugPrinting("..\\test.out");

	Circuit c = AdderCircuit(4);
	std::string name("ss");
	IOService ios(0);
	Endpoint ep0(ios, "localhost", 1212, EpMode::Server, name);
	Endpoint ep1(ios, "localhost", 1212, EpMode::Client, name);
	//Channel& recvChannel = ep1.addChannel(name, name);
	//Channel& senderChannel = ep0.addChannel(name, name);
	{
		PRNG prng0(_mm_set_epi32(4253465, 3434565, 234435, 23987045));
		PRNG prng1(prng0.get<block>());
		BitVector input0(4);
		*input0.data() = 2;

		BitVector input1(4);
		*input1.data() = 3;

		BitVector expected(5);
		*expected.data() = 5;

		auto thrd = std::thread([&]() {
			setThreadName("Actor0");

			DualExActor actor0(c, Role::First, numExe, bucketSize, numOpened, psiSecParam, ep0);
			Timer timer;
			actor0.init(prng0, 4, numConcurEvals, bucketSize, timer);
			std::vector<std::thread> evalThreads(numConcurEvals);

			for (u64 j = 0; j < numConcurEvals; ++j)
			{
				block seed = prng0.get<block>();
				evalThreads[j] = std::thread([&, j, seed]() {
					Timer timerj;
					PRNG prng(seed);
					for (u64 i = j; i < numExe; i += numConcurEvals)
					{

						BitVector output = actor0.execute(i, prng, input0, timerj);

						for (u64 b = 0; b < expected.size(); ++b)
							if (output[b] != expected[b])
								throw UnitTestFail();
					}
				});
			}
			for (auto& evalThrd : evalThreads)
				evalThrd.join();
		});

		DualExActor actor1(c, Role::Second, numExe, bucketSize, numOpened, psiSecParam, ep1);
		Timer timer;
		actor1.init(prng1, 4, numConcurEvals, bucketSize, timer);

		std::vector<std::thread> evalThreads(numConcurEvals);

		for (u64 j = 0; j < numConcurEvals; ++j)
		{
			block seed = prng1.get<block>();
			evalThreads[j] = std::thread([&actor1, j, seed, numExe, numConcurEvals, &input1, &expected]() {
				Timer timerj;
				PRNG prng(seed);
				for (u64 i = j; i < numExe; i += numConcurEvals)
				{
					BitVector output = actor1.execute(i, prng, input1, timerj);

					for (u64 b = 0; b < expected.size(); ++b)
						if (output[b] != expected[b])
							throw UnitTestFail();
				}
			});

		}
		for (auto& evalThrd : evalThreads)
			evalThrd.join();

		thrd.join();
	}

}
<|endoftext|>