GLTF中的Draco编译与测试

目录

1.GLTF::Primitive* primitive里面有什么属性?

2.primitive->extensions.find("KHR_draco_mesh_compression")什么意思? 

3.Draco下载

4.Draco编译与测试

5.点云实战

6、draco源码解析

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文件主要复杂一个场景组织

这篇讲的挺好:gltf解析概述_gltf和glb区别_hometoned的博客-CSDN博客

  

// 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());
}

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

江河地笑

实践是检验真理的唯一标准

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值