<commit_before>/**
 * This file is part of the CernVM File System.
 *
 * Fills an internal map of key-value pairs from ASCII files in key=value
 * style.  Parameters can be overwritten.  Used to read configuration from
 * /etc/cvmfs/...
 */

#include "cvmfs_config.h"
#include "options.h"

#include <unistd.h>

#include <cstdio>
#include <cassert>
#include <cstdlib>

#include <map>

#include "util.h"

using namespace std;  // NOLINT

#ifdef CVMFS_NAMESPACE_GUARD
namespace CVMFS_NAMESPACE_GUARD {
#endif

namespace options {

struct ConfigValue {
  string value;
  string source;
};

map<string, ConfigValue> *config_ = NULL;

void Init() {
  config_ = new map<string, ConfigValue>();
}

void Fini() {
  delete config_;
  config_ = NULL;
}

static string EscapeShell(const std::string &raw) {
  for (unsigned i = 0, l = raw.length(); i < l; ++i) {
    if (not (((raw[i] >= '0') && (raw[i] <= '9')) ||
             ((raw[i] >= 'A') && (raw[i] <= 'Z')) ||
             ((raw[i] >= 'a') && (raw[i] <= 'z')) ||
             (raw[i] == '/') || (raw[i] == ':') || (raw[i] == '.') ||
             (raw[i] == '_') || (raw[i] == '-') || (raw[i] == ',')))
    {
      goto escape_shell_quote;
    }
  }
  return raw;

 escape_shell_quote:
  string result = "'";
  for (unsigned i = 0, l = raw.length(); i < l; ++i) {
    if (raw[i] == '\'')
      result += "\\";
    result += raw[i];
  }
  result += "'";
  return result;
}

void ParsePath(const string &config_file) {
  FILE *fconfig = fopen(config_file.c_str(), "r");
  if (!fconfig)
    return;

  int retval;

  int fd_stdin;
  int fd_stdout;
  int fd_stderr;
  retval = Shell(&fd_stdin, &fd_stdout, &fd_stderr);
  assert(retval);

  // Let the shell read the file
  string line;
  const string newline = "\n";
  while (GetLineFile(fconfig, &line)) {
    WritePipe(fd_stdin, line.data(), line.length());
    WritePipe(fd_stdin, newline.data(), newline.length());
  }
  rewind(fconfig);

  // Read line by line and extract parameters
  while (GetLineFile(fconfig, &line)) {
    line = Trim(line);
    if (line.empty() || line[0] == '#' || line.find("if ") == 0)
      continue;
    vector<string> tokens = SplitString(line, '=');
    if (tokens.size() < 2)
      continue;

    ConfigValue value;
    value.source = config_file;
    string parameter = tokens[0];
    // Strip "readonly"
    if (parameter.find("readonly") == 0) {
      parameter = parameter.substr(8);
      parameter = Trim(parameter);
    }
    // Strip export
    if (parameter.find("export") == 0) {
      parameter = parameter.substr(6);
      parameter = Trim(parameter);
    }
    // Strip eval
    if (parameter.find("eval") == 0) {
      parameter = parameter.substr(4);
      parameter = Trim(parameter);
    }

    const string sh_echo = "echo $" + parameter + "\n";
    WritePipe(fd_stdin, sh_echo.data(), sh_echo.length());
    GetLineFd(fd_stdout, &value.value);
    (*config_)[parameter] = value;
    retval = setenv(parameter.c_str(), value.value.c_str(), 1);
    assert(retval == 0);
  }

  close(fd_stderr);
  close(fd_stdout);
  close(fd_stdin);
  fclose(fconfig);
}

void ParseDefault(const string &fqrn) {
  int retval = setenv("CVMFS_FQRN", fqrn.c_str(), 1);
  assert(retval == 0);

  ParsePath("/etc/cvmfs/default.conf");
  vector<string> dist_defaults = FindFiles("/etc/cvmfs/default.d", ".conf");
  for (unsigned i = 0; i < dist_defaults.size(); ++i) {
    ParsePath(dist_defaults[i]);
  }
  ParsePath("/etc/cernvm/default.conf");
  ParsePath("/etc/cvmfs/site.conf");
  ParsePath("/etc/cernvm/site.conf");
  ParsePath("/etc/cvmfs/default.local");

  if (fqrn != "") {
    string domain;
    vector<string> tokens = SplitString(fqrn, '.');
    assert(tokens.size() > 1);
    tokens.erase(tokens.begin());
    domain = JoinStrings(tokens, ".");

    ParsePath("/etc/cvmfs/domain.d/" + domain + ".conf");
    ParsePath("/etc/cvmfs/domain.d/" + domain + ".local");

    ParsePath("/etc/cvmfs/config.d/" + fqrn + ".conf");
    ParsePath("/etc/cvmfs/config.d/" + fqrn + ".local");
  }
}

void ClearConfig() {
  config_->clear();
}

bool IsDefined(const std::string &key) {
  map<string, ConfigValue>::const_iterator iter = config_->find(key);
  return iter != config_->end();
}

bool GetValue(const string &key, string *value) {
  map<string, ConfigValue>::const_iterator iter = config_->find(key);
  if (iter != config_->end()) {
    *value = iter->second.value;
    return true;
  }
  *value = "";
  return false;
}

bool GetSource(const string &key, string *value) {
  map<string, ConfigValue>::const_iterator iter = config_->find(key);
  if (iter != config_->end()) {
    *value = iter->second.source;
    return true;
  }
  *value = "";
  return false;
}

bool IsOn(const std::string &param_value) {
  const string uppercase = ToUpper(param_value);
  return ((uppercase == "YES") || (uppercase == "ON") || (uppercase == "1"));
}

vector<string> GetAllKeys() {
  vector<string> result;
  for (map<string, ConfigValue>::const_iterator i = config_->begin(),
       iEnd = config_->end(); i != iEnd; ++i)
  {
    result.push_back(i->first);
  }
  return result;
}

string Dump() {
  string result;
  vector<string> keys = GetAllKeys();
  for (unsigned i = 0, l = keys.size(); i < l; ++i) {
    bool retval;
    string value;
    string source;

    retval = GetValue(keys[i], &value);
    assert(retval);
    retval = GetSource(keys[i], &source);
    assert(retval);
    result += keys[i] + "=" + EscapeShell(value) +
              "    # from " + source + "\n";
  }
  return result;
}

bool ParseUIntMap(const string &path, map<uint64_t, uint64_t> *map) {
  assert(map);

  FILE *fmap = fopen(path.c_str(), "r");
  if (!fmap)
    return false;

  string line;
  while (GetLineFile(fmap, &line)) {
    line = Trim(line);
    if (line.empty() || line[0] == '#') {
      continue;
    }
    vector<string> components = SplitString(line, ' ');
    if (components.size() != 2) {
      fclose(fmap);
      return false;
    }
    uint64_t from = String2Uint64(components[0]);
    uint64_t to = String2Uint64(components[1]);
    map->insert(pair<uint64_t, uint64_t>(from, to));
  }
  fclose(fmap);
  return true;
}

}  // namespace options

#ifdef CVMFS_NAMESPACE_GUARD
}
#endif
<commit_msg>change directory to config file being parsed<commit_after>/**
 * This file is part of the CernVM File System.
 *
 * Fills an internal map of key-value pairs from ASCII files in key=value
 * style.  Parameters can be overwritten.  Used to read configuration from
 * /etc/cvmfs/...
 */

#include "cvmfs_config.h"
#include "options.h"

#include <unistd.h>

#include <cstdio>
#include <cassert>
#include <cstdlib>

#include <map>

#include "util.h"

using namespace std;  // NOLINT

#ifdef CVMFS_NAMESPACE_GUARD
namespace CVMFS_NAMESPACE_GUARD {
#endif

namespace options {

struct ConfigValue {
  string value;
  string source;
};

map<string, ConfigValue> *config_ = NULL;

void Init() {
  config_ = new map<string, ConfigValue>();
}

void Fini() {
  delete config_;
  config_ = NULL;
}

static string EscapeShell(const std::string &raw) {
  for (unsigned i = 0, l = raw.length(); i < l; ++i) {
    if (not (((raw[i] >= '0') && (raw[i] <= '9')) ||
             ((raw[i] >= 'A') && (raw[i] <= 'Z')) ||
             ((raw[i] >= 'a') && (raw[i] <= 'z')) ||
             (raw[i] == '/') || (raw[i] == ':') || (raw[i] == '.') ||
             (raw[i] == '_') || (raw[i] == '-') || (raw[i] == ',')))
    {
      goto escape_shell_quote;
    }
  }
  return raw;

 escape_shell_quote:
  string result = "'";
  for (unsigned i = 0, l = raw.length(); i < l; ++i) {
    if (raw[i] == '\'')
      result += "\\";
    result += raw[i];
  }
  result += "'";
  return result;
}

void ParsePath(const string &config_file) {
  FILE *fconfig = fopen(config_file.c_str(), "r");
  if (!fconfig)
    return;
  const string config_path = GetParentPath(config_file);

  int retval;

  int fd_stdin;
  int fd_stdout;
  int fd_stderr;
  retval = Shell(&fd_stdin, &fd_stdout, &fd_stderr);
  assert(retval);

  // Let the shell read the file
  string line;
  const string newline = "\n";
  const string cd = "cd \"" + ((config_path == "") ? "/" : config_path) + "\"" +
                    newline;
  WritePipe(fd_stdin, cd.data(), cd.length());
  while (GetLineFile(fconfig, &line)) {
    WritePipe(fd_stdin, line.data(), line.length());
    WritePipe(fd_stdin, newline.data(), newline.length());
  }
  rewind(fconfig);

  // Read line by line and extract parameters
  while (GetLineFile(fconfig, &line)) {
    line = Trim(line);
    if (line.empty() || line[0] == '#' || line.find("if ") == 0)
      continue;
    vector<string> tokens = SplitString(line, '=');
    if (tokens.size() < 2)
      continue;

    ConfigValue value;
    value.source = config_file;
    string parameter = tokens[0];
    // Strip "readonly"
    if (parameter.find("readonly") == 0) {
      parameter = parameter.substr(8);
      parameter = Trim(parameter);
    }
    // Strip export
    if (parameter.find("export") == 0) {
      parameter = parameter.substr(6);
      parameter = Trim(parameter);
    }
    // Strip eval
    if (parameter.find("eval") == 0) {
      parameter = parameter.substr(4);
      parameter = Trim(parameter);
    }

    const string sh_echo = "echo $" + parameter + "\n";
    WritePipe(fd_stdin, sh_echo.data(), sh_echo.length());
    GetLineFd(fd_stdout, &value.value);
    (*config_)[parameter] = value;
    retval = setenv(parameter.c_str(), value.value.c_str(), 1);
    assert(retval == 0);
  }

  close(fd_stderr);
  close(fd_stdout);
  close(fd_stdin);
  fclose(fconfig);
}

void ParseDefault(const string &fqrn) {
  int retval = setenv("CVMFS_FQRN", fqrn.c_str(), 1);
  assert(retval == 0);

  ParsePath("/etc/cvmfs/default.conf");
  vector<string> dist_defaults = FindFiles("/etc/cvmfs/default.d", ".conf");
  for (unsigned i = 0; i < dist_defaults.size(); ++i) {
    ParsePath(dist_defaults[i]);
  }
  ParsePath("/etc/cernvm/default.conf");
  ParsePath("/etc/cvmfs/site.conf");
  ParsePath("/etc/cernvm/site.conf");
  ParsePath("/etc/cvmfs/default.local");

  if (fqrn != "") {
    string domain;
    vector<string> tokens = SplitString(fqrn, '.');
    assert(tokens.size() > 1);
    tokens.erase(tokens.begin());
    domain = JoinStrings(tokens, ".");

    ParsePath("/etc/cvmfs/domain.d/" + domain + ".conf");
    ParsePath("/etc/cvmfs/domain.d/" + domain + ".local");

    ParsePath("/etc/cvmfs/config.d/" + fqrn + ".conf");
    ParsePath("/etc/cvmfs/config.d/" + fqrn + ".local");
  }
}

void ClearConfig() {
  config_->clear();
}

bool IsDefined(const std::string &key) {
  map<string, ConfigValue>::const_iterator iter = config_->find(key);
  return iter != config_->end();
}

bool GetValue(const string &key, string *value) {
  map<string, ConfigValue>::const_iterator iter = config_->find(key);
  if (iter != config_->end()) {
    *value = iter->second.value;
    return true;
  }
  *value = "";
  return false;
}

bool GetSource(const string &key, string *value) {
  map<string, ConfigValue>::const_iterator iter = config_->find(key);
  if (iter != config_->end()) {
    *value = iter->second.source;
    return true;
  }
  *value = "";
  return false;
}

bool IsOn(const std::string &param_value) {
  const string uppercase = ToUpper(param_value);
  return ((uppercase == "YES") || (uppercase == "ON") || (uppercase == "1"));
}

vector<string> GetAllKeys() {
  vector<string> result;
  for (map<string, ConfigValue>::const_iterator i = config_->begin(),
       iEnd = config_->end(); i != iEnd; ++i)
  {
    result.push_back(i->first);
  }
  return result;
}

string Dump() {
  string result;
  vector<string> keys = GetAllKeys();
  for (unsigned i = 0, l = keys.size(); i < l; ++i) {
    bool retval;
    string value;
    string source;

    retval = GetValue(keys[i], &value);
    assert(retval);
    retval = GetSource(keys[i], &source);
    assert(retval);
    result += keys[i] + "=" + EscapeShell(value) +
              "    # from " + source + "\n";
  }
  return result;
}

bool ParseUIntMap(const string &path, map<uint64_t, uint64_t> *map) {
  assert(map);

  FILE *fmap = fopen(path.c_str(), "r");
  if (!fmap)
    return false;

  string line;
  while (GetLineFile(fmap, &line)) {
    line = Trim(line);
    if (line.empty() || line[0] == '#') {
      continue;
    }
    vector<string> components = SplitString(line, ' ');
    if (components.size() != 2) {
      fclose(fmap);
      return false;
    }
    uint64_t from = String2Uint64(components[0]);
    uint64_t to = String2Uint64(components[1]);
    map->insert(pair<uint64_t, uint64_t>(from, to));
  }
  fclose(fmap);
  return true;
}

}  // namespace options

#ifdef CVMFS_NAMESPACE_GUARD
}
#endif
<|endoftext|>


