Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add rs-embed tool and update cameras 3d models #9764

Merged
merged 12 commits into from
Sep 14, 2021
23 changes: 13 additions & 10 deletions common/res/d415.h

Large diffs are not rendered by default.

23 changes: 13 additions & 10 deletions common/res/d435.h

Large diffs are not rendered by default.

23 changes: 13 additions & 10 deletions common/res/l500.h

Large diffs are not rendered by default.

23 changes: 13 additions & 10 deletions common/res/sr300.h

Large diffs are not rendered by default.

23 changes: 13 additions & 10 deletions common/res/t265.h

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions tools/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ add_subdirectory(fw-logger)
add_subdirectory(terminal)
add_subdirectory(recorder)
add_subdirectory(fw-update)
add_subdirectory(embed)

if(NOT WIN32)
if(BUILD_NETWORK_DEVICE)
Expand Down
38 changes: 38 additions & 0 deletions tools/embed/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# minimum required cmake version: 3.1.0
cmake_minimum_required(VERSION 3.1.0)

project(RealsenseToolsEmbed)
set(RS_TARGET rs-embed)

# Save the command line compile commands in the build output
set(CMAKE_EXPORT_COMPILE_COMMANDS 1)

include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X)
if(COMPILER_SUPPORTS_CXX11)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
elseif(COMPILER_SUPPORTS_CXX0X)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
endif()

# embed
add_executable(${RS_TARGET}
rs-embed.cpp
${LZ4_DIR}/lz4.h
${LZ4_DIR}/lz4.c
)

include_directories(${RS_TARGET} ${LZ4_DIR} ../../third-party ../../third-party/tclap/include)

set_target_properties (${RS_TARGET} PROPERTIES
FOLDER "Tools"
)

install(
TARGETS
${RS_TARGET}
RUNTIME DESTINATION
${CMAKE_INSTALL_PREFIX}/bin
)
41 changes: 41 additions & 0 deletions tools/embed/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# rs-embed Tool

## Goal

The rs-embed tool is used for embedding binary resources into code, it is an effective solution for distributing 3D-models, raster graphics and fonts.

This tool offers a standard way this can be done across the SDK.

## Description
This tool can embed 3d object / png files into code by generating a header file with all static data.
a common use case which use it inside libRealSense is rendering the 3D model on the [realsense-viewer tool](../realsense-viewer).

### 3D object file
The tool will read the input obj file, compress it using LZ4 compression tool and create a header file with the compressed data and an `uncompress(...)` method

### PNG file
The tool will read the input png file, and create a header file with the compressed data array and size

## Command Line Parameters

|Flag |Description |Default|
|---|---|---|
|`-i <input-file>`|png / obj file that we want to embed||
|`-o <output-file>`|The desired output file path||
|`-n <object-name>`|The embedded object name for the array/function name created||
|`--version`|Get the tool version string||

For example:
`rs-embed.exe -n d435 -o d435.h -i "inputs/d435.obj"`
Will create a header for the 3D model of the d435 obj file given as input.
The array name that will be generated is `static uint32_t d435_obj_data [] {...}`
and the decompress function signature will be `inline void uncompress_d435_obj(...)`.

## How to embed a RealSense device 3D model into code.
Here we will learn how to generate an embed a 3D model into code.

