diff --git a/geometry/render_gl/BUILD.bazel b/geometry/render_gl/BUILD.bazel index 2b390023f7cc..973213a7057c 100644 --- a/geometry/render_gl/BUILD.bazel +++ b/geometry/render_gl/BUILD.bazel @@ -241,6 +241,7 @@ drake_cc_googletest_linux_only( "//common/test_utilities:expect_throws_message", "//geometry/render:render_label", "@gflags", + "@tinygltf_internal//:tinygltf", "@vtk_internal//:vtkCommonCore", "@vtk_internal//:vtkCommonDataModel", "@vtk_internal//:vtkIOImage", diff --git a/geometry/render_gl/test/internal_render_engine_gl_test.cc b/geometry/render_gl/test/internal_render_engine_gl_test.cc index 799887ef2f6d..b58e88b50bbb 100644 --- a/geometry/render_gl/test/internal_render_engine_gl_test.cc +++ b/geometry/render_gl/test/internal_render_engine_gl_test.cc @@ -7,6 +7,7 @@ #include #include +#include // To ease build system upkeep, we annotate VTK includes with their deps. #include // vtkCommonDataModel @@ -204,6 +205,29 @@ ::testing::AssertionResult CompareColor( << "\n Found: " << tested << "\n with tolerance: " << tolerance; } +// Because we haven't bothered with tinygltf's stb_image dependency we're +// obliged to provide our own load image callback. In this case, we provide a +// no-op callback just to show that things work. +bool LocalGltfLoadImage(tinygltf::Image*, const int, std::string*, std::string*, + int, int, const unsigned char*, int, void*) { + return true; +} + +// Simply confirm that we can use tinygltf to load a gltf file. +GTEST_TEST(TinyGltf, SmokeTest) { + tinygltf::Model model; + tinygltf::TinyGLTF loader; + loader.SetImageLoader(LocalGltfLoadImage, nullptr); + + const std::string path = FindResourceOrThrow( + "drake/geometry/render/test/meshes/fully_textured_pyramid.gltf"); + const bool ret = loader.LoadASCIIFromFile(&model, nullptr /* err */, + nullptr /* warn */, path); + + ASSERT_TRUE(ret); + ASSERT_EQ(model.nodes.size(), 1); +} + class RenderEngineGlTest : public ::testing::Test { public: RenderEngineGlTest() diff --git a/tools/workspace/BUILD.bazel b/tools/workspace/BUILD.bazel index 10b2fc59dbbd..e4bf803e6ca5 100644 --- a/tools/workspace/BUILD.bazel +++ b/tools/workspace/BUILD.bazel @@ -55,6 +55,7 @@ drake_py_binary( "@nlopt_internal//:__subpackages__", "@qhull_internal//:__subpackages__", "@sdformat_internal//:__subpackages__", + "@tinygltf_internal//:__subpackages__", "@tinyobjloader_internal//:__subpackages__", "@yaml_cpp_internal//:__subpackages__", ], @@ -108,6 +109,7 @@ _DRAKE_EXTERNAL_PACKAGE_INSTALLS = ["@%s//:install" % p for p in [ "statsjs", "stduuid_internal", "suitesparse_internal", + "tinygltf_internal", "tinyobjloader_internal", "tinyxml2_internal", "usockets_internal", diff --git a/tools/workspace/default.bzl b/tools/workspace/default.bzl index eb5999a4b482..404986bc1c2c 100644 --- a/tools/workspace/default.bzl +++ b/tools/workspace/default.bzl @@ -88,6 +88,7 @@ load("//tools/workspace/stduuid_internal:repository.bzl", "stduuid_internal_repo load("//tools/workspace/styleguide:repository.bzl", "styleguide_repository") load("//tools/workspace/suitesparse_internal:repository.bzl", "suitesparse_internal_repository") # noqa load("//tools/workspace/sympy_py_internal:repository.bzl", "sympy_py_internal_repository") # noqa +load("//tools/workspace/tinygltf_internal:repository.bzl", "tinygltf_internal_repository") # noqa load("//tools/workspace/tinyobjloader_internal:repository.bzl", "tinyobjloader_internal_repository") # noqa load("//tools/workspace/tinyxml2_internal:repository.bzl", "tinyxml2_internal_repository") # noqa load("//tools/workspace/tomli_internal:repository.bzl", "tomli_internal_repository") # noqa @@ -292,6 +293,8 @@ def add_default_repositories(excludes = [], mirrors = DEFAULT_MIRRORS): suitesparse_internal_repository(name = "suitesparse_internal", mirrors = mirrors) # noqa if "sympy_py_internal" not in excludes: sympy_py_internal_repository(name = "sympy_py_internal", mirrors = mirrors) # noqa + if "tinygltf_internal" not in excludes: + tinygltf_internal_repository(name = "tinygltf_internal", mirrors = mirrors) # noqa if "tinyobjloader_internal" not in excludes: tinyobjloader_internal_repository(name = "tinyobjloader_internal", mirrors = mirrors) # noqa if "tinyxml2_internal" not in excludes: diff --git a/tools/workspace/tinygltf_internal/BUILD.bazel b/tools/workspace/tinygltf_internal/BUILD.bazel new file mode 100644 index 000000000000..b77b93ae0dbc --- /dev/null +++ b/tools/workspace/tinygltf_internal/BUILD.bazel @@ -0,0 +1,6 @@ +# This file exists to make our directory into a Bazel package, so that our +# neighboring *.bzl file can be loaded elsewhere. + +load("//tools/lint:lint.bzl", "add_lint_tests") + +add_lint_tests() diff --git a/tools/workspace/tinygltf_internal/package.BUILD.bazel b/tools/workspace/tinygltf_internal/package.BUILD.bazel new file mode 100644 index 000000000000..a33097c552d2 --- /dev/null +++ b/tools/workspace/tinygltf_internal/package.BUILD.bazel @@ -0,0 +1,37 @@ +# -*- bazel -*- + +load("@drake//tools/install:install.bzl", "install") +load("@drake//tools/workspace:vendor_cxx.bzl", "cc_library_vendored") + +licenses(["notice"]) # MIT + +package( + default_visibility = ["//visibility:public"], +) + +cc_library_vendored( + name = "tinygltf", + srcs = ["tiny_gltf.cc"], + srcs_vendored = ["drake_vendor/tiny_gltf.cc"], + hdrs = ["tiny_gltf.h"], + hdrs_vendored = ["drake_vendor/tiny_gltf.h"], + defines = [ + # Don't bother reading external images during glTF parsing; we'll + # handle while processing the resulting RenderMaterial. + "TINYGLTF_NO_EXTERNAL_IMAGE", + # We also won't use tinygltf to decode embedded images; we'll process + # the bytes in Drake. + "TINYGLTF_NO_STB_IMAGE", + "TINYGLTF_NO_STB_IMAGE_WRITE", + ], + includes = ["drake_vendor"], + linkstatic = 1, + deps = [ + "@nlohmann_internal//:nlohmann", + ], +) + +install( + name = "install", + docs = ["LICENSE"], +) diff --git a/tools/workspace/tinygltf_internal/patches/function_pointer.patch b/tools/workspace/tinygltf_internal/patches/function_pointer.patch new file mode 100644 index 000000000000..5a047df6d572 --- /dev/null +++ b/tools/workspace/tinygltf_internal/patches/function_pointer.patch @@ -0,0 +1,77 @@ +Replaces the typedef of a function pointer (for the image loading callback) +with the equivalent std::function alias. + +This doesn't bind *all* function pointer typedefs, just the ones that Drake +cares about right now. We've submitted a PR to tinygltf upgrading all such +callbacks and can eliminate this patch when our upstream version adopts it and +releases a new version. See https://github.com/syoyo/tinygltf/pull/489. + +--- tiny_gltf.h ++++ tiny_gltf.h +@@ -43,6 +43,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1292,10 +1293,9 @@ struct URICallbacks { + /// + /// LoadImageDataFunction type. Signature for custom image loading callbacks. + /// +-typedef bool (*LoadImageDataFunction)(Image *, const int, std::string *, +- std::string *, int, int, +- const unsigned char *, int, +- void *user_pointer); ++using LoadImageDataFunction = ++ std::function; + + /// + /// WriteImageDataFunction type. Signature for custom image writing callbacks. +@@ -4218,7 +4218,7 @@ static bool ParseImage(Image *image, const int image_idx, std::string *err, + bool store_original_json_for_extras_and_extensions, + const std::string &basedir, const size_t max_file_size, + FsCallbacks *fs, const URICallbacks *uri_cb, +- LoadImageDataFunction *LoadImageData = nullptr, ++ LoadImageDataFunction LoadImageData = nullptr, + void *load_image_user_data = nullptr) { + // A glTF image must either reference a bufferView or an image uri + +@@ -4349,14 +4349,14 @@ static bool ParseImage(Image *image, const int image_idx, std::string *err, + #endif + } + +- if (*LoadImageData == nullptr) { ++ if (LoadImageData == nullptr) { + if (err) { + (*err) += "No LoadImageData callback specified.\n"; + } + return false; + } +- return (*LoadImageData)(image, image_idx, err, warn, 0, 0, &img.at(0), +- static_cast(img.size()), load_image_user_data); ++ return LoadImageData(image, image_idx, err, warn, 0, 0, &img.at(0), ++ static_cast(img.size()), load_image_user_data); + } + + static bool ParseTexture(Texture *texture, std::string *err, +@@ -6307,7 +6307,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, + if (!ParseImage(&image, idx, err, warn, o, + store_original_json_for_extras_and_extensions_, base_dir, + max_external_file_size_, &fs, &uri_cb, +- &this->LoadImageData, load_image_user_data)) { ++ this->LoadImageData, load_image_user_data)) { + return false; + } + +@@ -6336,7 +6336,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, + } + const Buffer &buffer = model->buffers[size_t(bufferView.buffer)]; + +- if (*LoadImageData == nullptr) { ++ if (LoadImageData == nullptr) { + if (err) { + (*err) += "No LoadImageData callback specified.\n"; + } diff --git a/tools/workspace/tinygltf_internal/patches/json.patch b/tools/workspace/tinygltf_internal/patches/json.patch new file mode 100644 index 000000000000..b09d420d28b0 --- /dev/null +++ b/tools/workspace/tinygltf_internal/patches/json.patch @@ -0,0 +1,13 @@ +Redirects tinygltf to use Drake's vendored nlohmann json. + +--- tiny_gltf.h ++++ tiny_gltf.h +@@ -1705,7 +1705,7 @@ class TinyGLTF { + + #ifndef TINYGLTF_NO_INCLUDE_JSON + #ifndef TINYGLTF_USE_RAPIDJSON +-#include "json.hpp" ++#include + #else + #ifndef TINYGLTF_NO_INCLUDE_RAPIDJSON + #include "document.h" diff --git a/tools/workspace/tinygltf_internal/repository.bzl b/tools/workspace/tinygltf_internal/repository.bzl new file mode 100644 index 000000000000..ecddcd440cde --- /dev/null +++ b/tools/workspace/tinygltf_internal/repository.bzl @@ -0,0 +1,17 @@ +load("@drake//tools/workspace:github.bzl", "github_archive") + +def tinygltf_internal_repository( + name, + mirrors = None): + github_archive( + name = name, + repository = "syoyo/tinygltf", + commit = "v2.8.22", + sha256 = "97c3eb1080c1657cd749d0b49af189c6a867d5af30689c48d5e19521e7b5a070", # noqa + build_file = ":package.BUILD.bazel", + patches = [ + ":patches/function_pointer.patch", + ":patches/json.patch", + ], + mirrors = mirrors, + )