<commit_before>#include "echoS.h"

#include <iostream>

class Server : public CORBA::servant_traits<Example::Echo>::base_type
{
public:
  Server() = default;
  virtual ~Server() {};
  virtual std::string echoString(const std::string& mesg) override;
};

std::string Server::echoString(const std::string& mesg)
{
  std::cout << "Got \"" << mesg << "\", sending it back." << std::endl;
  return mesg;
}

int main(int argc, char** argv)
{
	try {
    IDL::traits<CORBA::ORB>::ref_type orb = CORBA::ORB_init (argc, argv);
    IDL::traits<CORBA::Object>::ref_type obj = orb->resolve_initial_references ("RootPOA");
    IDL::traits<PortableServer::POA>::ref_type poa = IDL::traits<PortableServer::POA>::narrow (obj);

   CORBA::servant_traits<Example::Echo>::ref_type server = CORBA::make_reference<Server> ();

   PortableServer::ObjectId id = poa->activate_object (server);

   std::string sior(orb->object_to_string(server->_this()));
   std::cout << sior << std::endl;

   IDL::traits<PortableServer::POAManager>::ref_type pman = poa->the_POAManager ();

   pman->activate();

   orb->run();
   orb->destroy();
  }
  catch (CORBA::SystemException& ex) {
    std::cerr << "Caught CORBA::" << ex << std::endl;
  }
  catch (CORBA::Exception& ex) {
    std::cerr << "Caught CORBA::Exception: " << ex << std::endl;
  }
  catch (std::exception& ex) {
    std::cerr << "Caught std exception: " << ex.what() << std::endl;
  }
  return 0;
}
<commit_msg>Use space instead of tab<commit_after>#include "echoS.h"

#include <iostream>

class Server : public CORBA::servant_traits<Example::Echo>::base_type
{
public:
  Server() = default;
  virtual ~Server() {};
  virtual std::string echoString(const std::string& mesg) override;
};

std::string Server::echoString(const std::string& mesg)
{
  std::cout << "Got \"" << mesg << "\", sending it back." << std::endl;
  return mesg;
}

int main(int argc, char** argv)
{
  try {
    IDL::traits<CORBA::ORB>::ref_type orb = CORBA::ORB_init (argc, argv);
    IDL::traits<CORBA::Object>::ref_type obj = orb->resolve_initial_references ("RootPOA");
    IDL::traits<PortableServer::POA>::ref_type poa = IDL::traits<PortableServer::POA>::narrow (obj);

   CORBA::servant_traits<Example::Echo>::ref_type server = CORBA::make_reference<Server> ();

   PortableServer::ObjectId id = poa->activate_object (server);

   std::string sior(orb->object_to_string(server->_this()));
   std::cout << sior << std::endl;

   IDL::traits<PortableServer::POAManager>::ref_type pman = poa->the_POAManager ();

   pman->activate();

   orb->run();
   orb->destroy();
  }
  catch (CORBA::SystemException& ex) {
    std::cerr << "Caught CORBA::" << ex << std::endl;
  }
  catch (CORBA::Exception& ex) {
    std::cerr << "Caught CORBA::Exception: " << ex << std::endl;
  }
  catch (std::exception& ex) {
    std::cerr << "Caught std exception: " << ex.what() << std::endl;
  }
  return 0;
}
<|endoftext|>


<commit_before>/*
 *  Copyright 2019-2022 Diligent Graphics LLC
 *  Copyright 2015-2019 Egor Yusov
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *  In no event and under no legal theory, whether in tort (including negligence),
 *  contract, or otherwise, unless required by applicable law (such as deliberate
 *  and grossly negligent acts) or agreed to in writing, shall any Contributor be
 *  liable for any damages, including any direct, indirect, special, incidental,
 *  or consequential damages of any character arising as a result of this License or
 *  out of the use or inability to use the software (including but not limited to damages
 *  for loss of goodwill, work stoppage, computer failure or malfunction, or any and
 *  all other commercial damages or losses), even if such Contributor has been advised
 *  of the possibility of such damages.
 */

#include "BasicFileSystem.hpp"
#include "DebugUtilities.hpp"
#include <algorithm>

namespace Diligent
{

String BasicFileSystem::m_strWorkingDirectory;

BasicFile::BasicFile(const FileOpenAttribs& OpenAttribs) :
    m_Path //
    {
        [](const Char* strPath) //
        {
            String Path = strPath != nullptr ? strPath : "";
            BasicFileSystem::CorrectSlashes(Path);
            return Path;
        }(OpenAttribs.strFilePath) //
    },
    m_OpenAttribs{m_Path.c_str(), OpenAttribs.AccessMode}
{
}

BasicFile::~BasicFile()
{
}

String BasicFile::GetOpenModeStr()
{
    std::string OpenModeStr;
    switch (m_OpenAttribs.AccessMode)
    {
        // clang-format off
        case EFileAccessMode::Read:      OpenModeStr += 'r'; break;
        case EFileAccessMode::Overwrite: OpenModeStr += 'w'; break;
        case EFileAccessMode::Append:    OpenModeStr += 'a'; break;
        // clang-format on
        default: break;
    }

    // Always open file in binary mode. Text mode is platform-specific
    OpenModeStr += 'b';

    return OpenModeStr;
}

BasicFile* BasicFileSystem::OpenFile(FileOpenAttribs& OpenAttribs)
{
    return nullptr;
}

void BasicFileSystem::ReleaseFile(BasicFile* pFile)
{
    if (pFile)
        delete pFile;
}

bool BasicFileSystem::FileExists(const Char* strFilePath)
{
    return false;
}

void BasicFileSystem::CorrectSlashes(String& Path, Char Slash)
{
    if (Slash != 0)
        DEV_CHECK_ERR(IsSlash(Slash), "Incorrect slash symbol");
    else
        Slash = SlashSymbol;
    Char RevSlashSym = (Slash == '\\') ? '/' : '\\';
    std::replace(Path.begin(), Path.end(), RevSlashSym, Slash);
}

void BasicFileSystem::GetPathComponents(const String& Path,
                                        String*       Directory,
                                        String*       FileName)
{
    auto LastSlashPos = Path.find_last_of("/\\");
    if (Directory)
    {
        if (LastSlashPos != String::npos)
            *Directory = Path.substr(0, LastSlashPos);
        else
            *Directory = "";
    }

    if (FileName)
    {
        if (LastSlashPos != String::npos)
            *FileName = Path.substr(LastSlashPos + 1);
        else
            *FileName = Path;
    }
}

bool BasicFileSystem::IsPathAbsolute(const Char* strPath)
{
    if (strPath == nullptr || strPath[0] == 0)
        return false;

#if PLATFORM_WIN32 || PLATFORM_UNIVERSAL_WINDOWS
    return ((strPath[1] == ':' && (strPath[2] == '\\' || strPath[2] == '/')) || // c:\Path or c:/Path
            (strPath[0] == '\\' && strPath[1] == '\\'));                        // \\?\Path
#elif PLATFORM_LINUX || PLATFORM_MACOS || PLATFORM_IOS || PLATFORM_TVOS || PLATFORM_ANDROID || PLATFORM_EMSCRIPTEN
    return strPath[0] == '/';
#else
#    error Unknown platform.
#endif
}

std::vector<String> BasicFileSystem::SplitPath(const Char* Path, bool Simplify)
{
    std::vector<std::string> Components;

    const auto* c = Path;
    while (*c != '\0')
    {
        while (*c != '\0' && IsSlash(*c))
            ++c;

        if (*c == '\0')
        {
            // a/
            break;
        }

        std::string PathCmp;
        while (*c != '\0' && !IsSlash(*c))
            PathCmp.push_back(*(c++));

        if (Simplify)
        {
            if (PathCmp == ".")
            {
                // Skip /.
                continue;
            }
            else if (PathCmp == ".." && !Components.empty() && Components.back() != "..")
            {
                // Pop previous subdirectory if /.. is found, but only if there is no .. already
                Components.pop_back();
                continue;
            }
        }

        Components.emplace_back(std::move(PathCmp));
    }

    return Components;
}

std::string BasicFileSystem::SimplifyPath(const Char* Path, Char Slash)
{
    if (Path == nullptr)
        return "";

    if (Slash != 0)
        DEV_CHECK_ERR(IsSlash(Slash), "Incorrect slash symbol");
    else
        Slash = SlashSymbol;

    const auto PathComponents = SplitPath(Path, true);

    std::string SimplifiedPath;
    for (const auto& Cmp : PathComponents)
    {
        if (!SimplifiedPath.empty())
            SimplifiedPath.push_back(Slash);
        SimplifiedPath.append(Cmp);
    }
    return SimplifiedPath;
}

std::string BasicFileSystem::GetRelativePath(const Char* PathFrom,
                                             bool        IsFromDirectory,
                                             const Char* PathTo,
                                             bool /*IsToDirectory*/)
{
    DEV_CHECK_ERR(PathFrom != nullptr, "Source path must not be null");
    DEV_CHECK_ERR(PathTo != nullptr, "Destination path must not be null");

    const auto FromPathComps = SplitPath(PathFrom, true);
    const auto ToPathComps   = SplitPath(PathTo, true);

    auto from_it = FromPathComps.begin();
    auto to_it   = ToPathComps.begin();
    while (from_it != FromPathComps.end() && to_it != ToPathComps.end() && *from_it == *to_it)
    {
        ++from_it;
        ++to_it;
    }
    if (from_it == FromPathComps.begin())
        return PathFrom; // No common prefix

    String RelPath;
    for (; from_it != FromPathComps.end(); ++from_it)
    {
        if (!IsFromDirectory && from_it + 1 == FromPathComps.end())
        {
            //                    from_it
            //                       V
            // from:    "common/from/file"
            // to:      "common/to"
            // RelPath: ".."
            break;
        }

        if (!RelPath.empty())
            RelPath.push_back(SlashSymbol);
        RelPath.append("..");
    }

    for (; to_it != ToPathComps.end(); ++to_it)
    {
        // IsToDirectory is in fact irrelevant
        if (!RelPath.empty())
            RelPath.push_back(SlashSymbol);
        RelPath.append(*to_it);
    }

    return RelPath;
}

} // namespace Diligent
<commit_msg>Some optimizations to BasicFileSystem::SplitPath<commit_after>/*
 *  Copyright 2019-2022 Diligent Graphics LLC
 *  Copyright 2015-2019 Egor Yusov
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *  In no event and under no legal theory, whether in tort (including negligence),
 *  contract, or otherwise, unless required by applicable law (such as deliberate
 *  and grossly negligent acts) or agreed to in writing, shall any Contributor be
 *  liable for any damages, including any direct, indirect, special, incidental,
 *  or consequential damages of any character arising as a result of this License or
 *  out of the use or inability to use the software (including but not limited to damages
 *  for loss of goodwill, work stoppage, computer failure or malfunction, or any and
 *  all other commercial damages or losses), even if such Contributor has been advised
 *  of the possibility of such damages.
 */

#include "BasicFileSystem.hpp"
#include "DebugUtilities.hpp"
#include <algorithm>

