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

#include "TextRenderer.h"

#include <freetype-gl/shader.h>
#include <freetype-gl/vertex-buffer.h>

#include "App.h"
#include "GlCanvas.h"
#include "GlUtils.h"
#include "Params.h"
#include "Path.h"

typedef struct {
  float x, y, z;     // position
  float s, t;        // texture
  float r, g, b, a;  // color
} vertex_t;

TextRenderer::TextRenderer()
    : m_Atlas(NULL),
      m_Buffer(NULL),
      m_Font(NULL),
      m_Canvas(NULL),
      m_Initialized(false),
      m_DrawOutline(false) {}

TextRenderer::~TextRenderer() {
  if (m_Font) {
    texture_font_delete(m_Font);
  }

  if (m_Buffer) {
    vertex_buffer_delete(m_Buffer);
  }
}

void TextRenderer::Init() {
  if (m_Initialized) return;

  m_Font = NULL;
  int atlasSize = 2 * 1024;
  m_Atlas = texture_atlas_new(atlasSize, atlasSize, 1);

  const auto exePath = Path::GetExecutablePath();
  const auto fontFileName = exePath + "fonts/Vera.ttf";

  static float fsize = GParams.font_size;
  m_Buffer = vertex_buffer_new("vertex:3f,tex_coord:2f,color:4f");
  m_Font = texture_font_new_from_file(m_Atlas, fsize, fontFileName.c_str());
  current_font_size_ = static_cast<int>(fsize);
  for (int i = 10; i <= 100; i += 1) {
    m_FontsBySize[i] =
        texture_font_new_from_file(m_Atlas, i, fontFileName.c_str());
  }

  m_Pen.x = 0;
  m_Pen.y = 0;

  glGenTextures(1, &m_Atlas->id);

  const auto vertShaderFileName = exePath + "shaders/v3f-t2f-c4f.vert";
  const auto fragShaderFileName = exePath + "shaders/v3f-t2f-c4f.frag";
  m_Shader =
      shader_load(vertShaderFileName.c_str(), fragShaderFileName.c_str());

  mat4_set_identity(&m_Proj);
  mat4_set_identity(&m_Model);
  mat4_set_identity(&m_View);

  m_Initialized = true;
}

void TextRenderer::SetFontSize(int size) {
  texture_font_t* font = m_FontsBySize[size];
  if (font) {
    m_Font = font;
    current_font_size_ = size;
  }
}

int TextRenderer::GetFontSize() {
  return current_font_size_;
}

void TextRenderer::Display(Batcher* batcher) {
  if (m_DrawOutline) {
    DrawOutline(batcher, m_Buffer);
  }

  // Lazy init
  if (!m_Initialized) {
    Init();
  }

  glBindTexture(GL_TEXTURE_2D, m_Atlas->id);

  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, static_cast<GLsizei>(m_Atlas->width),
               static_cast<GLsizei>(m_Atlas->height), 0, GL_RED,
               GL_UNSIGNED_BYTE, m_Atlas->data);

  // Get current projection matrix
  GLfloat matrix[16];
  glGetFloatv(GL_PROJECTION_MATRIX, matrix);
  mat4* proj = reinterpret_cast<mat4*>(&matrix[0]);
  m_Proj = *proj;

  mat4_set_orthographic(&m_Proj, 0, m_Canvas->getWidth(), 0,
                        m_Canvas->getHeight(), -1, 1);

  glUseProgram(m_Shader);
  {
    glUniform1i(glGetUniformLocation(m_Shader, "texture"), 0);
    glUniformMatrix4fv(glGetUniformLocation(m_Shader, "model"), 1, 0,
                       m_Model.data);
    glUniformMatrix4fv(glGetUniformLocation(m_Shader, "view"), 1, 0,
                       m_View.data);
    glUniformMatrix4fv(glGetUniformLocation(m_Shader, "projection"), 1, 0,
                       m_Proj.data);
    vertex_buffer_render(m_Buffer, GL_TRIANGLES);

    /*PRINT_VAR(m_Model);
    PRINT_VAR(m_View);
    PRINT_VAR(m_Proj);*/
  }

  glBindTexture(GL_TEXTURE_2D, 0);
  glUseProgram(0);
}

void TextRenderer::DrawOutline(Batcher* batcher, vertex_buffer_t* a_Buffer) {
  // TODO: No color was set here before.
  Color color(255, 255, 255, 255);

  for (size_t i = 0; i < a_Buffer->indices->size; i += 3) {
    GLuint i0 =
        *static_cast<const GLuint*>(vector_get(a_Buffer->indices, i + 0));
    GLuint i1 =
        *static_cast<const GLuint*>(vector_get(a_Buffer->indices, i + 1));
    GLuint i2 =
        *static_cast<const GLuint*>(vector_get(a_Buffer->indices, i + 2));

    vertex_t v0 =
        *static_cast<const vertex_t*>(vector_get(a_Buffer->vertices, i0));
    vertex_t v1 =
        *static_cast<const vertex_t*>(vector_get(a_Buffer->vertices, i1));
    vertex_t v2 =
        *static_cast<const vertex_t*>(vector_get(a_Buffer->vertices, i2));

    batcher->AddLine(Vec2(v0.x, v0.y), Vec2(v1.x, v1.y), v0.z, color,
                     PickingType::kPickable);
    batcher->AddLine(Vec2(v1.x, v1.y), Vec2(v2.x, v2.y), v1.z, color,
                     PickingType::kPickable);
    batcher->AddLine(Vec2(v2.x, v2.y), Vec2(v0.x, v0.y), v2.z, color,
                     PickingType::kPickable);
  }
}

void TextRenderer::AddTextInternal(texture_font_t* font, const char* text,
                                   const vec4& color, vec2* pen,
                                   float a_MaxSize, float a_Z, bool) {
  size_t i;
  float r = color.red, g = color.green, b = color.blue, a = color.alpha;
  float textZ = a_Z;

  float maxWidth = a_MaxSize == -1.f ? FLT_MAX : ToScreenSpace(a_MaxSize);
  float strWidth = 0.f;
  int minX = INT_MAX;
  int maxX = -INT_MAX;

  for (i = 0; i < strlen(text); ++i) {
    if (!texture_font_find_glyph(font, text + i)) {
      texture_font_load_glyph(font, text + i);
    }

    texture_glyph_t* glyph = texture_font_get_glyph(font, text + i);
    if (glyph != NULL) {
      float kerning = 0.0f;
      if (i > 0) {
        kerning = texture_glyph_get_kerning(glyph, text + i - 1);
      }
      pen->x += kerning;
      float x0 = static_cast<int>(pen->x + glyph->offset_x);
      float y0 = static_cast<int>(pen->y + glyph->offset_y);
      float x1 = static_cast<int>(x0 + glyph->width);
      float y1 = static_cast<int>(y0 - glyph->height);
      float s0 = glyph->s0;
      float t0 = glyph->t0;
      float s1 = glyph->s1;
      float t1 = glyph->t1;
      GLuint indices[6] = {0, 1, 2, 0, 2, 3};
      vertex_t vertices[4] = {{x0, y0, textZ, s0, t0, r, g, b, a},
                              {x0, y1, textZ, s0, t1, r, g, b, a},
                              {x1, y1, textZ, s1, t1, r, g, b, a},
                              {x1, y0, textZ, s1, t0, r, g, b, a}};

      minX = std::min(minX, static_cast<int>(x0));
      maxX = std::max(maxX, static_cast<int>(x1));
      strWidth = maxX - minX;

      if (strWidth > maxWidth) {
        break;
      }

      vertex_buffer_push_back(m_Buffer, vertices, 4, indices, 6);
      pen->x += glyph->advance_x;
    }
  }
}

void TextRenderer::AddText(const char* a_Text, float a_X, float a_Y, float a_Z,
                           const Color& a_Color, float a_MaxSize,
                           bool a_RightJustified) {
  ToScreenSpace(a_X, a_Y, m_Pen.x, m_Pen.y);

  if (a_RightJustified) {
    a_MaxSize = FLT_MAX;
    int stringWidth, stringHeight;
    GetStringSize(a_Text, stringWidth, stringHeight);
    m_Pen.x -= stringWidth;
  }

  AddTextInternal(m_Font, a_Text, ColorToVec4(a_Color), &m_Pen, a_MaxSize, a_Z);
}

void TextRenderer::AddTextTrailingCharsPrioritized(
    const char* a_Text, float a_X, float a_Y, float a_Z, const Color& a_Color,
    size_t a_TrailingCharsLength, float a_MaxSize) {
  if (!m_Initialized) {
    Init();
  }

  float tempPenX = ToScreenSpace(a_X);
  float maxWidth = a_MaxSize == -1.f ? FLT_MAX : ToScreenSpace(a_MaxSize);
  float strWidth = 0.f;
  int minX = INT_MAX;
  int maxX = -INT_MAX;

  const size_t textLen = strlen(a_Text);

  size_t i;
  for (i = 0; i < textLen; ++i) {
    if (!texture_font_find_glyph(m_Font, a_Text + i)) {
      texture_font_load_glyph(m_Font, a_Text + i);
    }

    texture_glyph_t* glyph = texture_font_get_glyph(m_Font, a_Text + i);
    if (glyph != NULL) {
      float kerning = 0.0f;
      if (i > 0) {
        kerning = texture_glyph_get_kerning(glyph, a_Text + i - 1);
      }
      tempPenX += kerning;
      int x0 = static_cast<int>(tempPenX + glyph->offset_x);
      int x1 = static_cast<int>(x0 + glyph->width);

      minX = std::min(minX, x0);
      maxX = std::max(maxX, x1);
      strWidth = float(maxX - minX);

      if (strWidth > maxWidth) {
        break;
      }

      tempPenX += glyph->advance_x;
    }
  }

  // TODO: Technically, we'd want the size of "... <TIME>" + remaining
  // characters

  auto fittingCharsCount = i;

  static const char* ELLIPSIS_TEXT = "... ";
  static const size_t ELLIPSIS_TEXT_LEN = strlen(ELLIPSIS_TEXT);
  static const size_t LEADING_CHARS_COUNT = 1;
  static const size_t ELLIPSIS_BUFFER_SIZE =
      ELLIPSIS_TEXT_LEN + LEADING_CHARS_COUNT;

  bool useEllipsisText =
      (fittingCharsCount < textLen) &&
      (fittingCharsCount > (a_TrailingCharsLength + ELLIPSIS_BUFFER_SIZE));

  if (!useEllipsisText) {
    AddText(a_Text, a_X, a_Y, a_Z, a_Color, a_MaxSize);
  } else {
    auto leadingCharCount =
        fittingCharsCount - (a_TrailingCharsLength + ELLIPSIS_TEXT_LEN);

    std::string modifiedText(a_Text, leadingCharCount);
    modifiedText.append(ELLIPSIS_TEXT);

    auto timePosition = textLen - a_TrailingCharsLength;
    modifiedText.append(&a_Text[timePosition], a_TrailingCharsLength);

    AddText(modifiedText.c_str(), a_X, a_Y, a_Z, a_Color, a_MaxSize);
  }
}

int TextRenderer::AddText2D(const char* a_Text, int a_X, int a_Y, float a_Z,
                            const Color& a_Color, float a_MaxSize,
                            bool a_RightJustified, bool a_InvertY) {
  if (a_RightJustified) {
    int stringWidth, stringHeight;
    GetStringSize(a_Text, stringWidth, stringHeight);
    a_X -= stringWidth;
  }

  m_Pen.x = a_X;
  m_Pen.y = a_InvertY ? m_Canvas->getHeight() - a_Y : a_Y;

  AddTextInternal(m_Font, a_Text, ColorToVec4(a_Color), &m_Pen, a_MaxSize, a_Z);

  return a_X;
}

void TextRenderer::GetStringSize(const char* a_Text, int& a_Width,
                                 int& a_Height) {
  float stringWidth = 0;
  float stringHeight = 0;

  std::size_t len = strlen(a_Text);
  for (std::size_t i = 0; i < len; ++i) {
    texture_glyph_t* glyph = texture_font_get_glyph(m_Font, a_Text + i);
    if (glyph != NULL) {
      float kerning = 0.0f;
      if (i > 0) {
        kerning = texture_glyph_get_kerning(glyph, a_Text + i - 1);
      }

      stringWidth += kerning;
      stringWidth += glyph->advance_x;

      if (glyph->height > stringHeight) stringHeight = glyph->height;
    }
  }

  a_Width = static_cast<int>(stringWidth);
  a_Height = static_cast<int>(stringHeight);
}

int TextRenderer::GetStringHeight(const char* a_Text) {
  int w, h;
  GetStringSize(a_Text, w, h);
  return h;
}

void TextRenderer::ToScreenSpace(float a_X, float a_Y, float& o_X, float& o_Y) {
  float WorldWidth = m_Canvas->GetWorldWidth();
  float WorldHeight = m_Canvas->GetWorldHeight();
  float WorldTopLeftX = m_Canvas->GetWorldTopLeftX();
  float WorldMinLeftY = m_Canvas->GetWorldTopLeftY() - WorldHeight;

  o_X = ((a_X - WorldTopLeftX) / WorldWidth) * m_Canvas->getWidth();
  o_Y = ((a_Y - WorldMinLeftY) / WorldHeight) * m_Canvas->getHeight();
}

float TextRenderer::ToScreenSpace(float a_Size) {
  return (a_Size / m_Canvas->GetWorldWidth()) * m_Canvas->getWidth();
}

