Merge branch 'master' into NoRenderingMode

This commit is contained in:
Manish 2019-01-30 10:49:15 +01:00
commit 23ef5d73cc
115 changed files with 2352 additions and 1151 deletions

View File

@ -4,11 +4,25 @@
* Upgraded Boost to 1.69.0
* Added point transformation functionality for LibCarla and PythonAPI
* Added "sensor_tick" attribute to sensors (cameras and lidars) to specify the capture rate in seconds
* Added Export/Import map tools
* Added "get_forward_vector()" to rotation and transform, retrieves the unit vector on the rotation's X-axis
* Added support for Deepin in PythonAPI's setup.py
* Added support for spawning and controlling walkers (pedestrians)
* Renamed vehicle.get_vehicle_control() to vehicle.get_control() to be consistent with walkers
* Remove crash reporter from packaged build
* Added a few methods to manage an actor:
- set_velocity: for setting the linear velocity
- set_angular_velocity: for setting the angular velocity
- get_angular_velocity: for getting the angular velocity
- add_impulse: for applying an impulse (in world axis)
* Added support for gnss_sensor
* Fixed TCP accept error, too many open files while creating and destroying a lot of sensors
* Fixed lost error messages in client-side, now when a request fails it reports the reason
* Improved simulator fatal error handling, now uses UE4 fatal error system
* Fixed crash when an actor was destroyed but not de-registered, e.g. falling out of world bounds
* LibCarla server pipeline now compiles with exceptions disabled for better performance and compatibility with UE4
* OpenDriveActor has been rewritten using the Waypoint API, this has fixed some bugs
* Updated BasicAgent to allow setting target_speed and handle US-style traffic lights properly
## CARLA 0.9.2

View File