1. Download the requested CAD model from the [RealSense website](https://dev.intelrealsense.com/docs/cad-files).
2. Convert the `step` file into an `obj` file. (Can be done using [freecad](https://wiki.freecadweb.org/Download) application)
maloel marked this conversation as resolved.
Show resolved Hide resolved
3. Optional - Reduce object size by narrowing data (Can be done using [meshmixer](https://www.meshmixer.com/download.html) application)
* [Here](https://i.materialise.com/blog/en/reduce-the-file-size-of-stl-and-obj-3d-models/) is a guide on how to reduce obj side using meshmixer application.
maloel marked this conversation as resolved.
Show resolved Hide resolved
4. Run rs-embed tool with the generated object as input.
235 changes: 235 additions & 0 deletions tools/embed/rs-embed.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
// License: Apache 2.0. See LICENSE file in root directory.
// Copyright(c) 2021 Intel Corporation. All Rights Reserved.

#include <iostream>
#include <string>
#include <string.h> // memcpy
#include <fstream>
#include <vector>

#include "tclap/CmdLine.h"
maloel marked this conversation as resolved.
Show resolved Hide resolved
#include <lz4.h>

#define STB_IMAGE_IMPLEMENTATION
#include "../third-party/stb_image.h"
maloel marked this conversation as resolved.
Show resolved Hide resolved


#define RS_EMBED_VERSION "0.0.0.1"

struct float3
{
float x, y, z;
};

struct short3
{
uint16_t x, y, z;
};

using namespace std;
using namespace TCLAP;

bool file_exists(const std::string& name) {
std::ifstream f(name.c_str());
return f.good();
}

struct int6
{
int x, y, z, a, b, c;
};

bool ends_with(const std::string& s, const std::string& suffix)
{
auto i = s.rbegin(), j = suffix.rbegin();
for (; i != s.rend() && j != suffix.rend() && *i == *j;
i++, j++);
return j == suffix.rend();
}

std::string get_current_time()
{
auto t = time(nullptr);
char buffer[20] = {};
const tm* time = localtime(&t);
if (nullptr != time)
strftime(buffer, sizeof(buffer), "%m/%d/%Y %H:%M:%S", time);
return std::string(buffer);
}

int main(int argc, char** argv) try
{
// Parse command line arguments
CmdLine cmd("librealsense rs-embed tool", ' ', RS_EMBED_VERSION);
ValueArg<string> inputFilename("i", "input", "Input filename", true, "", "input-file");
ValueArg<string> outputFilename("o", "output", "Output filename", false, "", "output-file");
ValueArg<string> objectName("n", "name", "Name", false, "", "object-name");

cmd.add(inputFilename);
cmd.add(outputFilename);
cmd.add(objectName);
cmd.parse(argc, argv);

auto input = inputFilename.getValue();
auto output = outputFilename.getValue();
auto name = objectName.getValue();

if (ends_with(input, ".obj"))
{
std::vector<float3> vertex_data;
std::vector<float3> normals_raw_data;
std::vector<float3> normals_data;
std::vector<int6> index_raw_data;
std::vector<short3> index_data;

if (file_exists(input))
{
std::ifstream file(input);
std::string str;
while (std::getline(file, str))
{
if (str.size())
{
if (str[0] == 'v')
{
float a, b, c;
if (str[1] == 'n')
{
sscanf(str.c_str(), "vn %f %f %f", &a, &b, &c);
normals_raw_data.push_back({ a, b, c });
}
else
{
sscanf(str.c_str(), "v %f %f %f", &a, &b, &c);
vertex_data.push_back({ a, b, c });
}
}
if (str[0] == 'f')
{
int x, y, z, a, b, c;
sscanf(str.c_str(), "f %d//%d %d//%d %d//%d", &x, &a, &y, &b, &z, &c);
index_raw_data.push_back({ x, y, z, a, b, c });
}
}
}
}
else
{
std::cout << "file: " << input << " could not be found!" << std::endl;
return EXIT_FAILURE;
}

normals_data.resize(vertex_data.size());
for (auto& idx : index_raw_data)
{
// TODO - normals data are currently disabled
// normals_data[idx.x] = normals_raw_data[idx.a];
// normals_data[idx.y] = normals_raw_data[idx.b];
// normals_data[idx.z] = normals_raw_data[idx.c];
index_data.push_back({ static_cast<uint16_t>(idx.x - 1),
static_cast<uint16_t>(idx.y - 1),
static_cast<uint16_t>(idx.z - 1) });
}

size_t vertex_data_size = vertex_data.size() * sizeof(float3);
size_t index_data_size = index_data.size() * sizeof(short3);
//size_t normals_data_size = normals_data.size() * sizeof(float3);

std::vector<uint8_t> data(vertex_data_size + index_data_size, 0);
memcpy(data.data(), vertex_data.data(), vertex_data_size);
memcpy(data.data() + vertex_data_size, index_data.data(), index_data_size);
//memcpy(data.data() + vertex_data_size + index_data_size, normals_data.data(), normals_data_size);

// compress szSource into pchCompressed
auto rawDataSize = (int)data.size();
auto compressBufSize = LZ4_compressBound(rawDataSize);
char* pchCompressed = new char[compressBufSize];
memset(pchCompressed, compressBufSize, 0);
int nCompressedSize = LZ4_compress_default((const char*)data.data(), pchCompressed, rawDataSize, compressBufSize);


ofstream myfile;
myfile.open(output);
myfile << "// License: Apache 2.0. See LICENSE file in root directory.\n";
myfile << "// Copyright(c) 2021 Intel Corporation. All Rights Reserved.\n\n";
myfile << "// This file is auto-generated from " << name << ".obj using rs-embed tool version: " << RS_EMBED_VERSION <<"\n";
myfile << "// Generation time: " << get_current_time() << ".\n\n";
myfile << "#pragma once\n";
myfile << "static uint32_t " << name << "_obj_data [] { ";

auto nAllignedCompressedSize = nCompressedSize;
auto leftover = nCompressedSize % 4;
if (leftover % 4 != 0) nAllignedCompressedSize += (4 - leftover);

for (int i = 0; i < nAllignedCompressedSize; i += 4)
{
uint32_t* ptr = (uint32_t*)(pchCompressed + i);
myfile << "0x" << std::hex << (int)(*ptr);
if (i < nAllignedCompressedSize - 1) myfile << ",";
}

myfile << "};\n";

myfile << "#include <lz4.h>\n";
myfile << "#include <vector>\n";

myfile << "inline void uncompress_" << name << "_obj(std::vector<float3>& vertex_data, std::vector<float3>& normals, std::vector<short3>& index_data)\n";
myfile << "{\n";
myfile << " std::vector<char> uncompressed(0x" << std::hex << rawDataSize << ", 0);\n";
myfile << " LZ4_decompress_safe((const char*)" << name << "_obj_data, uncompressed.data(), 0x" << std::hex << nCompressedSize << ", 0x" << std::hex << rawDataSize << ");\n";
myfile << " const int vertex_size = 0x" << std::hex << vertex_data.size() << " * sizeof(float3);\n";
myfile << " const int index_size = 0x" << std::hex << index_data.size() << " * sizeof(short3);\n";
myfile << " vertex_data.resize(0x" << std::hex << vertex_data.size() << ");\n";
myfile << " memcpy(vertex_data.data(), uncompressed.data(), vertex_size);\n";
myfile << " index_data.resize(0x" << std::hex << index_data.size() << ");\n";
myfile << " memcpy(index_data.data(), uncompressed.data() + vertex_size, index_size);\n";
myfile << " //normals.resize(0x" << std::hex << vertex_data.size() << ");\n";
myfile << " //memcpy(normals.data(), uncompressed.data() + vertex_size + index_size, vertex_size);\n";
myfile << "}\n";

myfile.close();
}

if (ends_with(input, ".png"))
{
ifstream ifs(input, ios::binary | ios::ate);
ifstream::pos_type pos = ifs.tellg();

std::vector<char> buffer(pos);

ifs.seekg(0, ios::beg);
ifs.read(&buffer[0], pos);

ofstream myfile;
myfile.open(output);
myfile << "// License: Apache 2.0. See LICENSE file in root directory.\n";
myfile << "// Copyright(c) 2021 Intel Corporation. All Rights Reserved.\n\n";
myfile << "// This file is auto-generated from " << name << ".png using rs-embed tool version: " << RS_EMBED_VERSION << "\n";
myfile << "// Generation time: " << get_current_time() << ".\n\n";

myfile << "static uint32_t " << name << "_png_size = 0x" << std::hex << buffer.size() << ";\n";

myfile << "static uint8_t " << name << "_png_data [] { ";
for (int i = 0; i < buffer.size(); i++)
{
uint8_t byte = buffer[i];
myfile << "0x" << std::hex << (int)byte;
if (i < buffer.size() - 1) myfile << ",";
}
myfile << "};\n";

myfile.close();
}

return EXIT_SUCCESS;
}
catch (const exception& e)
{
cerr << e.what() << endl;
return EXIT_FAILURE;
}
catch (...)
{
cerr << "some error" << endl;
return EXIT_FAILURE;
}