Skip to content

Commit

Permalink
Gfx-replay and JSON serialization fixes (#1758)
Browse files Browse the repository at this point in the history
* gfx-replay scaling fix (relevant for URDF scaling)

* add file-write error-checking to Recorder::writeSavedKeyframesToFile

* force compile errors for missing JSON serialization functions

* fix JSON serialize compile issues

* add python binding for gfx-replay writeSavedKeyframesToString

* add JSON serialization for std::pair

* update comment

* formatting fix
  • Loading branch information
eundersander authored Jul 14, 2022
1 parent d57275e commit 0c33790
Show file tree
Hide file tree
Showing 9 changed files with 146 additions and 68 deletions.
12 changes: 12 additions & 0 deletions src/esp/bindings/GfxReplayBindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,18 @@ void initGfxReplayBindings(py::module& m) {
},
R"(Write all saved keyframes to a file, then discard the keyframes.)")

.def(
"write_saved_keyframes_to_string",
[](ReplayManager& self) {
if (!self.getRecorder()) {
throw std::runtime_error(
"replay save not enabled. See "
"SimulatorConfiguration.enable_gfx_replay_save.");
}
return self.getRecorder()->writeSavedKeyframesToString();
},
R"(Write all saved keyframes to a string, then discard the keyframes.)")

.def("read_keyframes_from_file", &ReplayManager::readKeyframesFromFile,
R"(Create a Player object from a replay file.)");
}
Expand Down
19 changes: 16 additions & 3 deletions src/esp/gfx/replay/Recorder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "Recorder.h"