void TextRenderer::Clear() {
  m_Pen.x = 0.f;
  m_Pen.y = 0.f;
  if (m_Buffer) {
    vertex_buffer_clear(m_Buffer);
  }
}

const TextBox& TextRenderer::GetSceneBox() const {
  return m_Canvas->GetSceneBox();
}

int TextRenderer::GetNumCharacters() const {
  return int(m_Buffer->vertices->size) / 4;
}
<commit_msg>Fix memory leaks in TextRenderer<commit_after>// Copyright (c) 2020 The Orbit Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "TextRenderer.h"

#include <freetype-gl/shader.h>
#include <freetype-gl/vertex-buffer.h>

#include "App.h"
#include "GlCanvas.h"
#include "GlUtils.h"
#include "Params.h"
#include "Path.h"

typedef struct {
  float x, y, z;     // position
  float s, t;        // texture
  float r, g, b, a;  // color
} vertex_t;

TextRenderer::TextRenderer()
    : m_Atlas(nullptr),
      m_Buffer(nullptr),
      m_Font(nullptr),
      m_Canvas(nullptr),
      m_Initialized(false),
      m_DrawOutline(false) {}

TextRenderer::~TextRenderer() {
  // Freetype-gl doesn't handle nullptr in its
  // delete functions, hence the checks.

  if (m_Font) {
    texture_font_delete(m_Font);
  }

  for (const auto& pair : m_FontsBySize) {
    texture_font_delete(pair.second);
  }
  m_FontsBySize.clear();

  if (m_Buffer) {
    vertex_buffer_delete(m_Buffer);
  }

  if (m_Atlas) {
    texture_atlas_delete(m_Atlas);
  }
}

void TextRenderer::Init() {
  if (m_Initialized) return;

  int atlasSize = 2 * 1024;
  m_Atlas = texture_atlas_new(atlasSize, atlasSize, 1);

  const auto exePath = Path::GetExecutablePath();
  const auto fontFileName = exePath + "fonts/Vera.ttf";

  static float fsize = GParams.font_size;
  m_Buffer = vertex_buffer_new("vertex:3f,tex_coord:2f,color:4f");
  m_Font = texture_font_new_from_file(m_Atlas, fsize, fontFileName.c_str());
  current_font_size_ = static_cast<int>(fsize);
  for (int i = 10; i <= 100; i += 1) {
    m_FontsBySize[i] =
        texture_font_new_from_file(m_Atlas, i, fontFileName.c_str());
  }

  m_Pen.x = 0;
  m_Pen.y = 0;

  glGenTextures(1, &m_Atlas->id);

  const auto vertShaderFileName = exePath + "shaders/v3f-t2f-c4f.vert";
  const auto fragShaderFileName = exePath + "shaders/v3f-t2f-c4f.frag";
  m_Shader =
      shader_load(vertShaderFileName.c_str(), fragShaderFileName.c_str());

  mat4_set_identity(&m_Proj);
  mat4_set_identity(&m_Model);
  mat4_set_identity(&m_View);

  m_Initialized = true;
}

void TextRenderer::SetFontSize(int size) {
  texture_font_t* font = m_FontsBySize[size];
  if (font) {
    m_Font = font;
    current_font_size_ = size;
  }
}

int TextRenderer::GetFontSize() { return current_font_size_; }

void TextRenderer::Display(Batcher* batcher) {
  if (m_DrawOutline) {
    DrawOutline(batcher, m_Buffer);
  }

  // Lazy init
  if (!m_Initialized) {
    Init();
  }

  glBindTexture(GL_TEXTURE_2D, m_Atlas->id);

  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, static_cast<GLsizei>(m_Atlas->width),
               static_cast<GLsizei>(m_Atlas->height), 0, GL_RED,
               GL_UNSIGNED_BYTE, m_Atlas->data);

  // Get current projection matrix
  GLfloat matrix[16];
  glGetFloatv(GL_PROJECTION_MATRIX, matrix);
  mat4* proj = reinterpret_cast<mat4*>(&matrix[0]);
  m_Proj = *proj;

  mat4_set_orthographic(&m_Proj, 0, m_Canvas->getWidth(), 0,
                        m_Canvas->getHeight(), -1, 1);

  glUseProgram(m_Shader);
  {
    glUniform1i(glGetUniformLocation(m_Shader, "texture"), 0);
    glUniformMatrix4fv(glGetUniformLocation(m_Shader, "model"), 1, 0,
                       m_Model.data);
    glUniformMatrix4fv(glGetUniformLocation(m_Shader, "view"), 1, 0,
                       m_View.data);
    glUniformMatrix4fv(glGetUniformLocation(m_Shader, "projection"), 1, 0,
                       m_Proj.data);
    vertex_buffer_render(m_Buffer, GL_TRIANGLES);

    /*PRINT_VAR(m_Model);
    PRINT_VAR(m_View);
    PRINT_VAR(m_Proj);*/
  }

  glBindTexture(GL_TEXTURE_2D, 0);
  glUseProgram(0);
}

void TextRenderer::DrawOutline(Batcher* batcher, vertex_buffer_t* a_Buffer) {
  // TODO: No color was set here before.
  Color color(255, 255, 255, 255);

  for (size_t i = 0; i < a_Buffer->indices->size; i += 3) {
    GLuint i0 =
        *static_cast<const GLuint*>(vector_get(a_Buffer->indices, i + 0));
    GLuint i1 =
        *static_cast<const GLuint*>(vector_get(a_Buffer->indices, i + 1));
    GLuint i2 =
        *static_cast<const GLuint*>(vector_get(a_Buffer->indices, i + 2));

    vertex_t v0 =
        *static_cast<const vertex_t*>(vector_get(a_Buffer->vertices, i0));
    vertex_t v1 =
        *static_cast<const vertex_t*>(vector_get(a_Buffer->vertices, i1));
    vertex_t v2 =
        *static_cast<const vertex_t*>(vector_get(a_Buffer->vertices, i2));

    batcher->AddLine(Vec2(v0.x, v0.y), Vec2(v1.x, v1.y), v0.z, color,
                     PickingType::kPickable);
    batcher->AddLine(Vec2(v1.x, v1.y), Vec2(v2.x, v2.y), v1.z, color,
                     PickingType::kPickable);
    batcher->AddLine(Vec2(v2.x, v2.y), Vec2(v0.x, v0.y), v2.z, color,
                     PickingType::kPickable);
  }
}

void TextRenderer::AddTextInternal(texture_font_t* font, const char* text,
                                   const vec4& color, vec2* pen,
                                   float a_MaxSize, float a_Z, bool) {
  size_t i;
  float r = color.red, g = color.green, b = color.blue, a = color.alpha;
  float textZ = a_Z;

  float maxWidth = a_MaxSize == -1.f ? FLT_MAX : ToScreenSpace(a_MaxSize);
  float strWidth = 0.f;
  int minX = INT_MAX;
  int maxX = -INT_MAX;

  for (i = 0; i < strlen(text); ++i) {
    if (!texture_font_find_glyph(font, text + i)) {
      texture_font_load_glyph(font, text + i);
    }

    texture_glyph_t* glyph = texture_font_get_glyph(font, text + i);
    if (glyph != NULL) {
      float kerning = 0.0f;
      if (i > 0) {
        kerning = texture_glyph_get_kerning(glyph, text + i - 1);
      }
      pen->x += kerning;
      float x0 = static_cast<int>(pen->x + glyph->offset_x);
      float y0 = static_cast<int>(pen->y + glyph->offset_y);
      float x1 = static_cast<int>(x0 + glyph->width);
      float y1 = static_cast<int>(y0 - glyph->height);
      float s0 = glyph->s0;
      float t0 = glyph->t0;
      float s1 = glyph->s1;
      float t1 = glyph->t1;
      GLuint indices[6] = {0, 1, 2, 0, 2, 3};
      vertex_t vertices[4] = {{x0, y0, textZ, s0, t0, r, g, b, a},
                              {x0, y1, textZ, s0, t1, r, g, b, a},
                              {x1, y1, textZ, s1, t1, r, g, b, a},
                              {x1, y0, textZ, s1, t0, r, g, b, a}};

      minX = std::min(minX, static_cast<int>(x0));
      maxX = std::max(maxX, static_cast<int>(x1));
      strWidth = maxX - minX;

      if (strWidth > maxWidth) {
        break;
      }

      vertex_buffer_push_back(m_Buffer, vertices, 4, indices, 6);
      pen->x += glyph->advance_x;
    }
  }
}

void TextRenderer::AddText(const char* a_Text, float a_X, float a_Y, float a_Z,
                           const Color& a_Color, float a_MaxSize,
                           bool a_RightJustified) {
  ToScreenSpace(a_X, a_Y, m_Pen.x, m_Pen.y);

  if (a_RightJustified) {
    a_MaxSize = FLT_MAX;
    int stringWidth, stringHeight;
    GetStringSize(a_Text, stringWidth, stringHeight);
    m_Pen.x -= stringWidth;
  }

  AddTextInternal(m_Font, a_Text, ColorToVec4(a_Color), &m_Pen, a_MaxSize, a_Z);
}

void TextRenderer::AddTextTrailingCharsPrioritized(
    const char* a_Text, float a_X, float a_Y, float a_Z, const Color& a_Color,
    size_t a_TrailingCharsLength, float a_MaxSize) {
  if (!m_Initialized) {
    Init();
  }

  float tempPenX = ToScreenSpace(a_X);
  float maxWidth = a_MaxSize == -1.f ? FLT_MAX : ToScreenSpace(a_MaxSize);
  float strWidth = 0.f;
  int minX = INT_MAX;
  int maxX = -INT_MAX;

  const size_t textLen = strlen(a_Text);

  size_t i;
  for (i = 0; i < textLen; ++i) {
    if (!texture_font_find_glyph(m_Font, a_Text + i)) {
      texture_font_load_glyph(m_Font, a_Text + i);
    }

    texture_glyph_t* glyph = texture_font_get_glyph(m_Font, a_Text + i);
    if (glyph != NULL) {
      float kerning = 0.0f;
      if (i > 0) {
        kerning = texture_glyph_get_kerning(glyph, a_Text + i - 1);
      }
      tempPenX += kerning;
      int x0 = static_cast<int>(tempPenX + glyph->offset_x);
      int x1 = static_cast<int>(x0 + glyph->width);

      minX = std::min(minX, x0);
      maxX = std::max(maxX, x1);
      strWidth = float(maxX - minX);

      if (strWidth > maxWidth) {
        break;
      }

      tempPenX += glyph->advance_x;
    }
  }

  // TODO: Technically, we'd want the size of "... <TIME>" + remaining
  // characters

  auto fittingCharsCount = i;

  static const char* ELLIPSIS_TEXT = "... ";
  static const size_t ELLIPSIS_TEXT_LEN = strlen(ELLIPSIS_TEXT);
  static const size_t LEADING_CHARS_COUNT = 1;
  static const size_t ELLIPSIS_BUFFER_SIZE =
      ELLIPSIS_TEXT_LEN + LEADING_CHARS_COUNT;

  bool useEllipsisText =
      (fittingCharsCount < textLen) &&
      (fittingCharsCount > (a_TrailingCharsLength + ELLIPSIS_BUFFER_SIZE));

  if (!useEllipsisText) {
    AddText(a_Text, a_X, a_Y, a_Z, a_Color, a_MaxSize);
  } else {
    auto leadingCharCount =
        fittingCharsCount - (a_TrailingCharsLength + ELLIPSIS_TEXT_LEN);

    std::string modifiedText(a_Text, leadingCharCount);
    modifiedText.append(ELLIPSIS_TEXT);

    auto timePosition = textLen - a_TrailingCharsLength;
    modifiedText.append(&a_Text[timePosition], a_TrailingCharsLength);

    AddText(modifiedText.c_str(), a_X, a_Y, a_Z, a_Color, a_MaxSize);
  }
}

int TextRenderer::AddText2D(const char* a_Text, int a_X, int a_Y, float a_Z,
                            const Color& a_Color, float a_MaxSize,
                            bool a_RightJustified, bool a_InvertY) {
  if (a_RightJustified) {
    int stringWidth, stringHeight;
    GetStringSize(a_Text, stringWidth, stringHeight);
    a_X -= stringWidth;
  }

  m_Pen.x = a_X;
  m_Pen.y = a_InvertY ? m_Canvas->getHeight() - a_Y : a_Y;

  AddTextInternal(m_Font, a_Text, ColorToVec4(a_Color), &m_Pen, a_MaxSize, a_Z);

  return a_X;
}

void TextRenderer::GetStringSize(const char* a_Text, int& a_Width,
                                 int& a_Height) {
  float stringWidth = 0;
  float stringHeight = 0;

  std::size_t len = strlen(a_Text);
  for (std::size_t i = 0; i < len; ++i) {
    texture_glyph_t* glyph = texture_font_get_glyph(m_Font, a_Text + i);
    if (glyph != NULL) {
      float kerning = 0.0f;
      if (i > 0) {
        kerning = texture_glyph_get_kerning(glyph, a_Text + i - 1);
      }

      stringWidth += kerning;
      stringWidth += glyph->advance_x;

      if (glyph->height > stringHeight) stringHeight = glyph->height;
    }
  }

  a_Width = static_cast<int>(stringWidth);
  a_Height = static_cast<int>(stringHeight);
}

int TextRenderer::GetStringHeight(const char* a_Text) {
  int w, h;
  GetStringSize(a_Text, w, h);
  return h;
}