namespace Diligent
{

String BasicFileSystem::m_strWorkingDirectory;

BasicFile::BasicFile(const FileOpenAttribs& OpenAttribs) :
    m_Path //
    {
        [](const Char* strPath) //
        {
            String Path = strPath != nullptr ? strPath : "";
            BasicFileSystem::CorrectSlashes(Path);
            return Path;
        }(OpenAttribs.strFilePath) //
    },
    m_OpenAttribs{m_Path.c_str(), OpenAttribs.AccessMode}
{
}

BasicFile::~BasicFile()
{
}

String BasicFile::GetOpenModeStr()
{
    std::string OpenModeStr;
    switch (m_OpenAttribs.AccessMode)
    {
        // clang-format off
        case EFileAccessMode::Read:      OpenModeStr += 'r'; break;
        case EFileAccessMode::Overwrite: OpenModeStr += 'w'; break;
        case EFileAccessMode::Append:    OpenModeStr += 'a'; break;
        // clang-format on
        default: break;
    }

    // Always open file in binary mode. Text mode is platform-specific
    OpenModeStr += 'b';

    return OpenModeStr;
}

BasicFile* BasicFileSystem::OpenFile(FileOpenAttribs& OpenAttribs)
{
    return nullptr;
}

void BasicFileSystem::ReleaseFile(BasicFile* pFile)
{
    if (pFile)
        delete pFile;
}

bool BasicFileSystem::FileExists(const Char* strFilePath)
{
    return false;
}

void BasicFileSystem::CorrectSlashes(String& Path, Char Slash)
{
    if (Slash != 0)
        DEV_CHECK_ERR(IsSlash(Slash), "Incorrect slash symbol");
    else
        Slash = SlashSymbol;
    Char RevSlashSym = (Slash == '\\') ? '/' : '\\';
    std::replace(Path.begin(), Path.end(), RevSlashSym, Slash);
}

void BasicFileSystem::GetPathComponents(const String& Path,
                                        String*       Directory,
                                        String*       FileName)
{
    auto LastSlashPos = Path.find_last_of("/\\");
    if (Directory)
    {
        if (LastSlashPos != String::npos)
            *Directory = Path.substr(0, LastSlashPos);
        else
            *Directory = "";
    }

    if (FileName)
    {
        if (LastSlashPos != String::npos)
            *FileName = Path.substr(LastSlashPos + 1);
        else
            *FileName = Path;
    }
}

bool BasicFileSystem::IsPathAbsolute(const Char* strPath)
{
    if (strPath == nullptr || strPath[0] == 0)
        return false;

#if PLATFORM_WIN32 || PLATFORM_UNIVERSAL_WINDOWS
    return ((strPath[1] == ':' && (strPath[2] == '\\' || strPath[2] == '/')) || // c:\Path or c:/Path
            (strPath[0] == '\\' && strPath[1] == '\\'));                        // \\?\Path
#elif PLATFORM_LINUX || PLATFORM_MACOS || PLATFORM_IOS || PLATFORM_TVOS || PLATFORM_ANDROID || PLATFORM_EMSCRIPTEN
    return strPath[0] == '/';
#else
#    error Unknown platform.
#endif
}

std::vector<String> BasicFileSystem::SplitPath(const Char* Path, bool Simplify)
{
    std::vector<std::string> Components;

    // Estimate the number of components and reserve space in the vector
    {
        size_t CompnentCount = 1;
        for (const auto* c = Path; *c != '\0'; ++c)
        {
            if (IsSlash(c[0]) && (c == Path || !IsSlash(c[-1])))
                ++CompnentCount;
        }
        Components.reserve(CompnentCount);
    }

    const auto* c = Path;
    while (*c != '\0')
    {
        while (*c != '\0' && IsSlash(*c))
            ++c;

        if (*c == '\0')
        {
            // a/
            break;
        }

        const auto* const CmpStart = c;
        while (*c != '\0' && !IsSlash(*c))
            ++c;
        std::string PathCmp{CmpStart, c};

        if (Simplify)
        {
            if (PathCmp == ".")
            {
                // Skip /.
                continue;
            }
            else if (PathCmp == ".." && !Components.empty() && Components.back() != "..")
            {
                // Pop previous subdirectory if /.. is found, but only if there is no .. already
                Components.pop_back();
                continue;
            }
        }

        Components.emplace_back(std::move(PathCmp));
    }

    return Components;
}

std::string BasicFileSystem::SimplifyPath(const Char* Path, Char Slash)
{
    if (Path == nullptr)
        return "";

    if (Slash != 0)
        DEV_CHECK_ERR(IsSlash(Slash), "Incorrect slash symbol");
    else
        Slash = SlashSymbol;

    const auto PathComponents = SplitPath(Path, true);

    std::string SimplifiedPath;
    for (const auto& Cmp : PathComponents)
    {
        if (!SimplifiedPath.empty())
            SimplifiedPath.push_back(Slash);
        SimplifiedPath.append(Cmp);
    }
    return SimplifiedPath;
}

std::string BasicFileSystem::GetRelativePath(const Char* PathFrom,
                                             bool        IsFromDirectory,
                                             const Char* PathTo,
                                             bool /*IsToDirectory*/)
{
    DEV_CHECK_ERR(PathFrom != nullptr, "Source path must not be null");
    DEV_CHECK_ERR(PathTo != nullptr, "Destination path must not be null");

    const auto FromPathComps = SplitPath(PathFrom, true);
    const auto ToPathComps   = SplitPath(PathTo, true);

    auto from_it = FromPathComps.begin();
    auto to_it   = ToPathComps.begin();
    while (from_it != FromPathComps.end() && to_it != ToPathComps.end() && *from_it == *to_it)
    {
        ++from_it;
        ++to_it;
    }
    if (from_it == FromPathComps.begin())
        return PathFrom; // No common prefix

    String RelPath;
    for (; from_it != FromPathComps.end(); ++from_it)
    {
        if (!IsFromDirectory && from_it + 1 == FromPathComps.end())
        {
            //                    from_it
            //                       V
            // from:    "common/from/file"
            // to:      "common/to"
            // RelPath: ".."
            break;
        }

        if (!RelPath.empty())
            RelPath.push_back(SlashSymbol);
        RelPath.append("..");
    }

    for (; to_it != ToPathComps.end(); ++to_it)
    {
        // IsToDirectory is in fact irrelevant
        if (!RelPath.empty())
            RelPath.push_back(SlashSymbol);
        RelPath.append(*to_it);
    }

    return RelPath;
}

} // namespace Diligent
<|endoftext|>


<commit_before>#include "glcamera.h"

#ifdef __APPLE__
    #include <GLUT/glut.h>
#else
    #include <glut.h>
#endif

#include <QDebug>

GLCamera::GLCamera()
{
    moveSpeed = 10.0;

    x = 0.0;
    y = 0.0;
    z = 1.0;

    look_x = 0.0;
    look_y = 0.0;
    look_z = 0.0;

    zoomLevel = 1.0;
    zoomSpeed = 0.05;

    // lat="46.791803,9" lon="4.427704,7"
    planeLeft = 437.0;
    planeRight = 449.0;

    planeBottom = 4674.0;
    planeTop = 4686.0;

    planeNear = 0.0;
    planeFar = 10.0;

    canvasWidth = 0;
    canvasHeight = 0;
}

void GLCamera::left()
{
    x -= (moveSpeed * (1 / std::abs(zoomLevel)));
    look_x -= (moveSpeed * (1 / std::abs(zoomLevel)));
}

void GLCamera::right()
{
    x += (moveSpeed * (1 / std::abs(zoomLevel)));;
    look_x += (moveSpeed * (1 / std::abs(zoomLevel)));;
}

void GLCamera::up()
{
    y += (moveSpeed * (1 / std::abs(zoomLevel)));;
    look_y += (moveSpeed * (1 / std::abs(zoomLevel)));;
}

void GLCamera::down()
{
    y -= (moveSpeed * (1 / std::abs(zoomLevel)));;
    look_y -= (moveSpeed * (1 / std::abs(zoomLevel)));;
}

void GLCamera::move(int dx, int dy)
{
    // TODO : Need good scrolling
    x -= (dx * (0.10 * (1 / std::abs(zoomLevel))));
    look_x -= (dx * (0.10 * (1 / std::abs(zoomLevel))));
    y += (dy * (0.10 * (1 / std::abs(zoomLevel))));
    look_y += (dy * (0.10 * (1 / std::abs(zoomLevel))));
}

void GLCamera::zoomIn()
{
    if(zoomLevel == 121)
        return;

    if(planeRight - planeLeft <= 0.1 || planeTop - planeBottom <= 0.1)
        return;

    zoomLevel += 1;

    planeLeft += zoomSpeed;
    planeRight -= zoomSpeed;
    planeBottom += zoomSpeed;
    planeTop -= zoomSpeed;

    setViewingVolume();
}

void GLCamera::zoomOut()
{
    if(zoomLevel == 1)
        return;

    zoomLevel -= 1;

    planeLeft -= zoomSpeed;
    planeRight += zoomSpeed;
    planeBottom -= zoomSpeed;
    planeTop += zoomSpeed;

    setViewingVolume();
}

