mirror of
https://git.mirrors.martin98.com/https://github.com/google/draco
synced 2025-07-30 23:42:00 +08:00
302 lines
9.4 KiB
C++
302 lines
9.4 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_reader.h"
|
|
|
|
#include <array>
|
|
#include <regex>
|
|
|
|
#include "io/parser_utils.h"
|
|
#include "io/ply_property_writer.h"
|
|
|
|
namespace draco {
|
|
|
|
PlyProperty::PlyProperty(const std::string &name, DataType data_type,
|
|
DataType list_type)
|
|
: name_(name), data_type_(data_type), list_data_type_(list_type) {
|
|
data_type_num_bytes_ = DataTypeLength(data_type);
|
|
list_data_type_num_bytes_ = DataTypeLength(list_type);
|
|
}
|
|
|
|
PlyElement::PlyElement(const std::string &name, int64_t num_entries)
|
|
: name_(name), num_entries_(num_entries) {}
|
|
|
|
PlyReader::PlyReader() : format_(kLittleEndian) {}
|
|
|
|
bool PlyReader::Read(DecoderBuffer *buffer) {
|
|
error_message_.clear();
|
|
std::string value;
|
|
// The first line needs to by "ply".
|
|
if (!parser::ParseString(buffer, &value) || value != "ply") {
|
|
error_message_ = "Not a valid ply file.";
|
|
return false;
|
|
}
|
|
parser::SkipLine(buffer);
|
|
|
|
// The second line needs to be the format of the ply file.
|
|
parser::ParseLine(buffer, &value);
|
|
std::string format, version;
|
|
const std::vector<std::string> words = SplitWords(value);
|
|
if (words.size() >= 3 && words[0] == "format") {
|
|
format = words[1];
|
|
version = words[2];
|
|
} else {
|
|
error_message_ = "Missing or wrong format line.";
|
|
return false;
|
|
}
|
|
if (version != "1.0") {
|
|
error_message_ = "Unsupported PLY version.";
|
|
return false; // Wrong version.
|
|
}
|
|
if (format == "binary_big_endian") {
|
|
error_message_ =
|
|
"Unsupported format. Currently we support only ascii and"
|
|
" binary_little_endian format.";
|
|
return false;
|
|
}
|
|
if (format == "ascii") {
|
|
format_ = kAscii;
|
|
} else {
|
|
format_ = kLittleEndian;
|
|
}
|
|
if (!ParseHeader(buffer))
|
|
return false;
|
|
if (!ParsePropertiesData(buffer))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
bool PlyReader::ParseHeader(DecoderBuffer *buffer) {
|
|
while (error_message_.length() == 0 && !ParseEndHeader(buffer)) {
|
|
if (ParseElement(buffer))
|
|
continue;
|
|
if (ParseProperty(buffer))
|
|
continue;
|
|
parser::SkipLine(buffer);
|
|
}
|
|
if (error_message_.length() > 0) {
|
|
printf("ERROR %s\n", error_message_.c_str());
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PlyReader::ParseEndHeader(DecoderBuffer *buffer) {
|
|
parser::SkipWhitespace(buffer);
|
|
std::array<char, 10> c;
|
|
if (!buffer->Peek(&c)) {
|
|
error_message_ = "End of file reached before the end_header.";
|
|
return false;
|
|
}
|
|
if (std::memcmp(&c[0], "end_header", 10) != 0)
|
|
return false;
|
|
parser::SkipLine(buffer);
|
|
return true;
|
|
}
|
|
|
|
bool PlyReader::ParseElement(DecoderBuffer *buffer) {
|
|
DecoderBuffer line_buffer(*buffer);
|
|
std::string line;
|
|
parser::ParseLine(&line_buffer, &line);
|
|
|
|
std::string element_name;
|
|
int64_t count;
|
|
const std::vector<std::string> words = SplitWords(line);
|
|
if (words.size() >= 3 && words[0] == "element") {
|
|
element_name = words[1];
|
|
const std::string count_str = words[2];
|
|
count = strtoll(count_str.c_str(), NULL, 10);
|
|
} else {
|
|
return false;
|
|
}
|
|
element_index_[element_name] = elements_.size();
|
|
elements_.emplace_back(PlyElement(element_name, count));
|
|
*buffer = line_buffer;
|
|
return true;
|
|
}
|
|
|
|
bool PlyReader::ParseProperty(DecoderBuffer *buffer) {
|
|
if (elements_.empty())
|
|
return false; // Ignore properties if there is no active element.
|
|
DecoderBuffer line_buffer(*buffer);
|
|
std::string line;
|
|
parser::ParseLine(&line_buffer, &line);
|
|
|
|
std::string data_type_str, list_type_str, property_name;
|
|
bool property_search = false;
|
|
const std::vector<std::string> words = SplitWords(line);
|
|
if (words.size() >= 3 && words[0] == "property" && words[1] != "list") {
|
|
property_search = true;
|
|
data_type_str = words[1];
|
|
property_name = words[2];
|
|
}
|
|
|
|
bool property_list_search = false;
|
|
if (words.size() >= 5 && words[0] == "property" && words[1] == "list") {
|
|
property_list_search = true;
|
|
list_type_str = words[2];
|
|
data_type_str = words[3];
|
|
property_name = words[4];
|
|
}
|
|
if (!property_search && !property_list_search) {
|
|
return false;
|
|
}
|
|
const DataType data_type = GetDataTypeFromString(data_type_str);
|
|
if (data_type == DT_INVALID) {
|
|
error_message_ = "Wrong property data type.";
|
|
return true; // Parsed.
|
|
}
|
|
DataType list_type = DT_INVALID;
|
|
if (property_list_search) {
|
|
list_type = GetDataTypeFromString(list_type_str);
|
|
if (list_type == DT_INVALID) {
|
|
error_message_ = "Wrong property list type.";
|
|
return true; // Parsed.
|
|
}
|
|
}
|
|
elements_.back().AddProperty(
|
|
PlyProperty(property_name, data_type, list_type));
|
|
*buffer = line_buffer;
|
|
return true;
|
|
}
|
|
|
|
bool PlyReader::ParsePropertiesData(DecoderBuffer *buffer) {
|
|
for (int i = 0; i < static_cast<int>(elements_.size()); ++i) {
|
|
if (format_ == kLittleEndian) {
|
|
if (!ParseElementData(buffer, i)) {
|
|
return false;
|
|
}
|
|
} else if (format_ == kAscii) {
|
|
if (!ParseElementDataAscii(buffer, i)) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PlyReader::ParseElementData(DecoderBuffer *buffer, int element_index) {
|
|
PlyElement &element = elements_[element_index];
|
|
for (int entry = 0; entry < element.num_entries(); ++entry) {
|
|
for (int i = 0; i < element.num_properties(); ++i) {
|
|
PlyProperty &prop = element.property(i);
|
|
if (prop.is_list()) {
|
|
// Parse the number of entries for the list element.
|
|
int64_t num_entries = 0;
|
|
buffer->Decode(&num_entries, prop.list_data_type_num_bytes());
|
|
// Store offset to the main data entry.
|
|
prop.list_data_.push_back(prop.data_.size() /
|
|
prop.data_type_num_bytes_);
|
|
// Store the number of entries.
|
|
prop.list_data_.push_back(num_entries);
|
|
// Read and store the actual property data
|
|
const int64_t num_bytes_to_read =
|
|
prop.data_type_num_bytes() * num_entries;
|
|
prop.data_.insert(prop.data_.end(), buffer->data_head(),
|
|
buffer->data_head() + num_bytes_to_read);
|
|
buffer->Advance(num_bytes_to_read);
|
|
} else {
|
|
// Non-list property
|
|
prop.data_.insert(prop.data_.end(), buffer->data_head(),
|
|
buffer->data_head() + prop.data_type_num_bytes());
|
|
buffer->Advance(prop.data_type_num_bytes());
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PlyReader::ParseElementDataAscii(DecoderBuffer *buffer,
|
|
int element_index) {
|
|
PlyElement &element = elements_[element_index];
|
|
for (int entry = 0; entry < element.num_entries(); ++entry) {
|
|
for (int i = 0; i < element.num_properties(); ++i) {
|
|
PlyProperty &prop = element.property(i);
|
|
PlyPropertyWriter<double> prop_writer(&prop);
|
|
int32_t num_entries = 1;
|
|
if (prop.is_list()) {
|
|
parser::SkipWhitespace(buffer);
|
|
// Parse the number of entries for the list element.
|
|
if (!parser::ParseSignedInt(buffer, &num_entries))
|
|
return false;
|
|
|
|
// Store offset to the main data entry.
|
|
prop.list_data_.push_back(prop.data_.size() /
|
|
prop.data_type_num_bytes_);
|
|
// Store the number of entries.
|
|
prop.list_data_.push_back(num_entries);
|
|
}
|
|
// Read and store the actual property data.
|
|
for (int v = 0; v < num_entries; ++v) {
|
|
parser::SkipWhitespace(buffer);
|
|
if (prop.data_type() == DT_FLOAT32 || prop.data_type() == DT_FLOAT64) {
|
|
float val;
|
|
if (!parser::ParseFloat(buffer, &val))
|
|
return false;
|
|
prop_writer.PushBackValue(val);
|
|
} else {
|
|
int32_t val;
|
|
if (!parser::ParseSignedInt(buffer, &val))
|
|
return false;
|
|
prop_writer.PushBackValue(val);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::vector<std::string> PlyReader::SplitWords(const std::string &line) {
|
|
std::vector<std::string> output;
|
|
std::string::size_type start = 0;
|
|
std::string::size_type end = 0;
|
|
|
|
// Check for isspace chars.
|
|
while ((end = line.find_first_of(" \t\n\v\f\r", start)) !=
|
|
std::string::npos) {
|
|
const std::string word(line.substr(start, end - start));
|
|
if (!std::all_of(word.begin(), word.end(), isspace))
|
|
output.push_back(word);
|
|
start = end + 1;
|
|
}
|
|
|
|
const std::string last_word(line.substr(start));
|
|
if (!std::all_of(last_word.begin(), last_word.end(), isspace))
|
|
output.push_back(last_word);
|
|
return output;
|
|
}
|
|
|
|
DataType PlyReader::GetDataTypeFromString(const std::string &name) const {
|
|
if (name == "char" || name == "int8")
|
|
return DT_INT8;
|
|
if (name == "uchar" || name == "uint8")
|
|
return DT_UINT8;
|
|
if (name == "short" || name == "int16")
|
|
return DT_INT16;
|
|
if (name == "ushort" || name == "uint16")
|
|
return DT_UINT16;
|
|
if (name == "int" || name == "int32")
|
|
return DT_INT32;
|
|
if (name == "uint" || name == "uint32")
|
|
return DT_UINT32;
|
|
if (name == "float" || name == "float32")
|
|
return DT_FLOAT32;
|
|
if (name == "double" || name == "float64") {
|
|
return DT_FLOAT64;
|
|
}
|
|
return DT_INVALID;
|
|
}
|
|
|
|
} // namespace draco
|