void TextRenderer::ToScreenSpace(float a_X, float a_Y, float& o_X, float& o_Y) {
  float WorldWidth = m_Canvas->GetWorldWidth();
  float WorldHeight = m_Canvas->GetWorldHeight();
  float WorldTopLeftX = m_Canvas->GetWorldTopLeftX();
  float WorldMinLeftY = m_Canvas->GetWorldTopLeftY() - WorldHeight;

  o_X = ((a_X - WorldTopLeftX) / WorldWidth) * m_Canvas->getWidth();
  o_Y = ((a_Y - WorldMinLeftY) / WorldHeight) * m_Canvas->getHeight();
}

float TextRenderer::ToScreenSpace(float a_Size) {
  return (a_Size / m_Canvas->GetWorldWidth()) * m_Canvas->getWidth();
}

void TextRenderer::Clear() {
  m_Pen.x = 0.f;
  m_Pen.y = 0.f;
  if (m_Buffer) {
    vertex_buffer_clear(m_Buffer);
  }
}

const TextBox& TextRenderer::GetSceneBox() const {
  return m_Canvas->GetSceneBox();
}

int TextRenderer::GetNumCharacters() const {
  return int(m_Buffer->vertices->size) / 4;
}
<|endoftext|>


<commit_before><commit_msg>Bug#2717: patch to Unterminated INCLUDE section causes infinite loop with setExitOnFirstFatalError(false)<commit_after><|endoftext|>


<commit_before>/**
* @file Entity.cpp
*/
#include "Entity.h"
#include "Uniform.h"
#include <glm/gtc/matrix_transform.hpp>
#include <iostream>
#include <algorithm>

namespace Entity {

/**
* VertexDataUBOɓ].
*
* @param entity
* @param ubo
* @param matViewProjection
*/
void UpdateUniformVertexData(Entity& entity, void* ubo, const glm::mat4 matViewProjection[4], glm::u32 viewFlags)
{
  Uniform::VertexData data;
  data.matModel = entity.TRSMatrix();
  data.matNormal = glm::mat4_cast(entity.Rotation());
  for (int i = 0; i < Uniform::maxViewCount; ++i) {
    if (viewFlags & (1 << i)) {
      data.matMVP[i] = matViewProjection[i] * data.matModel;
    }
  }
  data.color = entity.Color();
  memcpy(ubo, &data, sizeof(data));
}

/**
* NIuWFNg̎Oɒǉ.
*
* @param p@ǉ郊NIuWFNgւ̃|C^.
*
* p̃NXg؂藣A̎Oɒǉ.
*/
void Buffer::Link::Insert(Link* p)
{
  p->Remove();
  p->prev = prev;
  p->next = this;
  prev->next = p;
  prev = p;
}

/**
* gNXg؂藣.
*
* ͂ǂɂڑĂȂԂɂȂ.
*/
void Buffer::Link::Remove()
{
  next->prev = prev;
  prev->next = next;
  prev = this;
  next = this;
}

/**
* gkE]Eړs擾.
*
* @return TRSs.
*/
glm::mat4 Entity::TRSMatrix() const
{
#if 1
  return glm::scale(glm::translate(glm::mat4(), position) * glm::mat4_cast(rotation), scale);
#else
  const glm::mat4 s = glm::scale(glm::mat4(), scale);
  const glm::mat4 r = glm::mat4_cast(rotation);
  const glm::mat4 t = glm::translate(glm::mat4(), position);
  return t * r * s;
#endif
}

/**
* GeBeBj.
*
* ̊֐ĂяoƂ́AGeBeB𑀍삵Ă͂ȂȂ.
*/
void Entity::Destroy()
{
  if (pBuffer) {
    pBuffer->RemoveEntity(this);
  }
}

/**
* GeBeBobt@쐬.
*
* @param maxEntityCount   GeBeB̍ő吔.
* @param ubSizePerEntity  GeBeBƂUniform Buffer̃oCg.
* @param bindingPoint     GeBeBpUBÕoCfBO|Cg.
* @param ubName           GeBeBpUniform Buffer̖O.
*
* @return 쐬GeBeBobt@ւ̃|C^.
*/
BufferPtr Buffer::Create(size_t maxEntityCount, GLsizeiptr ubSizePerEntity, int bindingPoint, const char* ubName)
{
  struct Impl : Buffer { Impl() {} ~Impl() {} };
  BufferPtr p = std::make_shared<Impl>();
  if (!p) {
    std::cerr << "WARNING in Entity::Buffer::Create: obt@̍쐬Ɏs." << std::endl;
    return {};
  }
  p->ubo = UniformBuffer::Create(maxEntityCount * ubSizePerEntity, bindingPoint, ubName);
  p->buffer.reset(new LinkEntity[maxEntityCount]);
  if (!p->ubo || !p->buffer) {
    std::cerr << "WARNING in Entity::Buffer::Create: obt@̍쐬Ɏs." << std::endl;
    return {};
  }
  p->bufferSize = maxEntityCount;
  p->ubSizePerEntity = ubSizePerEntity;
  GLintptr offset = 0;
  const LinkEntity* const end = &p->buffer[maxEntityCount];
  for (LinkEntity* itr = &p->buffer[0]; itr != end; ++itr) {
    itr->uboOffset = offset;
    itr->pBuffer = p.get();
    p->freeList.Insert(itr);
    offset += ubSizePerEntity;
  }
  p->collisionHandlerList.reserve(maxGroupId);
  for (auto& e : p->visibilityFlags) {
    e = 1;
  }
  return p;
}

/**
* GeBeBǉ.
*
* @param groupId  GeBeB̃O[vID.
* @param position GeBeB̍W.
* @param mesh     GeBeB̕\Ɏgp郁bV.
* @param texture  GeBeB̕\ɎgeNX`.
* @param program  GeBeB̕\ɎgpVF[_vO.
* @param func     GeBeB̏ԂXV֐(܂͊֐IuWFNg).
*
* @return ǉGeBeBւ̃|C^.
*         ȏGeBeBǉłȂꍇnullptrԂ.
*         ]g嗦͂̃|C^oRŐݒ肷.
*         ȂÃ|C^AvP[VŕێKv͂Ȃ.
*/
Entity* Buffer::AddEntity(int groupId, const glm::vec3& position, const Mesh::MeshPtr& mesh, const TexturePtr t[2], const Shader::ProgramPtr& program, const Entity::UpdateFuncType& func)
{
  if (freeList.prev == freeList.next) {
    std::cerr << "WARNING in Entity::Buffer::AddEntity: 󂫃GeBeB܂." << std::endl;
    return nullptr;
  }
  if (groupId < 0 || groupId > maxGroupId) {
    std::cerr << "ERROR in Entity::Buffer::AddEntity: ͈͊ÕO[vID(" << groupId << ")n܂.\nO[vID0`" << maxGroupId << "łȂ΂Ȃ܂." << std::endl;
    return nullptr;
  }
  LinkEntity* entity = static_cast<LinkEntity*>(freeList.prev);
  activeList[groupId].Insert(entity);
  entity->groupId = groupId;
  entity->position = position;
  entity->rotation = glm::quat();
  entity->scale = glm::vec3(1, 1, 1);
  entity->velocity = glm::vec3();
  entity->color = glm::vec4(1, 1, 1, 1);
  entity->mesh = mesh;
  entity->texture[0] = t[0];
  entity->texture[1] = t[1];
  entity->program = program;
  entity->updateFunc = func;
  entity->isActive = true;
  return entity;
}

/**
*@GeBeB폜.
*
* @param 폜GeBeB̃|C^.
*/
void Buffer::RemoveEntity(Entity* entity)
{
  if (!entity || !entity->isActive) {
    std::cerr << "WARNING in Entity::Buffer::RemoveEntity: ANeBuȃGeBeB폜悤Ƃ܂." << std::endl;
    return;
  }
  LinkEntity* p = static_cast<LinkEntity*>(entity);
  if (p < &buffer[0] || p >= &buffer[bufferSize]) {
    std::cerr << "WARNING in Entity::Buffer::RemoveEntity: قȂobt@擾GeBeB폜悤Ƃ܂." << std::endl;
    return;
  }
  if (p == itrUpdate) {
    itrUpdate = p->prev;
  }
  if (p == itrUpdateRhs) {
    itrUpdateRhs = p->prev;
  }
  freeList.Insert(p);
  p->mesh.reset();
  for (auto& e : p->texture) {
    e.reset();
  }
  p->program.reset();
  p->updateFunc = nullptr;
  p->isActive = false;
}

/**
* SẴGeBeB폜.
*/
void Buffer::RemoveAllEntity()
{
  for (int groupId = 0; groupId <= maxGroupId; ++groupId) {
    while (activeList[groupId].next != &activeList[groupId]) {
      RemoveEntity(static_cast<LinkEntity*>(activeList[groupId].next));
    }
  }
}

/**
* `m̏Փ˔.
*/
bool HasCollision(const CollisionData& lhs, const CollisionData& rhs)
{
  if (lhs.max.x < rhs.min.x || lhs.min.x > rhs.max.x) return false;
  if (lhs.max.y < rhs.min.y || lhs.min.y > rhs.max.y) return false;
  if (lhs.max.z < rhs.min.z || lhs.min.z > rhs.max.z) return false;
  return true;
}

#pragma optimize( "ts", off)
/**
* ANeBuȃGeBeB̏ԂXV.
*
* @param delta   O̍XV̌oߎ.
* @param matView Views.
* @param matProj Projections.
*/
void Buffer::Update(double delta, const glm::mat4* matView, const glm::mat4& matProj)
{
  // Wƃ[hWn̏Փˌ`XV.
  // eGeBeB̏ԂXV.
  for (int groupId = 0; groupId <= maxGroupId; ++groupId) {
    for (itrUpdate = activeList[groupId].next; itrUpdate != &activeList[groupId]; itrUpdate = itrUpdate->next) {
      LinkEntity& e = *static_cast<LinkEntity*>(itrUpdate);
      e.position += e.velocity * static_cast<float>(delta);
      if (e.updateFunc) {
        e.updateFunc(e, delta);
      }
      e.colWorld.min = e.colLocal.min + e.position;
      e.colWorld.max = e.colLocal.max + e.position;
    }
  }

  // Փ˔s.
  for (const auto& e : collisionHandlerList) {
    if (!e.handler) {
      continue;
    }
    Link* listL = &activeList[e.groupId[0]];
    Link* listR = &activeList[e.groupId[1]];
    for (itrUpdate = listL->next; itrUpdate != listL; itrUpdate = itrUpdate->next) {
      LinkEntity* entityL = static_cast<LinkEntity*>(itrUpdate);
      for (itrUpdateRhs = listR->next; itrUpdateRhs != listR; itrUpdateRhs = itrUpdateRhs->next) {
        LinkEntity* entityR = static_cast<LinkEntity*>(itrUpdateRhs);
        if (!HasCollision(entityL->colWorld, entityR->colWorld)) {
          continue;
        }
        e.handler(*entityL, *entityR);
        if (entityL != itrUpdate) {
          break; // ӂ폜ꂽꍇ͉Eӂ̃[vI.
        }
      }
    }
  }
  itrUpdate = nullptr;
  itrUpdateRhs = nullptr;

  uint8_t* p = static_cast<uint8_t*>(ubo->MapBuffer());
  std::vector<glm::mat4> matVP;
  matVP.resize(Uniform::maxViewCount);
  for (int i = 0; i < Uniform::maxViewCount; ++i) {
    matVP[i] = matProj * matView[i];
  }
  for (int groupId = 0; groupId <= maxGroupId; ++groupId) {
    for (Link* itr = activeList[groupId].next; itr != &activeList[groupId]; itr = itr->next) {
      LinkEntity& e = *static_cast<LinkEntity*>(itr);
      UpdateUniformVertexData(e, p + e.uboOffset, matVP.data(), visibilityFlags[groupId]);
    }
  }
  ubo->UnmapBuffer();
}

/**
* ANeBuȃGeBeB`悷.
*
* @param meshBuffer `Ɏgp郁bVobt@ւ̃|C^.
*/
void Buffer::Draw(const Mesh::BufferPtr& meshBuffer) const
{
  meshBuffer->BindVAO();
  for (int viewIndex = 0; viewIndex < Uniform::maxViewCount; ++viewIndex) {
    for (int groupId = 0; groupId <= maxGroupId; ++groupId) {
      if (!(visibilityFlags[groupId] & (1 << viewIndex))) {
        continue;
      }
      for (const Link* itr = activeList[groupId].next; itr != &activeList[groupId]; itr = itr->next) {
        const LinkEntity& e = *static_cast<const LinkEntity*>(itr);
        if (e.mesh && e.texture && e.program) {
          e.program->UseProgram();
          for (size_t i = 0; i < sizeof(e.texture) / sizeof(e.texture[0]); ++i) {
            e.program->BindTexture(GL_TEXTURE0 + i, GL_TEXTURE_2D, e.texture[i]->Id());
          }
          e.program->SetViewIndex(viewIndex);
          ubo->BindBufferRange(e.uboOffset, ubSizePerEntity);
          e.mesh->Draw(meshBuffer);
        }
      }
    }
  }
}

/**
* Փˉnhݒ肷.
*
* @param gid0    ՓˑΏۂ̃O[vID.
* @param gid1    ՓˑΏۂ̃O[vID.
* @param handler Փˉnh.
*
* Փ˂ՓˉnhĂтƂA菬O[vIDGeBeBɓn.
* Ŏw肵O[vID̏Ƃ͖֌Wł邱Ƃɒӂ邱.
* ex)
*   CollisionHandler(10, 1, Func)
*   ƂR[hŃnho^Ƃ. Փ˂ƁA
*   Func(O[vID=1̃GeBeBAO[vID=10̃GeBeB)
*   ̂悤ɌĂяo.
*/
void Buffer::CollisionHandler(int gid0, int gid1, const CollisionHandlerType& handler)
{
  if (gid0 > gid1) {
    std::swap(gid0, gid1);
  }
  auto itr = std::find_if(collisionHandlerList.begin(), collisionHandlerList.end(),
    [&](const CollisionHandlerElement& e) { return e.groupId[0] == gid0 && e.groupId[1] == gid1; });
  if (itr == collisionHandlerList.end()) {
    collisionHandlerList.push_back({ { gid0, gid1 }, handler });
  } else {
    itr->handler = handler;
  }
}

/**
* Փˉnh擾.
*
* @param gid0 ՓˑΏۂ̃O[vID.
* @param gid1 ՓˑΏۂ̃O[vID.
*
* @return Փˉnh.
*/
const CollisionHandlerType& Buffer::CollisionHandler(int gid0, int gid1) const
{
  if (gid0 > gid1) {
    std::swap(gid0, gid1);
  }
  auto itr = std::find_if(collisionHandlerList.begin(), collisionHandlerList.end(),
    [&](const CollisionHandlerElement& e) { return e.groupId[0] == gid0 && e.groupId[1] == gid1; });
  if (itr == collisionHandlerList.end()) {
    static const CollisionHandlerType dummy;
    return dummy;
  }
  return itr->handler;
}

/**
* SĂ̏Փˉnh폜.
*/
void Buffer::ClearCollisionHandlerList()
{
  collisionHandlerList.clear();
}

} // namespace Entity
<commit_msg>UpdateUniformVertexDataの変換行列の受け取り方を変更.<commit_after>/**
* @file Entity.cpp
*/
#include "Entity.h"
#include "Uniform.h"
#include <glm/gtc/matrix_transform.hpp>
#include <iostream>
#include <algorithm>