void GLCamera::setViewingVolume()
{
    glViewport(0, 0, canvasWidth, canvasHeight);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    GLdouble dx = planeRight - planeLeft;
    GLdouble dy = planeTop - planeBottom;

    if(fabs(dx) > 0.001 || fabs(dy) > 0.001)//Controls how far you can zoom in
    {
        if(dx >= dy)
        {
            GLdouble dY = dx * canvasHeight / canvasWidth;
            GLdouble yMax = planeBottom + dY;
            glOrtho(planeLeft, planeRight, planeBottom, yMax, planeNear, planeFar);
        }
        else
        {
            GLdouble dX = dy * canvasWidth / canvasHeight;
            GLdouble xMax = planeLeft + dX;
            glOrtho(planeLeft, xMax, planeBottom, planeTop, planeNear, planeFar);
        }
    }

//    glOrtho(planeLeft,
//            planeRight,
//            planeBottom,
//            planeTop,
//            planeNear,
//            planeFar);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

void GLCamera::center()
{
    x = 0.0;
    y = 0.0;
    z = 1.0;

    look_x = 0.0;
    look_y = 0.0;
    look_z = 0.0;
}

<commit_msg>Scaling Map Issue is NOT resolved<commit_after>#include <cmath>
#include "glcamera.h"

#ifdef __APPLE__
    #include <GLUT/glut.h>
#else
    #include <glut.h>
#endif

#include <QDebug>

GLCamera::GLCamera()
{
    moveSpeed = 10.0;

    x = 0.0;
    y = 0.0;
    z = 1.0;

    look_x = 0.0;
    look_y = 0.0;
    look_z = 0.0;

    zoomLevel = 1.0;
    zoomSpeed = 0.05;

    // lat="46.791803,9" lon="4.427704,7"
    planeLeft = 437.0;
    planeRight = 449.0;

    planeBottom = 4674.0;
    planeTop = 4686.0;

    planeNear = 0.0;
    planeFar = 10.0;

    canvasWidth = 0;
    canvasHeight = 0;
}

void GLCamera::left()
{
    x -= (moveSpeed * (1 / std::abs(zoomLevel)));
    look_x -= (moveSpeed * (1 / std::abs(zoomLevel)));
}

void GLCamera::right()
{
    x += (moveSpeed * (1 / std::abs(zoomLevel)));;
    look_x += (moveSpeed * (1 / std::abs(zoomLevel)));;
}

void GLCamera::up()
{
    y += (moveSpeed * (1 / std::abs(zoomLevel)));;
    look_y += (moveSpeed * (1 / std::abs(zoomLevel)));;
}

void GLCamera::down()
{
    y -= (moveSpeed * (1 / std::abs(zoomLevel)));;
    look_y -= (moveSpeed * (1 / std::abs(zoomLevel)));;
}

void GLCamera::move(int dx, int dy)
{
    // TODO : Need good scrolling
    x -= (dx * (0.10 * (1 / std::abs(zoomLevel))));
    look_x -= (dx * (0.10 * (1 / std::abs(zoomLevel))));
    y += (dy * (0.10 * (1 / std::abs(zoomLevel))));
    look_y += (dy * (0.10 * (1 / std::abs(zoomLevel))));
}

void GLCamera::zoomIn()
{
    if(zoomLevel == 121)
        return;

    if(planeRight - planeLeft <= 0.1 || planeTop - planeBottom <= 0.1)
        return;

    zoomLevel += 1;

    planeLeft += zoomSpeed;
    planeRight -= zoomSpeed;
    planeBottom += zoomSpeed;
    planeTop -= zoomSpeed;

    setViewingVolume();
}

void GLCamera::zoomOut()
{
    if(zoomLevel == 1)
        return;

    zoomLevel -= 1;

    planeLeft -= zoomSpeed;
    planeRight += zoomSpeed;
    planeBottom -= zoomSpeed;
    planeTop += zoomSpeed;

    setViewingVolume();
}

void GLCamera::setViewingVolume()
{
    glViewport(0, 0, canvasWidth, canvasHeight);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    GLdouble dx = planeRight - planeLeft;
    GLdouble dy = planeTop - planeBottom;

    if(fabs(dx) > 0.001 || fabs(dy) > 0.001)//Controls how far you can zoom in
    {
        if(dx >= dy)
        {
            GLdouble dY = dx * canvasHeight / canvasWidth;
            GLdouble yMax = planeBottom + dY;
            glOrtho(planeLeft, planeRight, planeBottom, yMax, planeNear, planeFar);
        }
        else
        {
            GLdouble dX = dy * canvasWidth / canvasHeight;
            GLdouble xMax = planeLeft + dX;
            glOrtho(planeLeft, xMax, planeBottom, planeTop, planeNear, planeFar);
        }
    }

//    glOrtho(planeLeft,
//            planeRight,
//            planeBottom,
//            planeTop,
//            planeNear,
//            planeFar);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

void GLCamera::center()
{
    x = 0.0;
    y = 0.0;
    z = 1.0;

    look_x = 0.0;
    look_y = 0.0;
    look_z = 0.0;
}

<|endoftext|>


<commit_before>/***************************************************************************
 *  @file       merge_sort_for_list.hpp
 *  @author     Alan.W
 *  @date       15   Jun 2014
 *  @remark     This code is for Introduction to Algorithms
 *  @note
 ***************************************************************************/
//!
//! merge_sort_for_list(node)
//! complexity  O(n lg n)
//!             no extra space needed
//! pseudo code:
/*      merge_sort_for_list(node)
 * 1    if(!node || !node->next)
 * 2        return node
 * 3    def left, right, temp, last
 * 4    left = right = temp = last = node
 * 5    while(temp && temp->next)   //search for the half node
 * 6        last    = right
 * 7        right   = right->next
 * 8        temp    = temp->next->next
 * 9    last->next = nullptr        //break into two lists
 * 10   left    = merge_sort_for_list(left)
 * 11   right   = merge_sort_for_list(right)
 * 12   def ret, next
 * 13   while(left || right)
 * 14       if(!right)
 * 15           next    =   left
 * 16           left    =   left->next
 * 17       else if(!left)
 * 18           next    =   right
 * 19           right   =   right->next
 * 20       else if(left->key < right->key)
 * 21           next    =   left
 * 22           left    =   left->next
 * 23       else
 * 24           next    =   right
 * 25           right   =   right->next
 * 26       if(!ret)
 * 27           ret     =   next
 * 28   return ret;
 */

#ifndef MERGE_SORT_FOR_LIST_HPP
#define MERGE_SORT_FOR_LIST_HPP

#include "list.hpp"
#include <memory>

namespace ch10 {

template<typename T>
std::shared_ptr<list::node<T> >
merge_sort_for_list(const std::shared_ptr<list::node<T> >& node);

/**
 * @brief merge_sort_for_list
 *
 * @complexity  time:   O(n lg n)
 *              space:  no extra space needed, compared with the same algorithm on array
 *                      which requires O(n) extra space.
 *
 * check above for pseud code which is based on an alorithm found on SO:
 * http://stackoverflow.com/questions/7685/merge-sort-a-linked-list
 */
template<typename T>
inline std::shared_ptr<list::node<T> >
merge_sort_for_list(const std::shared_ptr<list::node<T> >& node)
{
    using ValueType = T;
    using Node      = list::node<ValueType>;
    using sPointer  = std::shared_ptr<Node>;

    //! recursion stopping condition
    if(!node || !node->next)    return node;

    //! search for the middle node
    sPointer left, right, fast, last;
    left = right = fast = last = node;
    while(fast && fast->next)
    {
        last    =   right;
        right   =   right->next;
        fast    =   fast->next->next;
    }

    //! split the original linked list
    last->next  =   nullptr;

    //! recur
    left    =   merge_sort_for_list(left);
    right   =   merge_sort_for_list(right);

    //! merge
    sPointer ret, next;
    while(left || right)
    {
        if(!right)      //when right part is exhausted.
        {
            next    =   left;
            left    =   left->next;
        }
        else if(!left)  //when left  part is exhausted.
        {
            next    =   right;
            right   =   right->next;
        }
        else if(left->key < left->key)
        {
            next    =   left;
            left    =   left->next;
        }
        else
        {
            next    =   right;
            right   =   right->next;
        }

        if(!ret)
            ret     =   next;
    }
    return ret;
}

}//namespace
#endif // MERGE_SORT_FOR_LIST_HPP
<commit_msg>fixed a bug 	modified:   ch10/merge_sort_for_list.hpp<commit_after>/***************************************************************************
 *  @file       merge_sort_for_list.hpp
 *  @author     Alan.W
 *  @date       15   Jun 2014
 *  @remark     This code is for Introduction to Algorithms
 *  @note
 ***************************************************************************/
//!
//! merge_sort_for_list(node)
//! complexity  O(n lg n)
//!             no extra space needed
//! pseudo code:
/*      merge_sort_for_list(node)
 * 1    if(!node || !node->next)
 * 2        return node
 * 3    def left, right, temp, last
 * 4    left = right = temp = last = node
 * 5    while(temp && temp->next)   //search for the half node
 * 6        last    = right
 * 7        right   = right->next
 * 8        temp    = temp->next->next
 * 9    last->next = nullptr        //break into two lists
 * 10   left    = merge_sort_for_list(left)
 * 11   right   = merge_sort_for_list(right)
 * 12   def ret, next
 * 13   while(left || right)
 * 14       if(!right)
 * 15           next    =   left
 * 16           left    =   left->next
 * 17       else if(!left)
 * 18           next    =   right
 * 19           right   =   right->next
 * 20       else if(left->key < right->key)
 * 21           next    =   left
 * 22           left    =   left->next
 * 23       else
 * 24           next    =   right
 * 25           right   =   right->next
 * 26       if(!ret)
 * 27           ret     =   next
 * 28   return ret;
 */

#ifndef MERGE_SORT_FOR_LIST_HPP
#define MERGE_SORT_FOR_LIST_HPP

#include "list.hpp"
#include <memory>

namespace ch10 {

template<typename T>
std::shared_ptr<list::node<T> >
merge_sort_for_list(const std::shared_ptr<list::node<T> >& node);

/**
 * @brief merge_sort_for_list
 *
 * @complexity  time:   O(n lg n)
 *              space:  no extra space needed, compared with the same algorithm on array
 *                      which requires O(n) extra space.
 *
 * check above for pseud code which is based on an alorithm on SO:
 * http://stackoverflow.com/questions/7685/merge-sort-a-linked-list
 */
template<typename T>
inline std::shared_ptr<list::node<T> >
merge_sort_for_list(const std::shared_ptr<list::node<T> >& node)
{
    using ValueType = T;
    using Node      = list::node<ValueType>;
    using sPointer  = std::shared_ptr<Node>;

    //! recursion stopping condition
    if(!node || !node->next)    return node;

    //! search for the middle node
    sPointer left, right, fast, last;
    left = right = fast = last = node;
    while(fast && fast->next)
    {
        last    =   right;
        right   =   right->next;
        fast    =   fast->next->next;
    }

    //! split the original linked list
    last->next  =   nullptr;

    //! recur
    left    =   merge_sort_for_list(left);
    right   =   merge_sort_for_list(right);

    //! merge
    sPointer ret, next;
    while(left || right)
    {
        if(!right)      //when right part is exhausted.
        {
            next    =   left;
            left    =   left->next;
        }
        else if(!left)  //when left  part is exhausted.
        {
            next    =   right;
            right   =   right->next;
        }
        else if(left->key < right->key)
        {
            next    =   left;
            left    =   left->next;
        }
        else
        {
            next    =   right;
            right   =   right->next;
        }

        if(!ret)
            ret     =   next;
    }
    return ret;
}

}//namespace
#endif // MERGE_SORT_FOR_LIST_HPP
<|endoftext|>


<commit_before>/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/

#include "stdafx.h"
#include "openglgeometrybank.h"

#include "sn_utils.h"
#include "logs.h"
#include "globals.h"

void
basic_vertex::serialize( std::ostream &s ) const {

    sn_utils::ls_float32( s, position.x );
    sn_utils::ls_float32( s, position.y );
    sn_utils::ls_float32( s, position.z );

    sn_utils::ls_float32( s, normal.x );
    sn_utils::ls_float32( s, normal.y );
    sn_utils::ls_float32( s, normal.z );

    sn_utils::ls_float32( s, texture.x );
    sn_utils::ls_float32( s, texture.y );
}

void
basic_vertex::deserialize( std::istream &s ) {

    position.x = sn_utils::ld_float32( s );
    position.y = sn_utils::ld_float32( s );
    position.z = sn_utils::ld_float32( s );

    normal.x = sn_utils::ld_float32( s );
    normal.y = sn_utils::ld_float32( s );
    normal.z = sn_utils::ld_float32( s );

    texture.x = sn_utils::ld_float32( s );
    texture.y = sn_utils::ld_float32( s );
}

// generic geometry bank class, allows storage, update and drawing of geometry chunks

// creates a new geometry chunk of specified type from supplied vertex data. returns: handle to the chunk
geometry_handle
geometry_bank::create( vertex_array &Vertices, unsigned int const Type ) {

    if( true == Vertices.empty() ) { return geometry_handle( 0, 0 ); }

    m_chunks.emplace_back( Vertices, Type );
    // NOTE: handle is effectively (index into chunk array + 1) this leaves value of 0 to serve as error/empty handle indication
    geometry_handle chunkhandle { 0, static_cast<std::uint32_t>(m_chunks.size()) };
    // template method implementation
    create_( chunkhandle );
    // all done
    return chunkhandle;
}

// replaces data of specified chunk with the supplied vertex data, starting from specified offset
bool
geometry_bank::replace( vertex_array &Vertices, geometry_handle const &Geometry, std::size_t const Offset ) {

    if( ( Geometry.chunk == 0 ) || ( Geometry.chunk > m_chunks.size() ) ) { return false; }

    auto &chunk = geometry_bank::chunk( Geometry );

    if( ( Offset == 0 )
     && ( Vertices.size() == chunk.vertices.size() ) ) {
        // check first if we can get away with a simple swap...
        chunk.vertices.swap( Vertices );
    }
    else {
        // ...otherwise we need to do some legwork
        // NOTE: if the offset is larger than existing size of the chunk, it'll bridge the gap with 'blank' vertices
        // TBD: we could bail out with an error instead if such request occurs
        chunk.vertices.resize( Offset + Vertices.size(), basic_vertex() );
        chunk.vertices.insert( std::end( chunk.vertices ), std::begin( Vertices ), std::end( Vertices ) );
    }
    // template method implementation
    replace_( Geometry );
    // all done
    return true;
}

// adds supplied vertex data at the end of specified chunk
bool
geometry_bank::append( vertex_array &Vertices, geometry_handle const &Geometry ) {

    if( ( Geometry.chunk == 0 ) || ( Geometry.chunk > m_chunks.size() ) ) { return false; }

    return replace( Vertices, Geometry, geometry_bank::chunk( Geometry ).vertices.size() );
}

// draws geometry stored in specified chunk
void
geometry_bank::draw( geometry_handle const &Geometry, unsigned int const Streams ) {
    // template method implementation
    draw_( Geometry, Streams );
}

// frees subclass-specific resources associated with the bank, typically called when the bank wasn't in use for a period of time
void
geometry_bank::release() {
    // template method implementation
    release_();
}

vertex_array const &
geometry_bank::vertices( geometry_handle const &Geometry ) const {

    return geometry_bank::chunk( Geometry ).vertices;
}

// opengl vbo-based variant of the geometry bank

GLuint opengl_vbogeometrybank::m_activebuffer { NULL }; // buffer bound currently on the opengl end, if any
unsigned int opengl_vbogeometrybank::m_activestreams { stream::none }; // currently enabled data type pointers

// create() subclass details
void
opengl_vbogeometrybank::create_( geometry_handle const &Geometry ) {
    // adding a chunk means we'll be (re)building the buffer, which will fill the chunk records, amongst other things.
    // thus we don't need to initialize the values here
    m_chunkrecords.emplace_back( chunk_record() );
    // kiss the existing buffer goodbye, new overall data size means we'll be making a new one
    delete_buffer();

	glGenVertexArrays(1, &m_vao);
}

// replace() subclass details
void
opengl_vbogeometrybank::replace_( geometry_handle const &Geometry ) {

    auto &chunkrecord = m_chunkrecords[ Geometry.chunk - 1 ];
    chunkrecord.is_good = false;
    // if the overall length of the chunk didn't change we can get away with reusing the old buffer...
    if( geometry_bank::chunk( Geometry ).vertices.size() != chunkrecord.size ) {
        // ...but otherwise we'll need to allocate a new one
        // TBD: we could keep and reuse the old buffer also if the new chunk is smaller than the old one,
        // but it'd require some extra tracking and work to keep all chunks up to date; also wasting vram; may be not worth it?
        delete_buffer();
    }
}

// draw() subclass details
void
opengl_vbogeometrybank::draw_( geometry_handle const &Geometry, unsigned int const Streams ) {
    if( m_buffer == NULL ) {
        // if there's no buffer, we'll have to make one
        // NOTE: this isn't exactly optimal in terms of ensuring the gfx card doesn't stall waiting for the data
        // may be better to initiate upload earlier (during update phase) and trust this effort won't go to waste
        if( true == m_chunks.empty() ) { return; }
        std::size_t datasize{ 0 };
        auto chunkiterator = m_chunks.cbegin();
        for( auto &chunkrecord : m_chunkrecords ) {
            // fill records for all chunks, based on the chunk data
            chunkrecord.is_good = false; // if we're re-creating buffer, chunks might've been uploaded in the old one
            chunkrecord.offset = datasize;
            chunkrecord.size = chunkiterator->vertices.size();
            datasize += chunkrecord.size;
            ++chunkiterator;
        }
        // the odds for all created chunks to get replaced with empty ones are quite low, but the possibility does exist
        if( datasize == 0 ) { return; }
        // try to set up the buffer we need
        ::glGenBuffers( 1, &m_buffer );
        bind_buffer();
        // NOTE: we're using static_draw since it's generally true for all we have implemented at the moment
        // TODO: allow to specify usage hint at the object creation, and pass it here
		m_activestreams = stream::none;
        ::glBufferData(
            GL_ARRAY_BUFFER,
            datasize * sizeof( basic_vertex ),
            nullptr,
            GL_STATIC_DRAW );
        if( ::glGetError() == GL_OUT_OF_MEMORY ) {
            // TBD: throw a bad_alloc?
            ErrorLog( "openGL error: out of memory; failed to create a geometry buffer" );
            delete_buffer();
            return;
        }
        m_buffercapacity = datasize;
    }
    // actual draw procedure starts here
    // setup...
    if( m_activebuffer != m_buffer ) {
        bind_buffer();
    }
    auto &chunkrecord = m_chunkrecords[ Geometry.chunk - 1 ];
    auto const &chunk = geometry_bank::chunk( Geometry );
    if( false == chunkrecord.is_good ) {
        // we may potentially need to upload new buffer data before we can draw it
        ::glBufferSubData(
            GL_ARRAY_BUFFER,
            chunkrecord.offset * sizeof( basic_vertex ),
            chunkrecord.size * sizeof( basic_vertex ),
            chunk.vertices.data() );
        chunkrecord.is_good = true;
    }
	if( m_activestreams != Streams ) {
        bind_streams( Streams );
    }

    // ...render...
    ::glDrawArrays( chunk.type, (GLint)chunkrecord.offset, (GLsizei)chunkrecord.size );
    // ...post-render cleanup
}

// release () subclass details
void
opengl_vbogeometrybank::release_() {

    delete_buffer();
}

void
opengl_vbogeometrybank::bind_buffer() {
	glBindVertexArray(m_vao);
    ::glBindBuffer( GL_ARRAY_BUFFER, m_buffer );
    m_activebuffer = m_buffer;
}

void
opengl_vbogeometrybank::delete_buffer() {
	glDeleteVertexArrays(1, &m_vao);
    if( m_buffer != NULL ) {

        ::glDeleteBuffers( 1, &m_buffer );
        if( m_activebuffer == m_buffer ) {
            m_activebuffer = NULL;
            bind_streams( stream::none );
        }
        m_buffer = NULL;
        m_buffercapacity = 0;
        // NOTE: since we've deleted the buffer all chunks it held were rendered invalid as well
        // instead of clearing their state here we're delaying it until new buffer is created to avoid looping through chunk records twice
    }
}

void
opengl_vbogeometrybank::bind_streams( unsigned int const Streams ) {
    if( Streams & stream::position ) {
		glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 8, (GLvoid*)0);
		glEnableVertexAttribArray(0);
    }
    else {
		glDisableVertexAttribArray(0);
    }

    // NOTE: normal and color streams share the data, making them effectively mutually exclusive
    if( Streams & stream::normal ) {
		glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 8, (GLvoid*)(sizeof(float) * 3));
		glEnableVertexAttribArray(1);
    }
    else {
		glDisableVertexAttribArray(1);
    }

    if( Streams & stream::color ) {

    }
    else {

    }

    if( Streams & stream::texture ) {
		glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 8, (GLvoid*)(sizeof(float) * 6));
		glEnableVertexAttribArray(2);
    }
    else {
		glDisableVertexAttribArray(2);
    }

    m_activestreams = Streams;
}

