Skip to content

Commit

Permalink
C++ example for asset3d_out_of_tree and related cleanup (#3913)
Browse files Browse the repository at this point in the history
### What

* part of  #3751
* use Result for from_file
* move heavy impl to c++ (codegen TODO: need to not leak heavy headers
into public; make putting headers in _ext opt-in!)
* (unrelated) rolled back some local vscode cmake settings that weren't
that great after all

<img width="1269" alt="image"
src="https://github.com/rerun-io/rerun/assets/1220815/f98de83b-1f29-48a9-8703-39e05a9a4b26">


### Checklist
* [x] I have read and agree to [Contributor
Guide](https://github.com/rerun-io/rerun/blob/main/CONTRIBUTING.md) and
the [Code of
Conduct](https://github.com/rerun-io/rerun/blob/main/CODE_OF_CONDUCT.md)
* [x] I've included a screenshot or gif (if applicable)
* [x] I have tested [demo.rerun.io](https://demo.rerun.io/pr/3913) (if
applicable)
* [x] The PR title and labels are set such as to maximize their
usefulness for the next release's CHANGELOG

- [PR Build Summary](https://build.rerun.io/pr/3913)
- [Docs
preview](https://rerun.io/preview/317506389d3fc84a38d3c60db7843d35875898c2/docs)
<!--DOCS-PREVIEW-->
- [Examples
preview](https://rerun.io/preview/317506389d3fc84a38d3c60db7843d35875898c2/examples)
<!--EXAMPLES-PREVIEW-->
- [Recent benchmark results](https://ref.rerun.io/dev/bench/)
- [Wasm size tracking](https://ref.rerun.io/dev/sizes/)
  • Loading branch information
Wumpf authored Oct 18, 2023
1 parent 15902d2 commit 2b7baef
Show file tree
Hide file tree
Showing 10 changed files with 136 additions and 76 deletions.
4 changes: 0 additions & 4 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,6 @@
],
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools", // Use cmake-tools to grab configs.
"cmake.buildDirectory": "${workspaceRoot}/build/",
"cmake.buildTask": true, // Use a terminal instead of the output panel for builds since builds don't show colors in the output panel.
"cmake.environment": {
"CLICOLOR_FORCE": "1" // Force color output from cmake.
},
"C_Cpp.autoAddFileAssociations": false,
"rust-analyzer.showUnlinkedFileNotification": false,
"black-formatter.args": [
Expand Down
39 changes: 39 additions & 0 deletions docs/code-examples/asset3d_out_of_tree.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Log a simple 3D asset with an out-of-tree transform which will not affect its children.

#include <exception>
#include <rerun.hpp>
#include <rerun/demo_utils.hpp>

int main(int argc, char** argv) {
if (argc < 2) {
throw std::runtime_error("Usage: <path_to_asset.[gltf|glb]>");
}
auto path = argv[1];

auto rec = rerun::RecordingStream("rerun_example_asset3d_out_of_tree");
rec.connect("127.0.0.1:9876").throw_on_failure();

rec.log_timeless("world", rerun::ViewCoordinates::RIGHT_HAND_Z_UP); // Set an up-axis

rec.set_time_sequence("frame", 0);
rec.log("world/asset", rerun::Asset3D::from_file(path).value_or_throw());
// Those points will not be affected by their parent's out-of-tree transform!
rec.log(
"world/asset/points",
rerun::Points3D(rerun::demo::grid({-10.0f, -10.0f, -10.0f}, {10.0f, 10.0f, 10.0f}, 10))
);

for (int64_t i = 1; i < 20; ++i) {
rec.set_time_sequence("frame", i);

// Modify the asset's out-of-tree transform: this will not affect its children (i.e. the points)!
auto translation =
rerun::TranslationRotationScale3D({0.0, 0.0, static_cast<float>(i) - 10.0f});
rec.log(
"world/asset",
rerun::ComponentBatch<rerun::OutOfTreeTransform3D>(
rerun::OutOfTreeTransform3D(translation)
)
);
}
}
2 changes: 1 addition & 1 deletion docs/code-examples/asset3d_simple.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,5 @@ int main(int argc, char* argv[]) {
rec.connect("127.0.0.1:9876").throw_on_failure();

rec.log_timeless("world", rerun::ViewCoordinates::RIGHT_HAND_Z_UP); // Set an up-axis
rec.log("world/asset", rerun::Asset3D::from_file(path));
rec.log("world/asset", rerun::Asset3D::from_file(path).value_or_throw());
}
3 changes: 1 addition & 2 deletions docs/code-examples/roundtrips.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
# for one or more specific SDKs.
opt_out_run = {
"any_values": ["cpp", "rust"], # Not yet implemented
"asset3d_out_of_tree": ["cpp"], # TODO(#2919): Not yet implemented in C++
"custom_data": ["cpp"], # TODO(#2919): Not yet implemented in C++
"extra_values": ["cpp", "rust"], # Missing examples
"image_advanced": ["cpp", "rust"], # Missing examples
Expand All @@ -39,7 +38,7 @@
# data, but you still want to check whether the test runs properly and outputs _something_.
opt_out_compare = {
"arrow3d_simple": ["cpp", "py", "rust"], # TODO(#3206): examples use different RNGs
"asset3d_out_of_tree": ["py", "rust"], # # float precision issues
"asset3d_out_of_tree": ["cpp", "py", "rust"], # float issues since calculation is done slightly differently (also, Python uses doubles)
"mesh3d_partial_updates": ["cpp", "py", "rust"], # float precision issues
"pinhole_simple": ["cpp", "py", "rust"], # TODO(#3206): examples use different RNGs
"point2d_random": ["cpp", "py", "rust"], # TODO(#3206): examples use different RNGs
Expand Down
36 changes: 4 additions & 32 deletions rerun_cpp/src/rerun/archetypes/asset3d.hpp

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

74 changes: 40 additions & 34 deletions rerun_cpp/src/rerun/archetypes/asset3d_ext.cpp
Original file line number Diff line number Diff line change
@@ -1,46 +1,49 @@
#include <algorithm>
#include <filesystem>
#include <fstream>
#include <filesystem> // TODO(andreas): Should not leak into public header.
#include <fstream> // TODO(andreas): Should not leak into public header.
#include <string>

#include "asset3d.hpp"

// #define EDIT_EXTENSION
//#define EDIT_EXTENSION

namespace rerun {
namespace archetypes {

#ifdef EDIT_EXTENSION
// [CODEGEN COPY TO HEADER START]

static std::optional<rerun::components::MediaType> guess_media_type(
const std::string& path //
) {
std::filesystem::path file_path(path);
std::string ext = file_path.extension().string();
std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);

if (ext == ".glb") {
return rerun::components::MediaType::glb();
} else if (ext == ".gltf") {
return rerun::components::MediaType::gltf();
} else if (ext == ".obj") {
return rerun::components::MediaType::obj();
} else {
return std::nullopt;
}
}
static std::optional<rerun::components::MediaType> guess_media_type(const std::string& path
);

/// Creates a new [`Asset3D`] from the file contents at `path`.
///
/// The [`MediaType`] will be guessed from the file extension.
///
/// If no [`MediaType`] can be guessed at the moment, the Rerun Viewer will try to guess one
/// from the data at render-time. If it can't, rendering will fail with an error.
static Asset3D from_file(const std::filesystem::path& path) {
static Result<Asset3D> from_file(const std::filesystem::path& path);

/// Creates a new [`Asset3D`] from the given `bytes`.
///
/// If no [`MediaType`] is specified, the Rerun Viewer will try to guess one from the data
/// at render-time. If it can't, rendering will fail with an error.
static Asset3D from_bytes(
const std::vector<uint8_t> bytes, std::optional<rerun::components::MediaType> media_type
) {
// TODO(cmc): we could try and guess using magic bytes here, like rust does.
Asset3D asset = Asset3D(bytes);
asset.media_type = media_type;
return asset;
}

// [CODEGEN COPY TO HEADER END]
#endif

Result<Asset3D> Asset3D::from_file(const std::filesystem::path& path) {
std::ifstream file(path, std::ios::binary);
if (!file) {
throw std::runtime_error("Failed to open file: " + path.string());
return Error(ErrorCode::FileOpenFailure, "Failed to open file: " + path.string());
}

file.seekg(0, std::ios::end);
Expand All @@ -53,20 +56,23 @@ namespace rerun {
return Asset3D::from_bytes(data, Asset3D::guess_media_type(path));
}

/// Creates a new [`Asset3D`] from the given `bytes`.
///
/// If no [`MediaType`] is specified, the Rerun Viewer will try to guess one from the data
/// at render-time. If it can't, rendering will fail with an error.
static Asset3D from_bytes(
const std::vector<uint8_t> bytes, std::optional<rerun::components::MediaType> media_type
std::optional<rerun::components::MediaType> Asset3D::guess_media_type(
const std::string& path //
) {
// TODO(cmc): we could try and guess using magic bytes here, like rust does.
Asset3D asset = Asset3D(bytes);
asset.media_type = media_type;
return asset;
std::filesystem::path file_path(path);
std::string ext = file_path.extension().string();
std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);

if (ext == ".glb") {
return rerun::components::MediaType::glb();
} else if (ext == ".gltf") {
return rerun::components::MediaType::gltf();
} else if (ext == ".obj") {
return rerun::components::MediaType::obj();
} else {
return std::nullopt;
}
}

// [CODEGEN COPY TO HEADER END]
#endif
} // namespace archetypes
} // namespace rerun
38 changes: 38 additions & 0 deletions rerun_cpp/src/rerun/demo_utils.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Utilities used in examples.

#include <algorithm>
#include <cmath>
#include "components/position3d.hpp"

namespace rerun {
namespace demo {

/// Linearly interpolates from `a` through `b` in `n` steps, returning the intermediate result at
/// each step.
template <typename T>
std::vector<T> linspace(T start, T end, size_t num) {
std::vector<T> linspaced(num);
std::generate(linspaced.begin(), linspaced.end(), [&, i = 0]() mutable {
return start + i++ * (end - start) / static_cast<T>(num - 1);
});
return linspaced;
}

/// Given two 3D vectors `from` and `to`, linearly interpolates between them in `n` steps along
/// the three axes, returning the intermediate result at each step.
std::vector<components::Position3D> grid(
components::Position3D from, components::Position3D to, size_t n
) {
std::vector<components::Position3D> output;
for (float z : linspace(from.z(), to.z(), n)) {
for (float y : linspace(from.y(), to.y(), n)) {
for (float x : linspace(from.x(), to.x(), n)) {
output.push_back({x, y, z});
}
}
}
return output;
}

} // namespace demo
} // namespace rerun
4 changes: 4 additions & 0 deletions rerun_cpp/src/rerun/error.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ namespace rerun {
ArrowIpcMessageParsingFailure,
ArrowDataCellError,

// Errors relating to file IO.
_CategoryFileIO = 0x0001'0000,
FileOpenFailure,

// Errors directly translated from arrow::StatusCode.
_CategoryArrowCppStatus = 0x1000'0000,
ArrowStatusCode_KeyError,
Expand Down
8 changes: 7 additions & 1 deletion rerun_cpp/src/rerun/result.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,16 @@ namespace rerun {

#ifdef __cpp_exceptions
/// Returns the value if status is ok, throws otherwise.
T value_or_throw() const {
const T& value_or_throw() const& {
error.throw_on_failure();
return value;
}

/// Returns the value if status is ok, throws otherwise.
T value_or_throw() && {
error.throw_on_failure();
return std::move(value);
}
#endif

public:
Expand Down
4 changes: 2 additions & 2 deletions tests/cpp/roundtrips/image/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ rerun::half half_from_float(const float x) {
const uint32_t e = (b & 0x7F800000) >> 23; // exponent
// mantissa; in line below: 0x007FF000 = 0x00800000-0x00001000 = decimal indicator flag - initial rounding
const uint32_t m = b & 0x007FFFFF;
const uint16_t f16 = (b & 0x80000000) >> 16 |
const uint32_t f16 = (b & 0x80000000) >> 16 |
(e > 112) * ((((e - 112) << 10) & 0x7C00) | m >> 13) |
((e < 113) & (e > 101)) * ((((0x007FF000 + m) >> (125 - e)) + 1) >> 1) |
(e > 143) * 0x7FFF; // sign : normalized : denormalized : saturate
return rerun::half{f16};
return rerun::half{static_cast<uint16_t>(f16)};
}

int main(int argc, char** argv) {
Expand Down

0 comments on commit 2b7baef

Please sign in to comment.