namespace Entity {

/**
* VertexDataUBOɓ].
*
* @param entity
* @param ubo
* @param matViewProjection
*/
void UpdateUniformVertexData(Entity& entity, void* ubo, const glm::mat4* matViewProjection, glm::u32 viewFlags)
{
  Uniform::VertexData data;
  data.matModel = entity.TRSMatrix();
  data.matNormal = glm::mat4_cast(entity.Rotation());
  for (int i = 0; i < Uniform::maxViewCount; ++i) {
    if (viewFlags & (1 << i)) {
      data.matMVP[i] = matViewProjection[i] * data.matModel;
    }
  }
  data.color = entity.Color();
  memcpy(ubo, &data, sizeof(data));
}

/**
* NIuWFNg̎Oɒǉ.
*
* @param p@ǉ郊NIuWFNgւ̃|C^.
*
* p̃NXg؂藣A̎Oɒǉ.
*/
void Buffer::Link::Insert(Link* p)
{
  p->Remove();
  p->prev = prev;
  p->next = this;
  prev->next = p;
  prev = p;
}

/**
* gNXg؂藣.
*
* ͂ǂɂڑĂȂԂɂȂ.
*/
void Buffer::Link::Remove()
{
  next->prev = prev;
  prev->next = next;
  prev = this;
  next = this;
}

/**
* gkE]Eړs擾.
*
* @return TRSs.
*/
glm::mat4 Entity::TRSMatrix() const
{
#if 1
  return glm::scale(glm::translate(glm::mat4(), position) * glm::mat4_cast(rotation), scale);
#else
  const glm::mat4 s = glm::scale(glm::mat4(), scale);
  const glm::mat4 r = glm::mat4_cast(rotation);
  const glm::mat4 t = glm::translate(glm::mat4(), position);
  return t * r * s;
#endif
}

/**
* GeBeBj.
*
* ̊֐ĂяoƂ́AGeBeB𑀍삵Ă͂ȂȂ.
*/
void Entity::Destroy()
{
  if (pBuffer) {
    pBuffer->RemoveEntity(this);
  }
}

/**
* GeBeBobt@쐬.
*
* @param maxEntityCount   GeBeB̍ő吔.
* @param ubSizePerEntity  GeBeBƂUniform Buffer̃oCg.
* @param bindingPoint     GeBeBpUBÕoCfBO|Cg.
* @param ubName           GeBeBpUniform Buffer̖O.
*
* @return 쐬GeBeBobt@ւ̃|C^.
*/
BufferPtr Buffer::Create(size_t maxEntityCount, GLsizeiptr ubSizePerEntity, int bindingPoint, const char* ubName)
{
  struct Impl : Buffer { Impl() {} ~Impl() {} };
  BufferPtr p = std::make_shared<Impl>();
  if (!p) {
    std::cerr << "WARNING in Entity::Buffer::Create: obt@̍쐬Ɏs." << std::endl;
    return {};
  }
  p->ubo = UniformBuffer::Create(maxEntityCount * ubSizePerEntity, bindingPoint, ubName);
  p->buffer.reset(new LinkEntity[maxEntityCount]);
  if (!p->ubo || !p->buffer) {
    std::cerr << "WARNING in Entity::Buffer::Create: obt@̍쐬Ɏs." << std::endl;
    return {};
  }
  p->bufferSize = maxEntityCount;
  p->ubSizePerEntity = ubSizePerEntity;
  GLintptr offset = 0;
  const LinkEntity* const end = &p->buffer[maxEntityCount];
  for (LinkEntity* itr = &p->buffer[0]; itr != end; ++itr) {
    itr->uboOffset = offset;
    itr->pBuffer = p.get();
    p->freeList.Insert(itr);
    offset += ubSizePerEntity;
  }
  p->collisionHandlerList.reserve(maxGroupId);
  for (auto& e : p->visibilityFlags) {
    e = 1;
  }
  return p;
}

/**
* GeBeBǉ.
*
* @param groupId  GeBeB̃O[vID.
* @param position GeBeB̍W.
* @param mesh     GeBeB̕\Ɏgp郁bV.
* @param texture  GeBeB̕\ɎgeNX`.
* @param program  GeBeB̕\ɎgpVF[_vO.
* @param func     GeBeB̏ԂXV֐(܂͊֐IuWFNg).
*
* @return ǉGeBeBւ̃|C^.
*         ȏGeBeBǉłȂꍇnullptrԂ.
*         ]g嗦͂̃|C^oRŐݒ肷.
*         ȂÃ|C^AvP[VŕێKv͂Ȃ.
*/
Entity* Buffer::AddEntity(int groupId, const glm::vec3& position, const Mesh::MeshPtr& mesh, const TexturePtr t[2], const Shader::ProgramPtr& program, const Entity::UpdateFuncType& func)
{
  if (freeList.prev == freeList.next) {
    std::cerr << "WARNING in Entity::Buffer::AddEntity: 󂫃GeBeB܂." << std::endl;
    return nullptr;
  }
  if (groupId < 0 || groupId > maxGroupId) {
    std::cerr << "ERROR in Entity::Buffer::AddEntity: ͈͊ÕO[vID(" << groupId << ")n܂.\nO[vID0`" << maxGroupId << "łȂ΂Ȃ܂." << std::endl;
    return nullptr;
  }
  LinkEntity* entity = static_cast<LinkEntity*>(freeList.prev);
  activeList[groupId].Insert(entity);
  entity->groupId = groupId;
  entity->position = position;
  entity->rotation = glm::quat();
  entity->scale = glm::vec3(1, 1, 1);
  entity->velocity = glm::vec3();
  entity->color = glm::vec4(1, 1, 1, 1);
  entity->mesh = mesh;
  entity->texture[0] = t[0];
  entity->texture[1] = t[1];
  entity->program = program;
  entity->updateFunc = func;
  entity->isActive = true;
  return entity;
}

/**
*@GeBeB폜.
*
* @param 폜GeBeB̃|C^.
*/
void Buffer::RemoveEntity(Entity* entity)
{
  if (!entity || !entity->isActive) {
    std::cerr << "WARNING in Entity::Buffer::RemoveEntity: ANeBuȃGeBeB폜悤Ƃ܂." << std::endl;
    return;
  }
  LinkEntity* p = static_cast<LinkEntity*>(entity);
  if (p < &buffer[0] || p >= &buffer[bufferSize]) {
    std::cerr << "WARNING in Entity::Buffer::RemoveEntity: قȂobt@擾GeBeB폜悤Ƃ܂." << std::endl;
    return;
  }
  if (p == itrUpdate) {
    itrUpdate = p->prev;
  }
  if (p == itrUpdateRhs) {
    itrUpdateRhs = p->prev;
  }
  freeList.Insert(p);
  p->mesh.reset();
  for (auto& e : p->texture) {
    e.reset();
  }
  p->program.reset();
  p->updateFunc = nullptr;
  p->isActive = false;
}

/**
* SẴGeBeB폜.
*/
void Buffer::RemoveAllEntity()
{
  for (int groupId = 0; groupId <= maxGroupId; ++groupId) {
    while (activeList[groupId].next != &activeList[groupId]) {
      RemoveEntity(static_cast<LinkEntity*>(activeList[groupId].next));
    }
  }
}

/**
* `m̏Փ˔.
*/
bool HasCollision(const CollisionData& lhs, const CollisionData& rhs)
{
  if (lhs.max.x < rhs.min.x || lhs.min.x > rhs.max.x) return false;
  if (lhs.max.y < rhs.min.y || lhs.min.y > rhs.max.y) return false;
  if (lhs.max.z < rhs.min.z || lhs.min.z > rhs.max.z) return false;
  return true;
}

#pragma optimize( "ts", off)
/**
* ANeBuȃGeBeB̏ԂXV.
*
* @param delta   O̍XV̌oߎ.
* @param matView Views.
* @param matProj Projections.
*/
void Buffer::Update(double delta, const glm::mat4* matView, const glm::mat4& matProj)
{
  // Wƃ[hWn̏Փˌ`XV.
  // eGeBeB̏ԂXV.
  for (int groupId = 0; groupId <= maxGroupId; ++groupId) {
    for (itrUpdate = activeList[groupId].next; itrUpdate != &activeList[groupId]; itrUpdate = itrUpdate->next) {
      LinkEntity& e = *static_cast<LinkEntity*>(itrUpdate);
      e.position += e.velocity * static_cast<float>(delta);
      if (e.updateFunc) {
        e.updateFunc(e, delta);
      }
      e.colWorld.min = e.colLocal.min + e.position;
      e.colWorld.max = e.colLocal.max + e.position;
    }
  }

  // Փ˔s.
  for (const auto& e : collisionHandlerList) {
    if (!e.handler) {
      continue;
    }
    Link* listL = &activeList[e.groupId[0]];
    Link* listR = &activeList[e.groupId[1]];
    for (itrUpdate = listL->next; itrUpdate != listL; itrUpdate = itrUpdate->next) {
      LinkEntity* entityL = static_cast<LinkEntity*>(itrUpdate);
      for (itrUpdateRhs = listR->next; itrUpdateRhs != listR; itrUpdateRhs = itrUpdateRhs->next) {
        LinkEntity* entityR = static_cast<LinkEntity*>(itrUpdateRhs);
        if (!HasCollision(entityL->colWorld, entityR->colWorld)) {
          continue;
        }
        e.handler(*entityL, *entityR);
        if (entityL != itrUpdate) {
          break; // ӂ폜ꂽꍇ͉Eӂ̃[vI.
        }
      }
    }
  }
  itrUpdate = nullptr;
  itrUpdateRhs = nullptr;

  uint8_t* p = static_cast<uint8_t*>(ubo->MapBuffer());
  std::vector<glm::mat4> matVP;
  matVP.resize(Uniform::maxViewCount);
  for (int i = 0; i < Uniform::maxViewCount; ++i) {
    matVP[i] = matProj * matView[i];
  }
  for (int groupId = 0; groupId <= maxGroupId; ++groupId) {
    for (Link* itr = activeList[groupId].next; itr != &activeList[groupId]; itr = itr->next) {
      LinkEntity& e = *static_cast<LinkEntity*>(itr);
      UpdateUniformVertexData(e, p + e.uboOffset, matVP.data(), visibilityFlags[groupId]);
    }
  }
  ubo->UnmapBuffer();
}

/**
* ANeBuȃGeBeB`悷.
*
* @param meshBuffer `Ɏgp郁bVobt@ւ̃|C^.
*/
void Buffer::Draw(const Mesh::BufferPtr& meshBuffer) const
{
  meshBuffer->BindVAO();
  for (int viewIndex = 0; viewIndex < Uniform::maxViewCount; ++viewIndex) {
    for (int groupId = 0; groupId <= maxGroupId; ++groupId) {
      if (!(visibilityFlags[groupId] & (1 << viewIndex))) {
        continue;
      }
      for (const Link* itr = activeList[groupId].next; itr != &activeList[groupId]; itr = itr->next) {
        const LinkEntity& e = *static_cast<const LinkEntity*>(itr);
        if (e.mesh && e.texture && e.program) {
          e.program->UseProgram();
          for (size_t i = 0; i < sizeof(e.texture) / sizeof(e.texture[0]); ++i) {
            e.program->BindTexture(GL_TEXTURE0 + i, GL_TEXTURE_2D, e.texture[i]->Id());
          }
          e.program->SetViewIndex(viewIndex);
          ubo->BindBufferRange(e.uboOffset, ubSizePerEntity);
          e.mesh->Draw(meshBuffer);
        }
      }
    }
  }
}

/**
* Փˉnhݒ肷.
*
* @param gid0    ՓˑΏۂ̃O[vID.
* @param gid1    ՓˑΏۂ̃O[vID.
* @param handler Փˉnh.
*
* Փ˂ՓˉnhĂтƂA菬O[vIDGeBeBɓn.
* Ŏw肵O[vID̏Ƃ͖֌Wł邱Ƃɒӂ邱.
* ex)
*   CollisionHandler(10, 1, Func)
*   ƂR[hŃnho^Ƃ. Փ˂ƁA
*   Func(O[vID=1̃GeBeBAO[vID=10̃GeBeB)
*   ̂悤ɌĂяo.
*/
void Buffer::CollisionHandler(int gid0, int gid1, const CollisionHandlerType& handler)
{
  if (gid0 > gid1) {
    std::swap(gid0, gid1);
  }
  auto itr = std::find_if(collisionHandlerList.begin(), collisionHandlerList.end(),
    [&](const CollisionHandlerElement& e) { return e.groupId[0] == gid0 && e.groupId[1] == gid1; });
  if (itr == collisionHandlerList.end()) {
    collisionHandlerList.push_back({ { gid0, gid1 }, handler });
  } else {
    itr->handler = handler;
  }
}

/**
* Փˉnh擾.
*
* @param gid0 ՓˑΏۂ̃O[vID.
* @param gid1 ՓˑΏۂ̃O[vID.
*
* @return Փˉnh.
*/
const CollisionHandlerType& Buffer::CollisionHandler(int gid0, int gid1) const
{
  if (gid0 > gid1) {
    std::swap(gid0, gid1);
  }
  auto itr = std::find_if(collisionHandlerList.begin(), collisionHandlerList.end(),
    [&](const CollisionHandlerElement& e) { return e.groupId[0] == gid0 && e.groupId[1] == gid1; });
  if (itr == collisionHandlerList.end()) {
    static const CollisionHandlerType dummy;
    return dummy;
  }
  return itr->handler;
}

/**
* SĂ̏Փˉnh폜.
*/
void Buffer::ClearCollisionHandlerList()
{
  collisionHandlerList.clear();
}

} // namespace Entity
<|endoftext|>


