目录
1.GLTF::Primitive* primitive里面有什么属性?
2.primitive->extensions.find("KHR_draco_mesh_compression")什么意思?
6.1 darco_tests/ply_reader_test.cc
6.2 darco_tests/ply_decoder_test.cc
6.3 darco_tests/obj_encoder_test.cc
1.GLTF::Primitive* primitive里面有什么属性?
Draco压缩是跟GLTF::Primitive* primitive息息相关的。
下面是 `GLTF::Primitive` 中常见的属性:
1. `GLTF::Accessor* indices`:表示索引数据的访问器,用于指定网格的顶点索引顺序。这决定了网格如何连接形成三角形或其他图元。
2. `std::map<std::string, GLTF::Accessor*> attributes`:表示顶点属性的访问器,用于指定网格的顶点数据。这些属性可以包括位置、法线、纹理坐标、颜色等。
3. `GLTF::Material* material`:表示网格使用的材质。材质定义了渲染网格时使用的表面属性,如颜色、纹理、透明度等。
4. `std::string mode`:表示绘制网格的模式。常见的模式包括三角形列表(TRIANGLES)、线段列表(LINES)、点列表(POINTS)等。
5. `std::vector<GLTF::AttributeSemantic> attributes`:表示顶点属性的语义信息。语义信息描述了每个顶点属性的含义,如POSITION、NORMAL、TEXCOORD 等。
6. `std::vector<GLTF::MorphTarget*> targets`:表示网格的形态目标(morph target)。形态目标允许对网格进行动态变形,如脸部表情、形状调整等。
这些属性提供了描述网格几何和绘制方式所需的信息。通过访问这些属性,你可以获取和设置网格的索引数据、顶点数据、材质信息以及其他与网格相关的属性。具体的使用方法可以根据你所使用的 GLTF 库或工具进行适当的调整。
2.primitive->extensions.find("KHR_draco_mesh_compression")什么意思?
这行代码是在 `primitive` 的扩展属性中查找名为 "KHR_draco_mesh_compression" 的扩展。
在 GLTF 中,扩展属性允许向基本的 JSON 数据结构添加额外的自定义或扩展信息。通过扩展,可以实现一些非标准的功能或附加的元数据。
在这个例子中,`primitive->extensions` 是一个表示扩展属性的映射(map),其中包含了该 `primitive` 所支持的各种扩展。通过调用 `find()` 方法并提供扩展名称 "KHR_draco_mesh_compression" 作为参数,可以检查该扩展是否存在于 `primitive` 的扩展属性中。
返回值是一个迭代器,指向表示扩展属性的键值对。通过检查迭代器是否等于 `primitive->extensions.end()`,可以确定是否找到了指定的扩展。如果迭代器等于 `primitive->extensions.end()`,则表示未找到该扩展;否则,表示找到了该扩展。
该行代码的目的是检查 `primitive` 是否包含名为 "KHR_draco_mesh_compression" 的扩展,以确定是否对该网格应用了 Draco 压缩。
3.Draco下载
draco/BUILDING.md at master · google/draco · GitHub
拉取下来的时候一般没有安装第三方包,需要额外下载third_party中的内容。我编译好的已经上传到资源了,有需要可以下载。
4.Draco编译与测试
我就在编译这边吃亏了,公司的是Realse模式,x64位。我编译错了,导致一直error link。
最后得到的一个是编译得到的draco.lib。还有一个就是src/draco这个文件夹
问题1:
(1)看似缺少python,我先安装下图的64-bit-3.8.8
(1条消息) 超详细的Python安装和环境搭建教程_python安装教程_Pymili的博客-CSDN博客
(2)然后就用cmake编译,都是默认操作
输入、输出、configure
这里会出现几个value选项,记得加上DRACO_TESTS和DRACP_TRANSCOODER_SUPPORTED
随后重新configure和generate
(3)生成release解决方案
(4)draco_encoder测试与分析
cloudcompare下载链接:http://www.cloudcompare.org/release/CloudCompare_v2.12.4_setup_x64.exe)
bun_zipper.ply用cloudcompare打开
内部txt打开,一般只需要关注顶点属性x,y,z即可,至少我们公司是如此
draco压缩后的out1.drc
压缩前3066kb,压缩后66kb,压缩效率很高。
github官网的代码,这个-qp 14是对顶点位置的压缩,默认压缩率是11对原先没有任何影响,14压缩率更好,但是会有略微的影响质量。
./draco_encoder -i testdata/bun_zipper.ply -o out.drc -qp 14
github官网的代码,这个-cl 8是设置压缩能力,默认为7。最高设置 10 将具有最大的压缩能力,但解压缩速度最差。0 将具有最小的压缩,但最好的解压缩速度。默认设置为 7。
./draco_encoder -i testdata/bun_zipper.ply -o out.drc -cl 8
(4)draco_decoder测试与分析
简单展示,具体查看文档
(5)draco_transcoder
对标是官网的代码
./draco_transcoder -i in.glb -o out.glb
自己也用Box.glb进行了测试,可以正常转化
(6)c++ decoder api
用c++来处理读取经过draco压缩的数据,代码很简洁。一个将dracoMeshBuffer转换为meshBuffer,一个是将dracoPointCloudBuffer转换为pointCloud。
draco::DecoderBuffer buffer;
buffer.Init(data.data(), data.size());
const draco::EncodedGeometryType geom_type =
draco::GetEncodedGeometryType(&buffer);
if (geom_type == draco::TRIANGULAR_MESH) {
unique_ptr<draco::Mesh> mesh = draco::DecodeMeshFromBuffer(&buffer);
} else if (geom_type == draco::POINT_CLOUD) {
unique_ptr<draco::PointCloud> pc = draco::DecodePointCloudFromBuffer(&buffer);
}
实战:
#include "draco/compression/encode.h"
#include <cinttypes>
#include <fstream>
#include <sstream>
#include "draco/attributes/attribute_quantization_transform.h"
#include "draco/compression/config/compression_shared.h"
#include "draco/compression/decode.h"
#include "draco/compression/expert_encode.h"
#include "draco/core/vector_d.h"
#include "draco/io/file_utils.h"
#include "draco/io/obj_decoder.h"
#include "draco/mesh/triangle_soup_mesh_builder.h"
#include "draco/point_cloud/point_cloud_builder.h"
#include "draco/io/point_cloud_io.h" //to encode to buffer
int main(int argc, char** argv) {
draco::TriangleSoupMeshBuilder mesh_builder;
int kNumPoints = 20000;
// Create a simple mesh with one face.
mesh_builder.Start(kNumPoints);
// Initialize the attribute values.
const int32_t pos_att_id = mesh_builder.AddAttribute(
draco::GeometryAttribute::POSITION, 3, draco::DT_FLOAT32);
/*const int32_t tex_att_id_0 = mesh_builder.AddAttribute(
draco::GeometryAttribute::TEX_COORD, 2, draco::DT_FLOAT32);
const int32_t tex_att_id_1 = mesh_builder.AddAttribute(
draco::GeometryAttribute::TEX_COORD, 2, draco::DT_FLOAT32);*/
// Initialize the attribute values.
for (int i=0; i < kNumPoints; ++i) {
mesh_builder.SetAttributeValuesForFace(
pos_att_id, draco::FaceIndex(i), draco::Vector3f(0.11111f+i, 0.f + i, 0.223323f + i).data(),
draco::Vector3f(1.1111f + i, 0.f, 0.f).data(),
draco::Vector3f(1.232323f, 1.4343433f + i, 0.f+i).data());
/* mesh_builder.SetAttributeValuesForFace(
tex_att_id_0, draco::FaceIndex(i), draco::Vector2f(0.f, 0.f).data(),
draco::Vector2f(1.f, 0.f).data(), draco::Vector2f(1.f, 1.f).data());
mesh_builder.SetAttributeValuesForFace(
tex_att_id_1, draco::FaceIndex(i), draco::Vector2f(0.f, 0.f).data(),
draco::Vector2f(1.f, 0.f).data(), draco::Vector2f(1.f, 1.f).data());*/
}
std::unique_ptr<draco::PointCloud> geometry = mesh_builder.Finalize();
std::cout << "Number of points in the cloud: " << geometry->num_points();
// Sequential encoding allows to use unquantized attributes in the decoder.
/* Quantization converts floating point attributes into integer attributes (lossy operation) that are then compressed without loss.
If integer attributes are provided, Draco treats them as pre-quantized.*/
int32_t encoding_method = draco::POINT_CLOUD_SEQUENTIAL_ENCODING;
draco::Encoder encoder;
encoder.SetAttributeQuantization(draco::GeometryAttribute::POSITION, 7);
// encoder.SetEncodingMethod(encoding_method);
encoder.SetTrackEncodedProperties(true);
encoder.SetSpeedOptions(4, 4); // speed 0 gets best compression, speed 10 is fastest
draco::EncoderBuffer buffer;
draco::Status status = encoder.EncodePointCloudToBuffer(*geometry, &buffer);
if (status.ok()) {
std::cout << " - Encoded points: " << encoder.num_encoded_points();
}
else {
std::cout << "Error coding the point cloud.";
}
std::cout << " buffer.data " << buffer.data() << "buffer.size" << buffer.size();
//Save Encoder Buffer in file
std::string output_file_path = "compressed.drc";//保存压缩后的点云文件
std::ofstream outfile(output_file_path, std::ofstream::binary);
outfile.write(buffer.data(), buffer.size());
outfile.close();
std::cout << "Buffer saved in file: " << output_file_path << std::endl;
// Create a DecoderBuffer and copy coded data
draco::DecoderBuffer decoder_buffer;
decoder_buffer.Init(buffer.data(), buffer.size());
// Create a PointCloud for the decoded data
std::unique_ptr<draco::PointCloud> dec_point_cloud;
// Decode the data from DecoderBuffer to point_cloud
draco::Decoder decoder;
// Decode the input data into a geometry.
dec_point_cloud = decoder.DecodePointCloudFromBuffer(&decoder_buffer).value();
// 打开一个文件以写入PLY数据
//std::ofstream ply_file("output.ply", std::ios::out | std::ios::binary);
//if (!ply_file.is_open()) {
// std::cerr << "Failed to open the output file." << std::endl;
// return 1;
//}
写入PLY的头部
//ply_file << "ply\n";
//ply_file << "format ascii 1.0\n";
//ply_file << "element vertex " << dec_point_cloud->num_points() << "\n";
//ply_file << "property float x\n";
//ply_file << "property float y\n";
//ply_file << "property float z\n";
//ply_file << "end_header\n";
写入点云数据
//const auto& pos_att =*dec_point_cloud->GetNamedAttribute(draco::GeometryAttribute::POSITION);
//for (draco::PointIndex i(0); i < dec_point_cloud->num_points(); ++i) {
// const draco::AttributeValueIndex val_index = pos_att.mapped_index(i);
// float pos[3];
// pos_att.ConvertValue<float, 3>(val_index, pos);
// ply_file << pos[0] << " " << pos[1] << " " << pos[2] << "\n";
//}
关闭文件
//ply_file.close();
// 打开一个文件以写入PLY数据
std::ofstream ply_file("output.ply", std::ios::out | std::ios::binary);
if (!ply_file.is_open()) {
std::cerr << "Failed to open the output file." << std::endl;
return 1;
}
// 写入PLY的头部
ply_file << "ply\n";
ply_file << "format binary_little_endian 1.0\n"; // 使用小端字节序
ply_file << "element vertex " << dec_point_cloud->num_points() << "\n";
ply_file << "property float x\n";
ply_file << "property float y\n";
ply_file << "property float z\n";
ply_file << "end_header\n";
// 写入点云数据
const auto& pos_att = *dec_point_cloud->GetNamedAttribute(draco::GeometryAttribute::POSITION);
for (draco::PointIndex i(0); i < dec_point_cloud->num_points(); ++i) {
const draco::AttributeValueIndex val_index = pos_att.mapped_index(i);
float pos[3];
pos_att.ConvertValue<float, 3>(val_index, pos);
// 使用二进制写入数据
ply_file.write(reinterpret_cast<const char*>(&pos), sizeof(pos));
}
// 关闭文件
ply_file.close();
return 0;
}
初步编译少include包
增加包目录,第二个。
编译阶段通过,接下来链接阶段继续报错
添加lib进工程
链接成功!
5.点云实战
#include "draco/compression/encode.h"
#include <cinttypes>
#include <fstream>
#include <sstream>
#include "draco/attributes/attribute_quantization_transform.h"
#include "draco/compression/config/compression_shared.h"
#include "draco/compression/decode.h"
#include "draco/compression/expert_encode.h"
#include "draco/core/vector_d.h"
#include "draco/io/file_utils.h"
#include "draco/io/obj_decoder.h"
#include "draco/mesh/triangle_soup_mesh_builder.h"
#include "draco/point_cloud/point_cloud_builder.h"
#include "draco/io/point_cloud_io.h" //to encode to buffer
int main(int argc, char** argv) {
srand((unsigned)time(NULL));
draco::TriangleSoupMeshBuilder mesh_builder;
int kNumPoints = 20000;
// Create a simple mesh with one face.
mesh_builder.Start(kNumPoints);
// Initialize the attribute values.
const int32_t pos_att_id = mesh_builder.AddAttribute(
draco::GeometryAttribute::POSITION, 3, draco::DT_FLOAT32);
/*const int32_t tex_att_id_0 = mesh_builder.AddAttribute(
draco::GeometryAttribute::TEX_COORD, 2, draco::DT_FLOAT32);
const int32_t tex_att_id_1 = mesh_builder.AddAttribute(
draco::GeometryAttribute::TEX_COORD, 2, draco::DT_FLOAT32);*/
// Initialize the attribute values.
for (int i=0; i < kNumPoints; ++i) {
mesh_builder.SetAttributeValuesForFace(
pos_att_id, draco::FaceIndex(i), draco::Vector3f(0.f+ (rand() % 10000), 0.f + (rand() % 10000), 0.f + (rand() % 10000)).data(),
draco::Vector3f(0.f + (rand() % 10000), 0.f + (rand() % 10000), 0.f + (rand() % 10000)).data(),
draco::Vector3f(0.f + (rand() % 10000), 0.f + (rand() % 10000), 0.f + (rand() % 10000)).data());
/* mesh_builder.SetAttributeValuesForFace(
tex_att_id_0, draco::FaceIndex(i), draco::Vector2f(0.f, 0.f).data(),
draco::Vector2f(1.f, 0.f).data(), draco::Vector2f(1.f, 1.f).data());
mesh_builder.SetAttributeValuesForFace(
tex_att_id_1, draco::FaceIndex(i), draco::Vector2f(0.f, 0.f).data(),
draco::Vector2f(1.f, 0.f).data(), draco::Vector2f(1.f, 1.f).data());*/
}
std::unique_ptr<draco::PointCloud> geometry = mesh_builder.Finalize();
std::cout << "Number of points in the cloud: " << geometry->num_points();
// Sequential encoding allows to use unquantized attributes in the decoder.
/* Quantization converts floating point attributes into integer attributes (lossy operation) that are then compressed without loss.
If integer attributes are provided, Draco treats them as pre-quantized.*/
int32_t encoding_method = draco::POINT_CLOUD_SEQUENTIAL_ENCODING;
draco::Encoder encoder;
encoder.SetAttributeQuantization(draco::GeometryAttribute::POSITION,11);
encoder.SetEncodingMethod(encoding_method);
encoder.SetTrackEncodedProperties(true);
encoder.SetSpeedOptions(5, 5); // speed 0 gets best compression, speed 10 is fastest
draco::EncoderBuffer buffer;
draco::Status status = encoder.EncodePointCloudToBuffer(*geometry, &buffer);
if (status.ok()) {
std::cout << " - Encoded points: " << encoder.num_encoded_points();
}
else {
std::cout << "Error coding the point cloud.";
}
std::cout << " buffer.data " << buffer.data() << "buffer.size" << buffer.size();
//Save Encoder Buffer in file
std::string output_file_path = "compressed.drc";//保存压缩后的点云文件
std::ofstream outfile(output_file_path, std::ofstream::binary);
outfile.write(buffer.data(), buffer.size());
outfile.close();
std::cout << "Buffer saved in file: " << output_file_path << std::endl;
// Create a DecoderBuffer and copy coded data
draco::DecoderBuffer decoder_buffer;
decoder_buffer.Init(buffer.data(), buffer.size());
// Create a PointCloud for the decoded data
std::unique_ptr<draco::PointCloud> dec_point_cloud;
// Decode the data from DecoderBuffer to point_cloud
draco::Decoder decoder;
// Decode the input data into a geometry.
dec_point_cloud = decoder.DecodePointCloudFromBuffer(&decoder_buffer).value();
// 打开一个文件以写入PLY数据
std::ofstream ply_file("output.ply", std::ios::out | std::ios::binary);
if (!ply_file.is_open()) {
std::cerr << "Failed to open the output file." << std::endl;
return 1;
}
// 写入PLY的头部
ply_file << "ply\n";
ply_file << "format binary_little_endian 1.0\n"; // 使用小端字节序
ply_file << "element vertex " << dec_point_cloud->num_points() << "\n";
ply_file << "property float x\n";
ply_file << "property float y\n";
ply_file << "property float z\n";
ply_file << "end_header\n";
// 写入点云数据
const auto& pos_att = *dec_point_cloud->GetNamedAttribute(draco::GeometryAttribute::POSITION);
for (draco::PointIndex i(0); i < dec_point_cloud->num_points(); ++i) {
const draco::AttributeValueIndex val_index = pos_att.mapped_index(i);
float pos[3];
pos_att.ConvertValue<float, 3>(val_index, pos);
// 使用二进制写入数据
ply_file.write(reinterpret_cast<const char*>(&pos), sizeof(pos));
}
// 关闭文件
ply_file.close();
return 0;
}
未压缩的:
压缩的:
压缩率选的不高,704kb和224kb
6、draco源码解析
6.1 darco_tests/ply_reader_test.cc
运行参数
--gtest_filter=PlyReaderTest.TestReader
TEST_F(PlyReaderTest, TestReader) {
const std::string file_name = "test_pos_color.ply";
const std::vector<char> data = ReadPlyFile(file_name);
DecoderBuffer buf;
buf.Init(data.data(), data.size());
PlyReader reader;
Status status = reader.Read(&buf);
DRACO_ASSERT_OK(status);
ASSERT_EQ(reader.num_elements(), 2);
ASSERT_EQ(reader.element(0).num_properties(), 7);
ASSERT_EQ(reader.element(1).num_properties(), 1);
ASSERT_TRUE(reader.element(1).property(0).is_list());
ASSERT_TRUE(reader.element(0).GetPropertyByName("red") != nullptr);
const PlyProperty *const prop = reader.element(0).GetPropertyByName("red");
PlyPropertyReader<uint8_t> reader_uint8(prop);
PlyPropertyReader<uint32_t> reader_uint32(prop);
PlyPropertyReader<float> reader_float(prop);
for (int i = 0; i < reader.element(0).num_entries(); ++i) {
ASSERT_EQ(reader_uint8.ReadValue(i), reader_uint32.ReadValue(i));
ASSERT_EQ(reader_uint8.ReadValue(i), reader_float.ReadValue(i));
}
}
值得注意的是test_pos_color.ply是一个"binary_little_endian" 类型的ply的文件,也就是经过了draco压缩了。
PLY 文件格式用于存储三维几何数据。它可以采用两种不同的表示法:ASCII 和 binary。ASCII 格式是人类可读的,而 binary 格式则是计算机可读的,通常占用更少的磁盘空间。
binary分为"binary_little_endian"和"binary_big_endian"
这边首先读取ply得到char类型的容器,然后使用Read解码这个数据,我们分析一下这个Read函数
Status PlyReader::Read(DecoderBuffer *buffer) {
std::string value;
// The first line needs to by "ply".
if (!parser::ParseString(buffer, &value) || value != "ply") {
return Status(Status::INVALID_PARAMETER, "Not a valid ply file");
}
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 {
return Status(Status::INVALID_PARAMETER, "Missing or wrong format line");
}
if (version != "1.0") {
return Status(Status::UNSUPPORTED_VERSION, "Unsupported PLY version");
}
if (format == "binary_big_endian") {
return Status(Status::UNSUPPORTED_VERSION,
"Unsupported format. Currently we support only ascii and"
" binary_little_endian format.");
}
if (format == "ascii") {
format_ = kAscii;
} else {
format_ = kLittleEndian;
}
DRACO_RETURN_IF_ERROR(ParseHeader(buffer));
if (!ParsePropertiesData(buffer)) {
return Status(Status::INVALID_PARAMETER, "Couldn't parse properties");
}
return OkStatus();
}
读取的ply文件格式大概是如下:
至于当前.cc文件其他方法我已经探索过了,不是很重要,就不写了
6.2 darco_tests/ply_decoder_test.cc
运行参数
--gtest_filter=PlyDecoderTest.TestPlyDecoding
// 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/ply_decoder.h"
#include "draco/core/draco_test_base.h"
#include "draco/core/draco_test_utils.h"
namespace draco {
class PlyDecoderTest : public ::testing::Test {
protected:
template <class Geometry>
std::unique_ptr<Geometry> DecodePly(const std::string &file_name) const {
const std::string path = GetTestFileFullPath(file_name);
PlyDecoder decoder;
std::unique_ptr<Geometry> geometry(new Geometry());
Status status = decoder.DecodeFromFile(path, geometry.get());
if (!status.ok()) {
LOG(ERROR) << "Failed to decode " << file_name << ": " << status;
return nullptr;
}
return geometry;
}
void test_decoding(const std::string &file_name, int num_faces,
uint32_t num_points, std::unique_ptr<Mesh> *out_mesh) {
// Don't test mesh decoding when the input is point cloud.
if (num_faces > 0) {
std::unique_ptr<Mesh> mesh(DecodePly<Mesh>(file_name));
ASSERT_NE(mesh, nullptr) << "Failed to load test model " << file_name;
ASSERT_EQ(mesh->num_faces(), num_faces);
if (out_mesh) {
*out_mesh = std::move(mesh);
}
}
const std::unique_ptr<PointCloud> pc(DecodePly<PointCloud>(file_name));
ASSERT_NE(pc, nullptr) << "Failed to load test model " << file_name;
ASSERT_EQ(pc->num_points(), num_points);
}
void test_decoding(const std::string &file_name) {
const std::unique_ptr<Mesh> mesh(DecodePly<Mesh>(file_name));
ASSERT_NE(mesh, nullptr) << "Failed to load test model " << file_name;
ASSERT_GT(mesh->num_faces(), 0);
const std::unique_ptr<PointCloud> pc(DecodePly<PointCloud>(file_name));
ASSERT_NE(pc, nullptr) << "Failed to load test model " << file_name;
ASSERT_GT(pc->num_points(), 0);
}
};
TEST_F(PlyDecoderTest, TestPlyDecoding) {
const std::string file_name = "test_pos_color.ply";
test_decoding(file_name, 224, 114, nullptr);
}
TEST_F(PlyDecoderTest, TestPlyNormals) {
const std::string file_name = "cube_att.ply";
std::unique_ptr<Mesh> mesh;
test_decoding(file_name, 12, 3 * 8, &mesh);
ASSERT_NE(mesh, nullptr);
const int att_id = mesh->GetNamedAttributeId(GeometryAttribute::NORMAL);
ASSERT_GE(att_id, 0);
const PointAttribute *const att = mesh->attribute(att_id);
ASSERT_EQ(att->size(), 6); // 6 unique normal values.
}
TEST_F(PlyDecoderTest, TestPlyDecodingAll) {
// test if we can read all ply that are currently in test folder.
test_decoding("bun_zipper.ply");
// test_decoding("cube_att.ply"); // tested
test_decoding("test_extra_whitespace.ply");
test_decoding("test_more_datatypes.ply");
test_decoding("test_pos_color_ascii.ply");
test_decoding("int_point_cloud.ply", 0, 16, nullptr);
// test_decoding("test_pos_color.ply"); // tested
test_decoding("cube_quads.ply");
test_decoding("Box.ply");
test_decoding("delim_test.ply");
}
} // namespace draco
主要是就一个二进制的ply文件,读取并转换为std::unique_ptr<Mesh>类型,也可以转换为std::unique_ptr<PointCloud>类型
6.3 darco_tests/obj_encoder_test.cc
运行参数
--gtest_filter=ObjEncoderTest.TestObjEncodingAll
// Copyright 2017 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 <sstream>
#include "draco/attributes/geometry_attribute.h"
#include "draco/core/draco_test_base.h"
#include "draco/core/draco_test_utils.h"
#include "draco/io/file_reader_factory.h"
#include "draco/io/file_reader_interface.h"
#include "draco/io/file_utils.h"
#include "draco/io/obj_decoder.h"
namespace draco {
class ObjEncoderTest : public ::testing::Test {
protected:
void CompareMeshes(const Mesh *mesh0, const Mesh *mesh1) {
ASSERT_NE(mesh0, nullptr);
ASSERT_NE(mesh1, nullptr);
ASSERT_EQ(mesh0->num_faces(), mesh1->num_faces());
ASSERT_EQ(mesh0->num_attributes(), mesh1->num_attributes());
for (size_t att_id = 0; att_id < mesh0->num_attributes(); ++att_id) {
ASSERT_EQ(mesh0->attribute(att_id)->size(),
mesh1->attribute(att_id)->size());
}
}
// Encode a mesh using the ObjEncoder and then decode to verify the encoding.
std::unique_ptr<Mesh> EncodeAndDecodeMesh(const Mesh *mesh) {
EncoderBuffer encoder_buffer;
ObjEncoder encoder;
if (!encoder.EncodeToBuffer(*mesh, &encoder_buffer)) {
return nullptr;
}
DecoderBuffer decoder_buffer;
decoder_buffer.Init(encoder_buffer.data(), encoder_buffer.size());
std::unique_ptr<Mesh> decoded_mesh(new Mesh());
ObjDecoder decoder;
decoder.set_use_metadata(true);
if (!decoder.DecodeFromBuffer(&decoder_buffer, decoded_mesh.get()).ok()) {
return nullptr;
}
return decoded_mesh;
}
void test_encoding(const std::string &file_name) {
const std::unique_ptr<Mesh> mesh(ReadMeshFromTestFile(file_name, true));
ASSERT_NE(mesh, nullptr) << "Failed to load test model " << file_name;
ASSERT_GT(mesh->num_faces(), 0);
const std::unique_ptr<Mesh> decoded_mesh = EncodeAndDecodeMesh(mesh.get());
CompareMeshes(mesh.get(), decoded_mesh.get());
}
};
TEST_F(ObjEncoderTest, HasSubObject) { test_encoding("cube_att_sub_o.obj"); }
TEST_F(ObjEncoderTest, HasMaterial) {
const std::unique_ptr<Mesh> mesh0(ReadMeshFromTestFile("mat_test.obj", true));
ASSERT_NE(mesh0, nullptr);
const std::unique_ptr<Mesh> mesh1 = EncodeAndDecodeMesh(mesh0.get());
ASSERT_NE(mesh1, nullptr);
ASSERT_EQ(mesh0->num_faces(), mesh1->num_faces());
ASSERT_EQ(mesh0->num_attributes(), mesh1->num_attributes());
// Position attribute should be the same.
ASSERT_EQ(mesh0->attribute(0)->size(), mesh1->attribute(0)->size());
// Since |mesh1| is decoded from buffer, it has not material file. So the
// size of material attribute is the number of materials used in the obj
// file which is 7. The size of material attribute of |mesh0| decoded from
// the obj file will be the number of materials defined in the .mtl file.
ASSERT_EQ(mesh0->attribute(1)->size(), 29);
ASSERT_EQ(mesh1->attribute(1)->size(), 7);
}
TEST_F(ObjEncoderTest, TestObjEncodingAll) {
// Test decoded mesh from encoded obj file stays the same.
test_encoding("bunny_norm.obj");
test_encoding("cube_att.obj");
test_encoding("cube_att_partial.obj");
test_encoding("cube_quads.obj");
test_encoding("cube_subd.obj");
test_encoding("extra_vertex.obj");
test_encoding("multiple_isolated_triangles.obj");
test_encoding("multiple_tetrahedrons.obj");
test_encoding("one_face_123.obj");
test_encoding("one_face_312.obj");
test_encoding("one_face_321.obj");
test_encoding("sphere.obj");
test_encoding("test_nm.obj");
test_encoding("test_nm_trans.obj");
test_encoding("test_sphere.obj");
test_encoding("three_faces_123.obj");
test_encoding("three_faces_312.obj");
test_encoding("two_faces_123.obj");
test_encoding("two_faces_312.obj");
}
TEST_F(ObjEncoderTest, TestObjOctagonPreserved) {
// Test verifies that OBJ encoder can reconstruct and encode an octagon.
// Decode triangulated octagon and an extra attribute for reconstruction.
std::unique_ptr<draco::Mesh> mesh =
ReadMeshFromTestFile("octagon_preserved.drc");
ASSERT_NE(mesh, nullptr);
ASSERT_EQ(mesh->num_faces(), 6);
ASSERT_EQ(mesh->NumNamedAttributes(GeometryAttribute::GENERIC), 1);
ASSERT_NE(mesh->GetMetadata()->GetAttributeMetadataByStringEntry(
"name", "added_edges"),
nullptr);
// Reconstruct octagon and encode it into an OBJ file.
draco::ObjEncoder obj_encoder;
ASSERT_TRUE(obj_encoder.EncodeToFile(
*mesh, draco::GetTestTempFileFullPath("encoded.obj")));
// Read encoded OBJ file and golden OBJ file contents into buffers.
std::vector<char> data_encoded;
std::vector<char> data_golden;
ASSERT_TRUE(
ReadFileToBuffer(GetTestTempFileFullPath("encoded.obj"), &data_encoded));
ASSERT_TRUE(ReadFileToBuffer(GetTestFileFullPath("octagon_preserved.obj"),
&data_golden));
// Check that encoded OBJ file contents are correct.
ASSERT_EQ(data_encoded.size(), data_golden.size());
ASSERT_EQ(data_encoded, data_golden);
}
} // namespace draco
6.4 darco_tests/gltf_encoder_test.cc
运行参数
--gtest_filter=GltfEncoderTest.EncodeWithDracoCompression
这边是本章的重点,对点云以及mesh压缩和解压代码难度不大,GLTF比较复杂 ,下面是输入文件。
gltf文件和.bin文件是结合使用的.bin文件存放了buffers的实际数据信息。gltf文件主要复杂一个场景组织
// Tests encoding a simple glTF with Draco compression.
TEST_F(GltfEncoderTest, EncodeWithDracoCompression) {
const std::string file_name = "Box/glTF/Box.gltf";
const std::unique_ptr<Scene> scene(DecodeTestGltfFileToScene(file_name));
ASSERT_NE(scene, nullptr);
const DracoCompressionOptions options;
SceneUtils::SetDracoCompressionOptions(&options, scene.get());
EncodeSceneToGltfAndCompare(scene.get());
}