diff options
author | Benjamin Schmithüsen <UX3D-schmithuesen> | 2019-04-11 12:26:23 +0300 |
---|---|---|
committer | Brecht Van Lommel <brechtvanlommel@gmail.com> | 2019-04-11 13:04:53 +0300 |
commit | 4bad4bfc6ae5a81c44038cb1259f44befbb3afe0 (patch) | |
tree | 9b196d3ed0466406ba85441ea2314bedaeaea783 /extern/draco/dracoenc/src/draco/io/obj_encoder.cc | |
parent | a9d6356fee28d3567e956415a8eb08ab0bb7a0ab (diff) |
glTF: add Draco shared library for mesh compression.
Draco is added as a library under extern/ and builds a shared library that is
installed into the Python site-packages. This is then loaded by the glTF add-on
to do mesh compression.
Differential Revision: https://developer.blender.org/D4501
Diffstat (limited to 'extern/draco/dracoenc/src/draco/io/obj_encoder.cc')
-rw-r--r-- | extern/draco/dracoenc/src/draco/io/obj_encoder.cc | 314 |
1 files changed, 314 insertions, 0 deletions
diff --git a/extern/draco/dracoenc/src/draco/io/obj_encoder.cc b/extern/draco/dracoenc/src/draco/io/obj_encoder.cc new file mode 100644 index 00000000000..807506e3536 --- /dev/null +++ b/extern/draco/dracoenc/src/draco/io/obj_encoder.cc @@ -0,0 +1,314 @@ +// 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 "draco/io/obj_encoder.h" + +#include <fstream> + +#include "draco/metadata/geometry_metadata.h" + +namespace draco { + +ObjEncoder::ObjEncoder() + : pos_att_(nullptr), + tex_coord_att_(nullptr), + normal_att_(nullptr), + material_att_(nullptr), + sub_obj_att_(nullptr), + out_buffer_(nullptr), + in_point_cloud_(nullptr), + in_mesh_(nullptr), + current_sub_obj_id_(-1), + current_material_id_(-1) {} + +bool ObjEncoder::EncodeToFile(const PointCloud &pc, + const std::string &file_name) { + std::ofstream file(file_name); + if (!file) + return false; // File could not be opened. + file_name_ = file_name; + // Encode the mesh into a buffer. + EncoderBuffer buffer; + if (!EncodeToBuffer(pc, &buffer)) + return false; + // Write the buffer into the file. + file.write(buffer.data(), buffer.size()); + return true; +} + +bool ObjEncoder::EncodeToFile(const Mesh &mesh, const std::string &file_name) { + in_mesh_ = &mesh; + return EncodeToFile(static_cast<const PointCloud &>(mesh), file_name); +} + +bool ObjEncoder::EncodeToBuffer(const PointCloud &pc, + EncoderBuffer *out_buffer) { + in_point_cloud_ = &pc; + out_buffer_ = out_buffer; + if (!EncodeInternal()) + return ExitAndCleanup(false); + return ExitAndCleanup(true); +} + +bool ObjEncoder::EncodeToBuffer(const Mesh &mesh, EncoderBuffer *out_buffer) { + in_mesh_ = &mesh; + return EncodeToBuffer(static_cast<const PointCloud &>(mesh), out_buffer); +} + +bool ObjEncoder::EncodeInternal() { + pos_att_ = nullptr; + tex_coord_att_ = nullptr; + normal_att_ = nullptr; + material_att_ = nullptr; + sub_obj_att_ = nullptr; + current_sub_obj_id_ = -1; + current_material_id_ = -1; + if (!GetSubObjects()) + return false; + if (!EncodeMaterialFileName()) + return false; + if (!EncodePositions()) + return false; + if (!EncodeTextureCoordinates()) + return false; + if (!EncodeNormals()) + return false; + if (in_mesh_ && !EncodeFaces()) + return false; + return true; +} + +bool ObjEncoder::ExitAndCleanup(bool return_value) { + in_mesh_ = nullptr; + in_point_cloud_ = nullptr; + out_buffer_ = nullptr; + pos_att_ = nullptr; + tex_coord_att_ = nullptr; + normal_att_ = nullptr; + material_att_ = nullptr; + sub_obj_att_ = nullptr; + current_sub_obj_id_ = -1; + current_material_id_ = -1; + file_name_.clear(); + return return_value; +} + +bool ObjEncoder::GetSubObjects() { + const GeometryMetadata *pc_metadata = in_point_cloud_->GetMetadata(); + if (!pc_metadata) + return true; + const AttributeMetadata *sub_obj_metadata = + pc_metadata->GetAttributeMetadataByStringEntry("name", "sub_obj"); + if (!sub_obj_metadata) + return true; + sub_obj_id_to_name_.clear(); + for (const auto &entry : sub_obj_metadata->entries()) { + // Sub-object id must be int. + int value = 0; + if (!entry.second.GetValue(&value)) + continue; + sub_obj_id_to_name_[value] = entry.first; + } + sub_obj_att_ = in_point_cloud_->GetAttributeByUniqueId( + sub_obj_metadata->att_unique_id()); + if (sub_obj_att_ == nullptr || sub_obj_att_->size() == 0) + return false; + return true; +} + +bool ObjEncoder::EncodeMaterialFileName() { + const GeometryMetadata *pc_metadata = in_point_cloud_->GetMetadata(); + const AttributeMetadata *material_metadata = nullptr; + if (pc_metadata) { + material_metadata = + pc_metadata->GetAttributeMetadataByStringEntry("name", "material"); + } + std::string material_file_name; + std::string material_full_path; + if (!material_metadata) + return true; + if (!material_metadata->GetEntryString("file_name", &material_file_name)) + return false; + buffer()->Encode("mtllib ", 7); + buffer()->Encode(material_file_name.c_str(), material_file_name.size()); + buffer()->Encode("\n", 1); + material_id_to_name_.clear(); + for (const auto &entry : material_metadata->entries()) { + // Material id must be int. + int value = 0; + // Found entry that are not material id, e.g. file name as a string. + if (!entry.second.GetValue(&value)) + continue; + material_id_to_name_[value] = entry.first; + } + material_att_ = in_point_cloud_->GetAttributeByUniqueId( + material_metadata->att_unique_id()); + if (material_att_ == nullptr || material_att_->size() == 0) + return false; + return true; +} + +bool ObjEncoder::EncodePositions() { + const PointAttribute *const att = + in_point_cloud_->GetNamedAttribute(GeometryAttribute::POSITION); + if (att == nullptr || att->size() == 0) + return false; // Position attribute must be valid. + std::array<float, 3> value; + for (AttributeValueIndex i(0); i < static_cast<uint32_t>(att->size()); ++i) { + if (!att->ConvertValue<float, 3>(i, &value[0])) + return false; + buffer()->Encode("v ", 2); + EncodeFloatList(&value[0], 3); + buffer()->Encode("\n", 1); + } + pos_att_ = att; + return true; +} + +bool ObjEncoder::EncodeTextureCoordinates() { + const PointAttribute *const att = + in_point_cloud_->GetNamedAttribute(GeometryAttribute::TEX_COORD); + if (att == nullptr || att->size() == 0) + return true; // It's OK if we don't have texture coordinates. + std::array<float, 2> value; + for (AttributeValueIndex i(0); i < static_cast<uint32_t>(att->size()); ++i) { + if (!att->ConvertValue<float, 2>(i, &value[0])) + return false; + buffer()->Encode("vt ", 3); + EncodeFloatList(&value[0], 2); + buffer()->Encode("\n", 1); + } + tex_coord_att_ = att; + return true; +} + +bool ObjEncoder::EncodeNormals() { + const PointAttribute *const att = + in_point_cloud_->GetNamedAttribute(GeometryAttribute::NORMAL); + if (att == nullptr || att->size() == 0) + return true; // It's OK if we don't have normals. + std::array<float, 3> value; + for (AttributeValueIndex i(0); i < static_cast<uint32_t>(att->size()); ++i) { + if (!att->ConvertValue<float, 3>(i, &value[0])) + return false; + buffer()->Encode("vn ", 3); + EncodeFloatList(&value[0], 3); + buffer()->Encode("\n", 1); + } + normal_att_ = att; + return true; +} + +bool ObjEncoder::EncodeFaces() { + for (FaceIndex i(0); i < in_mesh_->num_faces(); ++i) { + if (sub_obj_att_) + if (!EncodeSubObject(i)) + return false; + if (material_att_) + if (!EncodeMaterial(i)) + return false; + buffer()->Encode('f'); + for (int j = 0; j < 3; ++j) { + if (!EncodeFaceCorner(i, j)) + return false; + } + buffer()->Encode("\n", 1); + } + return true; +} + +bool ObjEncoder::EncodeMaterial(FaceIndex face_id) { + int material_id = 0; + // Pick the first corner, all corners of a face should have same id. + const PointIndex vert_index = in_mesh_->face(face_id)[0]; + const AttributeValueIndex index_id(material_att_->mapped_index(vert_index)); + if (!material_att_->ConvertValue<int>(index_id, &material_id)) { + return false; + } + + if (material_id != current_material_id_) { + // Update material information. + buffer()->Encode("usemtl ", 7); + const auto mat_ptr = material_id_to_name_.find(material_id); + // If the material id is not found. + if (mat_ptr == material_id_to_name_.end()) + return false; + buffer()->Encode(mat_ptr->second.c_str(), mat_ptr->second.size()); + buffer()->Encode("\n", 1); + current_material_id_ = material_id; + } + return true; +} + +bool ObjEncoder::EncodeSubObject(FaceIndex face_id) { + int sub_obj_id = 0; + // Pick the first corner, all corners of a face should have same id. + const PointIndex vert_index = in_mesh_->face(face_id)[0]; + const AttributeValueIndex index_id(sub_obj_att_->mapped_index(vert_index)); + if (!sub_obj_att_->ConvertValue<int>(index_id, &sub_obj_id)) { + return false; + } + if (sub_obj_id != current_sub_obj_id_) { + buffer()->Encode("o ", 2); + const auto sub_obj_ptr = sub_obj_id_to_name_.find(sub_obj_id); + if (sub_obj_ptr == sub_obj_id_to_name_.end()) + return false; + buffer()->Encode(sub_obj_ptr->second.c_str(), sub_obj_ptr->second.size()); + buffer()->Encode("\n", 1); + current_sub_obj_id_ = sub_obj_id; + } + return true; +} + +bool ObjEncoder::EncodeFaceCorner(FaceIndex face_id, int local_corner_id) { + buffer()->Encode(' '); + const PointIndex vert_index = in_mesh_->face(face_id)[local_corner_id]; + // Note that in the OBJ format, all indices are encoded starting from index 1. + // Encode position index. + EncodeInt(pos_att_->mapped_index(vert_index).value() + 1); + if (tex_coord_att_ || normal_att_) { + // Encoding format is pos_index/tex_coord_index/normal_index. + // If tex_coords are not present, we must encode pos_index//normal_index. + buffer()->Encode('/'); + if (tex_coord_att_) { + EncodeInt(tex_coord_att_->mapped_index(vert_index).value() + 1); + } + if (normal_att_) { + buffer()->Encode('/'); + EncodeInt(normal_att_->mapped_index(vert_index).value() + 1); + } + } + return true; +} + +void ObjEncoder::EncodeFloat(float val) { + snprintf(num_buffer_, sizeof(num_buffer_), "%f", val); + buffer()->Encode(num_buffer_, strlen(num_buffer_)); +} + +void ObjEncoder::EncodeFloatList(float *vals, int num_vals) { + for (int i = 0; i < num_vals; ++i) { + if (i > 0) { + buffer()->Encode(' '); + } + EncodeFloat(vals[i]); + } +} + +void ObjEncoder::EncodeInt(int32_t val) { + snprintf(num_buffer_, sizeof(num_buffer_), "%d", val); + buffer()->Encode(num_buffer_, strlen(num_buffer_)); +} + +} // namespace draco |