Skip to content
Snippets Groups Projects
Select Git revision
  • fb619626ed8cfbaa646c2c48f190864aadb8cd1f
  • main default protected
  • YL
  • NS
4 results

dataset.py

Blame
  • geometry_pass.cpp 14.75 KiB
    //------------------------------------------------------------------------------
    // Project Phoenix
    //
    // Copyright (c) 2017-2018 RWTH Aachen University, Germany,
    // Virtual Reality & Immersive Visualization Group.
    //------------------------------------------------------------------------------
    //                                 License
    //
    // Licensed under the 3-Clause BSD License (the "License");
    // you may not use this file except in compliance with the License.
    // See the file LICENSE for the full text.
    // You may obtain a copy of the License at
    //
    //     https://opensource.org/licenses/BSD-3-Clause
    //
    // 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.
    //------------------------------------------------------------------------------
    
    #include "phx/rendering/render_passes/geometry_pass.hpp"
    
    #include <algorithm>
    #include <iostream>
    #include <memory>
    #include <numeric>
    #include <utility>
    #include <vector>
    
    SUPPRESS_WARNINGS_BEGIN
    #define GLM_ENABLE_EXPERIMENTAL
    #include "glm/glm.hpp"
    #include "glm/gtc/type_ptr.hpp"
    #include "glm/gtx/matrix_operation.hpp"
    SUPPRESS_WARNINGS_END
    
    #include "phx/core/logger.hpp"
    #include "phx/rendering/components/transform.hpp"
    #include "phx/resources/resource_utils.hpp"
    #include "phx/resources/types/mesh.hpp"
    #include "phx/resources/types/shader_source.hpp"
    
    namespace phx {
    
    GeometryPass::GeometryPass(RenderTarget* render_target)
        : light_buffer_(gl::buffer()), render_target_(render_target) {}
    
    void GeometryPass::SetData(
        const std::vector<RenderingInstance>& rendering_instances,
        const std::vector<std::pair<Light*, Transform*>>& light_transform_pairs) {
      bool needs_mesh_data_upload = false;
      for (const auto& instance : rendering_instances) {
        if (mesh_cache_.find(instance.mesh) == mesh_cache_.end()) {
          needs_mesh_data_upload = true;
          break;
        }
      }
      if (needs_mesh_data_upload) {
        UploadMeshData(rendering_instances);
      }
    
      rendering_instances_ = rendering_instances;
      light_transform_pairs_ = light_transform_pairs;
    }
    
    void GeometryPass::UploadMeshData(
        const std::vector<RenderingInstance>& rendering_instances) {
      mesh_cache_.clear();
      RenderOffset offset{0, 0};
      for (const auto& instance : rendering_instances) {
        if (instance.material != nullptr) {
          instance.material->UploadTextures();
        }
    
        Mesh* mesh = instance.mesh;
        if (mesh_cache_.find(mesh) != mesh_cache_.end()) {
          continue;
        }
    
        if (!CheckMeshValidity(mesh))
          error(
              "Mesh appears not to be valid(vertex count: {}, normal count: {}, "
              "tex coord channel count: {}, index count {}), try to load it "
              "anyways!",
              mesh->GetVertices().size(), mesh->GetNormals().size(),
              mesh->GetTextureCoords().size(), mesh->GetIndices().size());
        // TODO(anyone) get the mesh name or id
    
        if (rendering_resource_->index_buffer_size <=
                offset.index_offset + mesh->GetIndices().size() ||
            rendering_resource_->vertex_buffer_size <=
                offset.vertex_offset + mesh->GetVertices().size()) {
          error(
              "The models have more data than reserved space in the buffers, "
              "resize!");
          // TODO(anyone) implement a resizing strategy
        }
    
        rendering_resource_->vertex_buffer.set_sub_data(
            static_cast<GLsizeiptr>(offset.vertex_offset * sizeof(glm::vec3)),
            static_cast<GLsizeiptr>(mesh->GetVertices().size() * sizeof(glm::vec3)),
            mesh->GetVertices().data());
        rendering_resource_->normal_buffer.set_sub_data(
            static_cast<GLintptr>(offset.vertex_offset * sizeof(glm::vec3)),
            static_cast<GLsizeiptr>(mesh->GetNormals().size() * sizeof(glm::vec3)),
            mesh->GetNormals().data());
    
        if (mesh->GetNumberOfTextureCoordComponents(0) == 1) {
          const void* texture_coord_data = mesh->GetTextureCoords()[0].data();
          rendering_resource_->tex_coords_buffer_1d.set_sub_data(
              static_cast<GLintptr>(offset.vertex_offset *
                                    mesh->GetNumberOfTextureCoordComponents(0) *
                                    sizeof(float)),
              static_cast<GLsizeiptr>(mesh->GetNumberOfTextureCoordComponents(0) *
                                      mesh->GetVertices().size() * sizeof(float)),
              texture_coord_data);
        } else if (mesh->GetNumberOfTextureCoordComponents(0) == 2) {
          const void* texture_coord_data = mesh->GetTextureCoords()[0].data();
          rendering_resource_->tex_coords_buffer_2d.set_sub_data(
              static_cast<GLintptr>(offset.vertex_offset *
                                    mesh->GetNumberOfTextureCoordComponents(0) *
                                    sizeof(float)),
              static_cast<GLsizeiptr>(mesh->GetNumberOfTextureCoordComponents(0) *
                                      mesh->GetVertices().size() * sizeof(float)),
              texture_coord_data);
        }
    
        std::vector<unsigned int> shifted_indices;
        shifted_indices.reserve(mesh->GetIndices().size());
        std::for_each(
            mesh->GetIndices().begin(), mesh->GetIndices().end(),
            [&offset, &shifted_indices](unsigned int index) {
              shifted_indices.push_back(
                  index + static_cast<unsigned int>(offset.vertex_offset));
            });
    
        rendering_resource_->index_buffer.set_sub_data(
            static_cast<GLintptr>(offset.index_offset * sizeof(unsigned int)),
            static_cast<GLsizeiptr>(shifted_indices.size() * sizeof(unsigned int)),
            shifted_indices.data());
    
        mesh_cache_[mesh] = offset;
    
        offset.index_offset += mesh->GetIndices().size();
        offset.vertex_offset += mesh->GetVertices().size();
      }
    }
    
    bool GeometryPass::CheckMeshValidity(Mesh* mesh) {
      if (mesh->GetVertices().empty() ||
          mesh->GetNormals().size() != mesh->GetVertices().size() ||
          mesh->GetIndices().empty()) {
        return false;
      }
    
      const auto& texture_coords = mesh->GetTextureCoords();
      for (std::size_t i = 0; i < mesh->GetNumberOfTextureCoordChannels(); i++) {
        if (texture_coords[i].size() % mesh->GetVertices().size() != 0) {
          return false;
        }
    
        const std::size_t num_components =
            mesh->GetNumberOfTextureCoordComponents(i);
        if (num_components != 1 && num_components != 2 && num_components != 3) {
          return false;
        }
      }
    
      return true;
    }
    
    void GeometryPass::Initialize() {
      SetUpShaders();
      CreateRenderingResource();
    
      light_buffer_.set_data((2 * sizeof(glm::vec3) + sizeof(float)) * 512 +
                             sizeof(GLuint));
    }
    
    void GeometryPass::SetUpShaders() {
      auto vertex_shader =
          ResourceUtils::LoadResourceFromFile<ShaderSource>("shader/phong.vert");
      auto fragment_shader =
          ResourceUtils::LoadResourceFromFile<ShaderSource>("shader/phong.frag");
    
      shader_program_ = std::make_unique<ShaderProgram>();
      shader_program_->SetShader(ShaderProgram::VERTEX, vertex_shader);
      shader_program_->SetShader(ShaderProgram::FRAGMENT, fragment_shader);
      shader_program_->Link();
    }
    
    void GeometryPass::CreateRenderingResource() {
      rendering_resource_ = std::make_unique<RenderingResource>();
      if (!IsValid()) error("Created Rendering Resource is not valid!");
    
      // TODO(anyone) what to start with? make it resizeable!!!!
      const auto sixteen_mb = 16 * 1024 * 1024;
      rendering_resource_->index_buffer_size = sixteen_mb;
      rendering_resource_->vertex_buffer_size = sixteen_mb;
      rendering_resource_->vertex_buffer.set_data(
          rendering_resource_->vertex_buffer_size * sizeof(glm::vec3));
      rendering_resource_->normal_buffer.set_data(
          rendering_resource_->vertex_buffer_size * sizeof(glm::vec3));
      rendering_resource_->tex_coords_buffer_1d.set_data(
          rendering_resource_->vertex_buffer_size * sizeof(float));
      rendering_resource_->tex_coords_buffer_2d.set_data(
          rendering_resource_->vertex_buffer_size * sizeof(glm::vec2));
      rendering_resource_->index_buffer.set_data(
          rendering_resource_->index_buffer_size * sizeof(unsigned int));
    
      CreateAttachVertexArrayVertexBuffer(rendering_resource_->vertex_buffer, 0u, 3,
                                          sizeof(glm::vec3));
      CreateAttachVertexArrayVertexBuffer(rendering_resource_->normal_buffer, 1u, 3,
                                          sizeof(glm::vec3));
      CreateAttachVertexArrayVertexBuffer(rendering_resource_->tex_coords_buffer_2d,
                                          2u, 2, sizeof(glm::vec2));
      CreateAttachVertexArrayVertexBuffer(rendering_resource_->tex_coords_buffer_1d,
                                          3u, 1, sizeof(glm::vec2));
    
      rendering_resource_->vertex_array.set_element_buffer(
          rendering_resource_->index_buffer);
    }
    
    void GeometryPass::CreateAttachVertexArrayVertexBuffer(const gl::buffer& buffer,
                                                           GLuint attribute_index,
                                                           GLint num_components,
                                                           GLsizei stride) {
      rendering_resource_->vertex_array.set_attribute_enabled(attribute_index,
                                                              true);
      rendering_resource_->vertex_array.set_attribute_format(
          attribute_index, num_components, GL_FLOAT);
      rendering_resource_->vertex_array.set_vertex_buffer(attribute_index, buffer,
                                                          0, stride);
    }
    
    void GeometryPass::Execute() {
      BindResources();
      for (const RenderingInstance& instance : rendering_instances_) {
        Draw(instance);
      }
      UnbindResources();
    }
    
    void GeometryPass::BindResources() {
      rendering_resource_->vertex_array.bind();
      shader_program_->use();
      render_target_->bind();
      render_target_->SetViewport();
      light_buffer_.bind_base(GL_UNIFORM_BUFFER, 0);
    }
    
    void GeometryPass::Draw(const RenderingInstance& rendering_instance) {
      glm::mat4 projection_matrix = render_target_->GetProjection();
      glm::mat4 view_matrix = render_target_->GetView();
    
      Transform* transform = rendering_instance.transform;
      glm::mat4 model_matrix;
      if (transform) {
        model_matrix = transform->GetGlobalMatrix();
      }
    
      SetLightShaderUniforms();
    
      SetTransformShaderUniforms(model_matrix, view_matrix, projection_matrix);
      SetMaterialShaderUniforms(rendering_instance.material);
    
      const MeshRenderSettings* render_settings =
          rendering_instance.mesh_render_settings;
      if (render_settings == nullptr) {
        render_settings = MeshRenderSettings::GetDefault();
      }
    
      glEnable(GL_DEPTH_TEST);
      glPolygonMode(GL_FRONT_AND_BACK,
                    render_settings->GetWireframeMode() ? GL_LINE : GL_FILL);
      glDrawElements(
          GL_TRIANGLES,
          static_cast<GLsizei>(rendering_instance.mesh->GetIndices().size()),
          GL_UNSIGNED_INT,
          reinterpret_cast<GLvoid*>(
              mesh_cache_[rendering_instance.mesh].index_offset *
              sizeof(unsigned int)));
    }
    
    void GeometryPass::UnbindResources() {
      render_target_->unbind();
      shader_program_->unuse();
      light_buffer_.unbind_base(GL_UNIFORM_BUFFER, 0);
      glBindVertexArray(0);
    }
    
    void GeometryPass::SetTransformShaderUniforms(
        const glm::mat4& model_matrix, const glm::mat4& view_matrix,
        const glm::mat4& projection_matrix) {
      shader_program_->SetUniform("model_matrix", model_matrix);
      shader_program_->SetUniform("view_matrix", view_matrix);
      shader_program_->SetUniform("projection_matrix", projection_matrix);
    }
    
    void GeometryPass::SetLightShaderUniforms() {
      if (light_transform_pairs_.size() == 0) {
        warn("No light is given, a default light is used");
        light_transform_pairs_.push_back(std::make_pair(nullptr, nullptr));
      }
    
      const GLuint num_lights = static_cast<GLuint>(light_transform_pairs_.size());
      light_buffer_.set_sub_data(0, sizeof(num_lights), &num_lights);
    
      for (auto it_light = light_transform_pairs_.begin();
           it_light != light_transform_pairs_.end(); ++it_light) {
        glm::vec3 light_color(1, 1, 1);
        glm::vec3 light_dir(0, 0, -1);
        float light_intensity = 1.0f;
        if (it_light->first != nullptr && it_light->second != nullptr) {
          Light* light = it_light->first;
          Transform* transform = it_light->second;
          light_color = light->GetColor();
          light_intensity = light->GetIntensity();
          light_dir = transform->Forward();
        }
    
        constexpr GLsizeiptr base_offset =
            static_cast<GLsizeiptr>(sizeof(glm::vec4));
        constexpr GLsizeiptr vector_size =
            static_cast<GLsizeiptr>(sizeof(glm::vec4));
        constexpr GLsizeiptr entry_size = 3 * vector_size;
        const GLintptr entry_offset =
            base_offset + static_cast<GLuint>(std::distance(
                              light_transform_pairs_.begin(), it_light)) *
                              entry_size;
    
        light_buffer_.set_sub_data(entry_offset, sizeof(glm::vec3),
                                   glm::value_ptr(light_dir));
        light_buffer_.set_sub_data(entry_offset + vector_size, sizeof(glm::vec3),
                                   glm::value_ptr(light_color));
        light_buffer_.set_sub_data(entry_offset + 2 * vector_size, sizeof(float),
                                   &light_intensity);
      }
    }
    
    void GeometryPass::SetMaterialShaderUniforms(const Material* material) {
      if (material == nullptr) material = Material::GetDefault();
    
      const glm::uvec4 texture_toggle =
          glm::uvec4(material->GetAmbientTexture() != nullptr ? 1u : 0u,
                     material->GetDiffuseTexture() != nullptr ? 1u : 0u,
                     material->GetSpecularTexture() != nullptr ? 1u : 0u, 0u);
      if (material->GetAmbientTexture())
        shader_program_->set_uniform_handle(
            shader_program_->uniform_location("material.ambient_tex"),
            gl::texture_handle(*material->GetAmbientTexture()));
      if (material->GetDiffuseTexture())
        shader_program_->set_uniform_handle(
            shader_program_->uniform_location("material.diffuse_tex"),
            gl::texture_handle(*material->GetDiffuseTexture()));
      if (material->GetSpecularTexture())
        shader_program_->set_uniform_handle(
            shader_program_->uniform_location("material.specular_tex"),
            gl::texture_handle(*material->GetSpecularTexture()));
    
      shader_program_->SetUniform("material.texture_toggle", texture_toggle);
      shader_program_->SetUniform("material.ambient", material->GetAmbientColor());
      shader_program_->SetUniform("material.diffuse", material->GetDiffuseColor());
      shader_program_->SetUniform("material.specular",
                                  material->GetSpecularColor());
      shader_program_->SetUniform("material.shininess", material->GetShininess());
    }
    
    bool GeometryPass::IsValid() const {
      bool is_valid = shader_program_->is_valid();
      is_valid = is_valid && rendering_resource_->vertex_array.is_valid() &&
                 rendering_resource_->vertex_buffer.is_valid() &&
                 rendering_resource_->normal_buffer.is_valid() &&
                 rendering_resource_->index_buffer.is_valid();
    
      return is_valid;
    }
    
    }  // namespace phx