<commit_before>#include "../Material.h"
#include <iostream>

using namespace LepusEngine::Lepus3D;

Material::Material(char* name)
{
	m_Name = name;
}

Material::Material(char* name, char* shaderName) : Material(name)
{
	this->SetShader(shaderName);
}

bool Material::SetShader(char* shaderName)
{
	return m_Shader.Load(shaderName);
}

bool Material::SetAttributeI(char* attributeName, GLint value, GLint location)
{
	short index = -1;

	for (short i = 0; i < m_IntAttributes.size() && index < 0; i++)
	{
		if (m_IntAttributes[i].name == attributeName)
			index = i;
	}

	if (index >= 0)
	{
		m_IntAttributes[index].value = value;
		if (location >= 0)
			m_FloatAttributes[index].location = glGetUniformLocation(m_Shader.m_Compiled, attributeName);
	}
	else
	{
		m_IntAttributes.push_back(MaterialAttrib<GLint>(attributeName));
		m_IntAttributes[m_IntAttributes.size() - 1].value = value;
		if (location >= 0)
			m_FloatAttributes[m_IntAttributes.size() - 1].location = glGetUniformLocation(m_Shader.m_Compiled, attributeName);
		return false;
	}

	return true;
}

bool Material::SetAttributeF(char* attributeName, GLfloat value, GLint location)
{
	short index = -1;

	for (short i = 0; i < m_FloatAttributes.size() && index < 0; i++)
	{
		if (m_FloatAttributes[i].name == attributeName)
			index = i;
	}

	if (index >= 0)
	{
		m_FloatAttributes[index].value = value;
		if (location >= 0)
			m_FloatAttributes[index].location = glGetUniformLocation(m_Shader.m_Compiled, attributeName);
	}
	else
	{
		m_FloatAttributes.push_back(MaterialAttrib<GLfloat>(attributeName));
		m_FloatAttributes[m_FloatAttributes.size() - 1].value = value;
		if (location >= 0)
			m_FloatAttributes[m_FloatAttributes.size() - 1].location = glGetUniformLocation(m_Shader.m_Compiled, attributeName);
		return false;
	}

	return true;
}

bool Material::SetAttributeF4(char* attributeName, GLfloat value[4], GLint location)
{
	short index = -1;

	for (short i = 0; i < m_Vec4Attributes.size() && index < 0; i++)
	{
		if (m_Vec4Attributes[i].name == attributeName)
			index = i;
	}

	if (index >= 0)
	{
		m_Vec4Attributes[index].value[0] = value[0];
		m_Vec4Attributes[index].value[1] = value[1];
		m_Vec4Attributes[index].value[2] = value[2];
		m_Vec4Attributes[index].value[3] = value[3];
		if (location >= 0)
			m_Vec4Attributes[index].location = glGetUniformLocation(m_Shader.m_Compiled, attributeName);
	}
	else
	{
		m_Vec4Attributes.push_back(MaterialAttrib<GLfloat[4]>(attributeName));
		index = m_Vec4Attributes.size() - 1;
		m_Vec4Attributes[index].value[0] = value[0];
		m_Vec4Attributes[index].value[1] = value[1];
		m_Vec4Attributes[index].value[2] = value[2];
		m_Vec4Attributes[index].value[3] = value[3];
		if (location >= 0)
			m_Vec4Attributes[index].location = glGetUniformLocation(m_Shader.m_Compiled, attributeName);
		return false;
	}

	return true;
}

bool Material::SetAttributeF3(char* attributeName, GLfloat value[3], GLint location)
{
	short index = -1;

	for (short i = 0; i < m_Vec3Attributes.size() && index < 0; i++)
	{
		if (m_Vec3Attributes[i].name == attributeName)
			index = i;
	}

	if (index >= 0)
	{
		m_Vec3Attributes[index].value[0] = value[0];
		m_Vec3Attributes[index].value[1] = value[1];
		m_Vec3Attributes[index].value[2] = value[2];
		if (location >= 0)
			m_Vec3Attributes[index].location = glGetUniformLocation(m_Shader.m_Compiled, attributeName);
	}
	else
	{
		m_Vec3Attributes.push_back(MaterialAttrib<GLfloat[3]>(attributeName));
		index = m_Vec3Attributes.size() - 1;
		m_Vec3Attributes[index].value[0] = value[0];
		m_Vec3Attributes[index].value[1] = value[1];
		m_Vec3Attributes[index].value[2] = value[2];
		if (location >= 0)
			m_Vec3Attributes[index].location = glGetUniformLocation(m_Shader.m_Compiled, attributeName);
		return false;
	}

	return true;
}

bool Material::SetAttributeF2(char* attributeName, GLfloat value[2], GLint location)
{
	short index = -1;

	for (short i = 0; i < m_Vec2Attributes.size() && index < 0; i++)
	{
		if (m_Vec2Attributes[i].name == attributeName)
			index = i;
	}

	if (index >= 0)
	{
		m_Vec2Attributes[index].value[0] = value[0];
		m_Vec2Attributes[index].value[1] = value[1];
		if (location >= 0)
			m_Vec2Attributes[index].location = glGetUniformLocation(m_Shader.m_Compiled, attributeName);
	}
	else
	{
		m_Vec2Attributes.push_back(MaterialAttrib<GLfloat[2]>(attributeName));
		index = m_Vec3Attributes.size() - 1;
		m_Vec2Attributes[index].value[0] = value[0];
		m_Vec2Attributes[index].value[1] = value[1];
		if (location >= 0)
			m_Vec2Attributes[index].location = glGetUniformLocation(m_Shader.m_Compiled, attributeName);
		return false;
	}

	return true;
}

bool Material::SetAttributeFP(char* attributeName, GLfloat* value, GLint location)
{
	std::size_t valueCount = sizeof(value) / sizeof(GLfloat);
	switch (valueCount)
	{
	case 4:
		return this->SetAttributeF4(attributeName, new GLfloat[4]{ value[0], value[1], value[2], value[3] }, location);
		break;
	case 3:
		return this->SetAttributeF3(attributeName, new GLfloat[3]{ value[0], value[1], value[2] }, location);
		break;
	case 2:
		return this->SetAttributeF2(attributeName, new GLfloat[2]{ value[0], value[1] }, location);
		break;
	case 1:
		return this->SetAttributeF(attributeName, *value, location);
		break;
	default:
		std::cout << "Material::SetAttribute(attributeName, *value, location): invalid number of floating-point values (" << valueCount << ")!" << std::endl;
		return false;
	}
}

GLint Material::GetAttributeI(char* attributeName)
{
	for (unsigned short i = 0; i < m_IntAttributes.size(); i++)
	{
		if (m_IntAttributes[i].name == attributeName)
		{
			return m_IntAttributes[i].value;
		}
	}
}

GLfloat Material::GetAttributeF(char* attributeName)
{
	for (unsigned short i = 0; i < m_FloatAttributes.size(); i++)
	{
		if (m_FloatAttributes[i].name == attributeName)
		{
			return m_FloatAttributes[i].value;
		}
	}
}

void Material::Use()
{
	m_Shader.Run(); // use the GLSL program

	for (int i = 0; i < m_IntAttributes.size(); i++)
	{
		glUniform1i(m_IntAttributes[i].location, m_IntAttributes[i].value);
	}
	for (int i = 0; i < m_FloatAttributes.size(); i++)
	{
		glUniform1f(m_FloatAttributes[i].location, m_FloatAttributes[i].value);
	}
	for (int i = 0; i < m_Vec4Attributes.size(); i++)
	{
		glUniform4f(m_Vec4Attributes[i].location, m_Vec4Attributes[i].value[0], m_Vec4Attributes[i].value[1], m_Vec4Attributes[i].value[2], m_Vec4Attributes[i].value[3]);
	}
	for (int i = 0; i < m_Vec3Attributes.size(); i++)
	{
		glUniform3f(m_Vec3Attributes[i].location, m_Vec3Attributes[i].value[0], m_Vec3Attributes[i].value[1], m_Vec3Attributes[i].value[2]);
	}
	for (int i = 0; i < m_Vec2Attributes.size(); i++)
	{
		glUniform2f(m_Vec2Attributes[i].location, m_Vec2Attributes[i].value[0], m_Vec2Attributes[i].value[1]);
	}
}

void Material::Destroy()
{
	m_Shader.Unload();
}

Material::~Material()
{
	this->Destroy();
}<commit_msg>Changed SetShader to only be called once<commit_after>#include "../Material.h"
#include <iostream>

using namespace LepusEngine::Lepus3D;

Material::Material(char* name)
{
	m_Name = name;
}

Material::Material(char* name, char* shaderName) : Material(name)
{
	this->SetShader(shaderName);
}

bool Material::SetShader(char* shaderName)
{
	if (!m_Shader.m_Ready)
	{
		bool result = m_Shader.Load(shaderName);
		return result;
	}
	else
		return true;
}

bool Material::SetAttributeI(char* attributeName, GLint value, GLint location)
{
	short index = -1;

	for (short i = 0; i < m_IntAttributes.size() && index < 0; i++)
	{
		if (m_IntAttributes[i].name == attributeName)
			index = i;
	}

	if (index >= 0)
	{
		m_IntAttributes[index].value = value;
		if (location >= 0)
			m_FloatAttributes[index].location = glGetUniformLocation(m_Shader.m_Compiled, attributeName);
	}
	else
	{
		m_IntAttributes.push_back(MaterialAttrib<GLint>(attributeName));
		m_IntAttributes[m_IntAttributes.size() - 1].value = value;
		if (location >= 0)
			m_FloatAttributes[m_IntAttributes.size() - 1].location = glGetUniformLocation(m_Shader.m_Compiled, attributeName);
		return false;
	}

	return true;
}

bool Material::SetAttributeF(char* attributeName, GLfloat value, GLint location)
{
	short index = -1;

	for (short i = 0; i < m_FloatAttributes.size() && index < 0; i++)
	{
		if (m_FloatAttributes[i].name == attributeName)
			index = i;
	}

	if (index >= 0)
	{
		m_FloatAttributes[index].value = value;
		if (location >= 0)
			m_FloatAttributes[index].location = glGetUniformLocation(m_Shader.m_Compiled, attributeName);
	}
	else
	{
		m_FloatAttributes.push_back(MaterialAttrib<GLfloat>(attributeName));
		m_FloatAttributes[m_FloatAttributes.size() - 1].value = value;
		if (location >= 0)
			m_FloatAttributes[m_FloatAttributes.size() - 1].location = glGetUniformLocation(m_Shader.m_Compiled, attributeName);
		return false;
	}

	return true;
}

bool Material::SetAttributeF4(char* attributeName, GLfloat value[4], GLint location)
{
	short index = -1;

	for (short i = 0; i < m_Vec4Attributes.size() && index < 0; i++)
	{
		if (m_Vec4Attributes[i].name == attributeName)
			index = i;
	}

	if (index >= 0)
	{
		m_Vec4Attributes[index].value[0] = value[0];
		m_Vec4Attributes[index].value[1] = value[1];
		m_Vec4Attributes[index].value[2] = value[2];
		m_Vec4Attributes[index].value[3] = value[3];
		if (location >= 0)
			m_Vec4Attributes[index].location = glGetUniformLocation(m_Shader.m_Compiled, attributeName);
	}
	else
	{
		m_Vec4Attributes.push_back(MaterialAttrib<GLfloat[4]>(attributeName));
		index = m_Vec4Attributes.size() - 1;
		m_Vec4Attributes[index].value[0] = value[0];
		m_Vec4Attributes[index].value[1] = value[1];
		m_Vec4Attributes[index].value[2] = value[2];
		m_Vec4Attributes[index].value[3] = value[3];
		if (location >= 0)
			m_Vec4Attributes[index].location = glGetUniformLocation(m_Shader.m_Compiled, attributeName);
		return false;
	}

	return true;
}

