bool Mesh::Load(const GLfloat* positions,
                const GLfloat* colors, 
                const GLfloat* normals,
                const GLfloat* texCoords,
                const GLuint* indices,
                int numVertices, 
                int numIndices,
                GLenum drawMode, 
                StorageType storageType
              ) 
{ 
  if (!positions || numVertices <= 0)
  {
    return false;
  }

  mDrawMode = drawMode;
  mStorageType = storageType;
  mNumVertices = numVertices;
  mNumIndices  = (numIndices <= 0 || indices == nullptr) ? numVertices : numIndices;

  mHasColors   = (colors    != nullptr);
  mHasNormals  = (normals   != nullptr);
  mHasTexCoord = (texCoords != nullptr);
  mVertexSize  = 3 + 3*mHasColors + 3*mHasNormals + 2*mHasTexCoord;

  mVertices = new GLfloat [mVertexSize * mNumVertices];
  mIndices  = new GLuint  [mNumIndices];

  if (mStorageType == kTightlyPacked)
  {
    // Initialize vertices buffer array. 
    for (int i = 0; i < mNumVertices; i++)
    {
      float* position = PositionAt(i);
      float* texCoord = TexCoordAt(i);
      float* normal   = NormalAt(i); 
      float* color    = ColorAt(i);

      memcpy(position, positions + 3*i, sizeof(GLfloat)*3);
      if (HasColors())
      {
        memcpy(color, colors + 3*i, sizeof(GLfloat)*3);
      }
      if (HasNormals())
      {
        memcpy(normal, normals + 3*i, sizeof(GLfloat)*3);
      }
      if (HasTexCoord())
      {
        memcpy(texCoord, texCoords + 2*i, sizeof(GLfloat)*2);
      }
    }
  }
  else
  {
    GLfloat* dest = mVertices;
    memcpy(dest, positions, 3*sizeof(GLfloat)*mNumVertices);
    dest += 3*mNumVertices;
    if (HasColors())
    {
      memcpy(dest, colors, 3*sizeof(GLfloat)*mNumVertices);
      dest += 3*mNumVertices;
    }
    if (HasNormals())
    {
      memcpy(dest, normals, 3*sizeof(GLfloat)*mNumVertices);
      dest += 3*mNumVertices;
    }
    if (HasTexCoord())
    {
      memcpy(dest, texCoords, 2*sizeof(GLfloat)*mNumVertices);
      dest += 2*mNumVertices;
    }
  }

  // Initialize element array (indices array).
  if (indices)  // Element array provided.
  {
    memcpy(mIndices, indices, sizeof(GLuint)*mNumIndices);
  }
  else  // Element array wasn't provided -- build it up.
  {
    for (int i = 0; i < mNumIndices; i++)
    {
      mIndices[i] = i;
    }
  }

  mInitialized = true;
  Mesh::Upload();
  return true;
}
void Mesh::Upload()
{
  if (mProgramHandle == 0)
  {
    std::cerr << "ERROR Program Handle must be set before creating buffer objects.\n";
    return;
  }

  if (!mInitialized)
  {
    std::cerr << "ERROR The mesh must be initialized before creating buffer objects.\n";
    return;
  }

  // Specify how the arguments will be passed to shaders.
  GLuint locTexCoordAttrib = glGetAttribLocation(mProgramHandle, "in_tex_coord");
  GLuint locPositionAttrib = glGetAttribLocation(mProgramHandle, "in_position");
  GLuint locNormalAttrib   = glGetAttribLocation(mProgramHandle, "in_normal");
  GLuint locColorAttrib    = glGetAttribLocation(mProgramHandle, "in_color");

  // Generate Buffers.
  glGenVertexArrays(1, &mVao);
  glGenBuffers(1, &mVbo);
  glGenBuffers(1, &mEab);

  // Specify VAO.
  glBindVertexArray(mVao);
  
  // Upload indices to GPU.
  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mEab);  
  glBufferData(GL_ELEMENT_ARRAY_BUFFER, mNumIndices * sizeof(GLuint), mIndices, GL_STATIC_DRAW);

  // Enable/Disable each vertex attribute.
  glEnableVertexAttribArray(locPositionAttrib);
  if (HasColors())
  {
    glEnableVertexAttribArray(locColorAttrib);
  }
  else
  {
    glDisableVertexAttribArray(locColorAttrib);
  }
  
  if (HasNormals())
  {
    glEnableVertexAttribArray(locNormalAttrib);
  }
  else
  {
    glDisableVertexAttribArray(locNormalAttrib);
  }

  if (HasTexCoord())
  { 
    glEnableVertexAttribArray(locTexCoordAttrib);
  }
  else
  {
    glDisableVertexAttribArray(locTexCoordAttrib);
  }

  // Upload vertices to GPU.
  glBindBuffer(GL_ARRAY_BUFFER, mVbo);
  glBufferData(GL_ARRAY_BUFFER, mVertexSize * mNumVertices * sizeof(GLfloat), mVertices, GL_STATIC_DRAW);

  if (mStorageType == kTightlyPacked)
  {
    GLfloat stride = sizeof(GLfloat) * mVertexSize;
    glVertexAttribPointer(locPositionAttrib, 3, GL_FLOAT, GL_FALSE, stride, 0);
    
    glVertexAttribPointer(locColorAttrib, 3, GL_FLOAT, GL_FALSE, stride, 
      (void*)(sizeof(GLfloat) * 3));

    glVertexAttribPointer(locNormalAttrib, 3, GL_FLOAT, GL_FALSE, stride, 
      (void*)(sizeof(GLfloat) * (3 + 3*mHasColors)));

    glVertexAttribPointer(locTexCoordAttrib, 2, GL_FLOAT, GL_FALSE, stride, 
      (void*)(sizeof(GLfloat) * (3 + 3*mHasColors + 3*mHasNormals)) );
  }
  else   // Sub buffered storage type.
  {
    glVertexAttribPointer(locPositionAttrib, 3, GL_FLOAT, GL_FALSE, 0, 0);

    glVertexAttribPointer(locColorAttrib,    3, GL_FLOAT, GL_FALSE, 0, 
      (void*)(sizeof(GLfloat) * 3*mNumVertices));
    
    glVertexAttribPointer(locNormalAttrib,   3, GL_FLOAT, GL_FALSE, 0, 
      (void*)(sizeof(GLfloat) * (3 + 3*mHasColors)*mNumVertices));

    glVertexAttribPointer(locTexCoordAttrib, 2, GL_FLOAT, GL_FALSE, 0,   
      (void*)(sizeof(GLfloat) * (3 + 3*mHasColors + 3*mHasNormals)*mNumVertices));
  }
}
Esempio n. 3
0
// Assuming the metallic-roughness material model of models loaded with GLTF.
std::pair<ID, std::vector<std::pair<Texture::Type, std::string>>>
MeshManager::load_mesh(const std::string& directory, const std::string& file) {
    MeshInformation mesh_info;
    mesh_info.loaded_from_filepath = directory + file;

    Assimp::Importer importer;
    auto scene = importer.ReadFile(mesh_info.loaded_from_filepath.c_str(), aiProcess_Triangulate | aiProcess_JoinIdenticalVertices);

    if (scene == nullptr || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE) {
        Log::error(std::string(importer.GetErrorString()));
        return {0, {}};
    }

    std::vector<std::pair<Texture::Type, std::string>> texture_info;
    if (scene->HasMaterials()) {
        Log::info("Number of materials: " + std::to_string(scene->mNumMaterials));
        for (size_t i = 0; i < scene->mNumMaterials; i++) {
            auto material = scene->mMaterials[i];

            aiString material_name;
            material->Get(AI_MATKEY_NAME, material_name);
            Log::info("Material name: " + std::string(material_name.C_Str()));
          
            aiString diffuse_filepath;
            if (material->GetTexture(aiTextureType_DIFFUSE, 0, &diffuse_filepath) == AI_SUCCESS) {
                Log::info("Diffuse texture name: " + std::string(directory.c_str()) + std::string(diffuse_filepath.data));
                std::string texture_filepath(diffuse_filepath.data);
                texture_filepath.insert(0, directory);
                texture_info.push_back({Texture::Type::Diffuse, texture_filepath});
            }
          
            aiString specular_filepath;
            if (material->GetTexture(aiTextureType_SPECULAR, 0, &specular_filepath) == AI_SUCCESS) {
              Log::info("Specular texture name: " + std::string(directory.c_str()) + std::string(specular_filepath.data));
            }

            aiString ambient_filepath;
            if (material->GetTexture(aiTextureType_AMBIENT, 0, &ambient_filepath) == AI_SUCCESS) {
              Log::info("Ambient occlusion texture name: " + std::string(directory.c_str()) + std::string(ambient_filepath.data));
            }
            
            aiString shininess_filepath;
            if (material->GetTexture(aiTextureType_SHININESS, 0, &shininess_filepath) == AI_SUCCESS) {
              Log::info("Shininess texture name: " + std::string(directory.c_str()) + std::string(shininess_filepath.data));
            }

            aiString emissive_filepath;
            if (material->GetTexture(aiTextureType_EMISSIVE, 0, &emissive_filepath) == AI_SUCCESS) {
              Log::info("Emissive texture name: " + std::string(directory.c_str()) + std::string(emissive_filepath.data));
              std::string texture_filepath(emissive_filepath.data);
              texture_filepath.insert(0, directory);
              texture_info.push_back({Texture::Type::Emissive, texture_filepath});
              // TODO: Fetch emissive factor as well 
            }

            aiString displacement_filepath;
            if (material->GetTexture(aiTextureType_DISPLACEMENT, 0, &displacement_filepath) == AI_SUCCESS) {
              Log::info("Displacement texture name: " + std::string(directory.c_str()) + std::string(displacement_filepath.data));
            }

            aiString height_filepath;
            if (material->GetTexture(aiTextureType_HEIGHT, 0, &height_filepath) == AI_SUCCESS) {
              Log::info("Bumpmap texture name: " + std::string(directory.c_str()) + std::string(height_filepath.data));
            }

            // Lightmap is usually the ambient occlusion map ...
            aiString lightmap_filepath;
            if (material->GetTexture(aiTextureType_LIGHTMAP, 0, &lightmap_filepath) == AI_SUCCESS) {
              Log::info("Lightmap texture name: " + std::string(directory.c_str()) + std::string(lightmap_filepath.data));
              std::string texture_filepath(lightmap_filepath.data);
              texture_filepath.insert(0, directory);
              texture_info.push_back({Texture::Type::AmbientOcclusion, texture_filepath});
            }
             
            aiString normals_filepath;
            if (material->GetTexture(aiTextureType_NORMALS, 0, &normals_filepath) == AI_SUCCESS) {
              Log::info("Normals texture name: " + std::string(directory.c_str()) + std::string(normals_filepath.data));
            }

            aiString reflection_filepath;
            if (material->GetTexture(aiTextureType_REFLECTION, 0, &reflection_filepath) == AI_SUCCESS) {
              Log::info("Reflection texture name: " + std::string(directory.c_str()) + std::string(reflection_filepath.data));
            }

            aiString opacity_filepath;
            if (material->GetTexture(aiTextureType_OPACITY, 0, &opacity_filepath) == AI_SUCCESS) {
              Log::info("Opacity texture name: " + std::string(directory.c_str()) + std::string(opacity_filepath.data));
            }
          
            // NOTE: Roughness metallic textures are not detected so here we are assuming this is the unknown texture of the material.
            aiString unknown_filepath;
            if (material->GetTexture(aiTextureType_UNKNOWN, 0, &unknown_filepath) == AI_SUCCESS) {
              Log::info("Unknown texture name: " + std::string(directory.c_str()) + std::string(unknown_filepath.data));
              std::string texture_filepath(normals_filepath.data);
              texture_filepath.insert(0, directory);
              texture_info.push_back({Texture::Type::MetallicRoughness, texture_filepath});
            }
        }
    }

    if (scene->HasMeshes()) {
        // FIXME: Assumes the mesh is a single mesh and not a hierarchy
        Log::info("Scene: # meshes " + std::to_string(scene->mNumMeshes));
        for (size_t i = 0; i < scene->mNumMeshes; i++) {
            auto mesh = scene->mMeshes[i];
            Log::info("Loading mesh with name: " + std::string(mesh->mName.data));

            for (size_t j = 0; j < mesh->mNumVertices; j++) {
                Vertex vertex;

                auto pos = mesh->mVertices[j];
                vertex.position = {pos.x, pos.y, pos.z};

                if (mesh->HasTextureCoords(0)) {
                    auto tex_coord = mesh->mTextureCoords[0][j];
                    vertex.tex_coord = {tex_coord.x, -tex_coord.y}; // glTF (& .obj) has a flipped texture coordinate system compared to OpenGL 
                }

                if (mesh->HasNormals()) {
                    auto normal = mesh->mNormals[j];
                    vertex.normal = {normal.x, normal.y, normal.z};
                }

                mesh_info.mesh.vertices.push_back(vertex);
            }

            for (size_t j = 0; j < mesh->mNumFaces; j++) {
                auto face = &mesh->mFaces[j];
                if (face->mNumIndices != 3) {
                    Log::warn("Not 3 vertices per face in model.");
                    return {0, {}};
                }
                for (size_t k = 0; k < 3; k++) {
                    auto index = face->mIndices[k];
                    mesh_info.mesh.indices.push_back(index);
                }
            }
        }
    }
    // FIXME: Mesh id is worthless since it does not change or anything ...
    loaded_meshes.push_back(mesh_info.mesh);
    return {loaded_meshes.size() - 1, texture_info};
}
// Reloads the geometry. To keep some data constant, just specify nullptr.
void Mesh::Reload(const GLfloat* positions,
                  const GLfloat* colors,   
                  const GLfloat* normals,  
                  const GLfloat* texCoords 
                )
{
  if (!mInitialized)
  {
    std::cerr << "ERROR The mesh must be initialized before creating buffer objects.\n";
    return;
  }

  if (mStorageType == kTightlyPacked)
  {
    for (int i = 0; i < mNumVertices; i++)
    {
      float* position = PositionAt(i);
      float* texCoord = TexCoordAt(i);
      float* normal   = NormalAt(i); 
      float* color    = ColorAt(i);

      if (positions)
      {
        memcpy(position, positions + 3*i, sizeof(GLfloat)*3);
      }
      if (colors && HasColors())
      {
        memcpy(color, colors + 3*i, sizeof(GLfloat)*3);
      }
      if (normals && HasNormals())
      {
        memcpy(normal, normals + 3*i, sizeof(GLfloat)*3);
      }
      if (texCoords && HasTexCoord())
      {
        memcpy(texCoord, texCoords + 2*i, sizeof(GLfloat)*2);
      }
    }
  }
  else  // Sub-buffered.
  {
    GLfloat* dest = mVertices;
    if (positions)
    {
      memcpy(dest, positions, 3*sizeof(GLfloat)*mNumVertices);
      dest += 3*mNumVertices;
    }
    if (colors && HasColors())
    {
      memcpy(dest, colors, 3*sizeof(GLfloat)*mNumVertices);
      dest += 3*mNumVertices;
    }
    if (normals && HasNormals())
    {
      memcpy(dest, normals, 3*sizeof(GLfloat)*mNumVertices);
      dest += 3*mNumVertices;
    }
    if (texCoords && HasTexCoord())
    {
      memcpy(dest, texCoords, 2*sizeof(GLfloat)*mNumVertices);
      dest += 2*mNumVertices;
    }
  }

  Mesh::Upload();
}
Esempio n. 5
0
MeshPtr AssimpMeshIO::load(const std::string &mesh_name, const std::string &file_name, unsigned int mesh_index, const BufferManagerPtr &buffer_manager, const VaoManagerPtr &vao_manager)
{
	auto scene = importer->ReadFile(file_name, aiProcessPreset_TargetRealtime_MaxQuality | aiProcess_ConvertToLeftHanded);

	if (scene == nullptr)
	{
		throw clan::Exception(clan::string_format("Unable to locate (%1) for (%2)", file_name, mesh_name));
	}

	if (scene->mNumMeshes <= mesh_index)
		throw clan::Exception(clan::string_format("The mesh index (%1) was out of bounds for mesh %2.", mesh_index, mesh_name));

	auto scene_mesh = scene->mMeshes[mesh_index];

	unsigned int stride = 0;
	std::vector<Core::VertexAttribute> interleaved_spec;
	Core::VaoLayout vao_layout;
	Core::RenderCommand render_command;

	// vectors to store bounds position and size in.
	glm::vec3 mesh_bounds_pos = glm::vec3(0);
	glm::vec3 mesh_bounds_size = glm::vec3(0);

	if (scene_mesh->HasPositions() && scene_mesh->mNumVertices > 0)
	{
		mesh_bounds_pos = glm::vec3(scene_mesh->mVertices[0].x, scene_mesh->mVertices[0].y, scene_mesh->mVertices[0].z);
		mesh_bounds_size = mesh_bounds_pos;
	}

	//
	// Set up vertices
	//

	// Set up the interleaved vertex attributes
	if (scene_mesh->HasPositions())
	{
		stride += sizeof(glm::vec3);
		interleaved_spec.push_back(Core::VaoArg<glm::vec3>(Core::ShaderConstants::Position));
	}

	if (scene_mesh->HasNormals())
	{
		stride += sizeof(glm::vec3);
		interleaved_spec.push_back(Core::VaoArg<glm::vec3>(Core::ShaderConstants::Normal));
	}

	if (scene_mesh->HasTextureCoords(0))
	{
		stride += sizeof(glm::vec2);
		interleaved_spec.push_back(Core::VaoArg<glm::vec2>(Core::ShaderConstants::TexCoord));
	}

	if (scene_mesh->HasTangentsAndBitangents())
	{
		stride += 2*sizeof(glm::vec3);
		interleaved_spec.push_back(Core::VaoArg<glm::vec3>(Core::ShaderConstants::Tangent));
		interleaved_spec.push_back(Core::VaoArg<glm::vec3>(Core::ShaderConstants::Bitangent));
	}

	std::vector<float> vertices;
	vertices.reserve(scene_mesh->mNumVertices * stride / sizeof(float)); // This is how many floats we have

	for (unsigned int i = 0; i < scene_mesh->mNumVertices; i++) {
		// This may be made into a lambda for BuffeOperations::unsafe_upload
		if (scene_mesh->HasPositions())
		{
			auto position = scene_mesh->mVertices[i];
			vertices.push_back(position.x);
			vertices.push_back(position.y);
			vertices.push_back(position.z);

			// check for new min-max component in vertex, and update bounds.
			check_if_new_min_or_max_vertex(glm::vec3(position.x, position.y, position.z),
			                               mesh_bounds_pos, mesh_bounds_size);
		}

		if (scene_mesh->HasNormals())
		{
			auto normal = scene_mesh->mNormals[i];
			vertices.push_back(normal.x);
			vertices.push_back(normal.y);
			vertices.push_back(normal.z);
		}

		if (scene_mesh->HasTextureCoords(0))
		{
			auto texcoord = scene_mesh->mTextureCoords[0][i];
			vertices.push_back(texcoord.x);
			vertices.push_back(texcoord.y);
		}

		if (scene_mesh->HasTangentsAndBitangents())
		{
			auto tangent = scene_mesh->mTangents[i];
			vertices.push_back(tangent.x);
			vertices.push_back(tangent.y);
			vertices.push_back(tangent.z);

			auto bitangent = scene_mesh->mBitangents[i];
			vertices.push_back(bitangent.x);
			vertices.push_back(bitangent.y);
			vertices.push_back(bitangent.z);
		}
	}

	auto vertex_allocation = buffer_manager->allocate(vertices.size()*stride, stride);
	vertex_allocation.upload(vertices);

	vao_layout
		.for_buffer(vertex_allocation)
			.use_as(GL_ARRAY_BUFFER)
				.bind_interleaved(0, 0, interleaved_spec);
	
	render_command.set_draw_mode(GL_TRIANGLES);
	render_command.set_vertices(vertex_allocation, scene_mesh->mNumVertices, stride);

	//
	// Set up indices
	//

	if (scene_mesh->HasFaces()) {
		std::vector<unsigned int> indices;

		for (unsigned int i = 0; i < scene_mesh->mNumFaces; i++) {
			auto face = scene_mesh->mFaces[i];
			for (unsigned int j = 0; j < face.mNumIndices; j++) {
				indices.push_back(face.mIndices[j]);
			}
		}

		auto index_allocation = buffer_manager->allocate_and_upload(indices);

		vao_layout
			.for_buffer(index_allocation)
				.use_as(GL_ELEMENT_ARRAY_BUFFER);

		render_command.set_indices(index_allocation, indices);
	}

	// Ensure that mesh_bounds_size is the distance from mesh_bounds_pos to upper-right-back-corner of the bounds
	mesh_bounds_size = glm::vec3(
	                             mesh_bounds_size.x - mesh_bounds_pos.x,
	                             mesh_bounds_size.y - mesh_bounds_pos.y,
	                             mesh_bounds_size.z - mesh_bounds_pos.z
	                            );

	return std::make_shared<Mesh>(render_command, vao_layout, vao_manager, mesh_name, mesh_bounds_pos, mesh_bounds_size);
}