// opengl display list based variant of the geometry bank

// create() subclass details
void
opengl_dlgeometrybank::create_( geometry_handle const &Geometry ) {

    m_chunkrecords.emplace_back( chunk_record() );
}

// replace() subclass details
void
opengl_dlgeometrybank::replace_( geometry_handle const &Geometry ) {

    delete_list( Geometry );
}

// draw() subclass details
void
opengl_dlgeometrybank::draw_( geometry_handle const &Geometry, unsigned int const Streams ) {

    auto &chunkrecord = m_chunkrecords[ Geometry.chunk - 1 ];
    if( chunkrecord.streams != Streams ) {
        delete_list( Geometry );
    }
    if( chunkrecord.list == 0 ) {
        // we don't have a list ready, so compile one
        chunkrecord.streams = Streams;
        chunkrecord.list = ::glGenLists( 1 );
        auto const &chunk = geometry_bank::chunk( Geometry );
        ::glNewList( chunkrecord.list, GL_COMPILE );

        ::glBegin( chunk.type );
        for( auto const &vertex : chunk.vertices ) {
                 if( Streams & stream::normal ) { ::glNormal3fv( glm::value_ptr( vertex.normal ) ); }
            else if( Streams & stream::color )  { ::glColor3fv( glm::value_ptr( vertex.normal ) ); }
            if( Streams & stream::texture )     { ::glTexCoord2fv( glm::value_ptr( vertex.texture ) ); }
            if( Streams & stream::position )    { ::glVertex3fv( glm::value_ptr( vertex.position ) ); }
        }
        ::glEnd();
        ::glEndList();
    }
    // with the list done we can just play it
    ::glCallList( chunkrecord.list );
}

// release () subclass details
void
opengl_dlgeometrybank::release_() {

    for( auto &chunkrecord : m_chunkrecords ) {
        if( chunkrecord.list != 0 ) {
            ::glDeleteLists( chunkrecord.list, 1 );
            chunkrecord.list = 0;
        }
        chunkrecord.streams = stream::none;
    }
}

void
opengl_dlgeometrybank::delete_list( geometry_handle const &Geometry ) {
    // NOTE: given it's our own internal method we trust it to be called with valid parameters
    auto &chunkrecord = m_chunkrecords[ Geometry.chunk - 1 ];
    if( chunkrecord.list != 0 ) {
        ::glDeleteLists( chunkrecord.list, 1 );
        chunkrecord.list = 0;
    }
    chunkrecord.streams = stream::none;
}

// geometry bank manager, holds collection of geometry banks

// performs a resource sweep
void
geometrybank_manager::update() {

    m_garbagecollector.sweep();
}

// creates a new geometry bank. returns: handle to the bank or NULL
geometrybank_handle
geometrybank_manager::create_bank() {

	m_geometrybanks.emplace_back(std::make_shared<opengl_vbogeometrybank>(), std::chrono::steady_clock::time_point());
    // NOTE: handle is effectively (index into chunk array + 1) this leaves value of 0 to serve as error/empty handle indication
    return geometrybank_handle( (uint32_t)m_geometrybanks.size(), 0 );
}

// creates a new geometry chunk of specified type from supplied vertex data, in specified bank. returns: handle to the chunk or NULL
geometry_handle
geometrybank_manager::create_chunk( vertex_array &Vertices, geometrybank_handle const &Geometry, int const Type ) {

    auto const newchunkhandle = bank( Geometry ).first->create( Vertices, Type );

    if( newchunkhandle.chunk != 0 ) { return geometry_handle( Geometry.bank, newchunkhandle.chunk ); }
    else                            { return geometry_handle( 0, 0 ); }
}

// replaces data of specified chunk with the supplied vertex data, starting from specified offset
bool
geometrybank_manager::replace( vertex_array &Vertices, geometry_handle const &Geometry, std::size_t const Offset ) {

    return bank( Geometry ).first->replace( Vertices, Geometry, Offset );
}

// adds supplied vertex data at the end of specified chunk
bool
geometrybank_manager::append( vertex_array &Vertices, geometry_handle const &Geometry ) {

    return bank( Geometry ).first->append( Vertices, Geometry );
}
// draws geometry stored in specified chunk
void
geometrybank_manager::draw( geometry_handle const &Geometry, unsigned int const Streams ) {

    if( Geometry == NULL ) { return; }

    auto &bankrecord = bank( Geometry );

    bankrecord.second = m_garbagecollector.timestamp();
    bankrecord.first->draw( Geometry, Streams );
}

// provides direct access to vertex data of specfied chunk
vertex_array const &
geometrybank_manager::vertices( geometry_handle const &Geometry ) const {

    return bank( Geometry ).first->vertices( Geometry );
}
<commit_msg>przesadziłem?<commit_after>/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/

#include "stdafx.h"
#include "openglgeometrybank.h"

#include "sn_utils.h"
#include "logs.h"
#include "globals.h"

void
basic_vertex::serialize( std::ostream &s ) const {

    sn_utils::ls_float32( s, position.x );
    sn_utils::ls_float32( s, position.y );
    sn_utils::ls_float32( s, position.z );

    sn_utils::ls_float32( s, normal.x );
    sn_utils::ls_float32( s, normal.y );
    sn_utils::ls_float32( s, normal.z );

    sn_utils::ls_float32( s, texture.x );
    sn_utils::ls_float32( s, texture.y );
}

void
basic_vertex::deserialize( std::istream &s ) {

    position.x = sn_utils::ld_float32( s );
    position.y = sn_utils::ld_float32( s );
    position.z = sn_utils::ld_float32( s );

    normal.x = sn_utils::ld_float32( s );
    normal.y = sn_utils::ld_float32( s );
    normal.z = sn_utils::ld_float32( s );

    texture.x = sn_utils::ld_float32( s );
    texture.y = sn_utils::ld_float32( s );
}

// generic geometry bank class, allows storage, update and drawing of geometry chunks

// creates a new geometry chunk of specified type from supplied vertex data. returns: handle to the chunk
geometry_handle
geometry_bank::create( vertex_array &Vertices, unsigned int const Type ) {

    if( true == Vertices.empty() ) { return geometry_handle( 0, 0 ); }

    m_chunks.emplace_back( Vertices, Type );
    // NOTE: handle is effectively (index into chunk array + 1) this leaves value of 0 to serve as error/empty handle indication
    geometry_handle chunkhandle { 0, static_cast<std::uint32_t>(m_chunks.size()) };
    // template method implementation
    create_( chunkhandle );
    // all done
    return chunkhandle;
}

// replaces data of specified chunk with the supplied vertex data, starting from specified offset
bool
geometry_bank::replace( vertex_array &Vertices, geometry_handle const &Geometry, std::size_t const Offset ) {

    if( ( Geometry.chunk == 0 ) || ( Geometry.chunk > m_chunks.size() ) ) { return false; }

    auto &chunk = geometry_bank::chunk( Geometry );

    if( ( Offset == 0 )
     && ( Vertices.size() == chunk.vertices.size() ) ) {
        // check first if we can get away with a simple swap...
        chunk.vertices.swap( Vertices );
    }
    else {
        // ...otherwise we need to do some legwork
        // NOTE: if the offset is larger than existing size of the chunk, it'll bridge the gap with 'blank' vertices
        // TBD: we could bail out with an error instead if such request occurs
        chunk.vertices.resize( Offset + Vertices.size(), basic_vertex() );
        chunk.vertices.insert( std::end( chunk.vertices ), std::begin( Vertices ), std::end( Vertices ) );
    }
    // template method implementation
    replace_( Geometry );
    // all done
    return true;
}

// adds supplied vertex data at the end of specified chunk
bool
geometry_bank::append( vertex_array &Vertices, geometry_handle const &Geometry ) {

    if( ( Geometry.chunk == 0 ) || ( Geometry.chunk > m_chunks.size() ) ) { return false; }

    return replace( Vertices, Geometry, geometry_bank::chunk( Geometry ).vertices.size() );
}

// draws geometry stored in specified chunk
void
geometry_bank::draw( geometry_handle const &Geometry, unsigned int const Streams ) {
    // template method implementation
    draw_( Geometry, Streams );
}

// frees subclass-specific resources associated with the bank, typically called when the bank wasn't in use for a period of time
void
geometry_bank::release() {
    // template method implementation
    release_();
}