#include "esp/assets/RenderAssetInstanceCreationInfo.h"
#include "esp/core/Check.h"
#include "esp/io/Json.h"
#include "esp/io/JsonAllTypes.h"
#include "esp/scene/SceneNode.h"
Expand Down Expand Up @@ -54,7 +55,18 @@ void Recorder::onCreateRenderAssetInstance(

RenderAssetInstanceKey instanceKey = getNewInstanceKey();

getKeyframe().creations.emplace_back(std::make_pair(instanceKey, creation));
auto adjustedCreation = creation;

// bake node scale into creation
auto nodeScale = node->absoluteTransformation().scaling();
if (nodeScale != Mn::Vector3(1.f, 1.f, 1.f)) {
adjustedCreation.scale = adjustedCreation.scale
? *adjustedCreation.scale * nodeScale
: nodeScale;
}

getKeyframe().creations.emplace_back(
std::make_pair(instanceKey, adjustedCreation));

// Constructing NodeDeletionHelper here is equivalent to calling
// node->addFeature. We keep a pointer to deletionHelper so we can delete it
Expand Down Expand Up @@ -175,8 +187,9 @@ void Recorder::writeSavedKeyframesToFile(const std::string& filepath,
auto document = writeKeyframesToJsonDocument();
// replay::Keyframes use floats (not doubles) so this is plenty of precision
const float maxDecimalPlaces = 7;
esp::io::writeJsonToFile(document, filepath, usePrettyWriter,
maxDecimalPlaces);
auto ok = esp::io::writeJsonToFile(document, filepath, usePrettyWriter,
maxDecimalPlaces);
ESP_CHECK(ok, "writeSavedKeyframesToFile: unable to write to " << filepath);

consolidateSavedKeyframes();
}
Expand Down
1 change: 1 addition & 0 deletions src/esp/io/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ add_library(
JsonEspTypes.h
JsonMagnumTypes.cpp
JsonMagnumTypes.h
JsonStlTypes.cpp
JsonStlTypes.h
JsonUtils.h
URDFParser.cpp
Expand Down
3 changes: 3 additions & 0 deletions src/esp/io/JsonBuiltinTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ void addMember(JsonGenericValue& value,
template <typename T>
bool readMember(const JsonGenericValue& value, const char* name, T& x);

#if 0 // reference code to produce runtime errors for missing implementations
// (instead of compile errors)
/**
* @brief Fallback implementation for fromJsonValue to produce a runtime error
* for types that haven't implemented fromJsonValue.
Expand Down Expand Up @@ -70,6 +72,7 @@ JsonGenericValue toJsonValue(const T&, JsonAllocator&) {
<< typeid(T).name() << ".";
return JsonGenericValue(rapidjson::kObjectType);
}
#endif

// toJsonValue wrappers for the 7 rapidjson builtin types. A JsonGenericValue
// can be directly constructed from the builtin types.
Expand Down
56 changes: 56 additions & 0 deletions src/esp/io/JsonEspTypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,5 +101,61 @@ bool fromJsonValue(const JsonGenericValue& obj,
return true;
}

JsonGenericValue toJsonValue(const esp::assets::AssetInfo& x,
JsonAllocator& allocator) {
JsonGenericValue obj(rapidjson::kObjectType);
addMemberAsUint32(obj, "type", x.type, allocator);
addMember(obj, "filepath", x.filepath, allocator);
addMember(obj, "frame", x.frame, allocator);
addMember(obj, "virtualUnitToMeters", x.virtualUnitToMeters, allocator);
addMember(obj, "forceFlatShading", x.forceFlatShading, allocator);
addMember(obj, "splitInstanceMesh", x.splitInstanceMesh, allocator);
addMember(obj, "shaderTypeToUse", x.shaderTypeToUse, allocator);
addMember(obj, "overridePhongMaterial", x.overridePhongMaterial, allocator);
addMember(obj, "hasSemanticTextures", x.hasSemanticTextures, allocator);

return obj;
}

bool fromJsonValue(const JsonGenericValue& obj, esp::assets::AssetInfo& x) {
readMemberAsUint32(obj, "type", x.type);
readMember(obj, "filepath", x.filepath);
readMember(obj, "frame", x.frame);
readMember(obj, "virtualUnitToMeters", x.virtualUnitToMeters);
readMember(obj, "forceFlatShading", x.forceFlatShading);
readMember(obj, "splitInstanceMesh", x.splitInstanceMesh);
readMember(obj, "shaderTypeToUse", x.shaderTypeToUse);
readMember(obj, "overridePhongMaterial", x.overridePhongMaterial);
readMember(obj, "hasSemanticTextures", x.hasSemanticTextures);
return true;
}

JsonGenericValue toJsonValue(
const metadata::attributes::ObjectInstanceShaderType& x,
JsonAllocator& allocator) {
return toJsonValue(metadata::attributes::getShaderTypeName(x), allocator);
}

bool fromJsonValue(const JsonGenericValue& obj,
metadata::attributes::ObjectInstanceShaderType& x) {
std::string shaderTypeToUseString;
// read as string
bool shaderTypeSucceess = fromJsonValue(obj, shaderTypeToUseString);
// convert to enum
if (shaderTypeSucceess) {
const std::string shaderTypeLC =
Cr::Utility::String::lowercase(shaderTypeToUseString);
auto mapIter = metadata::attributes::ShaderTypeNamesMap.find(shaderTypeLC);
ESP_CHECK(
mapIter != metadata::attributes::ShaderTypeNamesMap.end(),
"Illegal shader_type value"
<< shaderTypeToUseString
<< "specified in JSON to be used to set AssetInfo.shaderTypeToUse. "
"Aborting.");
x = mapIter->second;
}
return shaderTypeSucceess;
}

} // namespace io
} // namespace esp
59 changes: 7 additions & 52 deletions src/esp/io/JsonEspTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,62 +81,17 @@ inline bool fromJsonValue(const JsonGenericValue& obj,
return success;
}

inline JsonGenericValue toJsonValue(const esp::assets::AssetInfo& x,
JsonAllocator& allocator) {
JsonGenericValue obj(rapidjson::kObjectType);
addMemberAsUint32(obj, "type", x.type, allocator);
addMember(obj, "filepath", x.filepath, allocator);
addMember(obj, "frame", x.frame, allocator);
addMember(obj, "virtualUnitToMeters", x.virtualUnitToMeters, allocator);
addMember(obj, "forceFlatShading", x.forceFlatShading, allocator);
addMember(obj, "splitInstanceMesh", x.splitInstanceMesh, allocator);
addMember(obj, "shaderTypeToUse", x.shaderTypeToUse, allocator);
addMember(obj, "overridePhongMaterial", x.overridePhongMaterial, allocator);
addMember(obj, "hasSemanticTextures", x.hasSemanticTextures, allocator);
JsonGenericValue toJsonValue(const esp::assets::AssetInfo& x,
JsonAllocator& allocator);

return obj;
}
bool fromJsonValue(const JsonGenericValue& obj, esp::assets::AssetInfo& x);

inline bool fromJsonValue(const JsonGenericValue& obj,
esp::assets::AssetInfo& x) {
readMemberAsUint32(obj, "type", x.type);
readMember(obj, "filepath", x.filepath);
readMember(obj, "frame", x.frame);
readMember(obj, "virtualUnitToMeters", x.virtualUnitToMeters);
readMember(obj, "forceFlatShading", x.forceFlatShading);
readMember(obj, "splitInstanceMesh", x.splitInstanceMesh);
readMember(obj, "shaderTypeToUse", x.shaderTypeToUse);
readMember(obj, "overridePhongMaterial", x.overridePhongMaterial);
readMember(obj, "hasSemanticTextures", x.hasSemanticTextures);
return true;
}

inline JsonGenericValue toJsonValue(
JsonGenericValue toJsonValue(
const metadata::attributes::ObjectInstanceShaderType& x,
JsonAllocator& allocator) {
return toJsonValue(metadata::attributes::getShaderTypeName(x), allocator);
}
JsonAllocator& allocator);

inline bool fromJsonValue(const JsonGenericValue& obj,
metadata::attributes::ObjectInstanceShaderType& x) {
std::string shaderTypeToUseString;
// read as string
bool shaderTypeSucceess = fromJsonValue(obj, shaderTypeToUseString);
// convert to enum
if (shaderTypeSucceess) {
const std::string shaderTypeLC =
Cr::Utility::String::lowercase(shaderTypeToUseString);
auto mapIter = metadata::attributes::ShaderTypeNamesMap.find(shaderTypeLC);
ESP_CHECK(
mapIter != metadata::attributes::ShaderTypeNamesMap.end(),
"Illegal shader_type value"
<< shaderTypeToUseString
<< "specified in JSON to be used to set AssetInfo.shaderTypeToUse. "
"Aborting.");
x = mapIter->second;
}
return shaderTypeSucceess;
}
bool fromJsonValue(const JsonGenericValue& obj,
metadata::attributes::ObjectInstanceShaderType& x);

inline JsonGenericValue toJsonValue(
const esp::assets::RenderAssetInstanceCreationInfo& x,
Expand Down
26 changes: 26 additions & 0 deletions src/esp/io/JsonStlTypes.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (c) Facebook, Inc. and its affiliates.
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.

#include "JsonAllTypes.h"

namespace esp {
namespace io {

JsonGenericValue toJsonValue(const std::string& str, JsonAllocator& allocator) {
JsonGenericValue strObj;
strObj.SetString(str.c_str(), allocator);
return strObj;
}

bool fromJsonValue(const JsonGenericValue& obj, std::string& val) {
if (obj.IsString()) {
val = obj.GetString();
return true;
}
ESP_ERROR() << "Invalid string value";
return false;
}

} // namespace io
} // namespace esp
32 changes: 19 additions & 13 deletions src/esp/io/JsonStlTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,7 @@
namespace esp {
namespace io {

inline JsonGenericValue toJsonValue(const std::string& str,
JsonAllocator& allocator) {
JsonGenericValue strObj;
strObj.SetString(str.c_str(), allocator);
return strObj;
}
JsonGenericValue toJsonValue(const std::string& str, JsonAllocator& allocator);

/**
* @brief Populate passed @p val with value. Returns whether successfully
Expand All @@ -30,13 +25,24 @@ inline JsonGenericValue toJsonValue(const std::string& str,
* @param val destination value to be populated
* @return whether successful or not
*/
inline bool fromJsonValue(const JsonGenericValue& obj, std::string& val) {
if (obj.IsString()) {
val = obj.GetString();
return true;
}
ESP_ERROR() << "Invalid string value";
return false;
bool fromJsonValue(const JsonGenericValue& obj, std::string& val);

template <typename T_first, typename T_second>
inline JsonGenericValue toJsonValue(const std::pair<T_first, T_second>& val,
JsonAllocator& allocator) {
esp::io::JsonGenericValue obj(rapidjson::kObjectType);
esp::io::addMember(obj, "first", val.first, allocator);
esp::io::addMember(obj, "second", val.second, allocator);
return obj;
}

template <typename T_first, typename T_second>
inline bool fromJsonValue(const JsonGenericValue& obj,
std::pair<T_first, T_second>& val) {
bool success = true;
success &= readMember(obj, "first", val.first);
success &= readMember(obj, "second", val.second);
return success;
}

// For std::vector, we use rapidjson::kArrayType. For an empty vector, we
Expand Down
6 changes: 6 additions & 0 deletions src/tests/IOTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,12 @@ void IOTest::testJsonStlTypes() {
std::string s{"hello world"};
_testJsonReadWrite(s, "s", d);

std::pair<float, std::string> pair(1.5f, "second");
esp::io::addMember(d, "pair", pair, allocator);
std::pair<float, std::string> pair2;
CORRADE_VERIFY(esp::io::readMember(d, "pair", pair2));
CORRADE_COMPARE(pair2, pair);

// test a vector of ints
std::vector<int> vec{3, 4, 5, 6};
_testJsonReadWrite(vec, "vec", d);
Expand Down

0 comments on commit 0c33790

Please sign in to comment.