@ -28,10 +28,13 @@ C++
rare occasions if it results in clearer code.
* Compilation should not give any error or warning
(`clang++-6.0 -Wall -Wextra -std=C++14 -Wno-missing-braces`).
* The use of `throw` is forbidden, use `carla::throw_exception` instead.
* Unreal C++ code (CarlaUE4 and Carla plugin) follow the
[Unreal Engine's Coding Standard][ue4link] with the exception of using
spaces instead of tabs.
* LibCarla uses a variation of [Google's style guide][googlelink].
* Uses of `try-catch` blocks should be surrounded by
`#ifndef LIBCARLA_NO_EXCEPTIONS` if the code is used in the server-side.
[ue4link]: https://docs.unrealengine.com/latest/INT/Programming/Development/CodingStandard/
[googlelink]: https://google.github.io/styleguide/cppguide.html

View File

@ -0,0 +1,44 @@
<h1>Importing and exporting maps for distribution builds</h1>
!!! important
The given scripts only works if the files are kept in the folders
detailed below and on Linux.
This method and its scripts are aimed to reducing the size of the distribution
build, allowing to import maps on distributed builds with a script, exporting
them with yet another script from the editor.
<h4>How to export a map</h4>
The current script only work for content placed into the
"CarlaRoot/Unreal/CarlaUE4/Content/Carla/ExportedMaps" folder, inside the unreal's project
structure. Once all the changes are saved in the editor and the maps to export
are located in that folder, running
```sh
make export-maps
```
will clean, cook and pack the necessary assets with the maps. Please, note that
there is no way of knowing what assets are on the project-to-import-to, so the
final package will usually contain assets needed for the current project to work
even though they might not be directly used in the exported map. Also, the maps
referenced inside the Editor Properties will be exported due to the current way
of cooking provided by Unreal.
!!! important
This will only work if "Use pak file" is unchecked under the Packaging settings
Once the script is completed, the packed file can be found under ExportedMaps in
the project's root.
<h4>How to import a map</h4>
In order to do so, the file generated in the import phase has to be placed in a folder
at the root of the distributed folder called "ExportedMaps". Once the zip is placed there,
by running
```sh
./ImportMaps.sh
```
the content will get extracted and located properly where the executable can reach it. All
the imported maps can be loaded by passing the convenient argument to the CarlaUE4.sh script.

View File

@ -19,7 +19,7 @@ Also:
- [Visual Studio](https://www.visualstudio.com/downloads/) (2017)
<h3>Environment Setup</h3>
In order to build CARLA you must **enable the x64 Visual C++ Toolset**.
In order to build CARLA you must **enable the x64 Visual C++ Toolset**. The Windows 8.1 SDK is also required for installation.
I recommend to use this environment for everything you do in this tutorial.
You have different options:

View File

@ -26,6 +26,7 @@
* [Running in a Docker](carla_docker.md)
* [How to make a new map with RoadRunner](how_to_make_a_new_map.md)
* [How to link Epic's Automotive Materials](epic_automotive_materials.md)
* [How to export and import maps to distribution builds](export_import_dist.md)
<h3>Contributing</h3>

7
Jenkinsfile vendored
View File

@ -14,7 +14,6 @@ pipeline {
stage('Setup') {
steps {
sh 'make setup'
sh './Update.sh'
}
}
@ -43,6 +42,12 @@ pipeline {
}
}
stage('Retrieve Content') {
steps {
sh './Update.sh'
}
}
stage('Package') {
steps {
sh 'make package'

View File

@ -13,10 +13,11 @@ if (CMAKE_BUILD_TYPE STREQUAL "Client")
add_subdirectory("client")
elseif (CMAKE_BUILD_TYPE STREQUAL "Server")
add_subdirectory("server")
if (NOT WIN32) # TODO(Andrei): Fix compilation for Windows
add_subdirectory("test")
endif()
else ()
message(FATAL_ERROR "Unknown build type '${CMAKE_BUILD_TYPE}'")
endif ()
# GTest is not compiled on Windows.
if (NOT WIN32)
add_subdirectory("test")
endif()

View File

@ -77,29 +77,35 @@ endif()
# carla_server library.
file(GLOB_RECURSE libcarla_server_sources
"${libcarla_source_path}/carla/*.cpp"
file(GLOB libcarla_server_sources
"${libcarla_source_path}/carla/*.h"
"${libcarla_source_path}/carla/Buffer.cpp"
"${libcarla_source_path}/carla/Exception.cpp"
"${libcarla_source_path}/carla/geom/*.cpp"
"${libcarla_source_path}/carla/geom/*.h"
"${libcarla_source_path}/carla/opendrive/*.cpp"
"${libcarla_source_path}/carla/opendrive/*.h"
"${libcarla_source_path}/carla/map/*.cpp"
"${libcarla_source_path}/carla/map/*.h"
"${libcarla_source_path}/carla/map/element/*.cpp"
"${libcarla_source_path}/carla/map/element/*.h"
"${libcarla_source_path}/carla/map/element/cephes/*.cpp"
"${libcarla_source_path}/carla/map/element/cephes/*.h"
"${libcarla_source_path}/carla/opendrive/parser/*.cpp"
"${libcarla_source_path}/carla/opendrive/parser/*.h"
"${libcarla_source_path}/carla/opendrive/parser/pugixml/*.cpp"
"${libcarla_source_path}/carla/opendrive/parser/pugixml/*.hpp"
"${libcarla_source_path}/carla/road/*.cpp"
"${libcarla_source_path}/carla/road/*.h"
"${libcarla_source_path}/carla/road/element/*.cpp"
"${libcarla_source_path}/carla/road/element/*.h"
"${libcarla_source_path}/carla/road/element/cephes/*.cpp"
"${libcarla_source_path}/carla/road/element/cephes/*.h"
"${libcarla_source_path}/carla/rpc/*.cpp"
"${libcarla_source_path}/carla/rpc/*.h"
"${libcarla_source_path}/carla/sensor/*.cpp"
"${libcarla_source_path}/carla/sensor/*.h"
"${libcarla_source_path}/carla/sensor/s11n/*.cpp"
"${libcarla_source_path}/carla/sensor/s11n/*.h"
"${libcarla_source_path}/carla/streaming/*.cpp"
"${libcarla_source_path}/carla/streaming/*.h")
"${libcarla_source_path}/carla/sensor/s11n/SensorHeaderSerializer.cpp"
"${libcarla_source_path}/carla/streaming/*.h"
"${libcarla_source_path}/carla/streaming/detail/*.cpp"
"${libcarla_source_path}/carla/streaming/detail/*.h"
"${libcarla_source_path}/carla/streaming/detail/tcp/*.cpp"
"${libcarla_source_path}/carla/streaming/detail/tcp/*.h"
"${libcarla_source_path}/carla/streaming/low_level/*.h")
# Create targets for debug and release in the same build type.
foreach(target carla_server_debug carla_server)

View File

@ -1,18 +1,30 @@
cmake_minimum_required(VERSION 3.5.1)
project(libcarla-unit-tests)
file(GLOB_RECURSE libcarla_test_sources
"${libcarla_source_path}/carla/profiler/*.h"
"${libcarla_source_path}/carla/profiler/*.cpp"
"${libcarla_source_path}/test/*.h"
"${libcarla_source_path}/test/*.cpp")
if (CMAKE_BUILD_TYPE STREQUAL "Client")
set(carla_config client)
elseif (CMAKE_BUILD_TYPE STREQUAL "Server")
set(carla_config server)
endif ()
link_directories(
${RPCLIB_LIB_PATH}
${GTEST_LIB_PATH})
file(GLOB libcarla_test_sources
"${libcarla_source_path}/carla/profiler/*.cpp"
"${libcarla_source_path}/carla/profiler/*.h"
"${libcarla_source_path}/test/*.cpp"
"${libcarla_source_path}/test/*.h"
"${libcarla_source_path}/test/${carla_config}/*.cpp"
"${libcarla_source_path}/test/${carla_config}/*.h"
"${libcarla_source_path}/test/common/*.cpp"
"${libcarla_source_path}/test/common/*.h")
file(GLOB libcarla_test_client_sources "")
# Create targets for debug and release in the same build type.
foreach(target libcarla_test_debug libcarla_test_release)
foreach(target libcarla_test_${carla_config}_debug libcarla_test_${carla_config}_release)
add_executable(${target} ${libcarla_test_sources})
target_compile_definitions(${target} PUBLIC
@ -22,7 +34,8 @@ foreach(target libcarla_test_debug libcarla_test_release)
target_include_directories(${target} PRIVATE
"${BOOST_INCLUDE_PATH}"
"${RPCLIB_INCLUDE_PATH}"
"${GTEST_INCLUDE_PATH}")
"${GTEST_INCLUDE_PATH}"
"${libcarla_source_path}/test")
if (WIN32)
target_link_libraries(${target} "gtest_main.lib")
@ -38,10 +51,10 @@ foreach(target libcarla_test_debug libcarla_test_release)
endforeach(target)
# Specific options for debug.
set_target_properties(libcarla_test_debug PROPERTIES COMPILE_FLAGS ${CMAKE_CXX_FLAGS_DEBUG})
target_link_libraries(libcarla_test_debug "carla_server_debug")
target_compile_definitions(libcarla_test_debug PUBLIC -DBOOST_ASIO_ENABLE_BUFFER_DEBUGGING)
set_target_properties(libcarla_test_${carla_config}_debug PROPERTIES COMPILE_FLAGS ${CMAKE_CXX_FLAGS_DEBUG})
target_link_libraries(libcarla_test_${carla_config}_debug "carla_${carla_config}_debug")
target_compile_definitions(libcarla_test_${carla_config}_debug PUBLIC -DBOOST_ASIO_ENABLE_BUFFER_DEBUGGING)
# Specific options for release.
set_target_properties(libcarla_test_release PROPERTIES COMPILE_FLAGS ${CMAKE_CXX_FLAGS_RELEASE})
target_link_libraries(libcarla_test_release "carla_server")
set_target_properties(libcarla_test_${carla_config}_release PROPERTIES COMPILE_FLAGS ${CMAKE_CXX_FLAGS_RELEASE})
target_link_libraries(libcarla_test_${carla_config}_release "carla_${carla_config}")

View File

@ -7,6 +7,7 @@
#pragma once
#include "carla/Debug.h"
#include "carla/Exception.h"
#include "carla/Logging.h"
#include <boost/asio/buffer.hpp>
@ -72,7 +73,9 @@ namespace carla {
/// @copydoc Buffer(size_type)
explicit Buffer(uint64_t size)
: Buffer([size]() {
DEBUG_ASSERT(size <= max_size());
if (size > max_size()) {
throw_exception(std::invalid_argument("message size too big"));
}
return static_cast<size_type>(size);
} ()) {}
@ -245,7 +248,9 @@ namespace carla {
/// @copydoc reset(size_type)
void reset(uint64_t size) {
DEBUG_ASSERT(size <= max_size());
if (size > max_size()) {
throw_exception(std::invalid_argument("message size too big"));
}
reset(static_cast<size_type>(size));
}

View File

@ -0,0 +1,50 @@
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
// de Barcelona (UAB).
//
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
#include "carla/Exception.h"
// =============================================================================
// -- Define boost::throw_exception --------------------------------------------
// =============================================================================
#ifdef BOOST_NO_EXCEPTIONS
namespace boost {
void throw_exception(const std::exception &e) {
carla::throw_exception(e);
}
} // namespace boost
#endif // BOOST_NO_EXCEPTIONS
// =============================================================================
// -- Workaround for Boost.Asio bundled with rpclib ----------------------------
// =============================================================================
#ifdef ASIO_NO_EXCEPTIONS
#include <exception>
#include <system_error>
#include <typeinfo>
namespace clmdep_asio {
namespace detail {
template <typename Exception>
void throw_exception(const Exception& e) {
carla::throw_exception(e);
}
template void throw_exception<std::bad_cast>(const std::bad_cast &);
template void throw_exception<std::exception>(const std::exception &);
template void throw_exception<std::system_error>(const std::system_error &);
} // namespace detail
} // namespace clmdep_asio
#endif // ASIO_NO_EXCEPTIONS

View File

@ -0,0 +1,43 @@
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
// de Barcelona (UAB).
//
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
#pragma once
#ifdef LIBCARLA_NO_EXCEPTIONS
namespace std {
class exception;
} // namespace std
namespace carla {
/// User define function, similar to Boost throw_exception.
///
/// @important Boost exceptions are also routed to this function.
///
/// When compiled with LIBCARLA_NO_EXCEPTIONS, this function is left undefined
/// in LibCarla, and the modules using LibCarla are expected to supply an
/// appropriate definition. Callers of throw_exception are allowed to assume
/// that the function never returns; therefore, if the user-defined
/// throw_exception returns, the behavior is undefined.
[[ noreturn ]] void throw_exception(const std::exception &e);
} // namespace carla
#else
namespace carla {
template <typename T>
[[ noreturn ]] void throw_exception(const T &e) {
throw e;
}
} // namespace carla
#endif // LIBCARLA_NO_EXCEPTIONS

View File

@ -0,0 +1,162 @@
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
// de Barcelona (UAB).
//
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
#pragma once
#include "carla/Exception.h"
#include "carla/MsgPack.h"
#include <boost/optional.hpp>
#include <boost/variant.hpp>
#include <tuple>
namespace clmdep_msgpack {
MSGPACK_API_VERSION_NAMESPACE(MSGPACK_DEFAULT_API_NS) {
namespace adaptor {
// ===========================================================================
// -- Adaptors for boost::optional -------------------------------------------
// ===========================================================================
template<typename T>
struct convert<boost::optional<T>> {
const clmdep_msgpack::object &operator()(
const clmdep_msgpack::object &o,
boost::optional<T> &v) const {
if (o.type != clmdep_msgpack::type::ARRAY) {
::carla::throw_exception(clmdep_msgpack::type_error());
}
if (o.via.array.size == 1) {
v.reset();
} else if (o.via.array.size == 2) {
v.reset(o.via.array.ptr[1].as<T>());
} else {
::carla::throw_exception(clmdep_msgpack::type_error());
}
return o;
}
};
template<typename T>
struct pack<boost::optional<T>> {
template <typename Stream>
packer<Stream> &operator()(
clmdep_msgpack::packer<Stream> &o,
const boost::optional<T> &v) const {
if (v.has_value()) {
o.pack_array(2);
o.pack(true);
o.pack(*v);
} else {
o.pack_array(1);
o.pack(false);
}
return o;
}
};
template<typename T>
struct object_with_zone<boost::optional<T>> {
void operator()(
clmdep_msgpack::object::with_zone &o,
const boost::optional<T> &v) const {
o.type = type::ARRAY;
if (v.has_value()) {
o.via.array.size = 2;
o.via.array.ptr = static_cast<clmdep_msgpack::object*>(o.zone.allocate_align(
sizeof(clmdep_msgpack::object) * o.via.array.size,
MSGPACK_ZONE_ALIGNOF(clmdep_msgpack::object)));
o.via.array.ptr[0] = clmdep_msgpack::object(true, o.zone);
o.via.array.ptr[1] = clmdep_msgpack::object(*v, o.zone);
} else {
o.via.array.size = 1;
o.via.array.ptr = static_cast<clmdep_msgpack::object*>(o.zone.allocate_align(
sizeof(clmdep_msgpack::object) * o.via.array.size,
MSGPACK_ZONE_ALIGNOF(clmdep_msgpack::object)));
o.via.array.ptr[0] = clmdep_msgpack::object(false, o.zone);
}
}
};
// ===========================================================================
// -- Adaptors for boost::variant --------------------------------------------
// ===========================================================================
template<typename... Ts>
struct convert<boost::variant<Ts...>> {
const clmdep_msgpack::object &operator()(
const clmdep_msgpack::object &o,
boost::variant<Ts...> &v) const {
if (o.type != clmdep_msgpack::type::ARRAY) {
::carla::throw_exception(clmdep_msgpack::type_error());
}
if (o.via.array.size != 2) {
::carla::throw_exception(clmdep_msgpack::type_error());
}
const auto index = o.via.array.ptr[0].as<int>();
copy_to_variant(index, o, v, std::make_index_sequence<sizeof...(Ts)>());
return o;
}
private:
template <size_t I>
static void copy_to_variant_impl(
const clmdep_msgpack::object &o,
boost::variant<Ts...> &v) {
/// @todo Workaround for finding the type.
auto dummy = std::get<I>(std::tuple<Ts...>{});
using T = decltype(dummy);
v = o.via.array.ptr[1].as<T>();
}
template <size_t... Is>
static void copy_to_variant(
const size_t index,
const clmdep_msgpack::object &o,
boost::variant<Ts...> &v,
std::index_sequence<Is...>) {
std::initializer_list<int> ({
(index == Is ? copy_to_variant_impl<Is>(o, v), 0 : 0)...
});
}
};
template<typename... Ts>
struct pack<boost::variant<Ts...>> {
template <typename Stream>
packer<Stream> &operator()(
clmdep_msgpack::packer<Stream> &o,
const boost::variant<Ts...> &v) const {
o.pack_array(2);
o.pack(static_cast<int>(v.which()));
boost::apply_visitor([&](const auto &value) { o.pack(value); }, v);
return o;
}
};
template<typename... Ts>
struct object_with_zone<boost::variant<Ts...>> {
void operator()(
clmdep_msgpack::object::with_zone &o,
const boost::variant<Ts...> &v) const {
o.type = type::ARRAY;
o.via.array.size = 2;
o.via.array.ptr = static_cast<clmdep_msgpack::object*>(o.zone.allocate_align(
sizeof(clmdep_msgpack::object) * o.via.array.size,
MSGPACK_ZONE_ALIGNOF(clmdep_msgpack::object)));
o.via.array.ptr[0] = clmdep_msgpack::object(static_cast<int>(v.which()), o.zone);
boost::apply_visitor([&](const auto &value) {
o.via.array.ptr[1] = clmdep_msgpack::object(value, o.zone);
}, v);
}
};
} // namespace adaptor
} // MSGPACK_API_VERSION_NAMESPACE(MSGPACK_DEFAULT_API_NS)
} // namespace msgpack

View File

@ -1,36 +0,0 @@
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
// de Barcelona (UAB).
//
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
#pragma once
#include <boost/optional.hpp>
namespace carla {
template <typename T>
class Optional : private boost::optional<T> {
using parent_type = boost::optional<T>;
public:
template <typename... Args>
explicit Optional(Args &&... args)
: parent_type(std::forward<Args>(args)...) {}
using parent_type::operator*;
using parent_type::operator->;
using parent_type::operator=;
using parent_type::emplace;
using parent_type::reset;
using parent_type::swap;
// using parent_type::value; disabled to avoid exceptions.
using parent_type::value_or;
bool has_value() const {
return parent_type::is_initialized();
}
};
} // namespace carla

View File

@ -6,6 +6,7 @@
#pragma once
#include "carla/Exception.h"
#include "carla/Time.h"
#include <boost/variant.hpp>
@ -99,10 +100,12 @@ namespace detail {
std::unique_lock<std::mutex> lock(_mutex);
auto &r = _map[&detail::thread_tag];
r.should_wait = true;
if (!_cv.wait_for(lock, timeout.to_chrono(), [&]() { return !r.should_wait; }))
throw std::runtime_error("RecurrentSharedFuture.WaitFor: time-out");
if (r.value.which() == 1)
throw boost::get<SharedException>(r.value);
if (!_cv.wait_for(lock, timeout.to_chrono(), [&]() { return !r.should_wait; })) {
throw_exception(std::runtime_error("RecurrentSharedFuture.WaitFor: time-out"));
}
if (r.value.which() == 1) {
throw_exception(boost::get<SharedException>(r.value));
}
return boost::get<T>(std::move(r.value));
}

View File

@ -24,6 +24,14 @@ namespace client {
return GetEpisode().Lock()->GetActorVelocity(*this);
}
geom::Vector3D Actor::GetAngularVelocity() const {
return GetEpisode().Lock()->GetActorAngularVelocity(*this);
}
void Actor::SetAngularVelocity(const geom::Vector3D &vector) {
GetEpisode().Lock()->SetActorAngularVelocity(*this, vector);
}
geom::Vector3D Actor::GetAcceleration() const {
return GetEpisode().Lock()->GetActorAcceleration(*this);
}
@ -36,6 +44,14 @@ namespace client {
GetEpisode().Lock()->SetActorTransform(*this, transform);
}
void Actor::SetVelocity(const geom::Vector3D &vector) {
GetEpisode().Lock()->SetActorVelocity(*this, vector);
}
void Actor::AddImpulse(const geom::Vector3D &vector) {
GetEpisode().Lock()->AddActorImpulse(*this, vector);
}
void Actor::SetSimulatePhysics(const bool enabled) {
GetEpisode().Lock()->SetActorSimulatePhysics(*this, enabled);
}

View File

@ -46,6 +46,12 @@ namespace client {
/// velocity received in the last tick.
geom::Vector3D GetVelocity() const;
/// Return the current 3D angular velocity of the actor.
///
/// @note This function does not call the simulator, it returns the
/// angular velocity received in the last tick.
geom::Vector3D GetAngularVelocity() const;
/// Return the current 3D acceleration of the actor.
///
/// @note This function does not call the simulator, it returns the
@ -58,9 +64,18 @@ namespace client {
/// Teleport and rotate the actor to @a transform.
void SetTransform(const geom::Transform &transform);
/// Set the actor velocity.
void SetVelocity(const geom::Vector3D &vector);
/// Add impulse to the actor.
void AddImpulse(const geom::Vector3D &vector);
/// Enable or disable physics simulation on this actor.
void SetSimulatePhysics(bool enabled = true);
/// Set the angular velocity of the actor
void SetAngularVelocity(const geom::Vector3D &vector);
/// @warning This method only checks whether this instance of Actor has
/// called the Destroy() method, it does not check whether the actor is
/// actually alive in the simulator.

View File

@ -6,16 +6,17 @@
#include "carla/client/ActorAttribute.h"
#include "carla/Exception.h"
#include "carla/Logging.h"
#include "carla/StringUtil.h"
namespace carla {
namespace client {
#define LIBCARLA_THROW_INVALID_VALUE(message) throw InvalidAttributeValue(GetId() + ": " + message);
#define LIBCARLA_THROW_INVALID_VALUE(message) throw_exception(InvalidAttributeValue(GetId() + ": " + message));
#define LIBCARLA_THROW_BAD_VALUE_CAST(type) \
if (GetType() != rpc::ActorAttributeType:: type) { \
throw BadAttributeCast(GetId() + ": bad attribute cast: cannot convert to " #type); \
throw_exception(BadAttributeCast(GetId() + ": bad attribute cast: cannot convert to " #type)); \
}
void ActorAttribute::Set(std::string value) {

View File

@ -6,6 +6,7 @@
#include "carla/client/ActorBlueprint.h"
#include "carla/Exception.h"
#include "carla/StringUtil.h"
#include <algorithm>
@ -41,7 +42,7 @@ namespace client {
auto it = _attributes.find(id);
if (it == _attributes.end()) {
using namespace std::string_literals;
throw std::out_of_range("attribute '"s + id + "' not found");
throw_exception(std::out_of_range("attribute '"s + id + "' not found"));
}
return it->second;
}

View File

@ -6,6 +6,8 @@
#include "carla/client/BlueprintLibrary.h"
#include "carla/Exception.h"
#include <algorithm>
#include <iterator>
@ -40,14 +42,15 @@ namespace client {
auto it = _blueprints.find(key);
if (it == _blueprints.end()) {
using namespace std::string_literals;
throw std::out_of_range("blueprint '"s + key + "' not found");
throw_exception(std::out_of_range("blueprint '"s + key + "' not found"));
}
return it->second;
}
BlueprintLibrary::const_reference BlueprintLibrary::at(size_type pos) const {
if (pos >= size())
throw std::out_of_range("index out of range");
if (pos >= size()) {
throw_exception(std::out_of_range("index out of range"));
}
return operator[](pos);
}

View File

@ -71,7 +71,7 @@ namespace client {
}
if (GetParent() == nullptr) {
throw std::runtime_error(GetDisplayId() + ": not attached to vehicle");
throw_exception(std::runtime_error(GetDisplayId() + ": not attached to vehicle"));
return;
}

View File

@ -75,7 +75,7 @@ namespace client {
}
void LaneDetector::Stop() {
throw std::runtime_error("LaneDetector::Stop(): not implemented.");
throw_exception(std::runtime_error("LaneDetector::Stop(): not implemented."));
}
SharedPtr<sensor::SensorData> LaneDetector::TickLaneDetector(

View File

@ -31,7 +31,7 @@ namespace client {
const geom::Location &location,
bool project_to_road) const {
DEBUG_ASSERT(_map != nullptr);
Optional<road::element::Waypoint> waypoint;
boost::optional<road::element::Waypoint> waypoint;
if (project_to_road) {
waypoint = _map->GetClosestWaypointOnRoad(location);
} else {

View File

@ -55,12 +55,7 @@ namespace client {
const ActorBlueprint &blueprint,
const geom::Transform &transform,
Actor *parent_actor) {
try {
return _episode.Lock()->SpawnActor(blueprint, transform, parent_actor);
} catch (const std::exception &e) {
log_warning("SpawnActor: failed with:", e.what());
throw;
}
return _episode.Lock()->SpawnActor(blueprint, transform, parent_actor);
}
SharedPtr<Actor> World::TrySpawnActor(
@ -69,7 +64,7 @@ namespace client {
Actor *parent_actor) noexcept {
try {
return SpawnActor(blueprint, transform, parent_actor);
} catch (const std::exception &) {
} catch (const std::exception &e) {
return nullptr;
}
}

View File

@ -10,6 +10,7 @@
#include "carla/rpc/ActorDescription.h"
#include "carla/rpc/Client.h"
#include "carla/rpc/DebugShape.h"
#include "carla/rpc/Response.h"
#include "carla/rpc/VehicleControl.h"
#include "carla/rpc/WalkerControl.h"
#include "carla/streaming/Client.h"
@ -20,6 +21,15 @@ namespace carla {
namespace client {
namespace detail {
template <typename T>
static T Get(carla::rpc::Response<T> &response) {
return response.Get();
}
static bool Get(carla::rpc::Response<void> &) {
return true;
}
// ===========================================================================
// -- Client::Pimpl ----------------------------------------------------------
// ===========================================================================
@ -35,8 +45,14 @@ namespace detail {
}
template <typename T, typename... Args>
T CallAndWait(const std::string &function, Args &&... args) {
return rpc_client.call(function, std::forward<Args>(args)...).template as<T>();
auto CallAndWait(const std::string &function, Args &&... args) {
auto object = rpc_client.call(function, std::forward<Args>(args)...);
using R = typename carla::rpc::Response<T>;
auto response = object.template as<R>();
if (response.HasError()) {
throw_exception(std::runtime_error(response.GetError().What()));
}
return Get(response);
}
template <typename... Args>
@ -121,7 +137,13 @@ namespace detail {
}
bool Client::DestroyActor(const rpc::Actor &actor) {
return _pimpl->CallAndWait<bool>("destroy_actor", actor);
try {
return _pimpl->CallAndWait<void>("destroy_actor", actor);
} catch (const std::exception &e) {
log_error("failed to destroy actor:", actor.id, actor.description.id);
log_error(e.what());
return false;
}
}
void Client::SetActorLocation(const rpc::Actor &actor, const geom::Location &location) {
@ -148,6 +170,18 @@ namespace detail {
_pimpl->AsyncCall("apply_control_to_walker", walker, control);
}
void Client::SetActorVelocity(const rpc::Actor &actor, const geom::Vector3D &vector) {
_pimpl->AsyncCall("set_actor_velocity", actor, vector);
}
void Client::SetActorAngularVelocity(const rpc::Actor &actor, const geom::Vector3D &vector) {
_pimpl->AsyncCall("set_actor_angular_velocity", actor, vector);
}
void Client::AddActorImpulse(const rpc::Actor &actor, const geom::Vector3D &vector) {
_pimpl->AsyncCall("add_actor_impulse", actor, vector);
}
void Client::SubscribeToStream(
const streaming::Token &token,
std::function<void(Buffer)> callback) {

View File

@ -107,6 +107,18 @@ namespace detail {
const rpc::Actor &walker,
const rpc::WalkerControl &control);
void SetActorVelocity(
const rpc::Actor &actor,
const geom::Vector3D &vector);
void SetActorAngularVelocity(
const rpc::Actor &actor,
const geom::Vector3D &vector);
void AddActorImpulse(
const rpc::Actor &actor,
const geom::Vector3D &vector);
void SubscribeToStream(
const streaming::Token &token,
std::function<void(Buffer)> callback);

View File

@ -6,6 +6,7 @@
#include "carla/client/detail/EpisodeProxy.h"
#include "carla/Exception.h"
#include "carla/client/detail/Simulator.h"
#include <exception>
@ -38,14 +39,14 @@ namespace detail {
typename EpisodeProxyImpl<T>::SharedPtrType EpisodeProxyImpl<T>::Lock() const {
auto ptr = Load(_simulator);
if (ptr == nullptr) {
throw std::runtime_error(
throw_exception(std::runtime_error(
"trying to operate on a destroyed actor; an actor's function "
"was called, but the actor is already destroyed.");
"was called, but the actor is already destroyed."));
}
if (_episode_id != ptr->GetCurrentEpisodeId()) {
throw std::runtime_error(
throw_exception(std::runtime_error(
"trying to access an expired episode; a new episode was started "
"in the simulation but an object tried accessing the old one.");
"in the simulation but an object tried accessing the old one."));
}
return ptr;
}

View File

@ -38,7 +38,7 @@ namespace detail {
DEBUG_ONLY(auto result = )
next->_actors.emplace(
actor.id,
ActorState{actor.transform, actor.velocity, acceleration, actor.state});
ActorState{actor.transform, actor.velocity, actor.angular_velocity, acceleration, actor.state});
DEBUG_ASSERT(result.second);
}
return next;

View File

@ -29,6 +29,7 @@ namespace detail {
struct ActorState {
geom::Transform transform;
geom::Vector3D velocity;
geom::Vector3D angular_velocity;
geom::Vector3D acceleration;
sensor::data::ActorDynamicState::TypeDependentState state;
};

View File

@ -173,6 +173,22 @@ namespace detail {
return GetActorDynamicState(actor).velocity;
}
void SetActorVelocity(const Actor &actor, const geom::Vector3D &vector) {
_client.SetActorVelocity(actor.Serialize(), vector);
}
geom::Vector3D GetActorAngularVelocity(const Actor &actor) const {
return GetActorDynamicState(actor).angular_velocity;
}
void SetActorAngularVelocity(const Actor &actor, const geom::Vector3D &vector) {
_client.SetActorAngularVelocity(actor.Serialize(), vector);
}
void AddActorImpulse(const Actor &actor, const geom::Vector3D &vector) {
_client.AddActorImpulse(actor.Serialize(), vector);
}
geom::Vector3D GetActorAcceleration(const Actor &actor) const {
return GetActorDynamicState(actor).acceleration;
}

View File

@ -131,6 +131,35 @@ namespace geom {
return !(*this == rhs);
}
// =========================================================================
// -- Conversions to UE4 types ---------------------------------------------
// =========================================================================
#ifdef LIBCARLA_INCLUDED_FROM_UE4
Vector3D(const FVector &vector)
: Vector3D(vector.X, vector.Y, vector.Z) {}
Vector3D &ToMeters(void) { // from centimeters to meters.
x *= 0.001f;
y *= 0.001f;
z *= 0.001f;
return *this;
}
Vector3D &ToCentimeters(void) { // from meters to centimeters.
x *= 100.0f;
y *= 100.0f;
z *= 100.0f;
return *this;
}
operator FVector() const {
return FVector{x, y, z};
}
#endif // LIBCARLA_INCLUDED_FROM_UE4
// =========================================================================
/// @todo The following is copy-pasted from MSGPACK_DEFINE_ARRAY.
/// This is a workaround for an issue in msgpack library. The

View File

@ -17,16 +17,16 @@ namespace road {
return Waypoint(shared_from_this(), loc);
}
Optional<Waypoint> Map::GetWaypoint(const geom::Location &loc) const {
boost::optional<Waypoint> Map::GetWaypoint(const geom::Location &loc) const {
Waypoint w = Waypoint(shared_from_this(), loc);
auto d = geom::Math::Distance2D(w.ComputeTransform().location, loc);
const RoadInfoLane *inf = _data.GetRoad(w._road_id)->GetInfo<RoadInfoLane>(w._dist);
if (d < inf->getLane(w._lane_id)->_width * 0.5) {
return Optional<Waypoint>(w);
return w;
}
return Optional<Waypoint>();
return {};
}
std::vector<element::LaneMarking> Map::CalculateCrossedLanes(

View File

@ -8,11 +8,12 @@
#include "carla/Memory.h"
#include "carla/NonCopyable.h"
#include "carla/Optional.h"
#include "carla/road/MapData.h"
#include "carla/road/element/LaneMarking.h"
#include "carla/road/element/Waypoint.h"
#include <boost/optional.hpp>
#include <vector>
namespace carla {
@ -29,7 +30,7 @@ namespace road {
element::Waypoint GetClosestWaypointOnRoad(const geom::Location &) const;
Optional<element::Waypoint> GetWaypoint(const geom::Location &) const;
boost::optional<element::Waypoint> GetWaypoint(const geom::Location &) const;
std::vector<element::LaneMarking> CalculateCrossedLanes(
const geom::Location &origin,

View File

@ -55,7 +55,7 @@ namespace road {
waypoint.GetRoadSegment().GetPrevLane(this_lane_id);
if (next_lanes.empty()) {
log_error("lane id =", this_lane_id, " road id=", this_road_id, ": missing next lanes");
log_error("road id =", this_road_id, "lane id =", this_lane_id, ": missing next lanes");
}
std::vector<Waypoint> result;
@ -120,6 +120,40 @@ namespace road {
return result;
}
std::vector<Waypoint> WaypointGenerator::GenerateLaneBegin(
const Map &map) {
std::vector<Waypoint> result;
for (auto &&road_segment : map.GetData().GetRoadSegments()) {
ForEachDrivableLane(road_segment, 0.0, [&](auto lane_id) {
auto distance = lane_id < 0 ? 0.0 : road_segment.GetLength();
auto this_waypoint = Waypoint(
map.shared_from_this(),
road_segment.GetId(),
lane_id,
distance);
result.push_back(this_waypoint);
});
}
return result;
}
std::vector<Waypoint> WaypointGenerator::GenerateLaneEnd(
const Map &map) {
std::vector<Waypoint> result;
for (auto &&road_segment : map.GetData().GetRoadSegments()) {
ForEachDrivableLane(road_segment, 0.0, [&](auto lane_id) {
auto distance = lane_id > 0 ? 0.0 : road_segment.GetLength();
auto this_waypoint = Waypoint(
map.shared_from_this(),
road_segment.GetId(),
lane_id,
distance);
result.push_back(this_waypoint);
});
}
return result;
}
std::vector<std::pair<Waypoint, Waypoint>> WaypointGenerator::GenerateTopology(
const Map &map) {
std::vector<std::pair<Waypoint, Waypoint>> result;

View File

@ -38,10 +38,19 @@ namespace road {
const Map &map,
double approx_distance);
/// Returns a list of waypoints at the beginning of each lane of the map.
static std::vector<Waypoint> GenerateLaneBegin(
const Map &map);
/// Returns a list of waypoints at the end of each lane of the map.
static std::vector<Waypoint> GenerateLaneEnd(
const Map &map);
/// Generate the minimum set of waypoints that define the topology of @a
/// map. The waypoints are placed at the entrance of each lane.
static std::vector<std::pair<Waypoint, Waypoint>> GenerateTopology(
const Map &map);
};
} // namespace road

View File

@ -24,7 +24,8 @@ namespace rpc {
actor_id_type id = 0u;
actor_id_type parent_id;
/// @todo parent should not be here as it may change during the simulation.
actor_id_type parent_id = 0u;
ActorDescription description;

View File

@ -6,15 +6,14 @@
#pragma once
#include "carla/MsgPack.h"
#include "carla/MsgPackAdaptors.h"
#include "carla/geom/BoundingBox.h"
#include "carla/geom/Location.h"
#include "carla/geom/Rotation.h"
#include "carla/rpc/Color.h"
#include "carla/rpc/Variant.h"
// #include <rpc/msgpack/adaptor/boost/msgpack_variant.hpp>
// #include <boost/variant.hpp>
#include <boost/variant.hpp>
namespace carla {
namespace rpc {
@ -55,7 +54,7 @@ namespace rpc {
MSGPACK_DEFINE_ARRAY(location, text, draw_shadow);
};
Variant<Point, Line, Arrow, Box, String> primitive;
boost::variant<Point, Line, Arrow, Box, String> primitive;
Color color = {255u, 0u, 0u};

View File

@ -0,0 +1,138 @@
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
// de Barcelona (UAB).
//
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
#pragma once
#include "carla/MsgPack.h"
#include "carla/MsgPackAdaptors.h"
#include <boost/optional.hpp>
#include <boost/variant.hpp>
#include <string>
namespace carla {
namespace rpc {
class ResponseError {
public:
ResponseError() = default;
explicit ResponseError(std::string message)
: _what(std::move(message)) {}
const std::string &What() const {
return _what;
}
MSGPACK_DEFINE_ARRAY(_what)
private:
std::string _what;
};
template <typename T>
class Response {
public:
using value_type = T;
using error_type = ResponseError;
Response() = default;
template <typename TValue>
Response(TValue &&value) : _data(std::forward<TValue>(value)) {}
template <typename TValue>
void Reset(TValue &&value) {
_data = std::forward<TValue>(value);
}
bool HasError() const {
return _data.which() == 0;
}
template <typename... Ts>
void SetError(Ts &&... args) {
_data = error_type(std::forward<Ts>(args)...);
}
const error_type &GetError() const {
DEBUG_ASSERT(HasError());
return boost::get<error_type>(_data);
}
value_type &Get() {
DEBUG_ASSERT(!HasError());
return boost::get<value_type>(_data);
}
const value_type &Get() const {
DEBUG_ASSERT(!HasError());
return boost::get<value_type>(_data);
}
operator bool() const {
return !HasError();
}
MSGPACK_DEFINE_ARRAY(_data)
private:
boost::variant<error_type, value_type> _data;
};
template <>
class Response<void> {
public:
using value_type = void;
using error_type = ResponseError;
static Response Success() {
return success_flag{};
}
Response() : _data(error_type{}) {}
Response(ResponseError error) : _data(std::move(error)) {}
bool HasError() const {
return _data.has_value();
}
template <typename... Ts>
void SetError(Ts &&... args) {
_data = error_type(std::forward<Ts>(args)...);
}
const error_type &GetError() const {
DEBUG_ASSERT(HasError());
return *_data;
}
operator bool() const {
return !HasError();
}
MSGPACK_DEFINE_ARRAY(_data)
private:
struct success_flag {};
Response(success_flag) {}
boost::optional<error_type> _data;
};
} // namespace rpc
} // namespace carla

View File

@ -108,8 +108,6 @@ namespace detail {
_server.stop();
}
static void RespondError(std::string error_message);
private:
boost::asio::io_service _sync_io_service;

View File

@ -1,70 +0,0 @@
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
// de Barcelona (UAB).
//
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
#pragma once
#include "carla/MsgPack.h"
#include <tuple>
namespace carla {
namespace rpc {
/// @todo Workaround for packing boost::variant; it uses tuple instead so it's
/// quite inefficient memory-wise.
template <typename... Ts>
class Variant {
private:
size_t _index = 0u;
std::tuple<Ts...> _tuple;
template <typename T, typename Tuple>
struct IndexFromType;
template <typename T, typename... Types>
struct IndexFromType<T, std::tuple<T, Types...>> {
static constexpr size_t value = 0u;
};
template <typename T, typename U, typename... Types>
struct IndexFromType<T, std::tuple<U, Types...>> {
static constexpr size_t value = 1u + IndexFromType<T, std::tuple<Types...>>::value;
};
template <typename VisitorT, size_t... Is>
void ApplyVisitorImpl(VisitorT &visitor, std::index_sequence<Is...>) const {
std::initializer_list<int> ({ (_index == Is ? visitor(std::get<Is>(_tuple)), 0 : 0)... });
}
public:
Variant() = default;
template <typename ObjT>
Variant(ObjT &&rhs) {
(*this) = std::forward<ObjT>(rhs);
}
template <typename ObjT>
Variant &operator=(ObjT &&rhs) {
constexpr auto index = IndexFromType<typename std::decay<ObjT>::type, decltype(_tuple)>::value;
_index = index;
std::get<index>(_tuple) = std::forward<ObjT>(rhs);
return *this;
}
template <typename VisitorT>
void ApplyVisitor(VisitorT visitor) const {
return ApplyVisitorImpl(visitor, std::make_index_sequence<sizeof...(Ts)>());
}
MSGPACK_DEFINE_ARRAY(_index, _tuple);
};
} // namespace rpc
} // namespace carla

View File

@ -4,16 +4,14 @@
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
#include "carla/rpc/Server.h"
#pragma once
#include <rpc/this_handler.h>
#include "carla/geom/Vector3D.h"
namespace carla {
namespace rpc {
void Server::RespondError(std::string error_message) {
::rpc::this_handler().respond_error(std::move(error_message));
}
using Vector3D = geom::Vector3D;
} // namespace carla
} // namespace rpc
} // namespace carla

View File

@ -88,6 +88,8 @@ namespace detail {
geom::Vector3D velocity;
geom::Vector3D angular_velocity;
union TypeDependentState {
rpc::TrafficLightState traffic_light_state;
detail::PackedVehicleControl vehicle_control;
@ -98,7 +100,7 @@ namespace detail {
#pragma pack(pop)
static_assert(
sizeof(ActorDynamicState) == 10u * sizeof(uint32_t) + sizeof(detail::PackedVehicleControl),
sizeof(ActorDynamicState) == 13u * sizeof(uint32_t) + sizeof(detail::PackedVehicleControl),
"Invalid ActorDynamicState size!");
} // namespace data

View File

@ -7,6 +7,7 @@
#pragma once
#include "carla/Debug.h"
#include "carla/Exception.h"
#include "carla/sensor/SensorData.h"
#include <exception>
@ -107,14 +108,14 @@ namespace data {
reference at(size_type pos) {
if (!(pos < size())) {
throw std::out_of_range("Array index out of range");
throw_exception(std::out_of_range("Array index out of range"));
}
return operator[](pos);
}
const_reference at(size_type pos) const {
if (!(pos < size())) {
throw std::out_of_range("Array index out of range");
throw_exception(std::out_of_range("Array index out of range"));
}
return operator[](pos);
}

View File

@ -6,6 +6,7 @@
#include "carla/streaming/detail/Dispatcher.h"
#include "carla/Exception.h"
#include "carla/Logging.h"
#include "carla/streaming/detail/MultiStreamState.h"
#include "carla/streaming/detail/StreamState.h"
@ -21,7 +22,7 @@ namespace detail {
auto ptr = std::make_shared<StreamStateT>(cached_token);
auto result = stream_map.emplace(std::make_pair(cached_token.get_stream_id(), ptr));
if (!result.second) {
throw std::runtime_error("failed to create stream!");
throw_exception(std::runtime_error("failed to create stream!"));
}
return ptr;
}
@ -31,11 +32,18 @@ namespace detail {
// session remaining since at this point the io_service should be already
// stopped.
for (auto &pair : _stream_map) {
#ifndef LIBCARLA_NO_EXCEPTIONS
try {
pair.second->ClearSessions();
#endif // LIBCARLA_NO_EXCEPTIONS
auto stream_state = pair.second.lock();
if (stream_state != nullptr) {
stream_state->ClearSessions();
}
#ifndef LIBCARLA_NO_EXCEPTIONS
} catch (const std::exception &e) {
log_error("failed to clear sessions:", e.what());
}
#endif // LIBCARLA_NO_EXCEPTIONS
}
}
@ -56,22 +64,36 @@ namespace detail {
std::lock_guard<std::mutex> lock(_mutex);
auto search = _stream_map.find(session->get_stream_id());
if (search != _stream_map.end()) {
DEBUG_ASSERT(search->second != nullptr);
search->second->ConnectSession(std::move(session));
return true;
} else {
log_error("Invalid session: no stream available with id", session->get_stream_id());
return false;
auto stream_state = search->second.lock();
if (stream_state != nullptr) {
stream_state->ConnectSession(std::move(session));
return true;
}
}
log_error("Invalid session: no stream available with id", session->get_stream_id());
return false;
}
void Dispatcher::DeregisterSession(std::shared_ptr<Session> session) {
DEBUG_ASSERT(session != nullptr);
std::lock_guard<std::mutex> lock(_mutex);
ClearExpiredStreams();
auto search = _stream_map.find(session->get_stream_id());
if (search != _stream_map.end()) {
DEBUG_ASSERT(search->second != nullptr);
search->second->DisconnectSession(session);
auto stream_state = search->second.lock();
if (stream_state != nullptr) {
stream_state->DisconnectSession(session);
}
}
}
void Dispatcher::ClearExpiredStreams() {
for (auto it = _stream_map.begin(); it != _stream_map.end(); ) {
if (it->second.expired()) {
it = _stream_map.erase(it);
} else {
++it;
}
}
}

View File

@ -41,6 +41,8 @@ namespace detail {
private:
void ClearExpiredStreams();
// We use a mutex here, but we assume that sessions and streams won't be
// created too often.
std::mutex _mutex;
@ -51,7 +53,7 @@ namespace detail {
/// them alive the whole run.
std::unordered_map<
stream_id_type,
std::shared_ptr<StreamStateBase>> _stream_map;
std::weak_ptr<StreamStateBase>> _stream_map;
};
} // namespace detail

View File

@ -6,6 +6,8 @@
#include "carla/streaming/detail/Token.h"
#include "carla/Exception.h"
#include <cstring>
#include <exception>
@ -21,7 +23,7 @@ namespace detail {
_token.address_type = token_data::address::ip_v6;
_token.address.v6 = addr.to_v6().to_bytes();
} else {
throw std::invalid_argument("invalid ip address!");
throw_exception(std::invalid_argument("invalid ip address!"));
}
}

View File

@ -8,6 +8,7 @@
#include "carla/BufferPool.h"
#include "carla/Debug.h"
#include "carla/Exception.h"
#include "carla/Logging.h"
#include "carla/Time.h"
@ -75,7 +76,7 @@ namespace tcp {
_connection_timer(io_service),
_buffer_pool(std::make_shared<BufferPool>()) {
if (!_token.protocol_is_tcp()) {
throw std::invalid_argument("invalid token, only TCP tokens supported");
throw_exception(std::invalid_argument("invalid token, only TCP tokens supported"));
}
}

View File

@ -44,8 +44,8 @@ namespace tcp {
auto handle_query = [this, self, callback=std::move(on_opened)](
const boost::system::error_code &ec,
size_t DEBUG_ONLY(bytes_received)) {
DEBUG_ASSERT_EQ(bytes_received, sizeof(_stream_id));
if (!ec) {
DEBUG_ASSERT_EQ(bytes_received, sizeof(_stream_id));
log_debug("session", _session_id, "for stream", _stream_id, " started");
_socket.get_io_service().post([=]() { callback(self); });
} else {

View File

@ -14,16 +14,14 @@
#endif // BOOST_ERROR_CODE_HEADER_ONLY
#ifndef BOOST_NO_EXCEPTIONS
# define BOOST_NO_EXCEPTIONS
# error LibCarla should be compiled with -DBOOST_NO_EXCEPTIONS inside UE4.
#endif // BOOST_NO_EXCEPTIONS
namespace boost {
static inline void throw_exception(const std::exception &e) {
UE_LOG(LogCarla, Fatal, TEXT("Exception thrown on Boost libraries: %s"), UTF8_TO_TCHAR(e.what()));
}
} // namespace boost
#ifndef ASIO_NO_EXCEPTIONS
# error LibCarla should be compiled with -DASIO_NO_EXCEPTIONS inside UE4.
#endif // ASIO_NO_EXCEPTIONS
#ifndef LIBCARLA_NO_EXCEPTIONS
# error LibCarla should be compiled with -DLIBCARLA_NO_EXCEPTIONS inside UE4.
#endif // LIBCARLA_NO_EXCEPTIONS
#endif // LIBCARLA_INCLUDED_DISABLE_UE4_MACROS_HEADER

View File

@ -27,6 +27,7 @@
// TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Notice: This file has been slightly adapted for its use by CARLA.
#pragma once
@ -156,18 +157,29 @@ namespace moodycamel { namespace details {
#define MOODYCAMEL_EXCEPTIONS_ENABLED
#endif
#endif
// ~~~ @begin Modified for CARLA ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#include <carla/Exception.h>
#if (defined(LIBCARLA_NO_EXCEPTIONS) && defined(MOODYCAMEL_EXCEPTIONS_ENABLED))
# undef MOODYCAMEL_EXCEPTIONS_ENABLED
#endif
#ifdef MOODYCAMEL_EXCEPTIONS_ENABLED
#define MOODYCAMEL_TRY try
#define MOODYCAMEL_CATCH(...) catch(__VA_ARGS__)
#define MOODYCAMEL_RETHROW throw
#define MOODYCAMEL_THROW(expr) throw (expr)
#define MOODYCAMEL_THROW(expr) ::carla::throw_exception(expr)
#else
#define MOODYCAMEL_TRY if (true)
#define MOODYCAMEL_CATCH(...) else if (false)
#define MOODYCAMEL_RETHROW
#define MOODYCAMEL_THROW(expr)
#define MOODYCAMEL_THROW(expr) ::carla::throw_exception(expr)
#endif
// ~~~ @end Modified for CARLA ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#ifndef MOODYCAMEL_NOEXCEPT
#if !defined(MOODYCAMEL_EXCEPTIONS_ENABLED)
#define MOODYCAMEL_NOEXCEPT

View File

@ -6,7 +6,16 @@
#include "Buffer.h"
#include <boost/random/independent_bits.hpp>
/// @todo This header uses deprecated functionality, please re-enable
/// pragma-messages after upgrading Boost 1.69 if possible.
#if defined(__clang__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-W#pragma-messages"
#endif
# include <boost/random/independent_bits.hpp>
#if defined(__clang__)
# pragma clang diagnostic pop
#endif
#include <climits>
#include <random>

View File

@ -6,9 +6,11 @@
#include "test.h"
#include <carla/MsgPackAdaptors.h>
#include <carla/ThreadGroup.h>
#include <carla/rpc/Actor.h>
#include <carla/rpc/Client.h>
#include <carla/rpc/Response.h>
#include <carla/rpc/Server.h>
#include <thread>
@ -57,21 +59,3 @@ TEST(rpc, server_bind_sync_run_on_game_thread) {
std::cout << "game thread: run " << i << " slices.\n";
ASSERT_TRUE(done);
}
TEST(rpc, msgpack) {
namespace c = carla;
namespace cg = carla::geom;
Actor actor;
actor.id = 42u;
actor.description.uid = 2u;
actor.description.id = "actor.random.whatever";
actor.bounding_box = cg::BoundingBox{cg::Vector3D{1.0f, 2.0f, 3.0f}};
auto buffer = c::MsgPack::Pack(actor);
auto result = c::MsgPack::UnPack<Actor>(buffer);
ASSERT_EQ(result.id, actor.id);
ASSERT_EQ(result.description.uid, actor.description.uid);
ASSERT_EQ(result.description.id, actor.description.id);
ASSERT_EQ(result.bounding_box, actor.bounding_box);
}

View File

@ -112,10 +112,13 @@ TEST(buffer, memcpy) {
ASSERT_EQ(*cpy, *msg);
}
/// @todo What to do with exceptions?
// TEST(buffer, message_too_big) {
// ASSERT_THROW(Buffer(4294967296ul), std::invalid_argument);
// }
#ifndef LIBCARLA_NO_EXCEPTIONS
TEST(buffer, message_too_big) {
ASSERT_THROW(Buffer(4294967296ul), std::invalid_argument);
Buffer buf;
ASSERT_THROW(buf.reset(4294967296ul), std::invalid_argument);
}
#endif // LIBCARLA_NO_EXCEPTIONS
TEST(buffer, buffer_pool) {
const std::string str = "Hello buffer!";

View File

@ -0,0 +1,107 @@
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
// de Barcelona (UAB).
//
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
#include "test.h"
#include <carla/MsgPackAdaptors.h>
#include <carla/rpc/Actor.h>
#include <carla/rpc/Response.h>
#include <thread>
using namespace carla::rpc;
TEST(msgpack, response) {
using mp = carla::MsgPack;
const std::string error = "Uh ah an error!";
Response<int> r = ResponseError(error);
auto s = mp::UnPack<decltype(r)>(mp::Pack(r));
ASSERT_TRUE(r.HasError());
ASSERT_EQ(r.GetError().What(), error);
ASSERT_TRUE(s.HasError());
ASSERT_EQ(s.GetError().What(), error);
r.Reset(42);
s = mp::UnPack<decltype(r)>(mp::Pack(r));
ASSERT_FALSE(r.HasError());
ASSERT_EQ(r.Get(), 42);
ASSERT_FALSE(s.HasError());
ASSERT_EQ(s.Get(), 42);
r.SetError(error);
s = mp::UnPack<decltype(r)>(mp::Pack(r));
ASSERT_FALSE(r);
ASSERT_EQ(r.GetError().What(), error);
ASSERT_FALSE(s);
ASSERT_EQ(s.GetError().What(), error);
Response<std::vector<float>> rv;
auto sv = mp::UnPack<decltype(rv)>(mp::Pack(rv));
ASSERT_TRUE(rv.HasError());
ASSERT_TRUE(sv.HasError());
Response<void> er;
ASSERT_TRUE(er.HasError());
er = Response<void>::Success();
auto es = mp::UnPack<decltype(er)>(mp::Pack(er));
ASSERT_FALSE(er.HasError());
ASSERT_FALSE(es.HasError());
er.SetError(error);
es = mp::UnPack<decltype(er)>(mp::Pack(er));
ASSERT_FALSE(er);
ASSERT_EQ(er.GetError().What(), error);
ASSERT_FALSE(es);
ASSERT_EQ(es.GetError().What(), error);
}
TEST(msgpack, actor) {
namespace c = carla;
namespace cg = carla::geom;
Actor actor;
actor.id = 42u;
actor.description.uid = 2u;
actor.description.id = "actor.random.whatever";
actor.bounding_box = cg::BoundingBox{cg::Vector3D{1.0f, 2.0f, 3.0f}};
auto buffer = c::MsgPack::Pack(actor);
auto result = c::MsgPack::UnPack<Actor>(buffer);
ASSERT_EQ(result.id, actor.id);
ASSERT_EQ(result.description.uid, actor.description.uid);
ASSERT_EQ(result.description.id, actor.description.id);
ASSERT_EQ(result.bounding_box, actor.bounding_box);
}
TEST(msgpack, variant) {
using mp = carla::MsgPack;
boost::variant<bool, float, std::string> var;
var = true;
auto result = mp::UnPack<decltype(var)>(mp::Pack(var));
ASSERT_EQ(result.which(), 0);
ASSERT_EQ(boost::get<bool>(result), true);
var = 42.0f;
result = mp::UnPack<decltype(var)>(mp::Pack(var));
ASSERT_EQ(result.which(), 1);
ASSERT_EQ(boost::get<float>(result), 42.0f);
var = std::string("hola!");
result = mp::UnPack<decltype(var)>(mp::Pack(var));
ASSERT_EQ(result.which(), 2);
ASSERT_EQ(boost::get<std::string>(result), "hola!");
}
TEST(msgpack, optional) {
using mp = carla::MsgPack;
boost::optional<float> var;
auto result = mp::UnPack<decltype(var)>(mp::Pack(var));
ASSERT_FALSE(result.has_value());
var = 42.0f;
result = mp::UnPack<decltype(var)>(mp::Pack(var));
ASSERT_TRUE(result.has_value());
ASSERT_EQ(*result, 42.0f);
}

View File

@ -122,7 +122,7 @@ private:
};
static size_t get_max_concurrency() {
size_t concurrency = 0.75 * std::thread::hardware_concurrency();
size_t concurrency = std::thread::hardware_concurrency() / 2u;
return std::max(2ul, concurrency);
}
@ -154,9 +154,9 @@ TEST(benchmark_streaming, image_200x200_mt) {
}
TEST(benchmark_streaming, image_800x600_mt) {
benchmark_image(800u * 600u, get_max_concurrency(), 0.8);
benchmark_image(800u * 600u, get_max_concurrency(), 0.9);
}
TEST(benchmark_streaming, image_1920x1080_mt) {
benchmark_image(1920u * 1080u, get_max_concurrency(), 0.7);
benchmark_image(1920u * 1080u, get_max_concurrency(), 0.9);
}

View File

@ -0,0 +1,24 @@
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
// de Barcelona (UAB).
//
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
#ifdef LIBCARLA_NO_EXCEPTIONS
#include <carla/Exception.h>
#include <carla/Logging.h>
#include <exception>
namespace carla {
void throw_exception(const std::exception &e) {
log_critical("carla::throw_exception:", e.what());
log_critical("calling std::terminate because exceptions are disabled.");
std::terminate();
}
} // namespace carla
#endif // LIBCARLA_NO_EXCEPTIONS

View File

@ -10,7 +10,7 @@
# define LIBCARLA_LOG_LEVEL LIBCARLA_LOG_LEVEL_INFO
#endif // NDEBUG
#include "test/util/Buffer.h"
#include "test/Buffer.h"
#include <carla/Logging.h>
#include <carla/profiler/Profiler.h>

View File

@ -37,6 +37,7 @@ class Agent(object):
self._vehicle = vehicle
self._world = self._vehicle.get_world()
self._map = self._vehicle.get_world().get_map()
self._last_traffic_light = None
def run_step(self, debug=False):
@ -137,8 +138,14 @@ class Agent(object):
if sel_traffic_light is not None:
if debug:
print('=== Magnitude = {} | Angle = {} | ID = {}'.format(sel_magnitude, min_angle, sel_traffic_light.id))
if sel_traffic_light.state == carla.libcarla.TrafficLightState.Red:
return (True, sel_traffic_light)
if self._last_traffic_light is None:
self._last_traffic_light = sel_traffic_light
if self._last_traffic_light.state == carla.libcarla.TrafficLightState.Red:
return (True, self._last_traffic_light)
else:
self._last_traffic_light = None
return (False, None)
@ -194,4 +201,4 @@ class Agent(object):
control.brake = 1.0
control.hand_brake = False
return control
return control

View File

@ -21,7 +21,7 @@ class BasicAgent(Agent):
target destination. This agent respects traffic lights and other vehicles.
"""
def __init__(self, vehicle):
def __init__(self, vehicle, target_speed=20):
"""
:param vehicle: actor to apply to local planner logic onto
@ -30,7 +30,7 @@ class BasicAgent(Agent):
self._proximity_threshold = 10.0 # meters
self._state = AgentState.NAVIGATING
self._local_planner = LocalPlanner(self._vehicle)
self._local_planner = LocalPlanner(self._vehicle, opt_dict={'target_speed' : target_speed})
# setting up global router
self._current_plan = None

View File

@ -59,9 +59,13 @@ void export_actor() {
.def("get_location", &cc::Actor::GetLocation)
.def("get_transform", &cc::Actor::GetTransform)
.def("get_velocity", &cc::Actor::GetVelocity)
.def("get_angular_velocity", &cc::Actor::GetAngularVelocity)
.def("get_acceleration", &cc::Actor::GetAcceleration)
.def("set_location", &cc::Actor::SetLocation, (arg("location")))
.def("set_transform", &cc::Actor::SetTransform, (arg("transform")))
.def("set_velocity", &cc::Actor::SetVelocity, (arg("vector")))
.def("set_angular_velocity", &cc::Actor::SetAngularVelocity, (arg("vector")))
.def("add_impulse", &cc::Actor::AddImpulse, (arg("vector")))
.def("set_simulate_physics", &cc::Actor::SetSimulatePhysics, (arg("enabled")=true))
.def("destroy", CALL_WITHOUT_GIL(cc::Actor, Destroy))
.def(self_ns::str(self_ns::self))

View File

@ -28,7 +28,7 @@ r.DefaultFeature.AmbientOcclusionStaticFraction=False
r.DefaultFeature.AutoExposure=False
r.CustomDepth=3
r.Streaming.PoolSize=2000
r.TextureStreaming=True
r.TextureStreaming=False
[/Script/AIModule.AISense_Sight]
bAutoRegisterAllPawnsAsSources=False

View File

@ -39,7 +39,7 @@ IncludeAppLocalPrerequisites=False
bShareMaterialShaderCode=False
bSharedMaterialNativeLibraries=False
ApplocalPrerequisitesDirectory=(Path="")
IncludeCrashReporter=True
IncludeCrashReporter=False
InternationalizationPreset=English
-CulturesToStage=en
+CulturesToStage=en

View File

@ -12,7 +12,7 @@
#include "GameFramework/Controller.h"
void FActorDispatcher::Bind(FActorDefinition Definition, SpawnFunctionType Functor)
void UActorDispatcher::Bind(FActorDefinition Definition, SpawnFunctionType Functor)
{
if (UActorBlueprintFunctionLibrary::CheckActorDefinition(Definition))
{
@ -27,7 +27,7 @@ void FActorDispatcher::Bind(FActorDefinition Definition, SpawnFunctionType Funct
}
}
void FActorDispatcher::Bind(ACarlaActorFactory &ActorFactory)
void UActorDispatcher::Bind(ACarlaActorFactory &ActorFactory)
{
for (const auto &Definition : ActorFactory.GetDefinitions())
{
@ -37,7 +37,7 @@ void FActorDispatcher::Bind(ACarlaActorFactory &ActorFactory)
}
}
TPair<EActorSpawnResultStatus, FActorView> FActorDispatcher::SpawnActor(
TPair<EActorSpawnResultStatus, FActorView> UActorDispatcher::SpawnActor(
const FTransform &Transform,
FActorDescription Description)
{
@ -59,7 +59,7 @@ TPair<EActorSpawnResultStatus, FActorView> FActorDispatcher::SpawnActor(
Result.Status = EActorSpawnResultStatus::UnknownError;
}
auto View = Result.IsValid() ? Registry.Register(*Result.Actor, std::move(Description)) : FActorView();
auto View = Result.IsValid() ? RegisterActor(*Result.Actor, std::move(Description)) : FActorView();
if (!View.IsValid())
{
@ -70,7 +70,7 @@ TPair<EActorSpawnResultStatus, FActorView> FActorDispatcher::SpawnActor(
return MakeTuple(Result.Status, View);
}
bool FActorDispatcher::DestroyActor(AActor *Actor)
bool UActorDispatcher::DestroyActor(AActor *Actor)
{
if (Actor == nullptr) {
UE_LOG(LogCarla, Error, TEXT("Trying to destroy nullptr actor"));
@ -83,7 +83,7 @@ bool FActorDispatcher::DestroyActor(AActor *Actor)
UE_LOG(LogCarla, Warning, TEXT("Trying to destroy actor that is not in the registry"));
return false;
}
const auto &Id = View.GetActorDescription()->Id;
const auto &Id = View.GetActorInfo()->Description.Id;
// Destroy its controller if present.
auto Pawn = Cast<APawn>(Actor);
@ -102,9 +102,18 @@ bool FActorDispatcher::DestroyActor(AActor *Actor)
UE_LOG(LogCarla, Log, TEXT("Destroying actor: '%s'"), *Id);
if (Actor->Destroy())
{
Registry.Deregister(Actor);
return true;
}
UE_LOG(LogCarla, Error, TEXT("Failed to destroy actor: '%s'"), *Id);
return false;
}
FActorView UActorDispatcher::RegisterActor(AActor &Actor, FActorDescription Description)
{
auto View = Registry.Register(Actor, std::move(Description));
if (View.IsValid())
{
Actor.OnDestroyed.AddDynamic(this, &UActorDispatcher::OnActorDestroyed);
}
return View;
}

View File

@ -14,12 +14,17 @@
#include "Containers/Array.h"
#include "Templates/Function.h"
#include "ActorDispatcher.generated.h"
class ACarlaActorFactory;
/// Actor in charge of binding ActorDefinitions to spawn functions, as well as
/// Object in charge of binding ActorDefinitions to spawn functions, as well as
/// keeping the registry of all the actors spawned.
class FActorDispatcher
UCLASS()
class CARLA_API UActorDispatcher : public UObject
{
GENERATED_BODY()
public:
using SpawnFunctionType = TFunction<FActorSpawnResult(const FTransform &, const FActorDescription &)>;
@ -51,16 +56,15 @@ public:
/// destruction, false if indestructible or nullptr.
bool DestroyActor(AActor *Actor);
/// Register an actor that was not created using "SpawnActor" function but
/// that should be kept in the registry.
FActorView RegisterActor(AActor &Actor, FActorDescription ActorDescription);
const TArray<FActorDefinition> &GetActorDefinitions() const
{
return Definitions;
}
FActorRegistry &GetActorRegistry()
{
return Registry;
}
const FActorRegistry &GetActorRegistry() const
{
return Registry;
@ -68,6 +72,12 @@ public:
private:
UFUNCTION()
void OnActorDestroyed(AActor *Actor)
{
Registry.Deregister(Actor);
}
TArray<FActorDefinition> Definitions;
TArray<SpawnFunctionType> SpawnFunctions;

View File

@ -0,0 +1,28 @@
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
// de Barcelona (UAB).
//
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
#pragma once
#include "Carla/Actor/ActorDescription.h"
#include "Carla/Game/Tagger.h"
#include <compiler/disable-ue4-macros.h>
#include <carla/rpc/Actor.h>
#include <compiler/enable-ue4-macros.h>
/// A view over an actor and its properties.
struct FActorInfo
{
public:
FActorDescription Description;
TSet<ECityObjectLabel> SemanticTags;
FBoundingBox BoundingBox;
carla::rpc::Actor SerializedData;
};

View File

@ -11,30 +11,35 @@
#include "Carla/Game/Tagger.h"
#include "Carla/Traffic/TrafficLightBase.h"
#include "Carla/Util/BoundingBoxCalculator.h"
static FActorView::ActorType FActorRegistry_GetActorType(const FActorView &View)
{
if (View.IsValid())
if (!View.IsValid())
{
if (nullptr != Cast<ACarlaWheeledVehicle>(View.GetActor()))
{
return FActorView::ActorType::Vehicle;
}
else if (nullptr != Cast<ACharacter>(View.GetActor()))
{
return FActorView::ActorType::Walker;
}
else if (nullptr != Cast<ATrafficLightBase>(View.GetActor()))
{
return FActorView::ActorType::TrafficLight;
}
return FActorView::ActorType::INVALID;
}
else if (nullptr != Cast<ACarlaWheeledVehicle>(View.GetActor()))
{
return FActorView::ActorType::Vehicle;
}
else if (nullptr != Cast<ACharacter>(View.GetActor()))
{
return FActorView::ActorType::Walker;
}
else if (nullptr != Cast<ATrafficLightBase>(View.GetActor()))
{
return FActorView::ActorType::TrafficLight;
}
else
{
return FActorView::ActorType::Other;
}
return FActorView::ActorType::Other;
}
static FString GetRelevantTagAsString(const FActorView &View)
static FString GetRelevantTagAsString(const TSet<ECityObjectLabel> &SemanticTags)
{
for (auto &&Tag : View.GetSemanticTags())
for (auto &&Tag : SemanticTags)
{
if ((Tag != ECityObjectLabel::None) && (Tag != ECityObjectLabel::Other))
{
@ -61,9 +66,7 @@ FActorView FActorRegistry::Register(AActor &Actor, FActorDescription Description
}
Ids.Emplace(&Actor, Id);
auto View = FActorView(Id, Actor, std::move(Description));
ATagger::GetTagsOfTaggedActor(Actor, View.SemanticTags);
View.Type = FActorRegistry_GetActorType(View);
auto View = MakeView(Id, Actor, std::move(Description));
auto Result = ActorDatabase.emplace(Id, View);
check(Result.second);
@ -74,7 +77,7 @@ FActorView FActorRegistry::Register(AActor &Actor, FActorDescription Description
void FActorRegistry::Deregister(IdType Id)
{
check(Contains(Id));
AActor *Actor = FindActor(Id);
AActor *Actor = Find(Id).GetActor();
check(Actor != nullptr);
ActorDatabase.erase(Id);
Actors.Remove(Id);
@ -84,26 +87,53 @@ void FActorRegistry::Deregister(IdType Id)
void FActorRegistry::Deregister(AActor *Actor)
{
check(Actor != nullptr);
auto View = Find(Actor);
check(View.IsValid());
check(View.GetActor() == Actor);
Deregister(View.GetActorId());
}
FActorView FActorRegistry::FindOrFake(AActor *Actor) const
{
if (Actor == nullptr)
{
return {};
}
auto View = Find(Actor);
if (!View.IsValid())
const bool bFakeActor = (View.GetActor() == nullptr) && (Actor != nullptr);
return bFakeActor ? MakeView(0u, *Actor, FActorDescription{}) : View;
}
FActorView FActorRegistry::MakeView(
IdType Id,
AActor &Actor,
FActorDescription Description) const
{
auto Info = MakeShared<FActorInfo>();
Info->Description = std::move(Description);
ATagger::GetTagsOfTaggedActor(Actor, Info->SemanticTags);
Info->BoundingBox = UBoundingBoxCalculator::GetActorBoundingBox(&Actor);
if (Info->Description.Id.IsEmpty())
{
View.TheActor = Actor;
ATagger::GetTagsOfTaggedActor(*Actor, View.SemanticTags);
auto Description = MakeShared<FActorDescription>();
Description->Id = TEXT("static.") + GetRelevantTagAsString(View);
View.Description = Description;
check(View.IsValid());
// This is a fake actor, let's fake the id based on their semantic tags.
Info->Description.Id = TEXT("static.") + GetRelevantTagAsString(Info->SemanticTags);
}
Info->SerializedData.id = Id;
Info->SerializedData.description = Info->Description;
Info->SerializedData.bounding_box = Info->BoundingBox;
Info->SerializedData.semantic_tags.reserve(Info->SemanticTags.Num());
for (auto &&Tag : Info->SemanticTags)
{
using tag_t = decltype(Info->SerializedData.semantic_tags)::value_type;
Info->SerializedData.semantic_tags.emplace_back(static_cast<tag_t>(Tag));
}
auto *Sensor = Cast<ASensor>(&Actor);
if (Sensor != nullptr)
{
const auto &Token = Sensor->GetToken();
Info->SerializedData.stream_token = decltype(Info->SerializedData.stream_token)(
std::begin(Token.data),
std::end(Token.data));
}
auto View = FActorView{Id, Actor, std::move(Info)};
View.Type = FActorRegistry_GetActorType(View);
return View;
}

View File

@ -10,6 +10,10 @@
#include "Containers/Map.h"
#include <compiler/disable-ue4-macros.h>
#include <carla/Iterator.h>
#include <compiler/enable-ue4-macros.h>
#include <unordered_map>
/// A registry of all the Carla actors.
@ -72,12 +76,6 @@ public:
return PtrToId != nullptr ? Find(*PtrToId) : FActorView();
}
AActor *FindActor(IdType Id) const
{
auto View = Find(Id);
return View.IsValid() ? View.GetActor() : nullptr;
}
/// If the actor is not found in the registry, create a fake actor view. The
/// returned FActorView has some information about the @a Actor but will have
/// an invalid id.
@ -90,24 +88,23 @@ public:
/// @{
public:
using key_type = DatabaseType::key_type;
using mapped_type = DatabaseType::mapped_type;
using value_type = DatabaseType::value_type;
using const_iterator = DatabaseType::const_iterator;
using value_type = DatabaseType::mapped_type;
const_iterator begin() const noexcept
auto begin() const noexcept
{
return ActorDatabase.begin();
return carla::iterator::make_map_values_iterator(ActorDatabase.begin());
}
const_iterator end() const noexcept
auto end() const noexcept
{
return ActorDatabase.end();
return carla::iterator::make_map_values_iterator(ActorDatabase.end());
}
/// @}
private:
FActorView MakeView(IdType Id, AActor &Actor, FActorDescription Description) const;
TMap<IdType, AActor *> Actors;
TMap<AActor *, IdType> Ids;

View File

@ -6,8 +6,7 @@
#pragma once
#include "Carla/Actor/ActorDescription.h"
#include "Carla/Game/Tagger.h"
#include "Carla/Actor/ActorInfo.h"
class AActor;
@ -22,15 +21,17 @@ public:
Other,
Vehicle,
Walker,
TrafficLight
TrafficLight,
INVALID
};
FActorView() = default;
FActorView(const FActorView &) = default;
FActorView(FActorView &&) = default;
bool IsValid() const
{
return (TheActor != nullptr) && Description.IsValid();
return (TheActor != nullptr) && !TheActor->IsPendingKill();
}
IdType GetActorId() const
@ -53,24 +54,19 @@ public:
return TheActor;
}
const FActorDescription *GetActorDescription() const
const FActorInfo *GetActorInfo() const
{
return Description.Get();
}
const TSet<ECityObjectLabel> &GetSemanticTags() const
{
return SemanticTags;
return Info.Get();
}
private:
friend class FActorRegistry;
FActorView(IdType ActorId, AActor &Actor, FActorDescription Description)
FActorView(IdType ActorId, AActor &Actor, TSharedPtr<const FActorInfo> Info)
: Id(ActorId),
TheActor(&Actor),
Description(MakeShared<FActorDescription>(std::move(Description))) {}
Info(std::move(Info)) {}
IdType Id = 0u;
@ -78,7 +74,5 @@ private:
AActor *TheActor = nullptr;
TSharedPtr<const FActorDescription> Description = nullptr;
TSet<ECityObjectLabel> SemanticTags;
TSharedPtr<const FActorInfo> Info = nullptr;
};

View File

@ -135,7 +135,9 @@ public class Carla : ModuleRules
PublicIncludePaths.Add(LibCarlaIncludePath);
PrivateIncludePaths.Add(LibCarlaIncludePath);
/// @todo This is necessary because rpclib uses exceptions to notify errors.
bEnableExceptions = true;
PublicDefinitions.Add("ASIO_NO_EXCEPTIONS");
PublicDefinitions.Add("BOOST_NO_EXCEPTIONS");
PublicDefinitions.Add("LIBCARLA_NO_EXCEPTIONS");
PublicDefinitions.Add("PUGIXML_NO_EXCEPTIONS");
}
}

View File

@ -29,23 +29,23 @@ void FCarlaModule::RegisterSettings()
// Registering some settings is just a matter of exposing the default UObject of
// your desired class, add here all those settings you want to expose
// to your LDs or artists.
if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr<ISettingsModule>("Settings"))
{
// Create the new category
ISettingsContainerPtr SettingsContainer = SettingsModule->GetContainer("Project");
SettingsContainer->DescribeCategory("CARLASettings",
LOCTEXT("RuntimeWDCategoryName", "CARLA Settings"),
LOCTEXT("RuntimeWDCategoryDescription", "CARLA plugin settings"));
// Register the settings
ISettingsSectionPtr SettingsSection = SettingsModule->RegisterSettings("Project", "CARLASettings", "General",
LOCTEXT("RuntimeGeneralSettingsName", "General"),
LOCTEXT("RuntimeGeneralSettingsDescription", "General configuration for the CARLA plugin"),
GetMutableDefault<UCarlaSettings>()
);
// Register the save handler to your settings, you might want to use it to
// validate those or just act to settings changes.
if (SettingsSection.IsValid())
@ -54,12 +54,12 @@ void FCarlaModule::RegisterSettings()
}
}
}
void FCarlaModule::UnregisterSettings()
{
// Ensure to unregister all of your registered settings here, hot-reload would
// otherwise yield unexpected results.
if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr<ISettingsModule>("Settings"))
{
SettingsModule->UnregisterSettings("Project", "CustomSettings", "General");
@ -70,18 +70,38 @@ bool FCarlaModule::HandleSettingsSaved()
{
UCarlaSettings* Settings = GetMutableDefault<UCarlaSettings>();
bool ResaveSettings = false;
// Put any validation code in here and resave the settings in case an invalid
// value has been entered
if (ResaveSettings)
{
Settings->SaveConfig();
}
return true;
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FCarlaModule, Carla)
// =============================================================================
// -- Implement carla throw_exception ------------------------------------------
// =============================================================================
#include <compiler/disable-ue4-macros.h>
#include <carla/Exception.h>
#include <compiler/enable-ue4-macros.h>
#include <exception>
namespace carla {
void throw_exception(const std::exception &e) {
UE_LOG(LogCarla, Fatal, TEXT("Exception thrown: %s"), UTF8_TO_TCHAR(e.what()));
// It should never reach this part.
std::terminate();
}
} // namespace carla

View File

@ -7,6 +7,8 @@
#include "Carla.h"
#include "Carla/Game/CarlaEpisode.h"
#include "Carla/Sensor/Sensor.h"
#include "Carla/Util/BoundingBoxCalculator.h"
#include "Carla/Vehicle/VehicleSpawnPoint.h"
#include "EngineUtils.h"
@ -36,9 +38,11 @@ UCarlaEpisode::UCarlaEpisode(const FObjectInitializer &ObjectInitializer)
Id([]() {
static uint32 COUNTER = 0u;
return ++COUNTER;
}()) {}
}()) {
ActorDispatcher = CreateDefaultSubobject<UActorDispatcher>(TEXT("ActorDispatcher"));
}
TArray<FTransform> UCarlaEpisode::GetRecommendedStartTransforms() const
TArray<FTransform> UCarlaEpisode::GetRecommendedSpawnPoints() const
{
TArray<FTransform> SpawnPoints;
for (TActorIterator<AVehicleSpawnPoint> It(GetWorld()); It; ++It) {
@ -47,22 +51,29 @@ TArray<FTransform> UCarlaEpisode::GetRecommendedStartTransforms() const
return SpawnPoints;
}
const AWorldObserver *UCarlaEpisode::StartWorldObserver(carla::streaming::MultiStream Stream)
carla::rpc::Actor UCarlaEpisode::SerializeActor(FActorView ActorView) const
{
UE_LOG(LogCarla, Log, TEXT("Starting AWorldObserver sensor"));
check(WorldObserver == nullptr);
auto *World = GetWorld();
check(World != nullptr);
WorldObserver = World->SpawnActorDeferred<AWorldObserver>(
AWorldObserver::StaticClass(),
FTransform(),
nullptr,
nullptr,
ESpawnActorCollisionHandlingMethod::AlwaysSpawn);
WorldObserver->SetEpisode(*this);
WorldObserver->SetStream(std::move(Stream));
UGameplayStatics::FinishSpawningActor(WorldObserver, FTransform());
return WorldObserver;
carla::rpc::Actor Actor;
if (ActorView.IsValid())
{
Actor = ActorView.GetActorInfo()->SerializedData;
auto Parent = ActorView.GetActor()->GetOwner();
if (Parent != nullptr)
{
Actor.parent_id = FindActor(Parent).GetActorId();
}
} else {
UE_LOG(LogCarla, Warning, TEXT("Trying to serialize invalid actor"));
}
return Actor;
}
void UCarlaEpisode::AttachActors(AActor *Child, AActor *Parent)
{
check(Child != nullptr);
check(Parent != nullptr);
Child->AttachToActor(Parent, FAttachmentTransformRules::KeepRelativeTransform);
Child->SetOwner(Parent);
}
void UCarlaEpisode::InitializeAtBeginPlay()
@ -81,7 +92,7 @@ void UCarlaEpisode::InitializeAtBeginPlay()
FActorDescription Description;
Description.Id = TEXT("spectator");
Description.Class = Spectator->GetClass();
ActorDispatcher.GetActorRegistry().Register(*Spectator, Description);
ActorDispatcher->RegisterActor(*Spectator, Description);
}
else
{
@ -95,6 +106,6 @@ void UCarlaEpisode::InitializeAtBeginPlay()
FActorDescription Description;
Description.Id = UCarlaEpisode_GetTrafficSignId(Actor->GetTrafficSignState());
Description.Class = Actor->GetClass();
ActorDispatcher.GetActorRegistry().Register(*Actor, Description);
ActorDispatcher->RegisterActor(*Actor, Description);
}
}

View File

@ -10,6 +10,12 @@
#include "Carla/Sensor/WorldObserver.h"
#include "Carla/Weather/Weather.h"
#include "GameFramework/Pawn.h"
#include <compiler/disable-ue4-macros.h>
#include <carla/rpc/Actor.h>
#include <compiler/enable-ue4-macros.h>
#include "CarlaEpisode.generated.h"
/// A simulation episode.
@ -20,21 +26,51 @@ class CARLA_API UCarlaEpisode : public UObject
{
GENERATED_BODY()
// ===========================================================================
// -- Constructor ------------------------------------------------------------
// ===========================================================================
public:
UCarlaEpisode(const FObjectInitializer &ObjectInitializer);
// ===========================================================================
// -- Retrieve info about this episode ---------------------------------------
// ===========================================================================
public:
/// Return the unique id of this episode.
auto GetId() const
{
return Id;
}
/// Return the name of the map loaded in this episode.
UFUNCTION(BlueprintCallable)
const FString &GetMapName() const
{
return MapName;
}
/// Return the list of actor definitions that are available to be spawned this
/// episode.
UFUNCTION(BlueprintCallable)
const TArray<FActorDefinition> &GetActorDefinitions() const
{
return ActorDispatcher->GetActorDefinitions();
}
/// Return the list of recommended spawn points for vehicles.
UFUNCTION(BlueprintCallable)
TArray<FTransform> GetRecommendedSpawnPoints() const;
// ===========================================================================
// -- Retrieve special actors ------------------------------------------------
// ===========================================================================
public:
UFUNCTION(BlueprintCallable)
APawn *GetSpectatorPawn() const
{
@ -47,17 +83,55 @@ public:
return Weather;
}
/// Return the list of actor definitions that are available to be spawned this
/// episode.
UFUNCTION(BlueprintCallable)
const TArray<FActorDefinition> &GetActorDefinitions() const
const AWorldObserver *GetWorldObserver() const
{
return ActorDispatcher.GetActorDefinitions();
return WorldObserver;
}
/// Return the list of recommended start positions.
UFUNCTION(BlueprintCallable)
TArray<FTransform> GetRecommendedStartTransforms() const;
const FActorRegistry &GetActorRegistry() const
{
return ActorDispatcher->GetActorRegistry();
}
// ===========================================================================
// -- Actor look up methods --------------------------------------------------
// ===========================================================================
public:
/// Find a Carla actor by id.
///
/// If the actor is not found or is pending kill, the returned view is
/// invalid.
FActorView FindActor(FActorView::IdType ActorId) const
{
return ActorDispatcher->GetActorRegistry().Find(ActorId);
}
/// Find the actor view of @a Actor.
///
/// If the actor is not found or is pending kill, the returned view is
/// invalid.
FActorView FindActor(AActor *Actor) const
{
return ActorDispatcher->GetActorRegistry().Find(Actor);
}
/// Find the actor view of @a Actor. If the actor is not found, a "fake" view
/// is returned emulating an existing Carla actor. Use this to return views
/// over static actors present in the map.
///
/// If the actor is pending kill, the returned view is invalid.
FActorView FindOrFakeActor(AActor *Actor) const
{
return ActorDispatcher->GetActorRegistry().FindOrFake(Actor);
}
// ===========================================================================
// -- Actor handling methods -------------------------------------------------
// ===========================================================================
public:
/// Spawns an actor based on @a ActorDescription at @a Transform. To properly
/// despawn an actor created with this function call DestroyActor.
@ -69,7 +143,7 @@ public:
const FTransform &Transform,
FActorDescription ActorDescription)
{
return ActorDispatcher.SpawnActor(Transform, std::move(ActorDescription));
return ActorDispatcher->SpawnActor(Transform, std::move(ActorDescription));
}
/// Spawns an actor based on @a ActorDescription at @a Transform. To properly
@ -86,24 +160,31 @@ public:
return SpawnActorWithInfo(Transform, std::move(ActorDescription)).Value.GetActor();
}
/// Attach @a Child to @a Parent.
///
/// @pre Actors cannot be null.
UFUNCTION(BlueprintCallable)
void AttachActors(AActor *Child, AActor *Parent);
/// @copydoc FActorDispatcher::DestroyActor(AActor*)
UFUNCTION(BlueprintCallable)
bool DestroyActor(AActor *Actor)
{
return ActorDispatcher.DestroyActor(Actor);
return ActorDispatcher->DestroyActor(Actor);
}
const FActorRegistry &GetActorRegistry() const
{
return ActorDispatcher.GetActorRegistry();
}
// ===========================================================================
// -- Other methods ----------------------------------------------------------
// ===========================================================================
const AWorldObserver *StartWorldObserver(carla::streaming::MultiStream Stream);
public:
const AWorldObserver *GetWorldObserver() const
{
return WorldObserver;
}
/// Create a serializable object describing the actor.
carla::rpc::Actor SerializeActor(FActorView ActorView) const;
// ===========================================================================
// -- Private methods and members --------------------------------------------
// ===========================================================================
private:
@ -113,7 +194,7 @@ private:
void RegisterActorFactory(ACarlaActorFactory &ActorFactory)
{
ActorDispatcher.Bind(ActorFactory);
ActorDispatcher->Bind(ActorFactory);
}
const uint32 Id = 0u;
@ -121,7 +202,8 @@ private:
UPROPERTY(VisibleAnywhere)
FString MapName;
FActorDispatcher ActorDispatcher;
UPROPERTY(VisibleAnywhere)
UActorDispatcher *ActorDispatcher = nullptr;
UPROPERTY(VisibleAnywhere)
APawn *Spectator = nullptr;

View File

@ -25,7 +25,7 @@ UCarlaGameInstance::UCarlaGameInstance() {
CarlaSettings->LogSettings();
}
UCarlaGameInstance::~UCarlaGameInstance() {}
UCarlaGameInstance::~UCarlaGameInstance() = default;
void UCarlaGameInstance::InitializeGameControllerIfNotPresent(
const FMockGameControllerSettings &MockControllerSettings)
@ -40,7 +40,7 @@ void UCarlaGameInstance::InitializeGameControllerIfNotPresent(
}
}
void UCarlaGameInstance::NotifyBeginEpisode(UCarlaEpisode &Episode)
void UCarlaGameInstance::StartServer()
{
if (!bServerIsRunning)
{
@ -48,10 +48,4 @@ void UCarlaGameInstance::NotifyBeginEpisode(UCarlaEpisode &Episode)
Server.AsyncRun(GetNumberOfThreadsForRPCServer());
bServerIsRunning = true;
}
Server.NotifyBeginEpisode(Episode);
}
void UCarlaGameInstance::NotifyEndEpisode()
{
Server.NotifyEndEpisode();
}

View File

@ -33,6 +33,9 @@ public:
void InitializeGameControllerIfNotPresent(
const FMockGameControllerSettings &MockControllerSettings);
/// Starts the Carla server if not already running.
void StartServer();
ICarlaGameControllerBase &GetGameController()
{
check(GameController != nullptr);
@ -58,19 +61,33 @@ public:
return CarlaSettings;
}
UFUNCTION(BlueprintCallable)
UCarlaEpisode *GetCarlaEpisode()
{
return CurrentEpisode;
}
FDataRouter &GetDataRouter()
{
return DataRouter;
}
void NotifyBeginEpisode(UCarlaEpisode &Episode);
void NotifyBeginEpisode(UCarlaEpisode &Episode)
{
CurrentEpisode = &Episode;
Server.NotifyBeginEpisode(Episode);
}
void Tick(float /*DeltaSeconds*/)
{
Server.RunSome(10u); /// @todo
}
void NotifyEndEpisode();
void NotifyEndEpisode()
{
Server.NotifyEndEpisode();
CurrentEpisode = nullptr;
}
const FTheNewCarlaServer &GetServer() const
{
@ -85,6 +102,9 @@ private:
UPROPERTY(Category = "CARLA Settings", EditAnywhere)
UCarlaSettings *CarlaSettings = nullptr;
UPROPERTY()
UCarlaEpisode *CurrentEpisode = nullptr;
FDataRouter DataRouter;
TUniquePtr<ICarlaGameControllerBase> GameController;

View File

@ -0,0 +1,68 @@
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
// de Barcelona (UAB).
//
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
#pragma once
#include "Carla/Game/CarlaEpisode.h"
#include "Carla/Game/CarlaGameInstance.h"
#include "Carla/Game/TheNewCarlaGameModeBase.h"
#include "Carla/Settings/CarlaSettings.h"
#include "Kismet/GameplayStatics.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "CarlaStatics.generated.h"
// =============================================================================
// -- UCarlaStatics declaration ------------------------------------------------
// =============================================================================
UCLASS()
class CARLA_API UCarlaStatics : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintPure, Category="CARLA", meta=(WorldContext="WorldContextObject"))
static ATheNewCarlaGameModeBase *GetGameMode(const UObject *WorldContextObject);
UFUNCTION(BlueprintPure, Category="CARLA", meta=(WorldContext="WorldContextObject"))
static UCarlaGameInstance *GetGameInstance(const UObject *WorldContextObject);
UFUNCTION(BlueprintPure, Category="CARLA", meta=(WorldContext="WorldContextObject"))
static UCarlaEpisode *GetCurrentEpisode(const UObject *WorldContextObject);
UFUNCTION(BlueprintPure, Category="CARLA", meta=(WorldContext="WorldContextObject"))
static UCarlaSettings *GetCarlaSettings(const UObject *WorldContextObject);
};
// =============================================================================
// -- UCarlaStatics implementation ---------------------------------------------
// =============================================================================
inline ATheNewCarlaGameModeBase *UCarlaStatics::GetGameMode(const UObject *WorldContext)
{
return Cast<ATheNewCarlaGameModeBase>(UGameplayStatics::GetGameMode(WorldContext));
}
inline UCarlaGameInstance *UCarlaStatics::GetGameInstance(const UObject *WorldContext)
{
return Cast<UCarlaGameInstance>(UGameplayStatics::GetGameInstance(WorldContext));
}
inline UCarlaEpisode *UCarlaStatics::GetCurrentEpisode(const UObject *WorldContext)
{
auto GameInstance = GetGameInstance(WorldContext);
return GameInstance != nullptr ? GameInstance->GetCarlaEpisode() : nullptr;
}
inline UCarlaSettings *UCarlaStatics::GetCarlaSettings(const UObject *WorldContext)
{
auto GameInstance = GetGameInstance(WorldContext);
return GameInstance != nullptr ? GameInstance->GetCARLASettings() : nullptr;
}

View File

@ -75,6 +75,12 @@ void ATheNewCarlaGameModeBase::InitGame(
UE_LOG(LogCarla, Error, TEXT("Missing weather class!"));
}
GameInstance->StartServer();
Episode->WorldObserver = World->SpawnActor<AWorldObserver>();
Episode->WorldObserver->SetEpisode(*Episode);
Episode->WorldObserver->SetStream(GameInstance->GetServer().OpenMultiStream());
SpawnActorFactories();
}

View File

@ -11,66 +11,76 @@
#include "Util/OpenDrive.h"
#include <compiler/disable-ue4-macros.h>
#include <carla/road/WaypointGenerator.h>
#include <carla/rpc/String.h>
#include <carla/geom/Math.h>
#include <carla/road/element/Waypoint.h>
#include <compiler/enable-ue4-macros.h>
#include <functional>
TArray<FVector> DirectedPointArray2FVectorArray(
const TArray<AOpenDriveActor::DirectedPoint> &DirectedPoints)
static TArray<FVector> WaypointVector2FVectorArray(
const std::vector<carla::road::element::Waypoint> &Waypoints,
const float TriggersHeight)
{
TArray<FVector> Positions;
Positions.Reserve(DirectedPoints.Num());
for (int i = 0; i < DirectedPoints.Num(); ++i)
Positions.Reserve(Waypoints.size());
for (int i = 0; i < Waypoints.size(); ++i)
{
Positions.Add(DirectedPoints[i].location);
// Add the trigger height because the z position of the points does not
// influence on the driver AI and is easy to visualize in the editor
Positions.Add(Waypoints[i].ComputeTransform().location +
FVector(0.f, 0.f, TriggersHeight));
}
return Positions;
}
AOpenDriveActor::AOpenDriveActor(const FObjectInitializer& ObjectInitializer) :
Super(ObjectInitializer)
AOpenDriveActor::AOpenDriveActor(const FObjectInitializer &ObjectInitializer)
: Super(ObjectInitializer)
{
PrimaryActorTick.bCanEverTick = false;
// Structure to hold one-time initialization
static struct FConstructorStatics
{
// A helper class object we use to find target UTexture2D object in resource package
// A helper class object we use to find target UTexture2D object in resource
// package
ConstructorHelpers::FObjectFinderOptional<UTexture2D> TextureObject;
FName Category;
FText Name;
FConstructorStatics()
// Use helper class object to find the texture resource path
: TextureObject(TEXT("/Carla/Icons/OpenDriveActorIcon"))
, Category(TEXT("OpenDriveActor"))
, Name(NSLOCTEXT("SpriteCategory", "OpenDriveActor", "OpenDriveActor"))
{
}
// Use helper class object to find the texture resource path
: TextureObject(TEXT("/Carla/Icons/OpenDriveActorIcon")),
Category(TEXT("OpenDriveActor")),
Name(NSLOCTEXT("SpriteCategory", "OpenDriveActor", "OpenDriveActor"))
{}
} ConstructorStatics;
// We need a scene component to attach Icon sprite
USceneComponent* SceneComponent = ObjectInitializer.CreateDefaultSubobject<USceneComponent>(this, TEXT("SceneComp"));
USceneComponent *SceneComponent =
ObjectInitializer.CreateDefaultSubobject<USceneComponent>(this, TEXT("SceneComp"));
RootComponent = SceneComponent;
RootComponent->Mobility = EComponentMobility::Static;
#if WITH_EDITORONLY_DATA
SpriteComponent = ObjectInitializer.CreateEditorOnlyDefaultSubobject<UBillboardComponent>(this, TEXT("Sprite"));
SpriteComponent =
ObjectInitializer.CreateEditorOnlyDefaultSubobject<UBillboardComponent>(this, TEXT("Sprite"));
if (SpriteComponent)
{
SpriteComponent->Sprite = ConstructorStatics.TextureObject.Get(); // Get the sprite texture from helper class object
SpriteComponent->SpriteInfo.Category = ConstructorStatics.Category; // Assign sprite category name
SpriteComponent->SpriteInfo.DisplayName = ConstructorStatics.Name; // Assign sprite display name
SpriteComponent->SetupAttachment(RootComponent); // Attach sprite to scene component
// Get the sprite texture from helper class object
SpriteComponent->Sprite = ConstructorStatics.TextureObject.Get();
// Assign sprite category name
SpriteComponent->SpriteInfo.Category = ConstructorStatics.Category;
// Assign sprite display name
SpriteComponent->SpriteInfo.DisplayName = ConstructorStatics.Name;
// Attach sprite to scene component
SpriteComponent->SetupAttachment(RootComponent);
SpriteComponent->Mobility = EComponentMobility::Static;
SpriteComponent->SetEditorScale(1.0f);
SpriteComponent->SetEditorScale(1.f);
}
#endif // WITH_EDITORONLY_DATA
}
#if WITH_EDITOR
void AOpenDriveActor::PostEditChangeProperty(struct FPropertyChangedEvent& Event)
void AOpenDriveActor::PostEditChangeProperty(struct FPropertyChangedEvent &Event)
{
Super::PostEditChangeProperty(Event);
@ -129,35 +139,21 @@ void AOpenDriveActor::PostEditChangeProperty(struct FPropertyChangedEvent& Event
}
#endif // WITH_EDITOR
ARoutePlanner *AOpenDriveActor::GenerateRoutePlanner(const TArray<DirectedPoint> &DirectedPoints)
{
using CarlaMath = carla::geom::Math;
TArray<FVector> Positions = DirectedPointArray2FVectorArray(DirectedPoints);
ARoutePlanner *RoutePlanner = GetWorld()->SpawnActor<ARoutePlanner>();
RoutePlanner->SetActorRotation(FRotator(0.0f, CarlaMath::to_degrees(DirectedPoints[0].tangent), 0.0f));
RoutePlanner->SetActorLocation(DirectedPoints[0].location);
RoutePlanner->SetBoxExtent(FVector(70.0f, 70.0f, 50.0f));
RoutePlanner->AddRoute(1.0f, Positions);
RoutePlanner->Init();
RoutePlanners.Add(RoutePlanner);
return RoutePlanner;
}
void AOpenDriveActor::BuildRoutes()
{
using CarlaMath = carla::geom::Math;
using IdType = carla::road::element::id_type;
using Waypoint = carla::road::element::Waypoint;
using WaypointGen = carla::road::WaypointGenerator;
std::string ParseError;
// NOTE(Andrei): As the OpenDrive file has the same name as level,
// build the path to the xodr file using the lavel name and the
// game content directory.
FString MapName = GetWorld()->GetMapName();
FString XodrContent = FOpenDrive::Load(MapName);
// As the OpenDrive file has the same name as level, build the path to the
// xodr file using the lavel name and the game content directory.
const FString XodrContent = FOpenDrive::Load(GetWorld()->GetMapName());
auto map_ptr = carla::opendrive::OpenDrive::Load(TCHAR_TO_UTF8(*XodrContent),
auto map_ptr = carla::opendrive::OpenDrive::Load(
TCHAR_TO_UTF8(*XodrContent),
XmlInputType::CONTENT,
&ParseError);
@ -168,63 +164,59 @@ void AOpenDriveActor::BuildRoutes()
}
const auto &map = map_ptr->GetData();
std::vector<carla::road::lane_junction_t> JunctionInfo = map.GetJunctionInformation();
// NOTE(Andrei): Build the roads that are not junctions
auto RoadIDsView = map.GetAllIds();
std::vector<IdType> roadIDs(RoadIDsView.begin(), RoadIDsView.end());
std::sort(roadIDs.begin(), roadIDs.end());
// List with waypoints, each one at the end of each lane of the map
const std::vector<Waypoint> MapLaneBeginWaypoint =
WaypointGen::GenerateLaneEnd(*map_ptr);
for (auto &&id : roadIDs)
for (auto &&EndLaneWaypoint : MapLaneBeginWaypoint)
{
GenerateWaypointsRoad(map.GetRoad(id));
}
std::vector<Waypoint> Successors = WaypointGen::GetSuccessors(EndLaneWaypoint);
// NOTE(Andrei): Build the roads that are junctions as one RoutePlanner
// can have more than one path that can be taken
// generate the RoutePlanner
ARoutePlanner *RoutePlanner = GetWorld()->SpawnActor<ARoutePlanner>();
RoutePlanner->bIsIntersection = std::any_of(Successors.begin(), Successors.end(), [](auto w) {
return w.IsIntersection();
});
RoutePlanner->SetBoxExtent(FVector(70.f, 70.f, 50.f));
RoutePlanner->SetActorRotation(EndLaneWaypoint.ComputeTransform().rotation);
RoutePlanner->SetActorLocation(EndLaneWaypoint.ComputeTransform().location +
FVector(0.f, 0.f, TriggersHeight));
// junctionId roadID laneID
std::map<int, std::map<int, std::map<int, ARoutePlanner *>>> Junctions;
for (auto && Junction : JunctionInfo)
{
TArray<TArray<DirectedPoint>> Waypoints;
int FromRoadID = Junction.incomming_road;
int ToRoadID = Junction.connection_road;
int JunctonID = Junction.junction_id;
GenerateWaypointsJunction(map.GetRoad(ToRoadID), Waypoints);
ARoutePlanner *routePlanner = nullptr;
std::sort(Junction.from_lane.begin(), Junction.from_lane.end(), std::greater<int>());
if (Junction.from_lane[0] < 0)
// fill the RoutePlanner with all the needed roads
for (auto &&Successor : Successors)
{
std::reverse(Junction.from_lane.begin(), Junction.from_lane.end());
}
const IdType RoadId = Successor.GetRoadId();
const float MaxDist = map.GetRoad(RoadId)->GetLength();
for (size_t n = 0; n < Junction.from_lane.size(); ++n)
{
int FromLaneID = Junction.from_lane[n];
routePlanner = Junctions[JunctonID][FromRoadID][FromLaneID];
std::vector<Waypoint> Waypoints;
if (routePlanner == nullptr)
Waypoints.emplace_back(Successor);
for (float Dist = RoadAccuracy; Dist < MaxDist; Dist += RoadAccuracy)
{
routePlanner = GenerateRoutePlanner(Waypoints[n]);
routePlanner->SetSplineColor(FColor::MakeRandomColor());
Junctions[JunctonID][FromRoadID][FromLaneID] = routePlanner;
}
else
{
routePlanner->AddRoute(1.0, DirectedPointArray2FVectorArray(Waypoints[n]));
const auto NewWaypoint = WaypointGen::GetNext(Successor, Dist);
check(Dist < MaxDist);
check(NewWaypoint.size() == 1);
Waypoints.emplace_back(NewWaypoint[0]);
}
// merge with the first waypoint of the next lane if needed
Waypoints.emplace_back(WaypointGen::GetNext(
Successor, CarlaMath::clamp(MaxDist - 0.1f, 0.f, MaxDist))[0]);
check(Waypoints.size() >= 2);
TArray<FVector> Positions = WaypointVector2FVectorArray(Waypoints, TriggersHeight);
RoutePlanner->AddRoute(1.f, Positions);
RoutePlanners.Add(RoutePlanner);
}
}
}
/// Remove all the existing ARoutePlanner and VehicleSpawners previously
/// generated by this class to avoid overlapping
void AOpenDriveActor::RemoveRoutes()
{
const int rp_num = RoutePlanners.Num();
@ -238,171 +230,6 @@ void AOpenDriveActor::RemoveRoutes()
RoutePlanners.Empty();
}
TArray<AOpenDriveActor::DirectedPoint> AOpenDriveActor::GenerateLaneZeroPoints(
const RoadSegment *road)
{
using RoadElevationInfo = carla::road::element::RoadElevationInfo;
size_t LanesOffsetIndex = 0;
TArray<DirectedPoint> LaneZeroPoints;
const RoadGeneralInfo *generalInfo =
road->GetInfo<RoadGeneralInfo>(0.0);
std::vector<std::pair<double, double>> LanesOffset = generalInfo->GetLanesOffset();
for (float WaypointsOffset = 0.0f; WaypointsOffset < road->GetLength() + RoadAccuracy; WaypointsOffset += RoadAccuracy)
{
// NOTE(Andrei): Calculate the which laneOffset has to be used
if (LanesOffsetIndex < LanesOffset.size() - 1 &&
WaypointsOffset >= LanesOffset[LanesOffsetIndex + 1].first)
{
++LanesOffsetIndex;
}
// NOTE(Andrei): Get waypoin at the offset, and invert the y axis
DirectedPoint Waypoint = road->GetDirectedPointIn(WaypointsOffset);
// Elevate the generated road 1m because the triggers and visualization
Waypoint.location.z += 1;
// NOTE(Andrei): Applyed the laneOffset of the lane section
Waypoint.ApplyLateralOffset(LanesOffset[LanesOffsetIndex].second);
LaneZeroPoints.Add(Waypoint);
}
return LaneZeroPoints;
}
TArray<TArray<AOpenDriveActor::DirectedPoint>> AOpenDriveActor::GenerateRightLaneWaypoints(
const RoadSegment *road,
const TArray<DirectedPoint> &laneZeroPoints)
{
const RoadInfoLane *lanesInfo =
road->GetInfo<RoadInfoLane>(0.0);
std::vector<int> rightLanes =
lanesInfo->getLanesIDs(RoadInfoLane::which_lane_e::Right);
TArray<TArray<DirectedPoint>> retWaypoints;
double currentOffset = 0.0;
for (size_t j = 0; j < rightLanes.size(); ++j)
{
const LaneInfo *laneInfo = lanesInfo->getLane(rightLanes[j]);
const float HalfWidth = laneInfo->_width * 0.5;
currentOffset += HalfWidth;
if (laneInfo->_type == "driving")
{
TArray<DirectedPoint> roadWaypoints;
for (int i = 0; i < laneZeroPoints.Num(); ++i)
{
DirectedPoint currentPoint = laneZeroPoints[i];
currentPoint.ApplyLateralOffset(-currentOffset);
roadWaypoints.Add(currentPoint);
}
if (roadWaypoints.Num() >= 2)
{
retWaypoints.Add(roadWaypoints);
}
}
currentOffset += HalfWidth;
}
return retWaypoints;
}
TArray<TArray<AOpenDriveActor::DirectedPoint>> AOpenDriveActor::GenerateLeftLaneWaypoints(
const RoadSegment *road,
const TArray<DirectedPoint> &laneZeroPoints)
{
using CarlaMath = carla::geom::Math;
const RoadInfoLane *lanesInfo =
road->GetInfo<RoadInfoLane>(0.0);
std::vector<int> leftLanes = lanesInfo->getLanesIDs(RoadInfoLane::which_lane_e::Left);
TArray<TArray<DirectedPoint>> retWaypoints;
double currentOffset = 0.0;
for (size_t j = 0; j < leftLanes.size(); ++j)
{
const LaneInfo *laneInfo = lanesInfo->getLane(leftLanes[j]);
const float HalfWidth = laneInfo->_width * 0.5;
currentOffset += HalfWidth;
if (laneInfo->_type == "driving")
{
TArray<DirectedPoint> roadWaypoints;
for (int i = 0; i < laneZeroPoints.Num(); ++i)
{
DirectedPoint currentPoint = laneZeroPoints[i];
currentPoint.ApplyLateralOffset(currentOffset);
if (currentPoint.tangent + CarlaMath::pi() < CarlaMath::pi_double())
{
currentPoint.tangent += CarlaMath::pi();
}
else
{
currentPoint.tangent -= CarlaMath::pi();
}
roadWaypoints.Add(currentPoint);
}
if (roadWaypoints.Num() >= 2)
{
Algo::Reverse(roadWaypoints);
retWaypoints.Add(roadWaypoints);
}
}
currentOffset += HalfWidth;
}
return retWaypoints;
}
void AOpenDriveActor::GenerateWaypointsRoad(const RoadSegment *road)
{
const RoadGeneralInfo *generalInfo =
road->GetInfo<RoadGeneralInfo>(0.0);
if (generalInfo->GetJunctionId() > -1)
{
return;
}
TArray<DirectedPoint> laneZeroPoints = GenerateLaneZeroPoints(road);
TArray<TArray<DirectedPoint>> rightLaneWaypoints = GenerateRightLaneWaypoints(road, laneZeroPoints);
TArray<TArray<DirectedPoint>> leftLaneWaypoints = GenerateLeftLaneWaypoints(road, laneZeroPoints);
for (int i = 0; i < rightLaneWaypoints.Num(); ++i)
{
GenerateRoutePlanner(rightLaneWaypoints[i]);
}
for (int i = 0; i < leftLaneWaypoints.Num(); ++i)
{
GenerateRoutePlanner(leftLaneWaypoints[i]);
}
}
void AOpenDriveActor::GenerateWaypointsJunction(
const RoadSegment *road,
TArray<TArray<DirectedPoint>> &out_waypoints)
{
const RoadGeneralInfo *generalInfo =
road->GetInfo<RoadGeneralInfo>(0.0);
if (generalInfo->GetJunctionId() == -1)
{
return;
}
TArray<DirectedPoint> laneZeroPoints = GenerateLaneZeroPoints(road);
out_waypoints = GenerateRightLaneWaypoints(road, laneZeroPoints);
if (out_waypoints.Num() == 0)
{
out_waypoints = GenerateLeftLaneWaypoints(road, laneZeroPoints);
}
}
void AOpenDriveActor::DebugRoutes() const
{
for (int i = 0; i < RoutePlanners.Num(); ++i)
@ -416,9 +243,9 @@ void AOpenDriveActor::DebugRoutes() const
void AOpenDriveActor::RemoveDebugRoutes() const
{
#if WITH_EDITOR
#if WITH_EDITOR
FlushPersistentDebugLines(GetWorld());
#endif // WITH_EDITOR
#endif // WITH_EDITOR
}
void AOpenDriveActor::AddSpawners()
@ -427,11 +254,18 @@ void AOpenDriveActor::AddSpawners()
{
if (RoutePlanners[i] != nullptr)
{
FTransform Trans = RoutePlanners[i]->GetActorTransform();
AVehicleSpawnPoint *Spawner = GetWorld()->SpawnActor<AVehicleSpawnPoint>();
Spawner->SetActorRotation(Trans.GetRotation());
Spawner->SetActorLocation(Trans.GetTranslation() + FVector(0.0f, 0.0f, SpawnersHeight));
VehicleSpawners.Add(Spawner);
if (!bOnIntersections && RoutePlanners[i]->bIsIntersection)
{
continue;
}
else
{
FTransform Trans = RoutePlanners[i]->GetActorTransform();
AVehicleSpawnPoint *Spawner = GetWorld()->SpawnActor<AVehicleSpawnPoint>();
Spawner->SetActorRotation(Trans.GetRotation());
Spawner->SetActorLocation(Trans.GetTranslation() + FVector(0.f, 0.f, SpawnersHeight));
VehicleSpawners.Add(Spawner);
}
}
}
}

View File

@ -28,10 +28,12 @@ class CARLA_API AOpenDriveActor : public AActor
GENERATED_BODY()
protected:
// A UBillboardComponent to hold Icon sprite
UBillboardComponent* SpriteComponent;
// Sprite for the Billboard Component
UTexture2D* SpriteTexture;
/// A UBillboardComponent to hold Icon sprite
UBillboardComponent *SpriteComponent;
/// Sprite for the Billboard Component
UTexture2D *SpriteTexture;
private:
@ -42,17 +44,23 @@ private:
TArray<AVehicleSpawnPoint *> VehicleSpawners;
#if WITH_EDITORONLY_DATA
/// Generate the road network using an OpenDrive file (named as the current .umap)
/// Generate the road network using an OpenDrive file (named as the current
/// .umap)
UPROPERTY(Category = "Generate", EditAnywhere)
bool bGenerateRoutes = false;
#endif // WITH_EDITORONLY_DATA
/// Distance between waypoints where the cars will drive
UPROPERTY(Category = "Generate", EditAnywhere, meta = (ClampMin = "0.01", UIMin = "0.01"))
float RoadAccuracy = 2.0f;
float RoadAccuracy = 2.f;
/// Trigger elevantion
UPROPERTY(Category = "Generate", EditAnywhere, meta = (ClampMin = "0.01", UIMin = "0.01"))
float TriggersHeight = 100.f;
#if WITH_EDITORONLY_DATA
/// Remove the previously generated road network. Also, it will remove spawners if necessary
/// Remove the previously generated road network. Also, it will remove
/// spawners if necessary
UPROPERTY(Category = "Generate", EditAnywhere)
bool bRemoveRoutes = false;
#endif // WITH_EDITORONLY_DATA
@ -61,9 +69,14 @@ private:
UPROPERTY(Category = "Spawners", EditAnywhere)
bool bAddSpawners = false;
/// Determine the height where the spawners will be placed, relative to each RoutePlanner
/// If true, spawners will be placed on junctions too
UPROPERTY(Category = "Spawners", EditAnywhere)
float SpawnersHeight = 300.0;
bool bOnIntersections = false;
/// Determine the height where the spawners will be placed, relative to each
/// RoutePlanner
UPROPERTY(Category = "Spawners", EditAnywhere)
float SpawnersHeight = 300.f;
#if WITH_EDITORONLY_DATA
/// Remove already placed spawners if necessary
@ -79,16 +92,12 @@ private:
public:
using RoadSegment = carla::road::element::RoadSegment;
using DirectedPoint = carla::road::element::DirectedPoint;
using LaneInfo = carla::road::element::LaneInfo;
using RoadGeneralInfo = carla::road::element::RoadGeneralInfo;
using RoadInfoLane = carla::road::element::RoadInfoLane;
AOpenDriveActor(const FObjectInitializer& ObjectInitializer);
AOpenDriveActor(const FObjectInitializer &ObjectInitializer);
void BuildRoutes();
/// Remove all the existing ARoutePlanner and VehicleSpawners previously
/// generated by this class to avoid overlapping
void RemoveRoutes();
void DebugRoutes() const;
@ -96,29 +105,12 @@ public:
void RemoveDebugRoutes() const;
#if WITH_EDITOR
void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent);
void PostEditChangeProperty(struct FPropertyChangedEvent &PropertyChangedEvent);
#endif // WITH_EDITOR
ARoutePlanner *GenerateRoutePlanner(const TArray<DirectedPoint> &waypoints);
TArray<DirectedPoint> GenerateLaneZeroPoints(
const RoadSegment *road);
TArray<TArray<DirectedPoint>> GenerateRightLaneWaypoints(
const RoadSegment *road,
const TArray<DirectedPoint> &laneZeroPoints);
TArray<TArray<DirectedPoint>> GenerateLeftLaneWaypoints(
const RoadSegment *road,
const TArray<DirectedPoint> &laneZeroPoints);
void GenerateWaypointsJunction(
const RoadSegment *road,
TArray<TArray<DirectedPoint>> &waypoints);
void GenerateWaypointsRoad(const RoadSegment *road);
void AddSpawners();
void RemoveSpawners();
};

View File

@ -0,0 +1,107 @@
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
// de Barcelona (UAB).
//
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
#pragma once
#include <compiler/disable-ue4-macros.h>
#include <carla/Buffer.h>
#include <carla/sensor/SensorRegistry.h>
#include <carla/sensor/s11n/SensorHeaderSerializer.h>
#include <carla/streaming/Stream.h>
#include <compiler/enable-ue4-macros.h>
template <typename T>
class FDataStreamTmpl;
// =============================================================================
// -- FAsyncDataStreamTmpl -----------------------------------------------------
// =============================================================================
/// A streaming channel for sending sensor data to clients, supports sending
/// data asynchronously. Data sent by the "Send" functions is passed to the
/// serializer registered with the sensor at carla::sensor:SensorRegistry before
/// being sent down the stream.
///
/// @warning This is a single-use object, a new one needs to be created for each
/// new message.
///
/// FAsyncDataStream also has a pool of carla::Buffer that allows reusing the
/// allocated memory, use it whenever possible.
template <typename T>
class FAsyncDataStreamTmpl
{
public:
using StreamType = T;
FAsyncDataStreamTmpl(FAsyncDataStreamTmpl &&) = default;
/// Return the token that allows subscribing to this stream.
auto GetToken() const
{
return Stream.GetToken();
}
/// Pop a Buffer from the pool. Buffers in the pool can reuse the memory
/// allocated by previous messages, significantly improving performance for
/// big messages.
carla::Buffer PopBufferFromPool()
{
return Stream.MakeBuffer();
}
/// Send some data down the stream.
template <typename SensorT, typename... ArgsT>
void Send(SensorT &Sensor, ArgsT &&... Args);
private:
friend class FDataStreamTmpl<T>;
/// @pre This functions needs to be called in the game-thread.
template <typename SensorT>
explicit FAsyncDataStreamTmpl(const SensorT &InSensor, StreamType InStream);
StreamType Stream;
carla::Buffer Header;
};
// =============================================================================
// -- FAsyncDataStream and FAsyncDataMultiStream -------------------------------
// =============================================================================
using FAsyncDataStream = FAsyncDataStreamTmpl<carla::streaming::Stream>;
using FAsyncDataMultiStream = FAsyncDataStreamTmpl<carla::streaming::MultiStream>;
// =============================================================================
// -- FAsyncDataStreamTmpl implementation --------------------------------------
// =============================================================================
template <typename T>
template <typename SensorT, typename... ArgsT>
inline void FAsyncDataStreamTmpl<T>::Send(SensorT &Sensor, ArgsT &&... Args)
{
Stream.Write(
std::move(Header),
carla::sensor::SensorRegistry::Serialize(Sensor, std::forward<ArgsT>(Args)...));
}
template <typename T>
template <typename SensorT>
inline FAsyncDataStreamTmpl<T>::FAsyncDataStreamTmpl(
const SensorT &Sensor,
StreamType InStream)
: Stream(std::move(InStream)),
Header([&Sensor]() {
check(IsInGameThread());
using Serializer = carla::sensor::s11n::SensorHeaderSerializer;
return Serializer::Serialize(
carla::sensor::SensorRegistry::template get<SensorT*>::index,
GFrameCounter,
Sensor.GetActorTransform());
}()) {}

View File

@ -50,13 +50,6 @@ void ACollisionSensor::BeginPlay()
return;
}
Episode = &GameMode->GetCarlaEpisode();
GameInstance = Cast<UCarlaGameInstance>(GetGameInstance());
if (GameMode == nullptr)
{
UE_LOG(LogCarla, Error, TEXT("ACollisionSensor: Game instance not compatible with this sensor"));
return;
}
}
void ACollisionSensor::OnCollisionEvent(
@ -65,16 +58,14 @@ void ACollisionSensor::OnCollisionEvent(
FVector NormalImpulse,
const FHitResult &Hit)
{
if ((Episode != nullptr) && (GameInstance != nullptr) && (Actor != nullptr) && (OtherActor != nullptr))
if ((Episode != nullptr) && (Actor != nullptr) && (OtherActor != nullptr))
{
const auto &Registry = Episode->GetActorRegistry();
const auto &Server = GameInstance->GetServer();
constexpr float TO_METERS = 1e-2;
NormalImpulse *= TO_METERS;
GetDataStream().Send_GameThread(
GetDataStream(*this).Send(
*this,
Server.SerializeActor(Registry.FindOrFake(Actor)),
Server.SerializeActor(Registry.FindOrFake(OtherActor)),
Episode->SerializeActor(Episode->FindOrFakeActor(Actor)),
Episode->SerializeActor(Episode->FindOrFakeActor(OtherActor)),
carla::geom::Vector3D{NormalImpulse.X, NormalImpulse.Y, NormalImpulse.Z});
}
}

View File

@ -6,6 +6,7 @@
#pragma once
#include "Carla/Actor/ActorDefinition.h"
#include "Carla/Sensor/Sensor.h"
#include "CollisionSensor.generated.h"
@ -40,7 +41,4 @@ private:
UPROPERTY()
const UCarlaEpisode *Episode = nullptr;
UPROPERTY()
const UCarlaGameInstance *GameInstance = nullptr;
};

View File

@ -6,47 +6,22 @@
#pragma once
#include "Carla/Sensor/AsyncDataStream.h"
#include <compiler/disable-ue4-macros.h>
#include <carla/Buffer.h>
#include <carla/Optional.h>
#include <carla/sensor/SensorRegistry.h>
#include <carla/sensor/s11n/SensorHeaderSerializer.h>
#include <carla/streaming/Stream.h>
#include <boost/optional.hpp>
#include <compiler/enable-ue4-macros.h>
// =============================================================================
// -- FSensorMessageHeader -----------------------------------------------------
// =============================================================================
/// Contains meta-information of a sensor message.
class FSensorMessageHeader
{
public:
FSensorMessageHeader(FSensorMessageHeader &&) = default;
private:
FSensorMessageHeader(carla::Buffer InBuffer) : Buffer(std::move(InBuffer)) {}
template <typename T>
friend class FDataStreamTmpl;
carla::Buffer Buffer;
};
// =============================================================================
// -- FDataStreamTmpl ----------------------------------------------------------
// =============================================================================
/// A streaming channel for sending sensor data to clients. Each sensor has its
/// own FDataStream. Use Send_GameThread and Send_Async for sending data
/// generated by the sensor. Data sent by these functions is passed to the
/// serializer registered with the sensor at carla::sensor:SensorRegistry before
/// being sent down the stream.
///
/// FDataStream also has a pool of carla::Buffer that allows reusing the
/// allocated memory, use it whenever possible.
/// own FDataStream. Note however that this class does not provide a send
/// function. In order to send data, a FAsyncDataStream needs to be created
/// using "MakeAsyncDataStream" function. FAsyncDataStream allows sending data
/// from any thread.
template <typename T>
class FDataStreamTmpl
{
@ -56,37 +31,28 @@ public:
FDataStreamTmpl() = default;
FDataStreamTmpl(FDataStreamTmpl &&) = default;
FDataStreamTmpl &operator=(FDataStreamTmpl &&) = default;
FDataStreamTmpl(StreamType InStream) : Stream(std::move(InStream)) {}
/// Create the meta-information header associated with the sensor message.
/// This functions needs to be called in the game-thread.
/// Create a FAsyncDataStream object.
///
/// @pre This functions needs to be called in the game-thread.
template <typename SensorT>
FSensorMessageHeader MakeHeader(const SensorT &Sensor);
auto MakeAsyncDataStream(const SensorT &Sensor)
{
check(Stream.has_value());
return FAsyncDataStreamTmpl<T>{Sensor, *Stream};
}
/// Pop a Buffer from the pool. Buffers in the pool can reuse the memory
/// allocated by previous messages, significantly improving performance for
/// big messages.
carla::Buffer PopBufferFromPool();
/// Send some data down the stream. This function can only be called from the
/// game-thread. No need to provide a FSensorMessageHeader to this function.
template <typename SensorT, typename... ArgsT>
void Send_GameThread(SensorT &Sensor, ArgsT &&... Args);
/// Send some data down the stream. This function can be called from a
/// different thread. It requires you however to provide a
/// FSensorMessageHeader previously generated in the game-thread.
template <typename SensorT, typename... ArgsT>
void Send_Async(FSensorMessageHeader Header, SensorT &Sensor, ArgsT &&... Args);
auto GetToken() const;
/// Return the token that allows subscribing to this stream.
auto GetToken() const
{
check(Stream.has_value());
return (*Stream).token();
}
private:
carla::Optional<StreamType> Stream;
boost::optional<StreamType> Stream;
};
// =============================================================================
@ -96,64 +62,3 @@ private:
using FDataStream = FDataStreamTmpl<carla::streaming::Stream>;
using FDataMultiStream = FDataStreamTmpl<carla::streaming::MultiStream>;
// =============================================================================
// -- FDataStreamTmpl implementation -------------------------------------------
// =============================================================================
template <typename T>
template <typename SensorT>
inline FSensorMessageHeader FDataStreamTmpl<T>::MakeHeader(const SensorT &Sensor)
{
check(IsInGameThread());
using Serializer = carla::sensor::s11n::SensorHeaderSerializer;
return {Serializer::Serialize(
carla::sensor::SensorRegistry::template get<SensorT*>::index,
GFrameCounter,
Sensor.GetActorTransform())};
}
template <typename T>
inline carla::Buffer FDataStreamTmpl<T>::PopBufferFromPool()
{
#ifdef WITH_EDITOR
if (!Stream.has_value())
{
UE_LOG(LogCarla, Error, TEXT("Sensor does not have a stream!"));
return {};
}
#endif // WITH_EDITOR
check(Stream.has_value());
return (*Stream).MakeBuffer();
}
template <typename T>
template <typename SensorT, typename... ArgsT>
inline void FDataStreamTmpl<T>::Send_Async(FSensorMessageHeader Header, SensorT &Sensor, ArgsT &&... Args)
{
#ifdef WITH_EDITOR
if (!Stream.has_value())
{
UE_LOG(LogCarla, Error, TEXT("Sensor does not have a stream!"));
return;
}
#endif // WITH_EDITOR
check(Stream.has_value());
(*Stream).Write(
std::move(Header.Buffer),
carla::sensor::SensorRegistry::Serialize(Sensor, std::forward<ArgsT>(Args)...));
}
template <typename T>
template <typename SensorT, typename... ArgsT>
inline void FDataStreamTmpl<T>::Send_GameThread(SensorT &Sensor, ArgsT &&... Args)
{
Send_Async(MakeHeader(Sensor), Sensor, std::forward<ArgsT>(Args)...);
}
template <typename T>
inline auto FDataStreamTmpl<T>::GetToken() const
{
check(Stream.has_value());
return (*Stream).token();
}

View File

@ -87,26 +87,31 @@ void FPixelReader::SendPixelsInRenderThread(TSensor &Sensor)
// First we create the message header (needs to be created in the
// game-thread).
auto Header = Sensor.GetDataStream().MakeHeader(Sensor);
auto AsyncStream = Sensor.GetDataStream(Sensor);
// We need a shared ptr here because UE4 macros do not move the arguments -_-
auto HeaderPtr = MakeShared<decltype(Header)>(std::move(Header));
auto StreamPtr = std::make_shared<decltype(AsyncStream)>(std::move(AsyncStream));
// Then we enqueue commands in the render-thread that will write the image
// buffer to the data stream.
auto WriteAndSend = [&Sensor, Hdr=std::move(HeaderPtr)](auto &InRHICmdList) mutable {
auto &Stream = Sensor.GetDataStream();
auto Buffer = Stream.PopBufferFromPool();
WritePixelsToBuffer(
*Sensor.CaptureRenderTarget,
Buffer,
carla::sensor::SensorRegistry::get<TSensor *>::type::header_offset,
InRHICmdList);
Stream.Send_Async(std::move(*Hdr), Sensor, std::move(Buffer));
auto WriteAndSend = [&Sensor, Stream=std::move(StreamPtr)](auto &InRHICmdList) mutable
{
/// @todo Can we make sure the sensor is not going to be destroyed?
if (!Sensor.IsPendingKill())
{
auto Buffer = Stream->PopBufferFromPool();
WritePixelsToBuffer(
*Sensor.CaptureRenderTarget,
Buffer,
carla::sensor::SensorRegistry::get<TSensor *>::type::header_offset,
InRHICmdList);
Stream->Send(Sensor, std::move(Buffer));
}
};
ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER(
FWritePixels_Vulkan,
FWritePixels_SendPixelsInRenderThread,
std::function<void(FRHICommandListImmediate &)>, WriteAndSendFunction, std::move(WriteAndSend),
{
WriteAndSendFunction(RHICmdList);

View File

@ -69,8 +69,8 @@ void ARayCastLidar::Tick(const float DeltaTime)
ReadPoints(DeltaTime);
auto &DataStream = GetDataStream();
DataStream.Send_GameThread(*this, LidarMeasurement, DataStream.PopBufferFromPool());
auto DataStream = GetDataStream(*this);
DataStream.Send(*this, LidarMeasurement, DataStream.PopBufferFromPool());
}
void ARayCastLidar::ReadPoints(const float DeltaTime)

View File

@ -6,9 +6,11 @@
#pragma once
#include "Carla/Sensor/PixelReader.h"
#include "Carla/Sensor/SceneCaptureSensor.h"
#include "Carla/Actor/ActorDefinition.h"
#include "Carla/Sensor/PixelReader.h"
#include "SceneCaptureCamera.generated.h"
/// A sensor that captures images from the scene.

View File

@ -7,6 +7,8 @@
#include "Carla.h"
#include "Carla/Sensor/SceneCaptureSensor.h"
#include "Carla/Game/CarlaStatics.h"
#include "Components/DrawFrustumComponent.h"
#include "Components/SceneCaptureComponent2D.h"
#include "Components/StaticMeshComponent.h"
@ -28,10 +30,9 @@ namespace SceneCaptureSensor_local_ns {
static auto GetQualitySettings(UWorld *World)
{
check(World != nullptr);
const auto *GameInstance = Cast<UCarlaGameInstance>(World->GetGameInstance());
check(GameInstance != nullptr);
return GameInstance->GetCarlaSettings().GetQualityLevel();
auto Settings = UCarlaStatics::GetCarlaSettings(World);
check(Settings != nullptr);
return Settings->GetQualityLevel();
}
} // namespace SceneCaptureSensor_local_ns

View File

@ -8,6 +8,8 @@
#include "Carla/Sensor/ShaderBasedSensor.h"
#include "Carla/Actor/ActorDefinition.h"
#include "SemanticSegmentationCamera.generated.h"
/// Sensor that produces "semantic segmentation" images.

View File

@ -0,0 +1,28 @@
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
// de Barcelona (UAB).
//
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
#include "Carla.h"
#include "Carla/Sensor/Sensor.h"
#include "Carla/Actor/ActorDescription.h"
#include "Carla/Actor/ActorBlueprintFunctionLibrary.h"
void ASensor::Set(const FActorDescription &Description)
{
// set the tick interval of the sensor
if (Description.Variations.Contains("sensor_tick"))
{
SetActorTickInterval(
UActorBlueprintFunctionLibrary::ActorAttributeToFloat(Description.Variations["sensor_tick"],
0.0f));
}
}
void ASensor::EndPlay(EEndPlayReason::Type EndPlayReason)
{
Super::EndPlay(EndPlayReason);
Stream = FDataStream();
}

View File

@ -6,14 +6,14 @@
#pragma once
#include "GameFramework/Actor.h"
#include "Carla/Actor/ActorDescription.h"
#include "Carla/Actor/ActorBlueprintFunctionLibrary.h"
#include "Carla/Sensor/DataStream.h"
#include "GameFramework/Actor.h"
#include "Sensor.generated.h"
struct FActorDescription;
/// Base class for sensors.
UCLASS(Abstract, hidecategories = (Collision, Attachment, Actor))
class CARLA_API ASensor : public AActor
@ -22,16 +22,7 @@ class CARLA_API ASensor : public AActor
public:
virtual void Set(const FActorDescription &Description)
{
// set the tick interval of the sensor
if (Description.Variations.Contains("sensor_tick"))
{
SetActorTickInterval(
UActorBlueprintFunctionLibrary::ActorAttributeToFloat(Description.Variations["sensor_tick"],
0.0f));
}
}
virtual void Set(const FActorDescription &Description);
/// Replace the FDataStream associated with this sensor.
///
@ -41,12 +32,24 @@ public:
Stream = std::move(InStream);
}
/// Return the token that allows subscribing to this sensor's stream.
auto GetToken() const
{
return Stream.GetToken();
}
protected:
void EndPlay(EEndPlayReason::Type EndPlayReason) override;
/// Return the FDataStream associated with this sensor.
FDataStream &GetDataStream()
///
/// You need to provide a reference to self, this is necessary for template
/// deduction.
template <typename SensorT>
FAsyncDataStream GetDataStream(const SensorT &Self)
{
return Stream;
return Stream.MakeAsyncDataStream(Self);
}
private:

View File

@ -7,6 +7,8 @@
#include "Carla.h"
#include "Carla/Sensor/SensorFactory.h"
#include "Carla/Game/CarlaGameInstance.h"
#include "Carla/Game/CarlaStatics.h"
#include "Carla/Sensor/Sensor.h"
#include <compiler/disable-ue4-macros.h>
@ -109,6 +111,14 @@ FActorSpawnResult ASensorFactory::SpawnActor(
UE_LOG(LogCarla, Error, TEXT("ASensorFactory: cannot spawn sensor into an empty world."));
return {};
}
UCarlaGameInstance *GameInstance = UCarlaStatics::GetGameInstance(World);
if (GameInstance == nullptr)
{
UE_LOG(LogCarla, Error, TEXT("ASensorFactory: cannot spawn sensor, incompatible game instance."));
return {};
}
auto *Sensor = World->SpawnActorDeferred<ASensor>(
Description.Class,
Transform,
@ -122,6 +132,7 @@ FActorSpawnResult ASensorFactory::SpawnActor(
else
{
Sensor->Set(Description);
Sensor->SetDataStream(GameInstance->GetServer().OpenStream());
}
UGameplayStatics::FinishSpawningActor(Sensor, Transform);
return FActorSpawnResult{Sensor};

View File

@ -75,16 +75,21 @@ static carla::Buffer AWorldObserver_Serialize(
write_data(header);
// Write every actor.
for (auto &&pair : Registry) {
auto &&actor_view = pair.second;
check(actor_view.GetActor() != nullptr);
for (auto &&View : Registry) {
check(View.IsValid());
constexpr float TO_METERS = 1e-2;
const auto velocity = TO_METERS * actor_view.GetActor()->GetVelocity();
const auto velocity = TO_METERS * View.GetActor()->GetVelocity();
// get the angular velocity
const auto RootComponent = Cast<UPrimitiveComponent>(View.GetActor()->GetRootComponent());
FVector angularVelocity { 0.0f, 0.0f, 0.0f };
if (RootComponent != nullptr)
angularVelocity = RootComponent->GetPhysicsAngularVelocityInDegrees();
ActorDynamicState info = {
actor_view.GetActorId(),
actor_view.GetActor()->GetActorTransform(),
View.GetActorId(),
View.GetActor()->GetActorTransform(),
carla::geom::Vector3D{velocity.X, velocity.Y, velocity.Z},
AWorldObserver_GetActorState(actor_view)
carla::geom::Vector3D{angularVelocity.X, angularVelocity.Y, angularVelocity.Z},
AWorldObserver_GetActorState(View)
};
write_data(info);
}
@ -107,11 +112,13 @@ void AWorldObserver::Tick(float DeltaSeconds)
GameTimeStamp += DeltaSeconds;
auto AsyncStream = Stream.MakeAsyncDataStream(*this);
auto buffer = AWorldObserver_Serialize(
Stream.PopBufferFromPool(),
AsyncStream.PopBufferFromPool(),
GameTimeStamp,
FPlatformTime::Seconds(),
Episode->GetActorRegistry());
Stream.Send_GameThread(*this, std::move(buffer));
AsyncStream.Send(*this, std::move(buffer));
}

Some files were not shown because too many files have changed in this diff Show More