bool Material::SetAttributeF3(char* attributeName, GLfloat value[3], GLint location)
{
	short index = -1;

	for (short i = 0; i < m_Vec3Attributes.size() && index < 0; i++)
	{
		if (m_Vec3Attributes[i].name == attributeName)
			index = i;
	}

	if (index >= 0)
	{
		m_Vec3Attributes[index].value[0] = value[0];
		m_Vec3Attributes[index].value[1] = value[1];
		m_Vec3Attributes[index].value[2] = value[2];
		if (location >= 0)
			m_Vec3Attributes[index].location = glGetUniformLocation(m_Shader.m_Compiled, attributeName);
	}
	else
	{
		m_Vec3Attributes.push_back(MaterialAttrib<GLfloat[3]>(attributeName));
		index = m_Vec3Attributes.size() - 1;
		m_Vec3Attributes[index].value[0] = value[0];
		m_Vec3Attributes[index].value[1] = value[1];
		m_Vec3Attributes[index].value[2] = value[2];
		if (location >= 0)
			m_Vec3Attributes[index].location = glGetUniformLocation(m_Shader.m_Compiled, attributeName);
		return false;
	}

	return true;
}

bool Material::SetAttributeF2(char* attributeName, GLfloat value[2], GLint location)
{
	short index = -1;

	for (short i = 0; i < m_Vec2Attributes.size() && index < 0; i++)
	{
		if (m_Vec2Attributes[i].name == attributeName)
			index = i;
	}

	if (index >= 0)
	{
		m_Vec2Attributes[index].value[0] = value[0];
		m_Vec2Attributes[index].value[1] = value[1];
		if (location >= 0)
			m_Vec2Attributes[index].location = glGetUniformLocation(m_Shader.m_Compiled, attributeName);
	}
	else
	{
		m_Vec2Attributes.push_back(MaterialAttrib<GLfloat[2]>(attributeName));
		index = m_Vec3Attributes.size() - 1;
		m_Vec2Attributes[index].value[0] = value[0];
		m_Vec2Attributes[index].value[1] = value[1];
		if (location >= 0)
			m_Vec2Attributes[index].location = glGetUniformLocation(m_Shader.m_Compiled, attributeName);
		return false;
	}

	return true;
}

bool Material::SetAttributeFP(char* attributeName, GLfloat* value, GLint location)
{
	std::size_t valueCount = sizeof(value) / sizeof(GLfloat);
	switch (valueCount)
	{
	case 4:
		return this->SetAttributeF4(attributeName, new GLfloat[4]{ value[0], value[1], value[2], value[3] }, location);
		break;
	case 3:
		return this->SetAttributeF3(attributeName, new GLfloat[3]{ value[0], value[1], value[2] }, location);
		break;
	case 2:
		return this->SetAttributeF2(attributeName, new GLfloat[2]{ value[0], value[1] }, location);
		break;
	case 1:
		return this->SetAttributeF(attributeName, *value, location);
		break;
	default:
		std::cout << "Material::SetAttribute(attributeName, *value, location): invalid number of floating-point values (" << valueCount << ")!" << std::endl;
		return false;
	}
}

GLint Material::GetAttributeI(char* attributeName)
{
	for (unsigned short i = 0; i < m_IntAttributes.size(); i++)
	{
		if (m_IntAttributes[i].name == attributeName)
		{
			return m_IntAttributes[i].value;
		}
	}
}

GLfloat Material::GetAttributeF(char* attributeName)
{
	for (unsigned short i = 0; i < m_FloatAttributes.size(); i++)
	{
		if (m_FloatAttributes[i].name == attributeName)
		{
			return m_FloatAttributes[i].value;
		}
	}
}

void Material::Use()
{
	m_Shader.Run(); // use the GLSL program

	for (int i = 0; i < m_IntAttributes.size(); i++)
	{
		glUniform1i(m_IntAttributes[i].location, m_IntAttributes[i].value);
	}
	for (int i = 0; i < m_FloatAttributes.size(); i++)
	{
		glUniform1f(m_FloatAttributes[i].location, m_FloatAttributes[i].value);
	}
	for (int i = 0; i < m_Vec4Attributes.size(); i++)
	{
		glUniform4f(m_Vec4Attributes[i].location, m_Vec4Attributes[i].value[0], m_Vec4Attributes[i].value[1], m_Vec4Attributes[i].value[2], m_Vec4Attributes[i].value[3]);
	}
	for (int i = 0; i < m_Vec3Attributes.size(); i++)
	{
		glUniform3f(m_Vec3Attributes[i].location, m_Vec3Attributes[i].value[0], m_Vec3Attributes[i].value[1], m_Vec3Attributes[i].value[2]);
	}
	for (int i = 0; i < m_Vec2Attributes.size(); i++)
	{
		glUniform2f(m_Vec2Attributes[i].location, m_Vec2Attributes[i].value[0], m_Vec2Attributes[i].value[1]);
	}
}

void Material::Destroy()
{
	m_Shader.Unload();
}

Material::~Material()
{
	this->Destroy();
}<|endoftext|>


<commit_before>/**************************************************************************
 * Copyright(c) 1998-1999, ALICE Experiment at CERN, All rights reserved. *
 *                                                                        *
 * Author: The ALICE Off-line Project.                                    *
 * Contributors are mentioned in the code where appropriate.              *
 *                                                                        *
 * Permission to use, copy, modify and distribute this software and its   *
 * documentation strictly for non-commercial purposes is hereby granted   *
 * without fee, provided that the above copyright notice appears in all   *
 * copies and that both the copyright notice and this permission notice   *
 * appear in the supporting documentation. The authors make no claims     *
 * about the suitability of this software for any purpose. It is          *
 * provided "as is" without express or implied warranty.                  *
 **************************************************************************/

// $Id$
//
// Class AliMUONSt2GeometryBuilderV2
// -------------------------------
// MUON Station2 coarse geometry construction class.
// Extracted from AliMUONv1

#include <TVirtualMC.h>
#include <TGeoMatrix.h>

#include "AliLog.h"

#include "AliMUONSt2GeometryBuilderV2.h"
#include "AliMUON.h"
#include "AliMUONChamber.h"
#include "AliMUONGeometryModule.h"
#include "AliMUONGeometryEnvelopeStore.h"

ClassImp(AliMUONSt2GeometryBuilderV2)

//______________________________________________________________________________
AliMUONSt2GeometryBuilderV2::AliMUONSt2GeometryBuilderV2(AliMUON* muon)
 : AliMUONVGeometryBuilder("st2.dat",
                           muon->Chamber(2).GetGeometry(),
			   muon->Chamber(3).GetGeometry()),
   fMUON(muon)
{
// Standard constructor

}

//______________________________________________________________________________
AliMUONSt2GeometryBuilderV2::AliMUONSt2GeometryBuilderV2()
 : AliMUONVGeometryBuilder(),
   fMUON(0)
{
// Default constructor
}

//______________________________________________________________________________
AliMUONSt2GeometryBuilderV2::AliMUONSt2GeometryBuilderV2(const AliMUONSt2GeometryBuilderV2& rhs)
  : AliMUONVGeometryBuilder(rhs)
{
// Protected copy constructor

  AliFatal("Copy constructor is not implemented.");
}

//______________________________________________________________________________
AliMUONSt2GeometryBuilderV2::~AliMUONSt2GeometryBuilderV2() {
//
}

//______________________________________________________________________________
AliMUONSt2GeometryBuilderV2&
AliMUONSt2GeometryBuilderV2::operator = (const AliMUONSt2GeometryBuilderV2& rhs)
{
// Protected assignement operator

  // check assignement to self
  if (this == &rhs) return *this;

  AliFatal("Assignment operator is not implemented.");

  return *this;
}

//
// public methods
//

//______________________________________________________________________________
void AliMUONSt2GeometryBuilderV2::CreateGeometry()
{
// From AliMUONv1::CreateGeometry()

//
//********************************************************************
//                            Station 2                             **
//********************************************************************
     // indices 1 and 2 for first and second chambers in the station
     // iChamber (first chamber) kept for other quanties than Z,
     // assumed to be the same in both chambers

     // Get tracking medias Ids
     Int_t *idtmed = fMUON->GetIdtmed()->GetArray()-1099;
     Int_t idAir= idtmed[1100]; // medium 1
  //     Int_t idAlu1=idtmed[1103]; // medium 4
     //     Int_t idAlu2=idtmed[1104]; // medium 5
     Int_t idGas=idtmed[1108];  // medium 9 = Ar-CO2 gas (80%+20%)

     const Float_t kDeltaQuad = 2.6;
     const Float_t kDeltaZ = 6.5/2.;

     // Rotation matrices in the x-y plane
     // phi= 0 deg
     Int_t irot1;
     fMUON->AliMatrix(irot1,  90.,   0., 90.,  90., 0., 0.);
     // phi= 90 deg
     Int_t irot2;
     fMUON->AliMatrix(irot2,  90.,  90., 90., 180., 0., 0.);

     AliMUONChamber* iChamber = &fMUON->Chamber(2);
     //     AliMUONChamber* iChamber1 = iChamber;
     //     AliMUONChamber* iChamber2 = &fMUON->Chamber(3);

     // Half of the total thickness of frame crosses (including DAlu)
     // for each chamber in stations 1 and 2:
     // 3% of X0 of composite material,
     // but taken as Aluminium here, with same thickness in number of X0
     //     Float_t dframez = 3. * 8.9 / 100;
     // DGas and DAlu not changed from standard values
     //     Double_t zfpos=-(iChamber->DGas()+dframez+iChamber->DAlu())/2;
             // The same parameters are defined in builder for station 1

     // Mother volume
     // Outer excess and inner recess for mother volume radius
     // with respect to ROuter and RInner
     //     Float_t dframep=.001; // Value for station 3 should be 6 ...
     // Width (RdPhi) of the frame crosses for stations 1 and 2 (cm)
     // Float_t dframep1=.001;
     //     Float_t phi=2*TMath::Pi()/12/2;
             // The same parameters are defined in builder for station 1 (30deg)

     Float_t tpar[5];
     //     Double_t dstation = (-iChamber2->Z()) - (-iChamber1->Z());

     Float_t posx, posy, posz;
//   Chamber Material represented by Alu sheet
     tpar[0]= iChamber->RInner();
     tpar[1]= iChamber->ROuter();
     tpar[2] = (iChamber->DGas())/2;
     tpar[3] = 0.;
     tpar[4] = 90.;

//
//   Sensitive volumes
     gMC->Gsvolu("S03G", "TUBS", idGas, tpar, 5);
     gMC->Gsvolu("S04G", "TUBS", idGas, tpar, 5);

     Int_t detElemId;

     posx = kDeltaQuad;
     posy = kDeltaQuad;
     posz = kDeltaZ;

     detElemId = 300;
     gMC->Gsvolu("LE01", "TUBS", idAir, tpar, 5);
     GetEnvelopes(2)->AddEnvelope("LE01", detElemId, true, TGeoTranslation(posx, posy, posz),
				   TGeoRotation("rot1",90,0,90,90,0,0) );
     detElemId = 400;
     gMC->Gsvolu("LF01", "TUBS", idAir, tpar, 5);
     GetEnvelopes(3)->AddEnvelope("LF01", detElemId, true, TGeoTranslation(posx, posy, posz),
				   TGeoRotation("rot1",90,0,90,90,0,0) );
     detElemId = 351;
     gMC->Gsvolu("LE02", "TUBS", idAir, tpar, 5);
     GetEnvelopes(2)->AddEnvelope("LE02", detElemId, true, TGeoTranslation(-posx, posy,-posz),
				  TGeoRotation("rot2",90,180,90,90,180,0) );
     detElemId = 451;
     gMC->Gsvolu("LF02", "TUBS", idAir, tpar, 5);
     GetEnvelopes(3)->AddEnvelope("LF02", detElemId, true, TGeoTranslation(-posx, posy,-posz),
				  TGeoRotation("rot2",90,180,90,90,180,0) );
     detElemId = 301;
     gMC->Gsvolu("LE03", "TUBS", idAir, tpar, 5);
     GetEnvelopes(2)->AddEnvelope("LE03", detElemId, true, TGeoTranslation(posx, -posy, -posz),
				  TGeoRotation("rot3",90,0,90,270,180,0) );
     detElemId = 401;
     gMC->Gsvolu("LF03", "TUBS", idAir, tpar, 5);
     GetEnvelopes(3)->AddEnvelope("LF03", detElemId, true, TGeoTranslation(posx, -posy, -posz),
				  TGeoRotation("rot3",90,0,90,270,180,0) );
     detElemId = 350;
     gMC->Gsvolu("LE04", "TUBS", idAir, tpar, 5);
     GetEnvelopes(2)->AddEnvelope("LE04", detElemId, true, TGeoTranslation(-posx, -posy, posz),
				  TGeoRotation("rot4",90,180,90,270,0,0) );
     detElemId = 450;
     gMC->Gsvolu("LF04", "TUBS", idAir, tpar, 5);
     GetEnvelopes(3)->AddEnvelope("LF04", detElemId, true, TGeoTranslation(-posx, -posy, posz),
				  TGeoRotation("rot4",90,180,90,270,0,0) );

     GetEnvelopes(2)->AddEnvelopeConstituent("S03G", "LE01", 1);
     GetEnvelopes(3)->AddEnvelopeConstituent("S04G", "LF01", 1);

     GetEnvelopes(2)->AddEnvelopeConstituent("S03G", "LE02", 2);
     GetEnvelopes(3)->AddEnvelopeConstituent("S04G", "LF02", 2);

     GetEnvelopes(2)->AddEnvelopeConstituent("S03G", "LE03", 3);
     GetEnvelopes(3)->AddEnvelopeConstituent("S04G", "LF03", 3);

     GetEnvelopes(2)->AddEnvelopeConstituent("S03G", "LE04", 4);
     GetEnvelopes(3)->AddEnvelopeConstituent("S04G", "LF04", 4);

}

