draco/io/ply_decoder.cc
2016-12-12 16:39:06 -08:00

222 lines
7.8 KiB
C++

// Copyright 2016 The Draco Authors.
//
// 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.
//
#include "io/ply_decoder.h"
#include <fstream>
#include "core/macros.h"
#include "io/ply_property_reader.h"
namespace draco {
PlyDecoder::PlyDecoder() : out_mesh_(nullptr), out_point_cloud_(nullptr) {}
bool PlyDecoder::DecodeFromFile(const std::string &file_name, Mesh *out_mesh) {
out_mesh_ = out_mesh;
return DecodeFromFile(file_name, static_cast<PointCloud *>(out_mesh));
}
bool PlyDecoder::DecodeFromFile(const std::string &file_name,
PointCloud *out_point_cloud) {
std::ifstream file(file_name, std::ios::binary);
if (!file)
return false;
// Read the whole file into a buffer.
int64_t file_size = file.tellg();
file.seekg(0, std::ios::end);
file_size = file.tellg() - file_size;
if (file_size == 0)
return false;
file.seekg(0, std::ios::beg);
std::vector<char> data(file_size);
file.read(&data[0], file_size);
buffer_.Init(&data[0], file_size);
return DecodeFromBuffer(&buffer_, out_point_cloud);
}
bool PlyDecoder::DecodeFromBuffer(DecoderBuffer *buffer, Mesh *out_mesh) {
out_mesh_ = out_mesh;
return DecodeFromBuffer(buffer, static_cast<PointCloud *>(out_mesh));
}
bool PlyDecoder::DecodeFromBuffer(DecoderBuffer *buffer,
PointCloud *out_point_cloud) {
out_point_cloud_ = out_point_cloud;
buffer_.Init(buffer->data_head(), buffer->remaining_size());
return DecodeInternal();
}
bool PlyDecoder::DecodeInternal() {
PlyReader ply_reader;
if (!ply_reader.Read(buffer()))
return false;
// First, decode the connectivity data.
if (out_mesh_ && !DecodeFaceData(ply_reader.GetElementByName("face")))
return false;
// Decode all attributes.
if (!DecodeVertexData(ply_reader.GetElementByName("vertex")))
return false;
// In case there are no faces this is just a point cloud which does
// not require deduplication.
if (out_mesh_ && out_mesh_->num_faces() != 0) {
if (!out_point_cloud_->DeduplicateAttributeValues())
return false;
out_point_cloud_->DeduplicatePointIds();
}
return true;
}
bool PlyDecoder::DecodeFaceData(const PlyElement *face_element) {
// We accept point clouds now.
if (face_element == nullptr) {
return true;
}
const int64_t num_faces = face_element->num_entries();
out_mesh_->SetNumFaces(num_faces);
const PlyProperty *const vertex_indices =
face_element->GetPropertyByName("vertex_indices");
if (vertex_indices == nullptr || !vertex_indices->is_list()) {
return false; // No faces defined.
}
PlyPropertyReader<PointIndex::ValueType> vertex_index_reader(vertex_indices);
Mesh::Face face;
FaceIndex face_index(0);
for (int i = 0; i < num_faces; ++i) {
const int64_t list_offset = vertex_indices->GetListEntryOffset(i);
const int64_t list_size = vertex_indices->GetListEntryNumValues(i);
// TODO(ostava): Assume triangular faces only for now.
if (list_size != 3)
continue; // All non-triangular faces are skipped.
for (int64_t c = 0; c < 3; ++c)
face[c] = vertex_index_reader.ReadValue(list_offset + c);
out_mesh_->SetFace(face_index, face);
face_index++;
}
out_mesh_->SetNumFaces(face_index.value());
return true;
}
bool PlyDecoder::DecodeVertexData(const PlyElement *vertex_element) {
if (vertex_element == nullptr)
return false;
// TODO(ostava): For now, try to load x,y,z vertices and red,green,blue,aplha
// colors. We need to add other properties later.
const PlyProperty *const x_prop = vertex_element->GetPropertyByName("x");
const PlyProperty *const y_prop = vertex_element->GetPropertyByName("y");
const PlyProperty *const z_prop = vertex_element->GetPropertyByName("z");
if (!x_prop || !y_prop || !z_prop) {
// Currently, we require 3 vertex coordinates (this should be generalized
// later on).
return false;
}
const PointIndex::ValueType num_vertices = vertex_element->num_entries();
out_point_cloud_->set_num_points(num_vertices);
// Decode vertex positions.
{
// TODO(ostava): For now assume the position types are float32.
PlyPropertyReader<float> x_reader(x_prop);
PlyPropertyReader<float> y_reader(y_prop);
PlyPropertyReader<float> z_reader(z_prop);
GeometryAttribute va;
va.Init(GeometryAttribute::POSITION, nullptr, 3, DT_FLOAT32, false,
sizeof(float) * 3, 0);
const int att_id = out_point_cloud_->AddAttribute(va, true, num_vertices);
for (PointIndex::ValueType i = 0; i < num_vertices; ++i) {
std::array<float, 3> val;
val[0] = x_reader.ReadValue(i);
val[1] = y_reader.ReadValue(i);
val[2] = z_reader.ReadValue(i);
out_point_cloud_->attribute(att_id)->SetAttributeValue(
AttributeValueIndex(i), &val[0]);
}
}
// Decode color data if present.
int num_colors = 0;
const PlyProperty *const r_prop = vertex_element->GetPropertyByName("red");
const PlyProperty *const g_prop = vertex_element->GetPropertyByName("green");
const PlyProperty *const b_prop = vertex_element->GetPropertyByName("blue");
const PlyProperty *const a_prop = vertex_element->GetPropertyByName("alpha");
if (r_prop)
++num_colors;
if (g_prop)
++num_colors;
if (b_prop)
++num_colors;
if (a_prop)
++num_colors;
if (num_colors) {
std::vector<std::unique_ptr<PlyPropertyReader<uint8_t>>> color_readers;
const PlyProperty *p;
if (r_prop) {
p = r_prop;
// TODO(ostava): For now ensure the data type of all components is uint8.
DCHECK_EQ(true, p->data_type() == DT_UINT8);
if (p->data_type() != DT_UINT8)
return false;
color_readers.push_back(std::unique_ptr<PlyPropertyReader<uint8_t>>(
new PlyPropertyReader<uint8_t>(p)));
}
if (g_prop) {
p = g_prop;
// TODO(ostava): For now ensure the data type of all components is uint8.
DCHECK_EQ(true, p->data_type() == DT_UINT8);
if (p->data_type() != DT_UINT8)
return false;
color_readers.push_back(std::unique_ptr<PlyPropertyReader<uint8_t>>(
new PlyPropertyReader<uint8_t>(p)));
}
if (b_prop) {
p = b_prop;
// TODO(ostava): For now ensure the data type of all components is uint8.
DCHECK_EQ(true, p->data_type() == DT_UINT8);
if (p->data_type() != DT_UINT8)
return false;
color_readers.push_back(std::unique_ptr<PlyPropertyReader<uint8_t>>(
new PlyPropertyReader<uint8_t>(p)));
}
if (a_prop) {
p = a_prop;
// TODO(ostava): For now ensure the data type of all components is uint8.
DCHECK_EQ(true, p->data_type() == DT_UINT8);
if (p->data_type() != DT_UINT8)
return false;
color_readers.push_back(std::unique_ptr<PlyPropertyReader<uint8_t>>(
new PlyPropertyReader<uint8_t>(p)));
}
GeometryAttribute va;
va.Init(GeometryAttribute::COLOR, nullptr, num_colors, DT_UINT8, true,
sizeof(uint8_t) * num_colors, 0);
const int32_t att_id =
out_point_cloud_->AddAttribute(va, true, num_vertices);
for (PointIndex::ValueType i = 0; i < num_vertices; ++i) {
std::array<uint8_t, 4> val;
for (int j = 0; j < num_colors; j++) {
val[j] = color_readers[j]->ReadValue(i);
}
out_point_cloud_->attribute(att_id)->SetAttributeValue(
AttributeValueIndex(i), &val[0]);
}
}
return true;
}
} // namespace draco