Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .DS_Store
Binary file not shown.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
DracoPy.egg-info/
MANIFEST
_skbuild/
venv/*
.eggs/*
*.so
.idea/
.vscode/
dist/
bunny_test.drc
*secret.json
__pycache__/
__pycache__/
10 changes: 6 additions & 4 deletions example.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import os
import DracoPy

with open('bunny.drc', 'rb') as draco_file:
testdata_directory = "testdata_files"

with open(os.path.join(testdata_directory, "Avocado.bin"), "rb") as draco_file:
file_content = draco_file.read()
mesh_object = DracoPy.decode_buffer_to_mesh(file_content)
print('number of points in original file: {0}'.format(len(mesh_object.points)))
print('number of faces in original file: {0}'.format(len(mesh_object.faces)))
encoding_test = DracoPy.encode_mesh_to_buffer(mesh_object.points, mesh_object.faces)
print('number of faces in original file: {0}'.format(len(mesh_object.tex_coord)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be the number of texture coordinates?

print('number of normal in original file: {0}'.format(len(mesh_object.normals)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

normal -> normals

encoding_test = DracoPy.encode_mesh_to_buffer(mesh_object.points, mesh_object.face)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

faces?

with open('bunny_test.drc', 'wb') as test_file:
test_file.write(encoding_test)

Expand Down
11 changes: 5 additions & 6 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ def read(fname):
setup_requires = []
try:
cmake_version = LegacyVersion(get_cmake_version())
if cmake_version < LegacyVersion("3.5") or cmake_version >= LegacyVersion("3.15"):
setup_requires.append('cmake<3.15')
if cmake_version < LegacyVersion("3.5") or cmake_version >= LegacyVersion("3.20"):
setup_requires.append('cmake<3.20')
except SKBuildError:
setup_requires.append('cmake<3.15')
setup_requires.append('cmake<3.20')

# If you want to re-build the cython cpp file (DracoPy.cpp), run:
# cython --cplus -3 -I./_skbuild/linux-x86_64-3.6/cmake-install/include/draco/ ./src/DracoPy.pyx
Expand Down Expand Up @@ -50,12 +50,11 @@ def read(fname):
'-std=c++11','-O3'
]


setup(
name='DracoPy',
version='0.0.19',
description = 'Python wrapper for Google\'s Draco Mesh Compression Library',
author = 'Manuel Castro',
author = 'Manuel Castro :: Contributors :: Fatih Erol, Faru Nuri Sonmez',
author_email = '[email protected]',
url = 'https://github.com/seung-lab/DracoPy',
long_description=read('README.md'),
Expand All @@ -69,7 +68,7 @@ def read(fname):
setuptools.Extension(
'DracoPy',
sources=[ os.path.join(src_dir, 'DracoPy.cpp') ],
depends=[ os.path.join(src_dir, 'DracoPy.h') ],
depends=[ os.path.join(src_dir, 'DracoPy.h'), os.path.join(src_dir, 'DracoPy.pyx')],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is right. The pyx is only necessary for generating the DracoPy.cpp files

language='c++',
include_dirs = [ os.path.join(CMAKE_INSTALL_DIR(), 'include/')],
extra_compile_args=extra_compile_args,
Expand Down
1,172 changes: 654 additions & 518 deletions src/DracoPy.cpp

Large diffs are not rendered by default.

71 changes: 64 additions & 7 deletions src/DracoPy.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
#include "draco/point_cloud/point_cloud_builder.h"

namespace DracoFunctions {

enum decoding_status { successful, not_draco_encoded, no_position_attribute, failed_during_decoding };
enum decoding_status { successful, not_draco_encoded, no_position_attribute, failed_during_decoding, no_tex_coord_attribute, no_normal_coord_attribute };
enum encoding_status { successful_encoding, failed_during_encoding };

struct PointCloudObject {
Expand All @@ -27,6 +26,7 @@ namespace DracoFunctions {
struct MeshObject : PointCloudObject {
std::vector<float> normals;
std::vector<unsigned int> faces;
std::vector<float> tex_coord;
};

struct EncodedObject {
Expand All @@ -35,11 +35,18 @@ namespace DracoFunctions {
};

MeshObject decode_buffer(const char *buffer, std::size_t buffer_len) {

std::cout << "Decode Buffer" << "Begin" << std::endl;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please either hide the std::couts behind a verbose flag or remove them. This library is often used in large deployments of thousands of CPUs.


MeshObject meshObject;
draco::DecoderBuffer decoderBuffer;
decoderBuffer.Init(buffer, buffer_len);
draco::Decoder decoder;

auto statusor = decoder.DecodeMeshFromBuffer(&decoderBuffer);

std::cout << "Decode Buffer " << "Status : "<< std::boolalpha << statusor.ok() << std::endl;

if (!statusor.ok()) {
std::string status_string = statusor.status().error_msg_string();
if (status_string.compare("Not a Draco file.") || status_string.compare("Failed to parse Draco header.")) {
Expand All @@ -50,15 +57,27 @@ namespace DracoFunctions {
}
return meshObject;
}

std::unique_ptr<draco::Mesh> in_mesh = std::move(statusor).value();
draco::Mesh *mesh = in_mesh.get();
//Position Attribute ID
const int pos_att_id = mesh->GetNamedAttributeId(draco::GeometryAttribute::POSITION);
std::cout << "Decode Buffer " << "Attribute Position : " << pos_att_id << std::endl;



if (pos_att_id < 0) {
meshObject.decode_status = no_position_attribute;
return meshObject;
}

std::cout << "Decode Buffer " << "Mesh Number Points : " << 3 * mesh->num_points() << std::endl;
meshObject.points.reserve(3 * mesh->num_points());
std::cout << "Decode Buffer " << "Mesh Number Faces : " << 3 * mesh->num_faces() << std::endl;
meshObject.faces.reserve(3 * mesh->num_faces());



const auto *const pos_att = mesh->attribute(pos_att_id);
std::array<float, 3> pos_val;
for (draco::PointIndex v(0); v < mesh->num_points(); ++v) {
Expand All @@ -76,6 +95,40 @@ namespace DracoFunctions {
meshObject.faces.push_back(*(reinterpret_cast<const uint32_t *>(&(f[1]))));
meshObject.faces.push_back(*(reinterpret_cast<const uint32_t *>(&(f[2]))));
}

const int tex_att_id = mesh->GetNamedAttributeId(draco::GeometryAttribute::TEX_COORD);
if(tex_att_id >= 0) {

const auto *const tex_att = mesh->attribute(tex_att_id);
meshObject.tex_coord.reserve(tex_att->size());
std::array<float, 2> tex_val;
std::cout << "Decode Buffer " << "Attribute Tex Coord : " << tex_att->size() << std::endl;

for (draco::PointIndex v(0); v < tex_att->size(); ++v) {
if (!tex_att->ConvertValue<float, 2>(tex_att->mapped_index(v), &tex_val[0])) {
std::cout << "Convert Error" << std::endl;
meshObject.decode_status = no_tex_coord_attribute;
}
meshObject.tex_coord.push_back(tex_val[0]);
meshObject.tex_coord.push_back(tex_val[1]);
}
}

const int normal_att_id = mesh->GetNamedAttributeId(draco::GeometryAttribute::NORMAL);
const auto *const normal_att = mesh->attribute(normal_att_id);
meshObject.normals.reserve(normal_att->size());
std::array<float, 3> normal_val;
std::cout << "Decode Buffer" << "Attribute Normal Coord : " << normal_att->size() << std::endl;
for (draco::PointIndex v(0); v < normal_att->size(); ++v){
if (!normal_att->ConvertValue<float, 3>(normal_att->mapped_index(v), &normal_val[0])){
std::cout << "Convert Error" << std::endl;
meshObject.decode_status = no_normal_coord_attribute;
}
meshObject.normals.push_back(normal_val[0]);
meshObject.normals.push_back(normal_val[1]);
meshObject.normals.push_back(normal_val[3]);
}

const draco::GeometryMetadata *metadata = mesh->GetMetadata();
meshObject.encoding_options_set = false;
if (metadata) {
Expand Down Expand Up @@ -161,12 +214,13 @@ namespace DracoFunctions {
}
}

EncodedObject encode_mesh(const std::vector<float> &points, const std::vector<unsigned int> &faces,
int quantization_bits, int compression_level, float quantization_range, const float *quantization_origin, bool create_metadata) {
EncodedObject encode_mesh(const std::vector<float> &points, const std::vector<unsigned int> &faces, const std::vector<float> &normals, int quantization_bits,
int compression_level, float quantization_range, const float *quantization_origin, bool create_metadata) {
draco::TriangleSoupMeshBuilder mb;
mb.Start(faces.size());
const int pos_att_id =
mb.AddAttribute(draco::GeometryAttribute::POSITION, 3, draco::DataType::DT_FLOAT32);

const int pos_att_id = mb.AddAttribute(draco::GeometryAttribute::POSITION, 3, draco::DataType::DT_FLOAT32); //attribute_type, num_components, data_type
std::cout << "POSITION :" << pos_att_id << std::endl;

for (std::size_t i = 0; i <= faces.size() - 3; i += 3) {
auto point1Index = faces[i]*3;
Expand All @@ -175,6 +229,9 @@ namespace DracoFunctions {
mb.SetAttributeValuesForFace(pos_att_id, draco::FaceIndex(i), draco::Vector3f(points[point1Index], points[point1Index+1], points[point1Index+2]).data(), draco::Vector3f(points[point2Index], points[point2Index+1], points[point2Index+2]).data(), draco::Vector3f(points[point3Index], points[point3Index+1], points[point3Index+2]).data());
}

const int tex_att_id = mb.AddAttribute(draco::GeometryAttribute::TEX_COORD, 2, draco::DataType::DT_FLOAT32);
std::cout << "TEX_COORD :" << tex_att_id << std::endl;

std::unique_ptr<draco::Mesh> ptr_mesh = mb.Finalize();
draco::Mesh *mesh = ptr_mesh.get();
draco::Encoder encoder;
Expand All @@ -183,6 +240,7 @@ namespace DracoFunctions {
const draco::Status status = encoder.EncodeMeshToBuffer(*mesh, &buffer);
EncodedObject encodedMeshObject;
encodedMeshObject.buffer = *((std::vector<unsigned char> *)buffer.buffer());

if (status.ok()) {
encodedMeshObject.encode_status = successful_encoding;
} else {
Expand All @@ -199,7 +257,6 @@ namespace DracoFunctions {
pcb.Start(num_points);
const int pos_att_id =
pcb.AddAttribute(draco::GeometryAttribute::POSITION, 3, draco::DataType::DT_FLOAT32);

for (draco::PointIndex i(0); i < num_points; i++) {
pcb.SetAttributeValueForPoint(pos_att_id, i, points.data() + 3 * i.value());
}
Expand Down
7 changes: 3 additions & 4 deletions src/DracoPy.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,8 @@ cdef extern from "DracoPy.h" namespace "DracoFunctions":
cdef struct MeshObject:
vector[float] points
vector[unsigned int] faces

# TODO: add support for normals, which are not currently supported.
vector[float] normals
vector[float] tex_coord

# Encoding options
bool encoding_options_set
Expand All @@ -47,8 +46,8 @@ cdef extern from "DracoPy.h" namespace "DracoFunctions":

PointCloudObject decode_buffer_to_point_cloud(const char *buffer, size_t buffer_len) except +

EncodedObject encode_mesh(vector[float] points, vector[uint32_t] faces, int quantization_bits,
EncodedObject encode_mesh(vector[float] points, vector[uint32_t] faces, vector[float] normals, int quantization_bits,
int compression_level, float quantization_range, const float *quantization_origin, bool create_metadata) except +

EncodedObject encode_point_cloud(vector[float] points, int quantization_bits,
int compression_level, float quantization_range, const float *quantization_origin, bool create_metadata) except +
8 changes: 6 additions & 2 deletions src/DracoPy.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ class DracoMesh(DracoPointCloud):
def normals(self):
return self.data_struct['normals']

@property
def tex_coord(self):
return self.data_struct['tex_coord']

class EncodingOptions(object):
def __init__(self, quantization_bits, quantization_range, quantization_origin):
self.quantization_bits = quantization_bits
Expand Down Expand Up @@ -70,7 +74,7 @@ class FileTypeException(Exception):
class EncodingFailedException(Exception):
pass

def encode_mesh_to_buffer(points, faces, quantization_bits=14, compression_level=1, quantization_range=-1, quantization_origin=None, create_metadata=False):
def encode_mesh_to_buffer(points, faces, normals, quantization_bits=14, compression_level=1, quantization_range=-1, quantization_origin=None, create_metadata=False):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a breaking change. Can normals be given a default value that is handled gracefully?

"""
Encode a list or numpy array of points/vertices (float) and faces (unsigned int) to a draco buffer.
Quantization bits should be an integer between 0 and 31
Expand All @@ -87,7 +91,7 @@ def encode_mesh_to_buffer(points, faces, quantization_bits=14, compression_level
quant_origin = <float *>PyMem_Malloc(sizeof(float) * num_dims)
for dim in range(num_dims):
quant_origin[dim] = quantization_origin[dim]
encoded_mesh = DracoPy.encode_mesh(points, faces, quantization_bits, compression_level, quantization_range, quant_origin, create_metadata)
encoded_mesh = DracoPy.encode_mesh(points, faces, normals, quantization_bits, compression_level, quantization_range, quant_origin, create_metadata)
if quant_origin != NULL:
PyMem_Free(quant_origin)
if encoded_mesh.encode_status == DracoPy.encoding_status.successful_encoding:
Expand Down