//______________________________________________________________________________
void AliMUONSt2GeometryBuilderV2::SetTransformations()
{
// Defines the transformations for the station2 chambers.
// ---

  AliMUONChamber* iChamber1 = &fMUON->Chamber(2);
  Double_t zpos1 = - iChamber1->Z();
  iChamber1->GetGeometry()
    ->SetTranslation(TGeoTranslation(0., 0., zpos1));

  AliMUONChamber* iChamber2 = &fMUON->Chamber(3);
  Double_t zpos2 = - iChamber2->Z();
  iChamber2->GetGeometry()
    ->SetTranslation(TGeoTranslation(0., 0., zpos2));
}

//______________________________________________________________________________
void AliMUONSt2GeometryBuilderV2::SetSensitiveVolumes()
{
// Defines the sensitive volumes for station2 chambers.
// ---

  GetGeometry(2)->SetSensitiveVolume("S03G");
  GetGeometry(3)->SetSensitiveVolume("S04G");
}
<commit_msg>Bug on Quadrant positionning of St2 (Christian)<commit_after>/**************************************************************************
 * Copyright(c) 1998-1999, ALICE Experiment at CERN, All rights reserved. *
 *                                                                        *
 * Author: The ALICE Off-line Project.                                    *
 * Contributors are mentioned in the code where appropriate.              *
 *                                                                        *
 * Permission to use, copy, modify and distribute this software and its   *
 * documentation strictly for non-commercial purposes is hereby granted   *
 * without fee, provided that the above copyright notice appears in all   *
 * copies and that both the copyright notice and this permission notice   *
 * appear in the supporting documentation. The authors make no claims     *
 * about the suitability of this software for any purpose. It is          *
 * provided "as is" without express or implied warranty.                  *
 **************************************************************************/

// $Id$
//
// Class AliMUONSt2GeometryBuilderV2
// -------------------------------
// MUON Station2 coarse geometry construction class.
// Extracted from AliMUONv1
// Dummy version of station 2 with the right DE id (Ch. Finck)

#include <TVirtualMC.h>
#include <TGeoMatrix.h>

#include "AliLog.h"

#include "AliMUONSt2GeometryBuilderV2.h"
#include "AliMUON.h"
#include "AliMUONChamber.h"
#include "AliMUONGeometryModule.h"
#include "AliMUONGeometryEnvelopeStore.h"

ClassImp(AliMUONSt2GeometryBuilderV2)

//______________________________________________________________________________
AliMUONSt2GeometryBuilderV2::AliMUONSt2GeometryBuilderV2(AliMUON* muon)
 : AliMUONVGeometryBuilder("st2.dat",
                           muon->Chamber(2).GetGeometry(),
			   muon->Chamber(3).GetGeometry()),
   fMUON(muon)
{
// Standard constructor

}

//______________________________________________________________________________
AliMUONSt2GeometryBuilderV2::AliMUONSt2GeometryBuilderV2()
 : AliMUONVGeometryBuilder(),
   fMUON(0)
{
// Default constructor
}

//______________________________________________________________________________
AliMUONSt2GeometryBuilderV2::AliMUONSt2GeometryBuilderV2(const AliMUONSt2GeometryBuilderV2& rhs)
  : AliMUONVGeometryBuilder(rhs)
{
// Protected copy constructor

  AliFatal("Copy constructor is not implemented.");
}

//______________________________________________________________________________
AliMUONSt2GeometryBuilderV2::~AliMUONSt2GeometryBuilderV2() {
//
}

//______________________________________________________________________________
AliMUONSt2GeometryBuilderV2&
AliMUONSt2GeometryBuilderV2::operator = (const AliMUONSt2GeometryBuilderV2& rhs)
{
// Protected assignement operator

  // check assignement to self
  if (this == &rhs) return *this;

  AliFatal("Assignment operator is not implemented.");

  return *this;
}

//
// public methods
//

//______________________________________________________________________________
void AliMUONSt2GeometryBuilderV2::CreateGeometry()
{
// From AliMUONv1::CreateGeometry()

//
//********************************************************************
//                            Station 2                             **
//********************************************************************
     // indices 1 and 2 for first and second chambers in the station
     // iChamber (first chamber) kept for other quanties than Z,
     // assumed to be the same in both chambers

     // Get tracking medias Ids
     Int_t *idtmed = fMUON->GetIdtmed()->GetArray()-1099;
     Int_t idAir= idtmed[1100]; // medium 1
  //     Int_t idAlu1=idtmed[1103]; // medium 4
     //     Int_t idAlu2=idtmed[1104]; // medium 5
     Int_t idGas=idtmed[1108];  // medium 9 = Ar-CO2 gas (80%+20%)

     const Float_t kDeltaQuad = 0.01;//2.6; dummy value til we find the good value
     const Float_t kDeltaZ = 6.5/2.;

     // Rotation matrices in the x-y plane
     // phi= 0 deg
     Int_t irot1;
     fMUON->AliMatrix(irot1,  90.,   0., 90.,  90., 0., 0.);
     // phi= 90 deg
     Int_t irot2;
     fMUON->AliMatrix(irot2,  90.,  90., 90., 180., 0., 0.);

     AliMUONChamber* iChamber = &fMUON->Chamber(2);
     //     AliMUONChamber* iChamber1 = iChamber;
     //     AliMUONChamber* iChamber2 = &fMUON->Chamber(3);

     // Half of the total thickness of frame crosses (including DAlu)
     // for each chamber in stations 1 and 2:
     // 3% of X0 of composite material,
     // but taken as Aluminium here, with same thickness in number of X0
     //     Float_t dframez = 3. * 8.9 / 100;
     // DGas and DAlu not changed from standard values
     //     Double_t zfpos=-(iChamber->DGas()+dframez+iChamber->DAlu())/2;
             // The same parameters are defined in builder for station 1

     // Mother volume
     // Outer excess and inner recess for mother volume radius
     // with respect to ROuter and RInner
     //     Float_t dframep=.001; // Value for station 3 should be 6 ...
     // Width (RdPhi) of the frame crosses for stations 1 and 2 (cm)
     // Float_t dframep1=.001;
     //     Float_t phi=2*TMath::Pi()/12/2;
             // The same parameters are defined in builder for station 1 (30deg)

     Float_t tpar[5];
     //     Double_t dstation = (-iChamber2->Z()) - (-iChamber1->Z());

     Float_t posx, posy, posz;
//   Chamber Material represented by Alu sheet
     tpar[0]= iChamber->RInner();
     tpar[1]= iChamber->ROuter();
     tpar[2] = (iChamber->DGas())/2;
     tpar[3] = 0.;
     tpar[4] = 90.;

//
//   Sensitive volumes
     gMC->Gsvolu("S03G", "TUBS", idGas, tpar, 5);
     gMC->Gsvolu("S04G", "TUBS", idGas, tpar, 5);

     Int_t detElemId;

     posx = kDeltaQuad;
     posy = kDeltaQuad;
     posz = -kDeltaZ;

     detElemId = 300;
     gMC->Gsvolu("LE01", "TUBS", idAir, tpar, 5);
     GetEnvelopes(2)->AddEnvelope("LE01", detElemId, true, TGeoTranslation(posx, posy, posz),
				   TGeoRotation("rot1",90,0,90,90,0,0) );
     detElemId = 400;
     gMC->Gsvolu("LF01", "TUBS", idAir, tpar, 5);
     GetEnvelopes(3)->AddEnvelope("LF01", detElemId, true, TGeoTranslation(posx, posy, posz),
				   TGeoRotation("rot1",90,0,90,90,0,0) );
     detElemId = 351;
     gMC->Gsvolu("LE02", "TUBS", idAir, tpar, 5);
     GetEnvelopes(2)->AddEnvelope("LE02", detElemId, true, TGeoTranslation(-posx, posy,-posz),
				  TGeoRotation("rot2",90,180,90,90,180,0) );
     detElemId = 451;
     gMC->Gsvolu("LF02", "TUBS", idAir, tpar, 5);
     GetEnvelopes(3)->AddEnvelope("LF02", detElemId, true, TGeoTranslation(-posx, posy,-posz),
				  TGeoRotation("rot2",90,180,90,90,180,0) );
     detElemId = 301;
     gMC->Gsvolu("LE03", "TUBS", idAir, tpar, 5);
     GetEnvelopes(2)->AddEnvelope("LE03", detElemId, true, TGeoTranslation(posx, -posy, -posz),
				  TGeoRotation("rot3",90,0,90,270,180,0) );
     detElemId = 401;
     gMC->Gsvolu("LF03", "TUBS", idAir, tpar, 5);
     GetEnvelopes(3)->AddEnvelope("LF03", detElemId, true, TGeoTranslation(posx, -posy, -posz),
				  TGeoRotation("rot3",90,0,90,270,180,0) );
     detElemId = 350;
     gMC->Gsvolu("LE04", "TUBS", idAir, tpar, 5);
     GetEnvelopes(2)->AddEnvelope("LE04", detElemId, true, TGeoTranslation(-posx, -posy, posz),
				  TGeoRotation("rot4",90,180,90,270,0,0) );
     detElemId = 450;
     gMC->Gsvolu("LF04", "TUBS", idAir, tpar, 5);
     GetEnvelopes(3)->AddEnvelope("LF04", detElemId, true, TGeoTranslation(-posx, -posy, posz),
				  TGeoRotation("rot4",90,180,90,270,0,0) );

     GetEnvelopes(2)->AddEnvelopeConstituent("S03G", "LE01", 1);
     GetEnvelopes(3)->AddEnvelopeConstituent("S04G", "LF01", 1);

     GetEnvelopes(2)->AddEnvelopeConstituent("S03G", "LE02", 2);
     GetEnvelopes(3)->AddEnvelopeConstituent("S04G", "LF02", 2);

     GetEnvelopes(2)->AddEnvelopeConstituent("S03G", "LE03", 3);
     GetEnvelopes(3)->AddEnvelopeConstituent("S04G", "LF03", 3);

     GetEnvelopes(2)->AddEnvelopeConstituent("S03G", "LE04", 4);
     GetEnvelopes(3)->AddEnvelopeConstituent("S04G", "LF04", 4);

}

//______________________________________________________________________________
void AliMUONSt2GeometryBuilderV2::SetTransformations()
{
// Defines the transformations for the station2 chambers.
// ---

  AliMUONChamber* iChamber1 = &fMUON->Chamber(2);
  Double_t zpos1 = - iChamber1->Z();
  iChamber1->GetGeometry()
    ->SetTranslation(TGeoTranslation(0., 0., zpos1));

  AliMUONChamber* iChamber2 = &fMUON->Chamber(3);
  Double_t zpos2 = - iChamber2->Z();
  iChamber2->GetGeometry()
    ->SetTranslation(TGeoTranslation(0., 0., zpos2));
}

//______________________________________________________________________________
void AliMUONSt2GeometryBuilderV2::SetSensitiveVolumes()
{
// Defines the sensitive volumes for station2 chambers.
// ---

  GetGeometry(2)->SetSensitiveVolume("S03G");
  GetGeometry(3)->SetSensitiveVolume("S04G");
}
<|endoftext|>


<commit_before>// ======================================================================== //
// Copyright 2009-2017 Intel Corporation                                    //
//                                                                          //
// 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.                                           //
// ======================================================================== //

#undef NDEBUG

// sg
#include "SceneGraph.h"
#include "sg/common/Texture2D.h"
#include "sg/geometry/TriangleMesh.h"

#define TINYOBJLOADER_IMPLEMENTATION // define this in only *one* .cc
#include "../3rdParty/tiny_obj_loader.h"

#include <cstring>

namespace ospray {
  namespace sg {

    std::shared_ptr<Texture2D> loadTexture(const FileName &fullPath,
                                           const bool preferLinear = false)
    {
      std::shared_ptr<Texture2D> tex = Texture2D::load(fullPath,
                                                       preferLinear);
      if (!tex)
        std::cout << "could not load texture " << fullPath.str() << " !\n";

      return tex;
    }

    void addTextureIfNeeded(Material &node,
                            const std::string &type,
                            const FileName &texName,
                            const FileName &containingPath,
                            bool preferLinear = false)
    {
      if (!texName.str().empty()) {
        auto tex = loadTexture(containingPath + texName, preferLinear);
        if (tex) {
          tex->setName(type);
          node.setChild(type, tex);
          tex->setParent(node);
        }
      }
    }

