From 6becbe55e6065f4058b8d472dadaf1ea5ed4a51b Mon Sep 17 00:00:00 2001 From: Quim Muntal Date: Sun, 13 Dec 2020 21:00:29 +0100 Subject: [PATCH 1/5] initial c api implementation --- CMakeLists.txt | 18 ++++- cmake/draco_options.cmake | 6 ++ cmake/draco_tests.cmake | 3 +- src/draco/c_api/c_api.h | 95 +++++++++++++++++++++++++ src/draco/c_api/c_api_decoder.cc | 115 +++++++++++++++++++++++++++++++ src/draco/c_api/c_api_test.cc | 71 +++++++++++++++++++ 6 files changed, 306 insertions(+), 2 deletions(-) create mode 100644 src/draco/c_api/c_api.h create mode 100644 src/draco/c_api/c_api_decoder.cc create mode 100644 src/draco/c_api/c_api_test.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 3da2c664..90bc15c2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -486,6 +486,11 @@ list( "${draco_src_root}/javascript/emscripten/draco_animation_encoder_glue_wrapper.cc" ) +list( + APPEND draco_c_api_dec_sources + "${draco_src_root}/c_api/c_api_decoder.cc" + ) + list(APPEND draco_unity_plug_sources "${draco_src_root}/unity/draco_unity_plugin.cc" "${draco_src_root}/unity/draco_unity_plugin.h") @@ -773,6 +778,16 @@ else() ${draco_defines} INCLUDES ${draco_include_paths}) + draco_add_library(NAME + draco_c_api_dec + TYPE + OBJECT + SOURCES + ${draco_c_api_dec_sources} + DEFINES + ${draco_defines} + INCLUDES + ${draco_include_paths}) set(draco_object_library_deps draco_attributes @@ -801,7 +816,8 @@ else() draco_animation_enc draco_point_cloud draco_points_dec - draco_points_enc) + draco_points_enc + draco_c_api_dec) # Library targets that consume the object collections. if(MSVC OR WIN32) diff --git a/cmake/draco_options.cmake b/cmake/draco_options.cmake index 832bfb69..a021bc68 100644 --- a/cmake/draco_options.cmake +++ b/cmake/draco_options.cmake @@ -64,6 +64,8 @@ macro(draco_set_default_options) "Enable attribute deduping." VALUE OFF) draco_option(NAME DRACO_TESTS HELPSTRING "Enables tests." VALUE OFF) draco_option(NAME DRACO_WASM HELPSTRING "Enables WASM support." VALUE OFF) + draco_option(NAME DRACO_C_API HELPSTRING + "Build C wrapper." VALUE OFF) draco_option(NAME DRACO_UNITY_PLUGIN HELPSTRING "Build plugin library for Unity." VALUE OFF) draco_option(NAME DRACO_ANIMATION_ENCODING HELPSTRING "Enable animation." @@ -170,6 +172,10 @@ macro(draco_set_optional_features) set(CMAKE_POSITION_INDEPENDENT_CODE ON) endif() + if(DRACO_C_API) + draco_enable_feature(FEATURE "DRACO_C_API_SUPPORTED") + endif() + endmacro() # Macro that handles tracking of Draco preprocessor symbols for the purpose of diff --git a/cmake/draco_tests.cmake b/cmake/draco_tests.cmake index a6dfc5b5..4f4e9388 100644 --- a/cmake/draco_tests.cmake +++ b/cmake/draco_tests.cmake @@ -52,7 +52,8 @@ list( "${draco_src_root}/metadata/metadata_encoder_test.cc" "${draco_src_root}/metadata/metadata_test.cc" "${draco_src_root}/point_cloud/point_cloud_builder_test.cc" - "${draco_src_root}/point_cloud/point_cloud_test.cc") + "${draco_src_root}/point_cloud/point_cloud_test.cc" + "${draco_src_root}/c_api/c_api_test.cc") list(APPEND draco_gtest_all "${draco_root}/../googletest/googletest/src/gtest-all.cc") diff --git a/src/draco/c_api/c_api.h b/src/draco/c_api/c_api.h new file mode 100644 index 00000000..68f20af1 --- /dev/null +++ b/src/draco/c_api/c_api.h @@ -0,0 +1,95 @@ +// Copyright 2020 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. +// +#ifndef DRACO_C_API_H_ +#define DRACO_C_API_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// If compiling with Visual Studio. +#if defined(_MSC_VER) +#define EXPORT_API __declspec(dllexport) +#else +// Other platforms don't need this. +#define EXPORT_API +#endif // defined(_MSC_VER) + +typedef const char* draco_string; // NULL terminated + +// draco::Status + +typedef struct draco_status draco_status; + +EXPORT_API void dracoStatusRelease(draco_status *status); + +EXPORT_API int dracoStatusCode(const draco_status *status); + +EXPORT_API bool dracoStatusOk(const draco_status *status); + +// Returns the status message. +// The memory backing memory is valid meanwhile status is not released. +EXPORT_API draco_string dracoStatusErrorMsg(const draco_status *status); + +// draco::Mesh + +typedef uint32_t draco_face[3]; +typedef struct draco_mesh draco_mesh; + +EXPORT_API draco_mesh* dracoNewMesh(); + +EXPORT_API void dracoMeshRelease(draco_mesh *mesh); + +EXPORT_API uint32_t dracoMeshNumFaces(const draco_mesh *mesh); + +EXPORT_API uint32_t dracoMeshNumPoints(const draco_mesh *mesh); + +// Queries an array of 3*face_count elements containing the triangle indices. +// out_values must be allocated to contain at least 3*face_count uint16_t elements. +// out_size must be exactly 3*face_count*sizeof(uint16_t), else out_values +// won´t be filled and returns false. +EXPORT_API bool dracoMeshGetTrianglesUint16(const draco_mesh *mesh, + const size_t out_size, + uint16_t *out_values); + +// Queries an array of 3*face_count elements containing the triangle indices. +// out_values must be allocated to contain at least 3*face_count uint32_t elements. +// out_size must be exactly 3*face_count*sizeof(uint32_t), else out_values +// won´t be filled and returns false. +EXPORT_API bool dracoMeshGetTrianglesUint32(const draco_mesh *mesh, + const size_t out_size, + uint32_t *out_values); + +// draco::Decoder + +typedef struct draco_decoder draco_decoder; + +EXPORT_API draco_decoder* dracoNewDecoder(); + +EXPORT_API void dracoDecoderRelease(draco_decoder *decoder); + +EXPORT_API draco_status* dracoDecoderArrayToMesh(draco_decoder *decoder, + const char *data, + size_t data_size, + draco_mesh *out_mesh); + +#ifdef __cplusplus +} +#endif + +#endif // DRACO_C_API_H_ \ No newline at end of file diff --git a/src/draco/c_api/c_api_decoder.cc b/src/draco/c_api/c_api_decoder.cc new file mode 100644 index 00000000..3cc35549 --- /dev/null +++ b/src/draco/c_api/c_api_decoder.cc @@ -0,0 +1,115 @@ +// Copyright 2020 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/draco_features.h" + +#ifdef DRACO_C_API_SUPPORTED + +#include + +#include "draco/c_api/c_api.h" +#include "draco/compression/decode.h" + +void dracoStatusRelease(draco_status *status) { + free(status); +} + +int dracoStatusCode(const draco_status *status) { + return reinterpret_cast(status)->code(); +} + +bool dracoStatusOk(const draco_status *status) { + return reinterpret_cast(status)->ok(); +} + +draco_string dracoStatusErrorMsg(const draco_status *status) { + auto msg = reinterpret_cast(status)->error_msg(); + return reinterpret_cast(msg); +} + +draco_mesh* dracoNewMesh() { + return reinterpret_cast(new draco::Mesh()); +} + +void dracoMeshRelease(draco_mesh *mesh) { + free(mesh); +} + +uint32_t dracoMeshNumFaces(const draco_mesh *mesh) { + return reinterpret_cast(mesh)->num_faces(); +} + + +uint32_t dracoMeshNumPoints(const draco_mesh *mesh) { + return reinterpret_cast(mesh)->num_points(); +} + +template +bool GetTrianglesArray(const draco::Mesh *m, const size_t out_size, + T *out_values) { + const uint32_t num_faces = m->num_faces(); + if (num_faces * 3 * sizeof(T) != out_size) { + return false; + } + + for (uint32_t face_id = 0; face_id < num_faces; ++face_id) { + const draco::Mesh::Face &face = m->face(draco::FaceIndex(face_id)); + out_values[face_id * 3 + 0] = static_cast(face[0].value()); + out_values[face_id * 3 + 1] = static_cast(face[1].value()); + out_values[face_id * 3 + 2] = static_cast(face[2].value()); + } + return true; +} + +bool dracoMeshGetTrianglesUint16(const draco_mesh *mesh, const size_t out_size, + uint16_t *out_values) { + auto m = reinterpret_cast(mesh); + if (m->num_points() > std::numeric_limits::max()) { + return false; + } + + return GetTrianglesArray(m, out_size, out_values); +} + +bool dracoMeshGetTrianglesUint32(const draco_mesh *mesh, const size_t out_size, + uint32_t *out_values) { + auto m = reinterpret_cast(mesh); + if (m->num_points() > std::numeric_limits::max()) { + return false; + } + + return GetTrianglesArray(m, out_size, out_values); +} + +draco_decoder* dracoNewDecoder() { + return reinterpret_cast(new draco::Decoder()); +} + +void dracoDecoderRelease(draco_decoder *decoder) { + free(decoder); +} + +draco_status* dracoDecoderArrayToMesh(draco_decoder *decoder, + const char *data, + size_t data_size, + draco_mesh *out_mesh) { + draco::DecoderBuffer buffer; + buffer.Init(data, data_size); + auto m = reinterpret_cast(out_mesh); + const auto &last_status_ = reinterpret_cast(decoder)->DecodeBufferToGeometry(&buffer, m); + return reinterpret_cast(new draco::Status(last_status_)); +} + +#endif // DRACO_C_API_SUPPORTED \ No newline at end of file diff --git a/src/draco/c_api/c_api_test.cc b/src/draco/c_api/c_api_test.cc new file mode 100644 index 00000000..da6c90e1 --- /dev/null +++ b/src/draco/c_api/c_api_test.cc @@ -0,0 +1,71 @@ +// Copyright 2020 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/draco_features.h" + +#ifdef DRACO_C_API_SUPPORTED + +#include +#include +#include +#include + +#include "draco/c_api/c_api.h" +#include "draco/core/draco_test_base.h" +#include "draco/core/draco_test_utils.h" + +namespace { + +draco_mesh *DecodeToDracoMesh(const std::string &file_name) { + std::ifstream input_file(draco::GetTestFileFullPath(file_name), + std::ios::binary); + if (!input_file) { + return nullptr; + } + // Read the file stream into a buffer. + std::streampos file_size = 0; + input_file.seekg(0, std::ios::end); + file_size = input_file.tellg() - file_size; + input_file.seekg(0, std::ios::beg); + std::vector data(file_size); + input_file.read(data.data(), file_size); + if (data.empty()) { + return nullptr; + } + + auto mesh = dracoNewMesh(); + auto decoder = dracoNewDecoder(); + dracoDecoderArrayToMesh(decoder, data.data(), data.size(), mesh); + dracoDecoderRelease(decoder); + return mesh; +} + +TEST(DracoCAPITest, TestDecode) { + auto mesh = DecodeToDracoMesh("test_nm.obj.edgebreaker.cl4.2.2.drc"); + ASSERT_NE(mesh, nullptr); + auto num_faces = dracoMeshNumFaces(mesh); + ASSERT_EQ(num_faces, 170); + ASSERT_EQ(dracoPointCloudNumPoints(mesh), 99); + + auto indices_size = 3 * num_faces * sizeof(uint32_t); + uint32_t *indices = (uint32_t *)malloc(indices_size); + ASSERT_TRUE(dracoMeshGetTrianglesUint32(mesh, indices_size, indices)); + free(indices); + dracoMeshRelease(mesh); +} + +} // namespace + +#endif // DRACO_C_API_SUPPORTED \ No newline at end of file From eb2e0940357d6fe586736c2ada405de3ef0c934f Mon Sep 17 00:00:00 2001 From: Quim Muntal Date: Mon, 1 Feb 2021 20:41:49 +0100 Subject: [PATCH 2/5] default to __declspec(dllimport) --- src/draco/c_api/c_api.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/draco/c_api/c_api.h b/src/draco/c_api/c_api.h index 68f20af1..7825f4da 100644 --- a/src/draco/c_api/c_api.h +++ b/src/draco/c_api/c_api.h @@ -23,8 +23,12 @@ extern "C" { #endif // If compiling with Visual Studio. -#if defined(_MSC_VER) -#define EXPORT_API __declspec(dllexport) +#ifdef _MSC_VER +# ifdef DRACO_BUILDING_DLL +# define EXPORT_API __declspec(dllexport) +# else +# define EXPORT_API __declspec(dllimport) +# endif #else // Other platforms don't need this. #define EXPORT_API From d345a2e30c99cd35e820cf2dd534c9d12d0fe1c2 Mon Sep 17 00:00:00 2001 From: Quim Muntal Date: Mon, 1 Feb 2021 23:28:07 +0100 Subject: [PATCH 3/5] define export_api for gnu and clang --- src/draco/c_api/c_api.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/draco/c_api/c_api.h b/src/draco/c_api/c_api.h index 7825f4da..274c7fd0 100644 --- a/src/draco/c_api/c_api.h +++ b/src/draco/c_api/c_api.h @@ -23,16 +23,18 @@ extern "C" { #endif // If compiling with Visual Studio. -#ifdef _MSC_VER +#ifdef _WIN32 # ifdef DRACO_BUILDING_DLL # define EXPORT_API __declspec(dllexport) # else # define EXPORT_API __declspec(dllimport) # endif +#elif __GNUC__ >= 4 || defined(__clang__) +# define EXPORT_API __attribute__((visibility ("default"))) #else // Other platforms don't need this. -#define EXPORT_API -#endif // defined(_MSC_VER) + #define EXPORT_API +#endif // defined(_WIN32) typedef const char* draco_string; // NULL terminated From 47944fe28760fbcc6e0763258ec63b1da28fcf3c Mon Sep 17 00:00:00 2001 From: Quim Muntal Date: Wed, 3 Feb 2021 09:01:36 +0100 Subject: [PATCH 4/5] fix cdraco test --- src/draco/c_api/c_api_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/draco/c_api/c_api_test.cc b/src/draco/c_api/c_api_test.cc index da6c90e1..928fa0fb 100644 --- a/src/draco/c_api/c_api_test.cc +++ b/src/draco/c_api/c_api_test.cc @@ -57,7 +57,7 @@ TEST(DracoCAPITest, TestDecode) { ASSERT_NE(mesh, nullptr); auto num_faces = dracoMeshNumFaces(mesh); ASSERT_EQ(num_faces, 170); - ASSERT_EQ(dracoPointCloudNumPoints(mesh), 99); + ASSERT_EQ(dracoMeshNumPoints(mesh), 99); auto indices_size = 3 * num_faces * sizeof(uint32_t); uint32_t *indices = (uint32_t *)malloc(indices_size); From 7fb1ff5d7231391280d150a34199c96c9eba1f03 Mon Sep 17 00:00:00 2001 From: Quim Muntal Date: Wed, 3 Feb 2021 18:56:59 +0100 Subject: [PATCH 5/5] add cdraco lib --- CMakeLists.txt | 68 +++++++++++++++++++++++++++-- cmake/draco_build_definitions.cmake | 6 +-- cmake/draco_tests.cmake | 17 +++++--- src/draco/c_api/c_api.h | 8 ++-- 4 files changed, 81 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 90bc15c2..3c50871b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -816,8 +816,11 @@ else() draco_animation_enc draco_point_cloud draco_points_dec - draco_points_enc - draco_c_api_dec) + draco_points_enc) + + list(APPEND draco_c_api_object_library_deps + ${draco_object_library_deps} + draco_c_api_dec) # Library targets that consume the object collections. if(MSVC OR WIN32) @@ -845,7 +848,28 @@ else() ${draco_include_paths} OBJLIB_DEPS ${draco_object_library_deps}) - + if(BUILD_SHARED_LIBS) + set_target_properties(draco PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE) + endif() + if(DRACO_C_API) + draco_add_library(NAME + cdraco + OUTPUT_NAME + cdraco + TYPE + ${draco_lib_type} + DEFINES + ${draco_defines} + INCLUDES + ${draco_include_paths} + PUBLIC_INCLUDES + ${draco_c_public_api_include_paths} + OBJLIB_DEPS + ${draco_c_api_object_library_deps}) + if(BUILD_SHARED_LIBS) + set_target_properties(cdraco PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS FALSE) + endif() + endif() else() draco_add_library(NAME draco_static @@ -859,7 +883,22 @@ else() ${draco_include_paths} OBJLIB_DEPS ${draco_object_library_deps}) - + if(DRACO_C_API) + draco_add_library(NAME + cdraco_static + OUTPUT_NAME + cdraco + TYPE + STATIC + DEFINES + ${draco_defines} + INCLUDES + ${draco_include_paths} + PUBLIC_INCLUDES + ${draco_c_public_api_include_paths} + OBJLIB_DEPS + ${draco_c_api_object_library_deps}) + endif() if(BUILD_SHARED_LIBS) draco_add_library(NAME draco_shared @@ -875,6 +914,27 @@ else() ${draco_include_paths} LIB_DEPS draco_static) + if(DRACO_C_API) + draco_add_library(NAME + cdraco_shared + SOURCES + "${draco_src_root}/core/draco_version.h" + OUTPUT_NAME + cdraco + TYPE + SHARED + DEFINES + ${draco_defines} + INCLUDES + ${draco_include_paths} + PUBLIC_INCLUDES + ${draco_c_public_api_include_paths} + OBJLIB_DEPS + cdraco_static) + set_target_properties(cdraco_static PROPERTIES C_VISIBILITY_PRESET hidden) + set_target_properties(cdraco_static PROPERTIES CXX_VISIBILITY_PRESET hidden) + set_target_properties(cdraco_static PROPERTIES VISIBILITY_INLINES_HIDDEN ON) + endif() endif() endif() diff --git a/cmake/draco_build_definitions.cmake b/cmake/draco_build_definitions.cmake index c1ada620..d8c0fff3 100644 --- a/cmake/draco_build_definitions.cmake +++ b/cmake/draco_build_definitions.cmake @@ -41,6 +41,8 @@ macro(draco_set_build_definitions) list(APPEND draco_include_paths "${draco_root}" "${draco_root}/src" "${draco_build}") + list(APPEND draco_c_public_api_include_paths "${draco_root}/src/draco/c_api") + if(DRACO_ABSL) list(APPEND draco_include_path "${draco_root}/third_party/abseil-cpp") endif() @@ -59,10 +61,6 @@ macro(draco_set_build_definitions) if(MSVC OR WIN32) list(APPEND draco_defines "_CRT_SECURE_NO_DEPRECATE=1" "NOMINMAX=1") - - if(BUILD_SHARED_LIBS) - set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE) - endif() endif() if(ANDROID) diff --git a/cmake/draco_tests.cmake b/cmake/draco_tests.cmake index 4f4e9388..b02744ca 100644 --- a/cmake/draco_tests.cmake +++ b/cmake/draco_tests.cmake @@ -55,6 +55,15 @@ list( "${draco_src_root}/point_cloud/point_cloud_test.cc" "${draco_src_root}/c_api/c_api_test.cc") +list(APPEND draco_test_lib_deps + draco_static + draco_gtest + draco_gtest_main) + +if(DRACO_C_API) + list(APPEND draco_test_lib_deps cdraco_static) +endif() + list(APPEND draco_gtest_all "${draco_root}/../googletest/googletest/src/gtest-all.cc") list(APPEND draco_gtest_main @@ -113,9 +122,7 @@ macro(draco_setup_test_targets) INCLUDES ${draco_test_include_paths} LIB_DEPS - draco_static - draco_gtest - draco_gtest_main) + ${draco_test_lib_deps}) draco_add_executable(NAME draco_factory_tests @@ -127,8 +134,6 @@ macro(draco_setup_test_targets) INCLUDES ${draco_test_include_paths} LIB_DEPS - draco_static - draco_gtest - draco_gtest_main) + ${draco_test_lib_deps}) endif() endmacro() diff --git a/src/draco/c_api/c_api.h b/src/draco/c_api/c_api.h index 274c7fd0..b9f2af2d 100644 --- a/src/draco/c_api/c_api.h +++ b/src/draco/c_api/c_api.h @@ -22,17 +22,17 @@ extern "C" { #endif -// If compiling with Visual Studio. #ifdef _WIN32 -# ifdef DRACO_BUILDING_DLL +# if defined(DRACO_BUILDING_DLL) # define EXPORT_API __declspec(dllexport) -# else +# elif !defined(DRACO_STATIC) # define EXPORT_API __declspec(dllimport) +# else +# define EXPORT_API # endif #elif __GNUC__ >= 4 || defined(__clang__) # define EXPORT_API __attribute__((visibility ("default"))) #else -// Other platforms don't need this. #define EXPORT_API #endif // defined(_WIN32)