draco是谷歌发布的三维模型压缩库,可以用于压缩三维模型的顶点等数据,减小模型大小,使用c++版本的draco压缩后的模型在cesium1.89上可以正常加载显示,但是cesium1.92版本时,无法正常加载,会报Cannot read property '__destroy__' of undefined的错,下面是cesium报错信息:
后来通过调试分析draco源码,发现我在使用draco压缩时只是对顶点纹理和面进行了操作,没有对材质进行绑定,所以cesium1.92以上版本无法加载,但是低版本的cesium是可以正常加载的,主要原因就是将其他数据格式组织成draco的mesh格式时存在问题,具体的mesh加载方式可以参考源码中ObjDecoder::DecodeInternal()方法。
在进行压缩时,需要设置材质相关属性:
//设置材质相关,此项为必要操作,否则cesium1.92以上版本无法加载该b3dm
{
draco::GeometryAttribute va;
const auto geometry_attribute_type=draco::GeometryAttribute::GENERIC;
va.Init(geometry_attribute_type,nullptr,1,draco::DT_UINT8,false,1,0);
material_att_id=pointCloud->AddAttribute(va,false,1);
int index=0;
pointCloud->attribute(material_att_id)->SetAttributeValue(draco::AttributeValueIndex(0),&index);
}
我的数据中只有一个纹理,所以index设置为0,如果有多个纹理材质,则需要遍历设置材质属性值:
{
for(int i=0;i<count;++i)
pointCloud->attribute(material_att_id)->SetAttributeValue(draco::AttrubuteValueIndex(i),&i);
}
在设置三角面时,需要将面的每一个顶点都绑定一个材质id:
{
for(int j=0;j<tri_indices.size();++j){
//三角面的第一个点
const draco::PointIndex vert_id1(3*j+0);
pointCloud->attribute(material_att_id)->SetPointMapEntry(vert_id1,draco::AttributeValueIndex(0));
//三角面的第二个点
const draco::PointIndex vert_id2(3*j+1);
pointCloud->attribute(material_att_id)->SetPointMapEntry(vert_id2,draco::AttributeValueIndex(0));
//三角面的第三个点
const draco::PointIndex vert_id3(3*j+2);
pointCloud->attribute(material_att_id)->SetPointMapEntry(vert_id3,draco::AttributeValueIndex(0));
}
}
因为我的数据中只有一个纹理,所以每个面的每一个点对应的材质属性值都是0,即draco::AttributeValueIndex(0),如果有多个材质,需要注意设置成对应的材质属性值。
下面附上部分代码:
{
std::vector<osg::Vec3> tri_positions;//osg读取的顶点,自己实现
std::vector<osg::Vec2> tri_texcoords0;//osg读取的纹理坐标,自己实现
std::vector<osg::Vec3> tri_indices;//osg读取的三角面,自己实现
//使用draco进行模型压缩代码
int vertexCount = tri_positions.size(); // 顶点数量
std::unique_ptr<draco::Mesh> dracoMesh(new draco::Mesh());
draco::PointCloud* pointCloud = static_cast<draco::PointCloud*>(dracoMesh.get());
dracoMesh->set_num_points(tri_indices.size() * 3);//设置要压缩的点个数
dracoMesh->SetNumFaces(tri_indices.size());//设置要压缩的面个数
int pos_att_id = 0;
int tex_att_id = 0;
int material_att_id = 0;
//设置材质,此项为必须操作,否则cesium1.92以上版本无法加载该b3dm
{
draco::GeometryAttribute va;
const auto geometry_attribute_type = draco::GeometryAttribute::GENERIC;
va.Init(geometry_attribute_type, nullptr, 1, draco::DT_UINT8, false, 1, 0);
material_att_id = pointCloud->AddAttribute(va, false, 1);
int vvv = 0;
pointCloud->attribute(material_att_id)->SetAttributeValue(draco::AttributeValueIndex(0), &vvv);
}
//设置顶点相关
{
draco::GeometryAttribute va;
va.Init(draco::GeometryAttribute::POSITION, nullptr, 3, draco::DT_FLOAT32, false, sizeof(float) * 3, 0);
pos_att_id = pointCloud->AddAttribute(va, false, vertexCount);//添加到dracoPointCloud中
draco::PointAttribute* att_ptr = pointCloud->attribute(pos_att_id);//获取添加的对象
//将顶点数据添加到压缩属性att中
for (int j = 0; j < tri_positions.size(); j++) {
att_ptr->SetAttributeValue(draco::AttributeValueIndex(j), &tri_positions[j][0]);//顶点数据添加到压缩属性att中
}
}
//设置纹理坐标相关
{
draco::GeometryAttribute va;
va.Init(draco::GeometryAttribute::TEX_COORD, nullptr, 2, draco::DT_FLOAT32, false, sizeof(float)*2, 0);
tex_att_id = pointCloud->AddAttribute(va, false, vertexCount);//添加到dracoPointCloud中
draco::PointAttribute* att_ptr = dracoMesh->attribute(tex_att_id);//获取添加的对象
//将纹理数据添加到压缩属性att中
for (int j = 0; j < tri_texcoords0.size(); j++) {
att_ptr->SetAttributeValue(draco::AttributeValueIndex(j), &tri_texcoords0[j][0]);//纹理数据添加到压缩属性att中
}
}
//遍历面,每一个三角面的三个点设置对应的材质、顶点和纹理坐标
for (int j = 0; j < tri_indices.size(); ++j) {
//三角面的第一个顶点
const draco::PointIndex vert_id1(3 * j + 0);
//设置对应的材质,此项为必须操作,否则cesium1.92以上版本无法加载该b3dm
pointCloud->attribute(material_att_id)->SetPointMapEntry(vert_id1, draco::AttributeValueIndex(0));
pointCloud->attribute(pos_att_id)->SetPointMapEntry(vert_id1, draco::AttributeValueIndex(tri_indices[j].x()));
pointCloud->attribute(tex_att_id)->SetPointMapEntry(vert_id1, draco::AttributeValueIndex(tri_indices[j].x()));
//三角面的第二个顶点
const draco::PointIndex vert_id2(3 * j + 1);
//设置对应材质,此项为必须操作,否则cesium1.92以上版本无法加载该b3dm
pointCloud->attribute(material_att_id)->SetPointMapEntry(vert_id2, draco::AttributeValueIndex(0));
pointCloud->attribute(pos_att_id)->SetPointMapEntry(vert_id2, draco::AttributeValueIndex(tri_indices[j].y()));
pointCloud->attribute(tex_att_id)->SetPointMapEntry(vert_id2, draco::AttributeValueIndex(tri_indices[j].y()));
//三角面的第三个顶点
const draco::PointIndex vert_id3(3 * j + 2);
//设置对应材质,此项为必须操作,否则cesium1.92以上版本无法加载该b3dm
pointCloud->attribute(material_att_id)->SetPointMapEntry(vert_id3, draco::AttributeValueIndex(0));
pointCloud->attribute(pos_att_id)->SetPointMapEntry(vert_id3, draco::AttributeValueIndex(tri_indices[j].z()));
pointCloud->attribute(tex_att_id)->SetPointMapEntry(vert_id3, draco::AttributeValueIndex(tri_indices[j].z()));
draco::Mesh::Face face;
for (int c = 0; c < 3; ++c) {
face[c] = 3 * j + c;
}
dracoMesh->SetFace(draco::FaceIndex(j), face);
}
//去除重复数据
pointCloud->DeduplicateAttributeValues();
pointCloud->DeduplicatePointIds();
//设置压缩参数
const int dracoCompressionSpeed = 5;
const int dracoPositionBits = 14;
draco::EncoderBuffer dracoBuffer;//压缩后的数据存储到draco的缓存区中
draco::Encoder encoder;//压缩对象
//设置压缩参数
encoder.SetSpeedOptions(dracoCompressionSpeed, dracoCompressionSpeed);
encoder.SetAttributeQuantization(draco::GeometryAttribute::POSITION,
dracoPositionBits);
//设置压缩方法
encoder.SetEncodingMethod(draco::MeshEncoderMethod::MESH_EDGEBREAKER_ENCODING);
}