Select Git revision
geometry_pass.cpp
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