    static inline std::vector<std::shared_ptr<Material>>
    createSgMaterials(std::vector<tinyobj::material_t> &mats,
                      const FileName &containingPath)
    {
      std::vector<std::shared_ptr<Material>> sgMaterials;

      for (auto &mat : mats) {
        auto matNodePtr = createNode(mat.name, "Material")->nodeAs<Material>();
        auto &matNode = *matNodePtr;

        auto type = mat.unknown_parameter["type"];
        if (!type.empty())
          matNode["type"].setValue(type);

        matNode["d"].setValue(mat.dissolve);
        matNode["Ka"].setValue(vec3f(mat.ambient[0],
                                     mat.ambient[1],
                                     mat.ambient[2]));
        matNode["Kd"].setValue(vec3f(mat.diffuse[0],
                                     mat.diffuse[1],
                                     mat.diffuse[2]));
        matNode["Ks"].setValue(vec3f(mat.specular[0],
                                     mat.specular[1],
                                     mat.specular[2]));

        addTextureIfNeeded(matNode, "map_Ka", mat.ambient_texname,
                           containingPath);
        addTextureIfNeeded(matNode, "map_Kd", mat.diffuse_texname,
                           containingPath);
        addTextureIfNeeded(matNode, "map_Ks", mat.specular_texname,
                           containingPath);
        addTextureIfNeeded(matNode, "map_Ns", mat.specular_highlight_texname,
                           containingPath, true);
        addTextureIfNeeded(matNode, "map_bump", mat.bump_texname,
                           containingPath);
        addTextureIfNeeded(matNode, "map_d", mat.alpha_texname,
                           containingPath, true);

        sgMaterials.push_back(matNodePtr);
      }

      return sgMaterials;
    }

    void importOBJ(const std::shared_ptr<Node> &world, const FileName &fileName)
    {
      tinyobj::attrib_t attrib;
      std::vector<tinyobj::shape_t> shapes;
      std::vector<tinyobj::material_t> materials;

      std::string err;
      auto containingPath = fileName.path().str() + '/';
      bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err,
                                  fileName.c_str(), containingPath.c_str());

      if (!err.empty())
        std::cerr << "#ospsg: obj parsing warning(s)...\n" << err << std::endl;

      if (!ret) {
        std::cerr << "#ospsg: FATAL error parsing obj file, no geometry added"
                  << " to the scene!" << std::endl;
        return;
      }

      auto sgMaterials = createSgMaterials(materials, containingPath);

      std::string base_name = fileName.name() + '_';
      int shapeId = 0;

      for (auto &shape : shapes) {
        for (int numVertsInFace : shape.mesh.num_face_vertices) {
          if (numVertsInFace != 3) {
            std::cerr << "Warning: more thant 3 verts in face!";
            PRINT(numVertsInFace);
          }
        }

        auto name = base_name + std::to_string(shapeId++) + '_' + shape.name;
        auto mesh = createNode(name, "TriangleMesh")->nodeAs<TriangleMesh>();

        auto v = createNode("vertex", "DataVector3f")->nodeAs<DataVector3f>();
        auto numSrcIndices = shape.mesh.indices.size();
        v->v.reserve(numSrcIndices);

        auto vi = createNode("index", "DataVector3i")->nodeAs<DataVector3i>();
        vi->v.reserve(numSrcIndices / 3);

        auto vn = createNode("normal", "DataVector3f")->nodeAs<DataVector3f>();
        vn->v.reserve(numSrcIndices);

        auto vt = createNode("texcoord","DataVector2f")->nodeAs<DataVector2f>();
        vt->v.reserve(numSrcIndices);

        for (int i = 0; i < shape.mesh.indices.size(); i += 3) {
          auto idx0 = shape.mesh.indices[i+0];
          auto idx1 = shape.mesh.indices[i+1];
          auto idx2 = shape.mesh.indices[i+2];

          auto prim = vec3i(i+0, i+1, i+2);
          vi->push_back(prim);

          v->push_back(vec3f(attrib.vertices[idx0.vertex_index*3+0],
                             attrib.vertices[idx0.vertex_index*3+1],
                             attrib.vertices[idx0.vertex_index*3+2]));
          v->push_back(vec3f(attrib.vertices[idx1.vertex_index*3+0],
                             attrib.vertices[idx1.vertex_index*3+1],
                             attrib.vertices[idx1.vertex_index*3+2]));
          v->push_back(vec3f(attrib.vertices[idx2.vertex_index*3+0],
                             attrib.vertices[idx2.vertex_index*3+1],
                             attrib.vertices[idx2.vertex_index*3+2]));

          if (!attrib.normals.empty()) {
            vn->push_back(vec3f(attrib.normals[idx0.normal_index*3+0],
                                attrib.normals[idx0.normal_index*3+1],
                                attrib.normals[idx0.normal_index*3+2]));
            vn->push_back(vec3f(attrib.normals[idx1.normal_index*3+0],
                                attrib.normals[idx1.normal_index*3+1],
                                attrib.normals[idx1.normal_index*3+2]));
            vn->push_back(vec3f(attrib.normals[idx2.normal_index*3+0],
                                attrib.normals[idx2.normal_index*3+1],
                                attrib.normals[idx2.normal_index*3+2]));
          }

          if (!attrib.texcoords.empty()) {
            vt->push_back(vec2f(attrib.texcoords[idx0.texcoord_index*2+0],
                                attrib.texcoords[idx0.texcoord_index*2+1]));
            vt->push_back(vec2f(attrib.texcoords[idx1.texcoord_index*2+0],
                                attrib.texcoords[idx1.texcoord_index*2+1]));
            vt->push_back(vec2f(attrib.texcoords[idx2.texcoord_index*2+0],
                                attrib.texcoords[idx2.texcoord_index*2+1]));
          }
        }

        mesh->add(v);
        mesh->add(vi);
        if(!vn->empty()) mesh->add(vn);
        if(!vt->empty()) mesh->add(vt);

        auto matIdx = shape.mesh.material_ids[0];
        if (!sgMaterials.empty()) {
          if (matIdx >= 0)
            mesh->setChild("material", sgMaterials[matIdx]);
          else
            mesh->setChild("material", sgMaterials[0]);
        }

        auto model = createNode(name + "_model", "Model");
        model->add(mesh);

        auto instance = createNode(name + "_instance", "Instance");
        instance->setChild("model", model);
        model->setParent(instance);

        world->add(instance);
      }
    }

  } // ::ospray::sg
} // ::ospray

<commit_msg>small fix for some obj files with textures<commit_after>// ======================================================================== //
// Copyright 2009-2017 Intel Corporation                                    //
//                                                                          //
// 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.                                           //
// ======================================================================== //

#undef NDEBUG

// sg
#include "SceneGraph.h"
#include "sg/common/Texture2D.h"
#include "sg/geometry/TriangleMesh.h"

#define TINYOBJLOADER_IMPLEMENTATION // define this in only *one* .cc
#include "../3rdParty/tiny_obj_loader.h"

#include <cstring>

namespace ospray {
  namespace sg {

    std::shared_ptr<Texture2D> loadTexture(const FileName &fullPath,
                                           const bool preferLinear = false)
    {
      std::shared_ptr<Texture2D> tex = Texture2D::load(fullPath,
                                                       preferLinear);
      if (!tex)
        std::cout << "could not load texture " << fullPath.str() << " !\n";

      return tex;
    }

    void addTextureIfNeeded(Material &node,
                            const std::string &type,
                            const FileName &texName,
                            const FileName &containingPath,
                            bool preferLinear = false)
    {
      if (!texName.str().empty()) {
        auto tex = loadTexture(containingPath + texName, preferLinear);
        if (tex) {
          tex->setName(type);
          node.setChild(type, tex);
          if (!tex->hasParent())
            tex->setParent(node);
        }
      }
    }

    static inline std::vector<std::shared_ptr<Material>>
    createSgMaterials(std::vector<tinyobj::material_t> &mats,
                      const FileName &containingPath)
    {
      std::vector<std::shared_ptr<Material>> sgMaterials;

      for (auto &mat : mats) {
        auto matNodePtr = createNode(mat.name, "Material")->nodeAs<Material>();
        auto &matNode = *matNodePtr;

        auto type = mat.unknown_parameter["type"];
        if (!type.empty())
          matNode["type"].setValue(type);

        matNode["d"].setValue(mat.dissolve);
        matNode["Ka"].setValue(vec3f(mat.ambient[0],
                                     mat.ambient[1],
                                     mat.ambient[2]));
        matNode["Kd"].setValue(vec3f(mat.diffuse[0],
                                     mat.diffuse[1],
                                     mat.diffuse[2]));
        matNode["Ks"].setValue(vec3f(mat.specular[0],
                                     mat.specular[1],
                                     mat.specular[2]));

        addTextureIfNeeded(matNode, "map_Ka", mat.ambient_texname,
                           containingPath);
        addTextureIfNeeded(matNode, "map_Kd", mat.diffuse_texname,
                           containingPath);
        addTextureIfNeeded(matNode, "map_Ks", mat.specular_texname,
                           containingPath);
        addTextureIfNeeded(matNode, "map_Ns", mat.specular_highlight_texname,
                           containingPath, true);
        addTextureIfNeeded(matNode, "map_bump", mat.bump_texname,
                           containingPath);
        addTextureIfNeeded(matNode, "map_d", mat.alpha_texname,
                           containingPath, true);

        sgMaterials.push_back(matNodePtr);
      }

      return sgMaterials;
    }

    void importOBJ(const std::shared_ptr<Node> &world, const FileName &fileName)
    {
      tinyobj::attrib_t attrib;
      std::vector<tinyobj::shape_t> shapes;
      std::vector<tinyobj::material_t> materials;

      std::string err;
      auto containingPath = fileName.path().str() + '/';
      bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err,
                                  fileName.c_str(), containingPath.c_str());

      if (!err.empty())
        std::cerr << "#ospsg: obj parsing warning(s)...\n" << err << std::endl;

      if (!ret) {
        std::cerr << "#ospsg: FATAL error parsing obj file, no geometry added"
                  << " to the scene!" << std::endl;
        return;
      }

      auto sgMaterials = createSgMaterials(materials, containingPath);

      std::string base_name = fileName.name() + '_';
      int shapeId = 0;

      for (auto &shape : shapes) {
        for (int numVertsInFace : shape.mesh.num_face_vertices) {
          if (numVertsInFace != 3) {
            std::cerr << "Warning: more thant 3 verts in face!";
            PRINT(numVertsInFace);
          }
        }

        auto name = base_name + std::to_string(shapeId++) + '_' + shape.name;
        auto mesh = createNode(name, "TriangleMesh")->nodeAs<TriangleMesh>();

        auto v = createNode("vertex", "DataVector3f")->nodeAs<DataVector3f>();
        auto numSrcIndices = shape.mesh.indices.size();
        v->v.reserve(numSrcIndices);

        auto vi = createNode("index", "DataVector3i")->nodeAs<DataVector3i>();
        vi->v.reserve(numSrcIndices / 3);

        auto vn = createNode("normal", "DataVector3f")->nodeAs<DataVector3f>();
        vn->v.reserve(numSrcIndices);

        auto vt = createNode("texcoord","DataVector2f")->nodeAs<DataVector2f>();
        vt->v.reserve(numSrcIndices);

        for (int i = 0; i < shape.mesh.indices.size(); i += 3) {
          auto idx0 = shape.mesh.indices[i+0];
          auto idx1 = shape.mesh.indices[i+1];
          auto idx2 = shape.mesh.indices[i+2];

          auto prim = vec3i(i+0, i+1, i+2);
          vi->push_back(prim);

          v->push_back(vec3f(attrib.vertices[idx0.vertex_index*3+0],
                             attrib.vertices[idx0.vertex_index*3+1],
                             attrib.vertices[idx0.vertex_index*3+2]));
          v->push_back(vec3f(attrib.vertices[idx1.vertex_index*3+0],
                             attrib.vertices[idx1.vertex_index*3+1],
                             attrib.vertices[idx1.vertex_index*3+2]));
          v->push_back(vec3f(attrib.vertices[idx2.vertex_index*3+0],
                             attrib.vertices[idx2.vertex_index*3+1],
                             attrib.vertices[idx2.vertex_index*3+2]));

          if (!attrib.normals.empty()) {
            vn->push_back(vec3f(attrib.normals[idx0.normal_index*3+0],
                                attrib.normals[idx0.normal_index*3+1],
                                attrib.normals[idx0.normal_index*3+2]));
            vn->push_back(vec3f(attrib.normals[idx1.normal_index*3+0],
                                attrib.normals[idx1.normal_index*3+1],
                                attrib.normals[idx1.normal_index*3+2]));
            vn->push_back(vec3f(attrib.normals[idx2.normal_index*3+0],
                                attrib.normals[idx2.normal_index*3+1],
                                attrib.normals[idx2.normal_index*3+2]));
          }

          if (!attrib.texcoords.empty()) {
            vt->push_back(vec2f(attrib.texcoords[idx0.texcoord_index*2+0],
                                attrib.texcoords[idx0.texcoord_index*2+1]));
            vt->push_back(vec2f(attrib.texcoords[idx1.texcoord_index*2+0],
                                attrib.texcoords[idx1.texcoord_index*2+1]));
            vt->push_back(vec2f(attrib.texcoords[idx2.texcoord_index*2+0],
                                attrib.texcoords[idx2.texcoord_index*2+1]));
          }
        }

        mesh->add(v);
        mesh->add(vi);
        if(!vn->empty()) mesh->add(vn);
        if(!vt->empty()) mesh->add(vt);

        auto matIdx = shape.mesh.material_ids[0];
        if (!sgMaterials.empty()) {
          if (matIdx >= 0)
            mesh->setChild("material", sgMaterials[matIdx]);
          else
            mesh->setChild("material", sgMaterials[0]);
        }

        auto model = createNode(name + "_model", "Model");
        model->add(mesh);

        auto instance = createNode(name + "_instance", "Instance");
        instance->setChild("model", model);
        model->setParent(instance);

        world->add(instance);
      }
    }

  } // ::ospray::sg
} // ::ospray

<|endoftext|>