vertex_array const &
geometry_bank::vertices( geometry_handle const &Geometry ) const {

    return geometry_bank::chunk( Geometry ).vertices;
}

// opengl vbo-based variant of the geometry bank

GLuint opengl_vbogeometrybank::m_activebuffer { NULL }; // buffer bound currently on the opengl end, if any
unsigned int opengl_vbogeometrybank::m_activestreams { stream::none }; // currently enabled data type pointers

// create() subclass details
void
opengl_vbogeometrybank::create_( geometry_handle const &Geometry ) {
    // adding a chunk means we'll be (re)building the buffer, which will fill the chunk records, amongst other things.
    // thus we don't need to initialize the values here
    m_chunkrecords.emplace_back( chunk_record() );
    // kiss the existing buffer goodbye, new overall data size means we'll be making a new one
    delete_buffer();

	bind_streams(stream::none);
	glGenVertexArrays(1, &m_vao);
}

// replace() subclass details
void
opengl_vbogeometrybank::replace_( geometry_handle const &Geometry ) {

    auto &chunkrecord = m_chunkrecords[ Geometry.chunk - 1 ];
    chunkrecord.is_good = false;
    // if the overall length of the chunk didn't change we can get away with reusing the old buffer...
    if( geometry_bank::chunk( Geometry ).vertices.size() != chunkrecord.size ) {
        // ...but otherwise we'll need to allocate a new one
        // TBD: we could keep and reuse the old buffer also if the new chunk is smaller than the old one,
        // but it'd require some extra tracking and work to keep all chunks up to date; also wasting vram; may be not worth it?
        delete_buffer();
    }
}

// draw() subclass details
void
opengl_vbogeometrybank::draw_( geometry_handle const &Geometry, unsigned int const Streams ) {
    if( m_buffer == NULL ) {
        // if there's no buffer, we'll have to make one
        // NOTE: this isn't exactly optimal in terms of ensuring the gfx card doesn't stall waiting for the data
        // may be better to initiate upload earlier (during update phase) and trust this effort won't go to waste
        if( true == m_chunks.empty() ) { return; }
        std::size_t datasize{ 0 };
        auto chunkiterator = m_chunks.cbegin();
        for( auto &chunkrecord : m_chunkrecords ) {
            // fill records for all chunks, based on the chunk data
            chunkrecord.is_good = false; // if we're re-creating buffer, chunks might've been uploaded in the old one
            chunkrecord.offset = datasize;
            chunkrecord.size = chunkiterator->vertices.size();
            datasize += chunkrecord.size;
            ++chunkiterator;
        }
        // the odds for all created chunks to get replaced with empty ones are quite low, but the possibility does exist
        if( datasize == 0 ) { return; }
        // try to set up the buffer we need
        ::glGenBuffers( 1, &m_buffer );
        bind_buffer();
        // NOTE: we're using static_draw since it's generally true for all we have implemented at the moment
        // TODO: allow to specify usage hint at the object creation, and pass it here
        ::glBufferData(
            GL_ARRAY_BUFFER,
            datasize * sizeof( basic_vertex ),
            nullptr,
            GL_STATIC_DRAW );
        if( ::glGetError() == GL_OUT_OF_MEMORY ) {
            // TBD: throw a bad_alloc?
            ErrorLog( "openGL error: out of memory; failed to create a geometry buffer" );
            delete_buffer();
            return;
        }
        m_buffercapacity = datasize;
    }
    // actual draw procedure starts here
    // setup...
    if( m_activebuffer != m_buffer ) {
        bind_buffer();
    }
    auto &chunkrecord = m_chunkrecords[ Geometry.chunk - 1 ];
    auto const &chunk = geometry_bank::chunk( Geometry );
    if( false == chunkrecord.is_good ) {
        // we may potentially need to upload new buffer data before we can draw it
        ::glBufferSubData(
            GL_ARRAY_BUFFER,
            chunkrecord.offset * sizeof( basic_vertex ),
            chunkrecord.size * sizeof( basic_vertex ),
            chunk.vertices.data() );
        chunkrecord.is_good = true;
    }
	if( m_activestreams != Streams ) {
        bind_streams( Streams );
    }

    // ...render...
    ::glDrawArrays( chunk.type, (GLint)chunkrecord.offset, (GLsizei)chunkrecord.size );
    // ...post-render cleanup
}

// release () subclass details
void
opengl_vbogeometrybank::release_() {

    delete_buffer();
}

void
opengl_vbogeometrybank::bind_buffer() {
	glBindVertexArray(m_vao);
    ::glBindBuffer( GL_ARRAY_BUFFER, m_buffer );
    m_activebuffer = m_buffer;
	bind_streams(stream::none);
}

void
opengl_vbogeometrybank::delete_buffer() {
	glDeleteVertexArrays(1, &m_vao);
    if( m_buffer != NULL ) {

        ::glDeleteBuffers( 1, &m_buffer );
        if( m_activebuffer == m_buffer ) {
            m_activebuffer = NULL;
            bind_streams( stream::none );
        }
        m_buffer = NULL;
        m_buffercapacity = 0;
        // NOTE: since we've deleted the buffer all chunks it held were rendered invalid as well
        // instead of clearing their state here we're delaying it until new buffer is created to avoid looping through chunk records twice
    }
}

void
opengl_vbogeometrybank::bind_streams( unsigned int const Streams ) {
    if( Streams & stream::position ) {
		glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 8, (GLvoid*)0);
		glEnableVertexAttribArray(0);
    }
    else {
		glDisableVertexAttribArray(0);
    }

    // NOTE: normal and color streams share the data, making them effectively mutually exclusive
    if( Streams & stream::normal ) {
		glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 8, (GLvoid*)(sizeof(float) * 3));
		glEnableVertexAttribArray(1);
    }
    else {
		glDisableVertexAttribArray(1);
    }

    if( Streams & stream::color ) {

    }
    else {

    }

    if( Streams & stream::texture ) {
		glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 8, (GLvoid*)(sizeof(float) * 6));
		glEnableVertexAttribArray(2);
    }
    else {
		glDisableVertexAttribArray(2);
    }

    m_activestreams = Streams;
}

// opengl display list based variant of the geometry bank

// create() subclass details
void
opengl_dlgeometrybank::create_( geometry_handle const &Geometry ) {

    m_chunkrecords.emplace_back( chunk_record() );
}

// replace() subclass details
void
opengl_dlgeometrybank::replace_( geometry_handle const &Geometry ) {

    delete_list( Geometry );
}

// draw() subclass details
void
opengl_dlgeometrybank::draw_( geometry_handle const &Geometry, unsigned int const Streams ) {

    auto &chunkrecord = m_chunkrecords[ Geometry.chunk - 1 ];
    if( chunkrecord.streams != Streams ) {
        delete_list( Geometry );
    }
    if( chunkrecord.list == 0 ) {
        // we don't have a list ready, so compile one
        chunkrecord.streams = Streams;
        chunkrecord.list = ::glGenLists( 1 );
        auto const &chunk = geometry_bank::chunk( Geometry );
        ::glNewList( chunkrecord.list, GL_COMPILE );

        ::glBegin( chunk.type );
        for( auto const &vertex : chunk.vertices ) {
                 if( Streams & stream::normal ) { ::glNormal3fv( glm::value_ptr( vertex.normal ) ); }
            else if( Streams & stream::color )  { ::glColor3fv( glm::value_ptr( vertex.normal ) ); }
            if( Streams & stream::texture )     { ::glTexCoord2fv( glm::value_ptr( vertex.texture ) ); }
            if( Streams & stream::position )    { ::glVertex3fv( glm::value_ptr( vertex.position ) ); }
        }
        ::glEnd();
        ::glEndList();
    }
    // with the list done we can just play it
    ::glCallList( chunkrecord.list );
}

// release () subclass details
void
opengl_dlgeometrybank::release_() {

    for( auto &chunkrecord : m_chunkrecords ) {
        if( chunkrecord.list != 0 ) {
            ::glDeleteLists( chunkrecord.list, 1 );
            chunkrecord.list = 0;
        }
        chunkrecord.streams = stream::none;
    }
}

void
opengl_dlgeometrybank::delete_list( geometry_handle const &Geometry ) {
    // NOTE: given it's our own internal method we trust it to be called with valid parameters
    auto &chunkrecord = m_chunkrecords[ Geometry.chunk - 1 ];
    if( chunkrecord.list != 0 ) {
        ::glDeleteLists( chunkrecord.list, 1 );
        chunkrecord.list = 0;
    }
    chunkrecord.streams = stream::none;
}

// geometry bank manager, holds collection of geometry banks

// performs a resource sweep
void
geometrybank_manager::update() {

    m_garbagecollector.sweep();
}

// creates a new geometry bank. returns: handle to the bank or NULL
geometrybank_handle
geometrybank_manager::create_bank() {

	m_geometrybanks.emplace_back(std::make_shared<opengl_vbogeometrybank>(), std::chrono::steady_clock::time_point());
    // NOTE: handle is effectively (index into chunk array + 1) this leaves value of 0 to serve as error/empty handle indication
    return geometrybank_handle( (uint32_t)m_geometrybanks.size(), 0 );
}

// creates a new geometry chunk of specified type from supplied vertex data, in specified bank. returns: handle to the chunk or NULL
geometry_handle
geometrybank_manager::create_chunk( vertex_array &Vertices, geometrybank_handle const &Geometry, int const Type ) {

    auto const newchunkhandle = bank( Geometry ).first->create( Vertices, Type );

    if( newchunkhandle.chunk != 0 ) { return geometry_handle( Geometry.bank, newchunkhandle.chunk ); }
    else                            { return geometry_handle( 0, 0 ); }
}

// replaces data of specified chunk with the supplied vertex data, starting from specified offset
bool
geometrybank_manager::replace( vertex_array &Vertices, geometry_handle const &Geometry, std::size_t const Offset ) {

    return bank( Geometry ).first->replace( Vertices, Geometry, Offset );
}

// adds supplied vertex data at the end of specified chunk
bool
geometrybank_manager::append( vertex_array &Vertices, geometry_handle const &Geometry ) {

    return bank( Geometry ).first->append( Vertices, Geometry );
}
// draws geometry stored in specified chunk
void
geometrybank_manager::draw( geometry_handle const &Geometry, unsigned int const Streams ) {

    if( Geometry == NULL ) { return; }

    auto &bankrecord = bank( Geometry );

    bankrecord.second = m_garbagecollector.timestamp();
    bankrecord.first->draw( Geometry, Streams );
}

// provides direct access to vertex data of specfied chunk
vertex_array const &
geometrybank_manager::vertices( geometry_handle const &Geometry ) const {

    return bank( Geometry ).first->vertices( Geometry );
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2009 Yahoo! Inc.  All rights reserved.  The copyrights
embodied in the content of this file are licensed under the BSD
(revised) open source license
 */

#include <fstream>
#include <iostream>
using namespace std;

#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>
#include "parse_regressor.h"
#include "loss_functions.h"
#include "global_data.h"
#include "io.h"

void initialize_regressor(regressor &r)
{
  size_t length = ((size_t)1) << global.num_bits;
  global.thread_mask = (global.stride * (length >> global.thread_bits)) - 1;
  size_t num_threads = global.num_threads();
  r.weight_vectors = (weight **)malloc(num_threads * sizeof(weight*));
  for (size_t i = 0; i < num_threads; i++)
    {
      r.weight_vectors[i] = (weight *)calloc(global.stride*length/num_threads, sizeof(weight));
      if (r.weight_vectors[i] == NULL)
        {
          cerr << global.program_name << ": Failed to allocate weight array: try decreasing -b <bits>" << endl;
          exit (1);
        }
      if (global.initial_weight != 0.)
	for (size_t j = 0; j < global.stride*length/num_threads; j+=global.stride)
	  r.weight_vectors[i][j] = global.initial_weight;
      if (global.random_weights)
	for (size_t j = 0; j < length/num_threads; j++) {
          r.weight_vectors[i][j] = drand48() - 0.5;
        }
      if (global.lda)
	{
	  size_t stride = global.stride;

          for (size_t j = 0; j < stride*length/num_threads; j+=stride)
	    {
	      for (size_t k = 0; k < global.lda; k++) {
                r.weight_vectors[i][j+k] = -log(drand48()) + 1.0;
//                 r.weight_vectors[i][j+k] *= r.weight_vectors[i][j+k];
//                 r.weight_vectors[i][j+k] *= r.weight_vectors[i][j+k];
		r.weight_vectors[i][j+k] *= (float)global.lda_D / (float)global.lda
		  / global.length() * 200;
              }
	      r.weight_vectors[i][j+global.lda] = global.initial_t;
	    }
	}
      if(global.adaptive)
        for (size_t j = 1; j < global.stride*length/num_threads; j+=global.stride)
	  r.weight_vectors[i][j] = 1;
    }
}

void parse_regressor_args(po::variables_map& vm, regressor& r, string& final_regressor_name, bool quiet)
{
  if (vm.count("final_regressor")) {
    final_regressor_name = vm["final_regressor"].as<string>();
    if (!quiet)
      cerr << "final_regressor = " << vm["final_regressor"].as<string>() << endl;
  }
  else
    final_regressor_name = "";

  vector<string> regs;
  if (vm.count("initial_regressor"))
    regs = vm["initial_regressor"].as< vector<string> >();

  /*
     Read in regressors.  If multiple regressors are specified, do a weighted
     average.  If none are specified, initialize according to global_seg &
     numbits.
  */
  bool initialized = false;

  for (size_t i = 0; i < regs.size(); i++)
    {
      ifstream regressor(regs[i].c_str());
      if (!regressor.is_open())
	{
	  cout << "can't open " << regs[i].c_str() << endl << " ... exiting." << endl;
	  exit(1);
	}

      size_t v_length;
      regressor.read((char*)&v_length, sizeof(v_length));
      char t[v_length];
      regressor.read(t,v_length);
      if (strcmp(t,version.c_str()) != 0)
	{
	  cout << "regressor has possibly incompatible version!" << endl;
	  exit(1);
	}

      regressor.read((char*)&global.min_label, sizeof(global.min_label));
      regressor.read((char*)&global.max_label, sizeof(global.max_label));

      size_t local_num_bits;
      regressor.read((char *)&local_num_bits, sizeof(local_num_bits));
      if (!initialized){
	global.num_bits = local_num_bits;
      }
      else
	if (local_num_bits != global.num_bits)
	  {
	    cout << "can't combine regressors with different feature number!" << endl;
	    exit (1);
	  }

      size_t local_thread_bits;
      regressor.read((char*)&local_thread_bits, sizeof(local_thread_bits));
      if (!initialized){
	global.thread_bits = local_thread_bits;
	global.partition_bits = global.thread_bits;
      }
      else
	if (local_thread_bits != global.thread_bits)
	  {
	    cout << "can't combine regressors trained with different numbers of threads!" << endl;
	    exit (1);
	  }

      int len;
      regressor.read((char *)&len, sizeof(len));

      vector<string> local_pairs;
      for (; len > 0; len--)
	{
	  char pair[2];
	  regressor.read(pair, sizeof(char)*2);
	  string temp(pair, 2);
	  local_pairs.push_back(temp);
	}
      if (!initialized)
	{
	  global.pairs = local_pairs;
	  initialize_regressor(r);
	}
      else
	if (local_pairs != global.pairs)
	  {
	    cout << "can't combine regressors with different features!" << endl;
	    for (size_t i = 0; i < local_pairs.size(); i++)
	      cout << local_pairs[i] << " " << local_pairs[i].size() << " ";
	    cout << endl;
	    for (size_t i = 0; i < global.pairs.size(); i++)
	      cout << global.pairs[i] << " " << global.pairs[i].size() << " ";
	    cout << endl;
	    exit (1);
	  }
      size_t local_ngram;
      regressor.read((char*)&local_ngram, sizeof(local_ngram));
      size_t local_skips;
      regressor.read((char*)&local_skips, sizeof(local_skips));
      if (!initialized)
	{
	  global.ngram = local_ngram;
	  global.skips = local_skips;
	  initialized = true;
	}
      else
	if (global.ngram != local_ngram || global.skips != local_skips)
	  {
	    cout << "can't combine regressors with different ngram features!" << endl;
	    exit(1);
	  }
      size_t stride = global.stride;
      while (regressor.good())
	{
	  uint32_t hash;
	  regressor.read((char *)&hash, sizeof(hash));
	  weight w = 0.;
	  regressor.read((char *)&w, sizeof(float));

	  size_t num_threads = global.num_threads();
	  if (regressor.good() && global.lda == 0)
	    r.weight_vectors[hash % num_threads][(hash*stride)/num_threads]
	      = r.weight_vectors[hash % num_threads][(hash*stride)/num_threads] + w;
	  else
	    r.weight_vectors[hash % num_threads][hash/num_threads]
	      = r.weight_vectors[hash % num_threads][hash/num_threads] + w;
	}
      regressor.close();
    }
  if (!initialized)
    {
      if(vm.count("noop") || vm.count("sendto"))
	r.weight_vectors = NULL;
      else
	initialize_regressor(r);
    }
}

void free_regressor(regressor &r)
{
  if (r.weight_vectors != NULL)
    {
      for (size_t i = 0; i < global.num_threads(); i++)
	if (r.weight_vectors[i] != NULL)
	  free(r.weight_vectors[i]);
      free(r.weight_vectors);
    }
}

void dump_regressor(string reg_name, regressor &r)
{
  if (reg_name == string(""))
    return;
  string start_name = reg_name+string(".writing");
  io_buf io_temp;

  int f = io_temp.open_file(start_name.c_str(),io_buf::WRITE);

  if (f<0)
    {
      cout << "can't open: " << start_name << " for writing, exiting" << endl;
      exit(1);
    }
  size_t v_length = version.length()+1;
  io_temp.write_file(f,(char*)&v_length, sizeof(v_length));
  io_temp.write_file(f,version.c_str(),v_length);

  io_temp.write_file(f,(char*)&global.min_label, sizeof(global.min_label));
  io_temp.write_file(f,(char*)&global.max_label, sizeof(global.max_label));

  io_temp.write_file(f,(char *)&global.num_bits, sizeof(global.num_bits));
  io_temp.write_file(f,(char *)&global.thread_bits, sizeof(global.thread_bits));
  int len = global.pairs.size();
  io_temp.write_file(f,(char *)&len, sizeof(len));
  for (vector<string>::iterator i = global.pairs.begin(); i != global.pairs.end();i++)
    io_temp.write_file(f,i->c_str(),2);

  io_temp.write_file(f,(char*)&global.ngram, sizeof(global.ngram));
  io_temp.write_file(f,(char*)&global.skips, sizeof(global.skips));

  uint32_t length = 1 << global.num_bits;
  size_t num_threads = global.num_threads();
  size_t stride = global.stride;
  for(uint32_t i = 0; i < length; i++)
    {
      if (global.lda == 0)
	{
	  weight v = r.weight_vectors[i%num_threads][stride*(i/num_threads)];
	  if (v != 0.)
	    {
	      io_temp.write_file(f,(char *)&i, sizeof (i));
	      io_temp.write_file(f,(char *)&v, sizeof (v));
	    }
	}
      else
	for (size_t k = 0; k < global.lda; k++)
	  {
	    weight v = r.weight_vectors[i%num_threads][(stride*i+k)/num_threads];
	    io_temp.write_file(f,(char *)&i, sizeof (i));
	    io_temp.write_file(f,(char *)&v, sizeof (v));
	  }
    }

  rename(start_name.c_str(),reg_name.c_str());

  io_temp.close_file();
}

void finalize_regressor(string reg_name, regressor &r)
{
  dump_regressor(reg_name,r);
  free_regressor(r);
}
<commit_msg>fixed bit check for regressor when testing from Paul Mineiro<commit_after>/*
Copyright (c) 2009 Yahoo! Inc.  All rights reserved.  The copyrights
embodied in the content of this file are licensed under the BSD
(revised) open source license
 */

#include <fstream>
#include <iostream>
using namespace std;

#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>
#include "parse_regressor.h"
#include "loss_functions.h"
#include "global_data.h"
#include "io.h"

void initialize_regressor(regressor &r)
{
  size_t length = ((size_t)1) << global.num_bits;
  global.thread_mask = (global.stride * (length >> global.thread_bits)) - 1;
  size_t num_threads = global.num_threads();
  r.weight_vectors = (weight **)malloc(num_threads * sizeof(weight*));
  for (size_t i = 0; i < num_threads; i++)
    {
      r.weight_vectors[i] = (weight *)calloc(global.stride*length/num_threads, sizeof(weight));
      if (r.weight_vectors[i] == NULL)
        {
          cerr << global.program_name << ": Failed to allocate weight array: try decreasing -b <bits>" << endl;
          exit (1);
        }
      if (global.initial_weight != 0.)
	for (size_t j = 0; j < global.stride*length/num_threads; j+=global.stride)
	  r.weight_vectors[i][j] = global.initial_weight;
      if (global.random_weights)
	for (size_t j = 0; j < length/num_threads; j++) {
          r.weight_vectors[i][j] = drand48() - 0.5;
        }
      if (global.lda)
	{
	  size_t stride = global.stride;

          for (size_t j = 0; j < stride*length/num_threads; j+=stride)
	    {
	      for (size_t k = 0; k < global.lda; k++) {
                r.weight_vectors[i][j+k] = -log(drand48()) + 1.0;
//                 r.weight_vectors[i][j+k] *= r.weight_vectors[i][j+k];
//                 r.weight_vectors[i][j+k] *= r.weight_vectors[i][j+k];
		r.weight_vectors[i][j+k] *= (float)global.lda_D / (float)global.lda
		  / global.length() * 200;
              }
	      r.weight_vectors[i][j+global.lda] = global.initial_t;
	    }
	}
      if(global.adaptive)
        for (size_t j = 1; j < global.stride*length/num_threads; j+=global.stride)
	  r.weight_vectors[i][j] = 1;
    }
}

void parse_regressor_args(po::variables_map& vm, regressor& r, string& final_regressor_name, bool quiet)
{
  if (vm.count("final_regressor")) {
    final_regressor_name = vm["final_regressor"].as<string>();
    if (!quiet)
      cerr << "final_regressor = " << vm["final_regressor"].as<string>() << endl;
  }
  else
    final_regressor_name = "";

  vector<string> regs;
  if (vm.count("initial_regressor"))
    regs = vm["initial_regressor"].as< vector<string> >();

  /*
     Read in regressors.  If multiple regressors are specified, do a weighted
     average.  If none are specified, initialize according to global_seg &
     numbits.
  */
  bool initialized = false;

  for (size_t i = 0; i < regs.size(); i++)
    {
      ifstream regressor(regs[i].c_str());
      if (!regressor.is_open())
	{
	  cout << "can't open " << regs[i].c_str() << endl << " ... exiting." << endl;
	  exit(1);
	}

      size_t v_length;
      regressor.read((char*)&v_length, sizeof(v_length));
      char t[v_length];
      regressor.read(t,v_length);
      if (strcmp(t,version.c_str()) != 0)
	{
	  cout << "regressor has possibly incompatible version!" << endl;
	  exit(1);
	}

      regressor.read((char*)&global.min_label, sizeof(global.min_label));
      regressor.read((char*)&global.max_label, sizeof(global.max_label));

      size_t local_num_bits;
      regressor.read((char *)&local_num_bits, sizeof(local_num_bits));
      if (!initialized){
	global.default_bits = false;
	global.num_bits = local_num_bits;
      }
      else
	if (local_num_bits != global.num_bits)
	  {
	    cout << "can't combine regressors with different feature number!" << endl;
	    exit (1);
	  }

      size_t local_thread_bits;
      regressor.read((char*)&local_thread_bits, sizeof(local_thread_bits));
      if (!initialized){
	global.thread_bits = local_thread_bits;
	global.partition_bits = global.thread_bits;
      }
      else
	if (local_thread_bits != global.thread_bits)
	  {
	    cout << "can't combine regressors trained with different numbers of threads!" << endl;
	    exit (1);
	  }

      int len;
      regressor.read((char *)&len, sizeof(len));

      vector<string> local_pairs;
      for (; len > 0; len--)
	{
	  char pair[2];
	  regressor.read(pair, sizeof(char)*2);
	  string temp(pair, 2);
	  local_pairs.push_back(temp);
	}
      if (!initialized)
	{
	  global.pairs = local_pairs;
	  initialize_regressor(r);
	}
      else
	if (local_pairs != global.pairs)
	  {
	    cout << "can't combine regressors with different features!" << endl;
	    for (size_t i = 0; i < local_pairs.size(); i++)
	      cout << local_pairs[i] << " " << local_pairs[i].size() << " ";
	    cout << endl;
	    for (size_t i = 0; i < global.pairs.size(); i++)
	      cout << global.pairs[i] << " " << global.pairs[i].size() << " ";
	    cout << endl;
	    exit (1);
	  }
      size_t local_ngram;
      regressor.read((char*)&local_ngram, sizeof(local_ngram));
      size_t local_skips;
      regressor.read((char*)&local_skips, sizeof(local_skips));
      if (!initialized)
	{
	  global.ngram = local_ngram;
	  global.skips = local_skips;
	  initialized = true;
	}
      else
	if (global.ngram != local_ngram || global.skips != local_skips)
	  {
	    cout << "can't combine regressors with different ngram features!" << endl;
	    exit(1);
	  }
      size_t stride = global.stride;
      while (regressor.good())
	{
	  uint32_t hash;
	  regressor.read((char *)&hash, sizeof(hash));
	  weight w = 0.;
	  regressor.read((char *)&w, sizeof(float));

	  size_t num_threads = global.num_threads();
	  if (regressor.good() && global.lda == 0)
	    r.weight_vectors[hash % num_threads][(hash*stride)/num_threads]
	      = r.weight_vectors[hash % num_threads][(hash*stride)/num_threads] + w;
	  else
	    r.weight_vectors[hash % num_threads][hash/num_threads]
	      = r.weight_vectors[hash % num_threads][hash/num_threads] + w;
	}
      regressor.close();
    }
  if (!initialized)
    {
      if(vm.count("noop") || vm.count("sendto"))
	r.weight_vectors = NULL;
      else
	initialize_regressor(r);
    }
}

void free_regressor(regressor &r)
{
  if (r.weight_vectors != NULL)
    {
      for (size_t i = 0; i < global.num_threads(); i++)
	if (r.weight_vectors[i] != NULL)
	  free(r.weight_vectors[i]);
      free(r.weight_vectors);
    }
}

void dump_regressor(string reg_name, regressor &r)
{
  if (reg_name == string(""))
    return;
  string start_name = reg_name+string(".writing");
  io_buf io_temp;

  int f = io_temp.open_file(start_name.c_str(),io_buf::WRITE);

  if (f<0)
    {
      cout << "can't open: " << start_name << " for writing, exiting" << endl;
      exit(1);
    }
  size_t v_length = version.length()+1;
  io_temp.write_file(f,(char*)&v_length, sizeof(v_length));
  io_temp.write_file(f,version.c_str(),v_length);

  io_temp.write_file(f,(char*)&global.min_label, sizeof(global.min_label));
  io_temp.write_file(f,(char*)&global.max_label, sizeof(global.max_label));

  io_temp.write_file(f,(char *)&global.num_bits, sizeof(global.num_bits));
  io_temp.write_file(f,(char *)&global.thread_bits, sizeof(global.thread_bits));
  int len = global.pairs.size();
  io_temp.write_file(f,(char *)&len, sizeof(len));
  for (vector<string>::iterator i = global.pairs.begin(); i != global.pairs.end();i++)
    io_temp.write_file(f,i->c_str(),2);

  io_temp.write_file(f,(char*)&global.ngram, sizeof(global.ngram));
  io_temp.write_file(f,(char*)&global.skips, sizeof(global.skips));

  uint32_t length = 1 << global.num_bits;
  size_t num_threads = global.num_threads();
  size_t stride = global.stride;
  for(uint32_t i = 0; i < length; i++)
    {
      if (global.lda == 0)
	{
	  weight v = r.weight_vectors[i%num_threads][stride*(i/num_threads)];
	  if (v != 0.)
	    {
	      io_temp.write_file(f,(char *)&i, sizeof (i));
	      io_temp.write_file(f,(char *)&v, sizeof (v));
	    }
	}
      else
	for (size_t k = 0; k < global.lda; k++)
	  {
	    weight v = r.weight_vectors[i%num_threads][(stride*i+k)/num_threads];
	    io_temp.write_file(f,(char *)&i, sizeof (i));
	    io_temp.write_file(f,(char *)&v, sizeof (v));
	  }
    }

  rename(start_name.c_str(),reg_name.c_str());

  io_temp.close_file();
}

void finalize_regressor(string reg_name, regressor &r)
{
  dump_regressor(reg_name,r);
  free_regressor(r);
}
<|endoftext|>


<commit_before>// The idea behind this technique is to render the scene to an intermediate FBO repeatedly (SUBFRAMES times),
// advancing the scene forward a little at a time (in sub-frame increments). So rather than rendering @ time 1.0, time 2.0, etc
// we render at time 1.1, 1.2, etc.
// At the end of rendering each sub-frame, accumulate (add) it into a floating point FBO. And at the end, we get an average by dividing
// by the number of sub-frames we rendered. This is a slightly naive, but very general technique for rendering motion blur.
// We achieve the accumulation by drawing the final sub-frame into the accumulation FBO using addivite blending.
// To do the divide by SUBFRAMES at the end, we just set the color to be 1.0/SUBFRAMES and draw the final accumulation buffer color texture.

#include "cinder/app/App.h"
#include "cinder/app/RendererGl.h"
#include "cinder/gl/gl.h"

using namespace ci;
using namespace ci::app;

class MotionBlurFboApp : public App {
  public:
	void setup();
	void keyDown( KeyEvent event );
	void updateCubeRotation( double time );
	void draw();

	CameraPersp			mCam;
	mat4			mCubeRotation;
	gl::BatchRef		mBatch;
	gl::FboRef			mFbo, mAccumFbo;
	bool				mPaused;
};

const int SUBFRAMES = 16; // increasing this number increases quality

void MotionBlurFboApp::setup()
{
	mCam.lookAt( vec3( 0, 0, 7 ), vec3( 0 ) );
	mCam.setPerspective( 60, getWindowAspectRatio(), 1, 1000 );

	mBatch = gl::Batch::create( geom::Cube().colors(), gl::getStockShader( gl::ShaderDef().color() ) );
	mFbo = gl::Fbo::create( toPixels( getWindowWidth() ), toPixels( getWindowHeight() ) );

#if defined( CINDER_GL_ES_2 )
	mAccumFbo = gl::Fbo::create( toPixels( getWindowWidth() ), toPixels( getWindowHeight() ),
		gl::Fbo::Format().colorTexture( gl::Texture::Format().dataType( GL_HALF_FLOAT_OES )
													.internalFormat( GL_RGB ) ).disableDepth() );
#else
	mAccumFbo = gl::Fbo::create( getWindowWidth(), getWindowHeight(),
		gl::Fbo::Format().colorTexture( gl::Texture::Format().internalFormat( GL_RGB16F ) ).disableDepth() );
#endif

	mPaused = false;
}

void MotionBlurFboApp::keyDown( KeyEvent event )
{
	if( event.getChar() == ' ' )
		mPaused = ! mPaused;
}

void MotionBlurFboApp::updateCubeRotation( double time )
{
	// Rotate the cube by .03 radians around an arbitrary axis (which changes over time)
	auto axis = normalize( vec3( sin( time ), cos( time * 3 ), 0.5f ) );
	mCubeRotation *= rotate( sinf( time ) * 0.02f, axis );
}

void MotionBlurFboApp::draw()
{
	gl::viewport( vec2(), mAccumFbo->getSize() );

	if( ! mPaused ) {
		// make 'mAccumFbo' the active framebuffer
		gl::ScopedFramebuffer fbScp( mAccumFbo );
		// clear out both of our FBOs
		gl::clear( Color::black() );
		gl::color( 1, 1, 1, 1 );

		// iterate all the sub-frames
		double startTime = getElapsedSeconds();
		for( int i = 0; i < SUBFRAMES; ++i ) {
			// draw the Cube's sub-frame into mFbo
			gl::enableDepthRead();
			gl::enableDepthWrite();
			gl::enableAlphaBlending();
			mFbo->bindFramebuffer();
			gl::clear();
			gl::setMatrices( mCam );
			updateCubeRotation( startTime + i / (float)SUBFRAMES );
			gl::multModelMatrix( mCubeRotation );
			mBatch->draw();

			// now add this frame to the accumulation FBO
			mAccumFbo->bindFramebuffer();
			gl::setMatricesWindow( mAccumFbo->getSize() );
			gl::enableAdditiveBlending();
			gl::disableDepthWrite();
			gl::disableDepthRead();
			gl::draw( mFbo->getColorTexture() );
		}
	}

	gl::disableDepthRead();
	gl::enableAlphaBlending();
	// set the color to be 1/SUBFRAMES, which divides the HDR image by the number of sub-frames we rendered
	gl::color( 1.0f / SUBFRAMES, 1.0f / SUBFRAMES, 1.0f / SUBFRAMES, 1 );
	gl::setMatricesWindow( getWindowSize() );
	gl::draw( mAccumFbo->getColorTexture() );
}

CINDER_APP( MotionBlurFboApp, RendererGl, []( App::Settings *settings ) {
	settings->setResizable( false );
	settings->setHighDensityDisplayEnabled( false );
} )
<commit_msg>Minor updates to MotionBlurFbo sample<commit_after>// The idea behind this technique is to render the scene to an intermediate FBO repeatedly (SUBFRAMES times),
// advancing the scene forward a little at a time (in sub-frame increments). So rather than rendering @ time 1.0, time 2.0, etc
// we render at time 1.1, 1.2, etc.
// At the end of rendering each sub-frame, accumulate (add) it into a floating point FBO. And at the end, we get an average by dividing
// by the number of sub-frames we rendered. This is a slightly naive, but very general technique for rendering motion blur.
// We achieve the accumulation by drawing the final sub-frame into the accumulation FBO using addivite blending.
// To do the divide by SUBFRAMES at the end, we just set the color to be 1.0/SUBFRAMES and draw the final accumulation buffer color texture.

#include "cinder/app/App.h"
#include "cinder/app/RendererGl.h"
#include "cinder/gl/gl.h"

using namespace ci;
using namespace ci::app;

class MotionBlurFboApp : public App {
  public:
	void setup();
	void keyDown( KeyEvent event );
	void updateCubeRotation( double time );
	void draw();

	CameraPersp			mCam;
	mat4			mCubeRotation;
	gl::BatchRef		mBatch;
	gl::FboRef			mFbo, mAccumFbo;
	bool				mPaused;
};

const int SUBFRAMES = 16; // increasing this number increases quality

void MotionBlurFboApp::setup()
{
	mCam.lookAt( vec3( 0, 0, 2.5 ), vec3( 0 ) );
	mCam.setPerspective( 60, getWindowAspectRatio(), 1, 1000 );

	mBatch = gl::Batch::create( geom::Cube().colors(), gl::getStockShader( gl::ShaderDef().color() ) );
	mFbo = gl::Fbo::create( toPixels( getWindowWidth() ), toPixels( getWindowHeight() ) );

#if defined( CINDER_GL_ES_2 )
	mAccumFbo = gl::Fbo::create( toPixels( getWindowWidth() ), toPixels( getWindowHeight() ),
		gl::Fbo::Format().colorTexture( gl::Texture::Format().dataType( GL_HALF_FLOAT_OES )
													.internalFormat( GL_RGB ) ).disableDepth() );
#else
	mAccumFbo = gl::Fbo::create( getWindowWidth(), getWindowHeight(),
		gl::Fbo::Format().colorTexture( gl::Texture::Format().internalFormat( GL_RGB16F ) ).disableDepth() );
#endif

	mPaused = false;
}

void MotionBlurFboApp::keyDown( KeyEvent event )
{
	if( event.getChar() == ' ' )
		mPaused = ! mPaused;
}

void MotionBlurFboApp::updateCubeRotation( double time )
{
	// Rotate the cube by .03 radians around an arbitrary axis (which changes over time)
	auto axis = normalize( vec3( sin( time ), cos( time * 3 ), 0.5f ) );
	mCubeRotation *= rotate( sinf( time ) * 0.02f, axis );
}

void MotionBlurFboApp::draw()
{
	gl::viewport( vec2(), mAccumFbo->getSize() );

	if( ! mPaused ) {
		// make 'mAccumFbo' the active framebuffer
		gl::ScopedFramebuffer fbScp( mAccumFbo );
		// clear out both of our FBOs
		gl::clear( Color::black() );
		gl::color( 1, 1, 1, 1 );

		// iterate all the sub-frames
		double startTime = getElapsedSeconds();
		for( int i = 0; i < SUBFRAMES; ++i ) {
			// draw the Cube's sub-frame into mFbo
			gl::enableDepth();
			gl::enableAlphaBlending();
			mFbo->bindFramebuffer();
			gl::clear();
			gl::enableDepth();
			gl::setMatrices( mCam );
			updateCubeRotation( startTime + i / (float)SUBFRAMES );
			gl::multModelMatrix( mCubeRotation );
			mBatch->draw();

			// now add this frame to the accumulation FBO
			mAccumFbo->bindFramebuffer();
			gl::setMatricesWindow( mAccumFbo->getSize() );
			gl::enableAdditiveBlending();
			gl::enableDepth( false );
			gl::draw( mFbo->getColorTexture() );
		}
	}

	gl::disableDepthRead();
	gl::disableAlphaBlending();
	// set the color to be 1/SUBFRAMES, which divides the HDR image by the number of sub-frames we rendered
	gl::color( 1.0f / SUBFRAMES, 1.0f / SUBFRAMES, 1.0f / SUBFRAMES, 1 );
	gl::setMatricesWindow( getWindowSize() );
	gl::draw( mAccumFbo->getColorTexture() );
}

CINDER_APP( MotionBlurFboApp, RendererGl, []( App::Settings *settings ) {
	settings->setResizable( false );
	settings->setHighDensityDisplayEnabled( false );
} )
<|endoftext|>