Control walker skeleton from Python API (#1719)

This commit is contained in:
Daniel Novillo 2019-06-27 22:16:17 +02:00 committed by Néstor Subirón
parent 33dc5dde8d
commit d8145c8dba
18 changed files with 682 additions and 209 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 530 KiB

View File

@ -290,6 +290,19 @@
- `__eq__(other)`
- `__ne__(other)`
## `carla.WalkerControl`
- `direction`
- `speed`
- `jump`
- `__eq__(other)`
- `__ne__(other)`
## `carla.WalkerBoneControl`
- `bone_transforms`
## `carla.Map`
- `__init__(name, xodr_content)`

View File

@ -0,0 +1,76 @@
<h1>Walker Bone Control</h1>
In this tutorial we describe how to manually control and animate the
skeletons of walkers from the CARLA Python API. The reference of
all classes and methods available can be found at
[Python API reference](python_api.md).
!!! note
**This document assumes the user is familiar with the Python API**. <br>
The user should read the Python API tutorial before reading this document.
[Python API tutorial](python_api_tutorial.md).
### Walker skeleton structure
All walkers have the same skeleton hierarchy and bone names. Below is an image of the skeleton
hierarchy.
![Skeleton Hierarchy](img/skeleton_hierarchy.jpg)
### How to manually control a walker's bones
Following is a detailed step-by-step example of how to change the bone transforms of a walker
from the CARLA Python API
#### Connecting to the simulator
Import neccessary libraries used in this example
```py
import carla
import random
```
Initialize the carla client
```py
client = carla.Client('127.0.0.1', 2000)
client.set_timeout(2.0)
```
#### Spawning a walker
Spawn a random walker at one of the map's spawn points
```py
world = client.get_world()
blueprint = random.choice(self.world.get_blueprint_library().filter('walker.*'))
spawn_points = world.get_map().get_spawn_points()
spawn_point = random.choice(spawn_points) if spawn_points else carla.Transform()
world.try_spawn_actor(blueprint, spawn_point)
```
#### Controlling a walker's skeleton
A walker's skeleton can be modified by passing an instance of the WalkerBoneControl class
to the walker's apply_control function. The WalkerBoneControl class contains the transforms
of the bones to be modified. Its bone_transforms member is a list of tuples of value pairs
where the first value is the bone name and the second value is the bone transform. The
apply_control function can be called on every tick to animate a walker's skeleton. The
location and rotation of each transform is relative to its parent. Therefore when a
parent bone's transform is modified, the transforms of the child bones in model space
will also be changed relatively.
In the example below, the rotations of the walker's hands are set to be 90 degrees around
the forward axis and the locations are set to the origin.
```py
control = carla.WalkerBoneControl()
first_tuple = ('crl_hand__R', carla.Transform(rotation=carla.Rotation(roll=90)))
second_tuple = ('crl_hand__L', carla.Transform(rotation=carla.Rotation(roll=90)))
control.bone_transforms = [first_tuple, second_tuple]
world.player.apply_control(control)
```

View File

@ -18,9 +18,12 @@ namespace client {
}
}
void Walker::ApplyControl(const BoneControl &bone_control) {
GetEpisode().Lock()->ApplyBoneControlToWalker(*this, bone_control);
}
Walker::Control Walker::GetWalkerControl() const {
return GetEpisode().Lock()->GetActorSnapshot(*this).state.walker_control;
}
} // namespace client
} // namespace carla

View File

@ -8,6 +8,7 @@
#include "carla/client/Actor.h"
#include "carla/rpc/WalkerControl.h"
#include "carla/rpc/WalkerBoneControl.h"
namespace carla {
namespace client {
@ -16,6 +17,7 @@ namespace client {
public:
using Control = rpc::WalkerControl;
using BoneControl = rpc::WalkerBoneControl;
explicit Walker(ActorInitializer init) : Actor(std::move(init)) {}
@ -24,6 +26,8 @@ namespace client {
/// Apply @a control to this Walker.
void ApplyControl(const Control &control);
void ApplyControl(const BoneControl &bone_control);
/// Return the control last applied to this Walker.
///
/// @note This function does not call the simulator, it returns the Control

View File

@ -10,10 +10,12 @@
#include "carla/Version.h"
#include "carla/client/TimeoutException.h"
#include "carla/rpc/ActorDescription.h"
#include "carla/rpc/BoneTransformData.h"
#include "carla/rpc/Client.h"
#include "carla/rpc/DebugShape.h"
#include "carla/rpc/Response.h"
#include "carla/rpc/VehicleControl.h"
#include "carla/rpc/WalkerBoneControl.h"
#include "carla/rpc/WalkerControl.h"
#include "carla/streaming/Client.h"
@ -50,18 +52,18 @@ namespace detail {
worker_threads > 0u ? worker_threads : std::thread::hardware_concurrency());
}
template <typename... Args>
auto RawCall(const std::string &function, Args &&... args) {
template <typename ... Args>
auto RawCall(const std::string &function, Args && ... args) {
try {
return rpc_client.call(function, std::forward<Args>(args)...);
return rpc_client.call(function, std::forward<Args>(args) ...);
} catch (const ::rpc::timeout &) {
throw_exception(TimeoutException(endpoint, GetTimeout()));
}
}
template <typename T, typename... Args>
auto CallAndWait(const std::string &function, Args &&... args) {
auto object = RawCall(function, std::forward<Args>(args)...);
template <typename T, typename ... Args>
auto CallAndWait(const std::string &function, Args && ... args) {
auto object = RawCall(function, std::forward<Args>(args) ...);
using R = typename carla::rpc::Response<T>;
auto response = object.template as<R>();
if (response.HasError()) {
@ -70,10 +72,10 @@ namespace detail {
return Get(response);
}
template <typename... Args>
void AsyncCall(const std::string &function, Args &&... args) {
template <typename ... Args>
void AsyncCall(const std::string &function, Args && ... args) {
// Discard returned future.
rpc_client.async_call(function, std::forward<Args>(args)...);
rpc_client.async_call(function, std::forward<Args>(args) ...);
}
time_duration GetTimeout() const {
@ -242,6 +244,10 @@ namespace detail {
_pimpl->AsyncCall("apply_control_to_walker", walker, control);
}
void Client::ApplyBoneControlToWalker(rpc::ActorId walker, const rpc::WalkerBoneControl &control) {
_pimpl->AsyncCall("apply_bone_control_to_walker", walker, control);
}
void Client::SetTrafficLightState(
rpc::ActorId traffic_light,
const rpc::TrafficLightState traffic_light_state) {

View File

@ -35,6 +35,7 @@ namespace rpc {
class DebugShape;
class VehicleControl;
class WalkerControl;
class WalkerBoneControl;
}
namespace sensor {
class SensorData;
@ -147,6 +148,10 @@ namespace detail {
rpc::ActorId walker,
const rpc::WalkerControl &control);
void ApplyBoneControlToWalker(
rpc::ActorId walker,
const rpc::WalkerBoneControl &control);
void SetTrafficLightState(
rpc::ActorId traffic_light,
const rpc::TrafficLightState trafficLightState);

View File

@ -296,6 +296,10 @@ namespace detail {
_client.ApplyControlToWalker(walker.GetId(), control);
}
void ApplyBoneControlToWalker(Walker &walker, const rpc::WalkerBoneControl &control) {
_client.ApplyBoneControlToWalker(walker.GetId(), control);
}
void ApplyPhysicsControlToVehicle(Vehicle &vehicle, const rpc::VehiclePhysicsControl &physicsControl) {
_client.ApplyPhysicsControlToVehicle(vehicle.GetId(), physicsControl);
}

View File

@ -0,0 +1,20 @@
// Copyright (c) 2019 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/rpc/Transform.h"
#include <string>
#include <utility>
namespace carla {
namespace rpc {
using BoneTransformData = std::pair<std::string, geom::Transform>;
} // namespace rpc
} // namespace carla

View File

@ -48,14 +48,17 @@ namespace rpc {
struct DestroyActor : CommandBase<DestroyActor> {
DestroyActor() = default;
DestroyActor(ActorId id) : actor(id) {}
DestroyActor(ActorId id)
: actor(id) {}
ActorId actor;
MSGPACK_DEFINE_ARRAY(actor);
};
struct ApplyVehicleControl : CommandBase<ApplyVehicleControl> {
ApplyVehicleControl() = default;
ApplyVehicleControl(ActorId id, const VehicleControl &value) : actor(id), control(value) {}
ApplyVehicleControl(ActorId id, const VehicleControl &value)
: actor(id),
control(value) {}
ActorId actor;
VehicleControl control;
MSGPACK_DEFINE_ARRAY(actor, control);
@ -63,7 +66,9 @@ namespace rpc {
struct ApplyWalkerControl : CommandBase<ApplyWalkerControl> {
ApplyWalkerControl() = default;
ApplyWalkerControl(ActorId id, const WalkerControl &value) : actor(id), control(value) {}
ApplyWalkerControl(ActorId id, const WalkerControl &value)
: actor(id),
control(value) {}
ActorId actor;
WalkerControl control;
MSGPACK_DEFINE_ARRAY(actor, control);
@ -71,7 +76,9 @@ namespace rpc {
struct ApplyTransform : CommandBase<ApplyTransform> {
ApplyTransform() = default;
ApplyTransform(ActorId id, const geom::Transform &value) : actor(id), transform(value) {}
ApplyTransform(ActorId id, const geom::Transform &value)
: actor(id),
transform(value) {}
ActorId actor;
geom::Transform transform;
MSGPACK_DEFINE_ARRAY(actor, transform);
@ -79,7 +86,9 @@ namespace rpc {
struct ApplyVelocity : CommandBase<ApplyVelocity> {
ApplyVelocity() = default;
ApplyVelocity(ActorId id, const geom::Vector3D &value) : actor(id), velocity(value) {}
ApplyVelocity(ActorId id, const geom::Vector3D &value)
: actor(id),
velocity(value) {}
ActorId actor;
geom::Vector3D velocity;
MSGPACK_DEFINE_ARRAY(actor, velocity);
@ -87,7 +96,9 @@ namespace rpc {
struct ApplyAngularVelocity : CommandBase<ApplyAngularVelocity> {
ApplyAngularVelocity() = default;
ApplyAngularVelocity(ActorId id, const geom::Vector3D &value) : actor(id), angular_velocity(value) {}
ApplyAngularVelocity(ActorId id, const geom::Vector3D &value)
: actor(id),
angular_velocity(value) {}
ActorId actor;
geom::Vector3D angular_velocity;
MSGPACK_DEFINE_ARRAY(actor, angular_velocity);
@ -95,7 +106,9 @@ namespace rpc {
struct ApplyImpulse : CommandBase<ApplyImpulse> {
ApplyImpulse() = default;
ApplyImpulse(ActorId id, const geom::Vector3D &value) : actor(id), impulse(value) {}
ApplyImpulse(ActorId id, const geom::Vector3D &value)
: actor(id),
impulse(value) {}
ActorId actor;
geom::Vector3D impulse;
MSGPACK_DEFINE_ARRAY(actor, impulse);
@ -103,7 +116,9 @@ namespace rpc {
struct SetSimulatePhysics : CommandBase<SetSimulatePhysics> {
SetSimulatePhysics() = default;
SetSimulatePhysics(ActorId id, bool value) : actor(id), enabled(value) {}
SetSimulatePhysics(ActorId id, bool value)
: actor(id),
enabled(value) {}
ActorId actor;
bool enabled;
MSGPACK_DEFINE_ARRAY(actor, enabled);
@ -111,7 +126,9 @@ namespace rpc {
struct SetAutopilot : CommandBase<SetAutopilot> {
SetAutopilot() = default;
SetAutopilot(ActorId id, bool value) : actor(id), enabled(value) {}
SetAutopilot(ActorId id, bool value)
: actor(id),
enabled(value) {}
ActorId actor;
bool enabled;
MSGPACK_DEFINE_ARRAY(actor, enabled);

View File

@ -0,0 +1,50 @@
// Copyright (c) 2019 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/rpc/BoneTransformData.h"
#include "carla/rpc/String.h"
#include "carla/rpc/Transform.h"
#ifdef LIBCARLA_INCLUDED_FROM_UE4
# include "Carla/Walker/WalkerBoneControl.h"
#endif // LIBCARLA_INCLUDED_FROM_UE4
#include <vector>
namespace carla {
namespace rpc {
class WalkerBoneControl {
public:
WalkerBoneControl() = default;
explicit WalkerBoneControl(
std::vector<rpc::BoneTransformData> bone_transforms)
: bone_transforms(bone_transforms) {}
#ifdef LIBCARLA_INCLUDED_FROM_UE4
operator FWalkerBoneControl() const {
FWalkerBoneControl Control;
for (auto &bone_transform : bone_transforms) {
Control.BoneTransforms.Add(ToFString(bone_transform.first), bone_transform.second);
}
return Control;
}
#endif // LIBCARLA_INCLUDED_FROM_UE4
std::vector<rpc::BoneTransformData> bone_transforms;
MSGPACK_DEFINE_ARRAY(bone_transforms);
};
} // namespace rpc
} // namespace carla

View File

@ -43,6 +43,11 @@ static auto GetGroupTrafficLights(carla::client::TrafficLight &self) {
return result;
}
template <typename ControlT>
static void ApplyControl(carla::client::Walker &self, const ControlT &control) {
self.ApplyControl(control);
}
void export_actor() {
using namespace boost::python;
namespace cc = carla::client;
@ -54,87 +59,89 @@ void export_actor() {
;
class_<cc::Actor, boost::noncopyable, boost::shared_ptr<cc::Actor>>("Actor", no_init)
// work-around, force return copy to resolve Actor instead of ActorState.
.add_property("id", CALL_RETURNING_COPY(cc::Actor, GetId))
.add_property("type_id", CALL_RETURNING_COPY(cc::Actor, GetTypeId))
.add_property("parent", CALL_RETURNING_COPY(cc::Actor, GetParent))
.add_property("semantic_tags", &GetSemanticTags)
.add_property("is_alive", CALL_RETURNING_COPY(cc::Actor, IsAlive))
.add_property("attributes", +[] (const cc::Actor &self) {
boost::python::dict atttribute_dict;
for (auto &&attribute_value : self.GetAttributes()) {
atttribute_dict[attribute_value.GetId()] = attribute_value.GetValue();
}
return atttribute_dict;
})
.def("get_world", CALL_RETURNING_COPY(cc::Actor, GetWorld))
.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))
// work-around, force return copy to resolve Actor instead of ActorState.
.add_property("id", CALL_RETURNING_COPY(cc::Actor, GetId))
.add_property("type_id", CALL_RETURNING_COPY(cc::Actor, GetTypeId))
.add_property("parent", CALL_RETURNING_COPY(cc::Actor, GetParent))
.add_property("semantic_tags", &GetSemanticTags)
.add_property("is_alive", CALL_RETURNING_COPY(cc::Actor, IsAlive))
.add_property("attributes", +[] (const cc::Actor &self) {
boost::python::dict atttribute_dict;
for (auto &&attribute_value : self.GetAttributes()) {
atttribute_dict[attribute_value.GetId()] = attribute_value.GetValue();
}
return atttribute_dict;
})
.def("get_world", CALL_RETURNING_COPY(cc::Actor, GetWorld))
.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))
;
class_<cc::Vehicle, bases<cc::Actor>, boost::noncopyable, boost::shared_ptr<cc::Vehicle>>("Vehicle", no_init)
.add_property("bounding_box", CALL_RETURNING_COPY(cc::Vehicle, GetBoundingBox))
.def("apply_control", &cc::Vehicle::ApplyControl, (arg("control")))
.def("get_control", &cc::Vehicle::GetControl)
.def("apply_physics_control", &cc::Vehicle::ApplyPhysicsControl, (arg("physics_control")))
.def("get_physics_control", CONST_CALL_WITHOUT_GIL(cc::Vehicle, GetPhysicsControl))
.def("set_autopilot", &cc::Vehicle::SetAutopilot, (arg("enabled") = true))
.def("get_speed_limit", &cc::Vehicle::GetSpeedLimit)
.def("get_traffic_light_state", &cc::Vehicle::GetTrafficLightState)
.def("is_at_traffic_light", &cc::Vehicle::IsAtTrafficLight)
.def("get_traffic_light", &cc::Vehicle::GetTrafficLight)
.def(self_ns::str(self_ns::self))
class_<cc::Vehicle, bases<cc::Actor>, boost::noncopyable, boost::shared_ptr<cc::Vehicle>>("Vehicle",
no_init)
.add_property("bounding_box", CALL_RETURNING_COPY(cc::Vehicle, GetBoundingBox))
.def("apply_control", &cc::Vehicle::ApplyControl, (arg("control")))
.def("get_control", &cc::Vehicle::GetControl)
.def("apply_physics_control", &cc::Vehicle::ApplyPhysicsControl, (arg("physics_control")))
.def("get_physics_control", CONST_CALL_WITHOUT_GIL(cc::Vehicle, GetPhysicsControl))
.def("set_autopilot", &cc::Vehicle::SetAutopilot, (arg("enabled") = true))
.def("get_speed_limit", &cc::Vehicle::GetSpeedLimit)
.def("get_traffic_light_state", &cc::Vehicle::GetTrafficLightState)
.def("is_at_traffic_light", &cc::Vehicle::IsAtTrafficLight)
.def("get_traffic_light", &cc::Vehicle::GetTrafficLight)
.def(self_ns::str(self_ns::self))
;
class_<cc::Walker, bases<cc::Actor>, boost::noncopyable, boost::shared_ptr<cc::Walker>>("Walker", no_init)
.add_property("bounding_box", CALL_RETURNING_COPY(cc::Walker, GetBoundingBox))
.def("apply_control", &cc::Walker::ApplyControl, (arg("control")))
.def("get_control", &cc::Walker::GetWalkerControl)
.def(self_ns::str(self_ns::self))
.add_property("bounding_box", CALL_RETURNING_COPY(cc::Walker, GetBoundingBox))
.def("apply_control", &ApplyControl<cr::WalkerControl>, (arg("control")))
.def("apply_control", &ApplyControl<cr::WalkerBoneControl>, (arg("control")))
.def("get_control", &cc::Walker::GetWalkerControl)
.def(self_ns::str(self_ns::self))
;
class_<cc::TrafficSign, bases<cc::Actor>, boost::noncopyable, boost::shared_ptr<cc::TrafficSign>>(
"TrafficSign",
no_init)
.add_property("trigger_volume", CALL_RETURNING_COPY(cc::TrafficSign, GetTriggerVolume))
.add_property("trigger_volume", CALL_RETURNING_COPY(cc::TrafficSign, GetTriggerVolume))
;
enum_<cr::TrafficLightState>("TrafficLightState")
.value("Red", cr::TrafficLightState::Red)
.value("Yellow", cr::TrafficLightState::Yellow)
.value("Green", cr::TrafficLightState::Green)
.value("Off", cr::TrafficLightState::Off)
.value("Unknown", cr::TrafficLightState::Unknown)
.value("Red", cr::TrafficLightState::Red)
.value("Yellow", cr::TrafficLightState::Yellow)
.value("Green", cr::TrafficLightState::Green)
.value("Off", cr::TrafficLightState::Off)
.value("Unknown", cr::TrafficLightState::Unknown)
;
class_<cc::TrafficLight, bases<cc::TrafficSign>, boost::noncopyable, boost::shared_ptr<cc::TrafficLight>>(
"TrafficLight",
no_init)
.add_property("state", &cc::TrafficLight::GetState)
.def("set_state", &cc::TrafficLight::SetState, (arg("state")))
.def("get_state", &cc::TrafficLight::GetState)
.def("set_green_time", &cc::TrafficLight::SetGreenTime, (arg("green_time")))
.def("get_green_time", &cc::TrafficLight::GetGreenTime)
.def("set_yellow_time", &cc::TrafficLight::SetYellowTime, (arg("yellow_time")))
.def("get_yellow_time", &cc::TrafficLight::GetYellowTime)
.def("set_red_time", &cc::TrafficLight::SetRedTime, (arg("red_time")))
.def("get_red_time", &cc::TrafficLight::GetRedTime)
.def("get_elapsed_time", &cc::TrafficLight::GetElapsedTime)
.def("freeze", &cc::TrafficLight::Freeze, (arg("freeze")))
.def("is_frozen", &cc::TrafficLight::IsFrozen)
.def("get_pole_index", &cc::TrafficLight::GetPoleIndex)
.def("get_group_traffic_lights", &GetGroupTrafficLights)
.def(self_ns::str(self_ns::self))
.add_property("state", &cc::TrafficLight::GetState)
.def("set_state", &cc::TrafficLight::SetState, (arg("state")))
.def("get_state", &cc::TrafficLight::GetState)
.def("set_green_time", &cc::TrafficLight::SetGreenTime, (arg("green_time")))
.def("get_green_time", &cc::TrafficLight::GetGreenTime)
.def("set_yellow_time", &cc::TrafficLight::SetYellowTime, (arg("yellow_time")))
.def("get_yellow_time", &cc::TrafficLight::GetYellowTime)
.def("set_red_time", &cc::TrafficLight::SetRedTime, (arg("red_time")))
.def("get_red_time", &cc::TrafficLight::GetRedTime)
.def("get_elapsed_time", &cc::TrafficLight::GetElapsedTime)
.def("freeze", &cc::TrafficLight::Freeze, (arg("freeze")))
.def("is_frozen", &cc::TrafficLight::IsFrozen)
.def("get_pole_index", &cc::TrafficLight::GetPoleIndex)
.def("get_group_traffic_lights", &GetGroupTrafficLights)
.def(self_ns::str(self_ns::self))
;
}

View File

@ -98,33 +98,33 @@ void export_blueprint() {
;
class_<cc::ActorAttribute>("ActorAttribute", no_init)
.add_property("id", CALL_RETURNING_COPY(cc::ActorAttribute, GetId))
.add_property("type", &cc::ActorAttribute::GetType)
.add_property("recommended_values", CALL_RETURNING_LIST(cc::ActorAttribute, GetRecommendedValues))
.add_property("is_modifiable", &cc::ActorAttribute::IsModifiable)
.def("as_bool", &cc::ActorAttribute::As<bool>)
.def("as_int", &cc::ActorAttribute::As<int>)
.def("as_float", &cc::ActorAttribute::As<float>)
.def("as_str", &cc::ActorAttribute::As<std::string>)
.def("as_color", &cc::ActorAttribute::As<csd::Color>)
.def("__eq__", &cc::ActorAttributeValueAccess::operator==<bool>)
.def("__eq__", &cc::ActorAttributeValueAccess::operator==<int>)
.def("__eq__", &cc::ActorAttributeValueAccess::operator==<float>)
.def("__eq__", &cc::ActorAttributeValueAccess::operator==<std::string>)
.def("__eq__", &cc::ActorAttributeValueAccess::operator==<csd::Color>)
.def("__eq__", &cc::ActorAttributeValueAccess::operator==<cc::ActorAttribute>)
.def("__ne__", &cc::ActorAttributeValueAccess::operator!=<bool>)
.def("__ne__", &cc::ActorAttributeValueAccess::operator!=<int>)
.def("__ne__", &cc::ActorAttributeValueAccess::operator!=<float>)
.def("__ne__", &cc::ActorAttributeValueAccess::operator!=<std::string>)
.def("__ne__", &cc::ActorAttributeValueAccess::operator!=<csd::Color>)
.def("__ne__", &cc::ActorAttributeValueAccess::operator!=<cc::ActorAttribute>)
.def("__nonzero__", &cc::ActorAttribute::As<bool>)
.def("__bool__", &cc::ActorAttribute::As<bool>)
.def("__int__", &cc::ActorAttribute::As<int>)
.def("__float__", &cc::ActorAttribute::As<float>)
.def("__str__", &cc::ActorAttribute::As<std::string>)
.def(self_ns::str(self_ns::self))
.add_property("id", CALL_RETURNING_COPY(cc::ActorAttribute, GetId))
.add_property("type", &cc::ActorAttribute::GetType)
.add_property("recommended_values", CALL_RETURNING_LIST(cc::ActorAttribute, GetRecommendedValues))
.add_property("is_modifiable", &cc::ActorAttribute::IsModifiable)
.def("as_bool", &cc::ActorAttribute::As<bool>)
.def("as_int", &cc::ActorAttribute::As<int>)
.def("as_float", &cc::ActorAttribute::As<float>)
.def("as_str", &cc::ActorAttribute::As<std::string>)
.def("as_color", &cc::ActorAttribute::As<csd::Color>)
.def("__eq__", &cc::ActorAttributeValueAccess::operator==<bool>)
.def("__eq__", &cc::ActorAttributeValueAccess::operator==<int>)
.def("__eq__", &cc::ActorAttributeValueAccess::operator==<float>)
.def("__eq__", &cc::ActorAttributeValueAccess::operator==<std::string>)
.def("__eq__", &cc::ActorAttributeValueAccess::operator==<csd::Color>)
.def("__eq__", &cc::ActorAttributeValueAccess::operator==<cc::ActorAttribute>)
.def("__ne__", &cc::ActorAttributeValueAccess::operator!=<bool>)
.def("__ne__", &cc::ActorAttributeValueAccess::operator!=<int>)
.def("__ne__", &cc::ActorAttributeValueAccess::operator!=<float>)
.def("__ne__", &cc::ActorAttributeValueAccess::operator!=<std::string>)
.def("__ne__", &cc::ActorAttributeValueAccess::operator!=<csd::Color>)
.def("__ne__", &cc::ActorAttributeValueAccess::operator!=<cc::ActorAttribute>)
.def("__nonzero__", &cc::ActorAttribute::As<bool>)
.def("__bool__", &cc::ActorAttribute::As<bool>)
.def("__int__", &cc::ActorAttribute::As<int>)
.def("__float__", &cc::ActorAttribute::As<float>)
.def("__str__", &cc::ActorAttribute::As<std::string>)
.def(self_ns::str(self_ns::self))
;
class_<cc::ActorBlueprint>("ActorBlueprint", no_init)

View File

@ -8,6 +8,7 @@
#include <carla/rpc/VehiclePhysicsControl.h>
#include <carla/rpc/WheelPhysicsControl.h>
#include <carla/rpc/WalkerControl.h>
#include <carla/rpc/WalkerBoneControl.h>
#include <ostream>
@ -16,7 +17,7 @@ namespace rpc {
static auto boolalpha(bool b) {
return b ? "True" : "False";
};
}
std::ostream &operator<<(std::ostream &out, const VehicleControl &control) {
out << "VehicleControl(throttle=" << std::to_string(control.throttle)
@ -36,6 +37,16 @@ namespace rpc {
return out;
}
std::ostream &operator<<(std::ostream &out, const WalkerBoneControl &control) {
out << "WalkerBoneControl(bone_transforms(";
for (auto bone_transform : control.bone_transforms) {
out << "(name=" << bone_transform.first
<< ", transform=" << bone_transform.second << ')';
}
out << "))";
return out;
}
std::ostream &operator<<(std::ostream &out, const WheelPhysicsControl &control) {
out << "WheelPhysicsControl(tire_friction=" << std::to_string(control.tire_friction)
<< ", damping_rate=" << std::to_string(control.damping_rate)
@ -65,6 +76,7 @@ namespace rpc {
} // namespace rpc
} // namespace carla
static auto GetVectorOfVector2DFromList(const boost::python::list &list) {
std::vector<carla::geom::Vector2D> v;
@ -83,6 +95,24 @@ static auto GetVectorOfVector2DFromList(const boost::python::list &list) {
return v;
}
static auto GetVectorOfBoneTransformFromList(const boost::python::list &list) {
std::vector<carla::rpc::BoneTransformData> v;
auto length = boost::python::len(list);
v.reserve(static_cast<size_t>(length));
for (auto i = 0u; i < length; ++i) {
boost::python::extract<carla::rpc::BoneTransformData> ext(list[i]);
if (ext.check()) {
v.push_back(ext);
} else {
v.push_back(carla::rpc::BoneTransformData{
boost::python::extract<std::string>(list[i][0u]),
boost::python::extract<carla::geom::Transform>(list[i][1u])});
}
}
return v;
}
static auto GetWheels(const carla::rpc::VehiclePhysicsControl &self) {
const auto &wheels = self.GetWheels();
boost::python::object get_iter = boost::python::iterator<std::vector<carla::rpc::WheelPhysicsControl>>();
@ -122,60 +152,99 @@ static void SetSteeringCurve(carla::rpc::VehiclePhysicsControl &self, const boos
}
boost::python::object VehiclePhysicsControl_init(boost::python::tuple args, boost::python::dict kwargs) {
// Args names
const uint32_t NUM_ARGUMENTS = 16;
const char* args_names[NUM_ARGUMENTS] = {
"torque_curve",
"max_rpm",
"moi",
"damping_rate_full_throttle",
"damping_rate_zero_throttle_clutch_engaged",
"damping_rate_zero_throttle_clutch_disengaged",
// Args names
const uint32_t NUM_ARGUMENTS = 16;
const char *args_names[NUM_ARGUMENTS] = {
"torque_curve",
"max_rpm",
"moi",
"damping_rate_full_throttle",
"damping_rate_zero_throttle_clutch_engaged",
"damping_rate_zero_throttle_clutch_disengaged",
"use_gear_autobox",
"gear_switch_time",
"clutch_strength",
"use_gear_autobox",
"gear_switch_time",
"clutch_strength",
"mass",
"drag_coefficient",
"mass",
"drag_coefficient",
"center_of_mass",
"steering_curve",
"wheels"
};
"center_of_mass",
"steering_curve",
"wheels"
};
boost::python::object self = args[0];
args = boost::python::tuple(args.slice(1, boost::python::_));
boost::python::object self = args[0];
args = boost::python::tuple(args.slice(1, boost::python::_));
auto res = self.attr("__init__")();
if (len(args) > 0) {
for (unsigned int i=0; i < len(args); ++i)
self.attr(args_names[i]) = args[i];
auto res = self.attr("__init__")();
if (len(args) > 0) {
for (unsigned int i = 0; i < len(args); ++i) {
self.attr(args_names[i]) = args[i];
}
}
for (unsigned int i = 0; i < NUM_ARGUMENTS; ++i) {
if (kwargs.contains(args_names[i])) {
self.attr(args_names[i]) = kwargs[args_names[i]];
}
for (unsigned int i = 0; i < NUM_ARGUMENTS; ++i) {
if (kwargs.contains(args_names[i])) {
self.attr(args_names[i]) = kwargs[args_names[i]];
}
}
return res;
return res;
}
static auto GetBonesTransform(const carla::rpc::WalkerBoneControl &self) {
const std::vector<carla::rpc::BoneTransformData> &bone_transform_data = self.bone_transforms;
boost::python::object get_iter =
boost::python::iterator<const std::vector<carla::rpc::BoneTransformData>>();
boost::python::object iter = get_iter(bone_transform_data);
return boost::python::list(iter);
}
static void SetBonesTransform(carla::rpc::WalkerBoneControl &self, const boost::python::list &list) {
self.bone_transforms = GetVectorOfBoneTransformFromList(list);
}
boost::python::object WalkerBoneControl_init(boost::python::tuple args, boost::python::dict kwargs) {
// Args names
const uint32_t NUM_ARGUMENTS = 1;
const char *args_names[NUM_ARGUMENTS] = {
"bone_transforms"
};
boost::python::object self = args[0];
args = boost::python::tuple(args.slice(1, boost::python::_));
auto res = self.attr("__init__")();
if (len(args) > 0) {
for (unsigned int i = 0; i < len(args); ++i) {
self.attr(args_names[i]) = args[i];
}
}
for (unsigned int i = 0; i < NUM_ARGUMENTS; ++i) {
if (kwargs.contains(args_names[i])) {
self.attr(args_names[i]) = kwargs[args_names[i]];
}
}
return res;
}
void export_control() {
using namespace boost::python;
namespace cr = carla::rpc;
namespace cg = carla::geom;
namespace cr = carla::rpc;
class_<cr::VehicleControl>("VehicleControl")
.def(init<float, float, float, bool, bool, bool, int>(
(arg("throttle")=0.0f,
arg("steer")=0.0f,
arg("brake")=0.0f,
arg("hand_brake")=false,
arg("reverse")=false,
arg("manual_gear_shift")=false,
arg("gear")=0)))
(arg("throttle") = 0.0f,
arg("steer") = 0.0f,
arg("brake") = 0.0f,
arg("hand_brake") = false,
arg("reverse") = false,
arg("manual_gear_shift") = false,
arg("gear") = 0)))
.def_readwrite("throttle", &cr::VehicleControl::throttle)
.def_readwrite("steer", &cr::VehicleControl::steer)
.def_readwrite("brake", &cr::VehicleControl::brake)
@ -190,9 +259,9 @@ void export_control() {
class_<cr::WalkerControl>("WalkerControl")
.def(init<cg::Vector3D, float, bool>(
(arg("direction")=cg::Vector3D{1.0f, 0.0f, 0.0f},
arg("speed")=0.0f,
arg("jump")=false)))
(arg("direction") = cg::Vector3D{1.0f, 0.0f, 0.0f},
arg("speed") = 0.0f,
arg("jump") = false)))
.def_readwrite("direction", &cr::WalkerControl::direction)
.def_readwrite("speed", &cr::WalkerControl::speed)
.def_readwrite("jump", &cr::WalkerControl::jump)
@ -201,9 +270,16 @@ void export_control() {
.def(self_ns::str(self_ns::self))
;
class_<cr::WalkerBoneControl>("WalkerBoneControl")
.def("__init__", raw_function(WalkerBoneControl_init))
.def(init<>())
.add_property("bone_transforms", &GetBonesTransform, &SetBonesTransform)
.def(self_ns::str(self_ns::self))
;
class_<std::vector<cr::WheelPhysicsControl>>("vector_of_wheels")
.def(boost::python::vector_indexing_suite<std::vector<cr::WheelPhysicsControl>>())
.def(self_ns::str(self_ns::self))
.def(boost::python::vector_indexing_suite<std::vector<cr::WheelPhysicsControl>>())
.def(self_ns::str(self_ns::self))
;
class_<cr::WheelPhysicsControl>("WheelPhysicsControl")
@ -224,28 +300,25 @@ void export_control() {
;
class_<cr::VehiclePhysicsControl>("VehiclePhysicsControl", no_init)
.def("__init__", raw_function(VehiclePhysicsControl_init), "raw ctor")
.def("__init__", raw_function(VehiclePhysicsControl_init))
.def(init<>())
.add_property("torque_curve", &GetTorqueCurve, &SetTorqueCurve)
.def_readwrite("max_rpm", &cr::VehiclePhysicsControl::max_rpm)
.def_readwrite("moi", &cr::VehiclePhysicsControl::moi)
.def_readwrite("damping_rate_full_throttle", &cr::VehiclePhysicsControl::damping_rate_full_throttle)
.def_readwrite("damping_rate_zero_throttle_clutch_engaged", &cr::VehiclePhysicsControl::damping_rate_zero_throttle_clutch_engaged)
.def_readwrite("damping_rate_zero_throttle_clutch_disengaged", &cr::VehiclePhysicsControl::damping_rate_zero_throttle_clutch_disengaged)
.def_readwrite("damping_rate_full_throttle",
&cr::VehiclePhysicsControl::damping_rate_full_throttle)
.def_readwrite("damping_rate_zero_throttle_clutch_engaged",
&cr::VehiclePhysicsControl::damping_rate_zero_throttle_clutch_engaged)
.def_readwrite("damping_rate_zero_throttle_clutch_disengaged",
&cr::VehiclePhysicsControl::damping_rate_zero_throttle_clutch_disengaged)
.def_readwrite("use_gear_autobox", &cr::VehiclePhysicsControl::use_gear_autobox)
.def_readwrite("gear_switch_time", &cr::VehiclePhysicsControl::gear_switch_time)
.def_readwrite("clutch_strength", &cr::VehiclePhysicsControl::clutch_strength)
.def_readwrite("mass", &cr::VehiclePhysicsControl::mass)
.def_readwrite("drag_coefficient", &cr::VehiclePhysicsControl::drag_coefficient)
.def_readwrite("center_of_mass", &cr::VehiclePhysicsControl::center_of_mass)
.add_property("steering_curve", &GetSteeringCurve, &SetSteeringCurve)
.add_property("wheels", &GetWheels, &SetWheels)
.def("__eq__", &cr::VehiclePhysicsControl::operator==)
.def("__ne__", &cr::VehiclePhysicsControl::operator!=)
.def(self_ns::str(self_ns::self))

View File

@ -32,6 +32,7 @@
#include <carla/rpc/Vector3D.h>
#include <carla/rpc/VehicleControl.h>
#include <carla/rpc/VehiclePhysicsControl.h>
#include <carla/rpc/WalkerBoneControl.h>
#include <carla/rpc/WalkerControl.h>
#include <carla/rpc/WeatherParameters.h>
#include <carla/streaming/Server.h>
@ -81,7 +82,6 @@ public:
private:
void BindActions();
};
// =============================================================================
@ -103,10 +103,11 @@ private:
return carla::rpc::ResponseError(carla::rpc::FromFString(fstr)); }
#define REQUIRE_CARLA_EPISODE() \
CARLA_ENSURE_GAME_THREAD(); \
if (Episode == nullptr) { RESPOND_ERROR("episode not ready"); }
CARLA_ENSURE_GAME_THREAD(); \
if (Episode == nullptr) { RESPOND_ERROR("episode not ready"); }
class ServerBinder {
class ServerBinder
{
public:
constexpr ServerBinder(const char *name, carla::rpc::Server &srv, bool sync)
@ -115,10 +116,14 @@ public:
_sync(sync) {}
template <typename FuncT>
auto operator<<(FuncT func) {
if (_sync) {
auto operator<<(FuncT func)
{
if (_sync)
{
_server.BindSync(_name, func);
} else {
}
else
{
_server.BindAsync(_name, func);
}
return func;
@ -133,8 +138,8 @@ private:
bool _sync;
};
#define BIND_SYNC(name) auto name = ServerBinder(#name, Server, true)
#define BIND_ASYNC(name) auto name = ServerBinder(#name, Server, false)
#define BIND_SYNC(name) auto name = ServerBinder(# name, Server, true)
#define BIND_ASYNC(name) auto name = ServerBinder(# name, Server, false)
// =============================================================================
// -- Bind Actions -------------------------------------------------------------
@ -145,7 +150,7 @@ void FCarlaServer::FPimpl::BindActions()
namespace cr = carla::rpc;
namespace cg = carla::geom;
BIND_ASYNC(version) << []() -> R<std::string>
BIND_ASYNC(version) << [] () -> R<std::string>
{
return carla::version();
};
@ -188,8 +193,8 @@ void FCarlaServer::FPimpl::BindActions()
{
REQUIRE_CARLA_EPISODE();
return cr::EpisodeInfo{
Episode->GetId(),
BroadcastStream.token()};
Episode->GetId(),
BroadcastStream.token()};
};
BIND_SYNC(get_map_info) << [this]() -> R<cr::MapInfo>
@ -351,10 +356,10 @@ void FCarlaServer::FPimpl::BindActions()
RESPOND_ERROR("unable to set actor location: actor not found");
}
ActorView.GetActor()->SetActorRelativeLocation(
Location,
false,
nullptr,
ETeleportType::TeleportPhysics);
Location,
false,
nullptr,
ETeleportType::TeleportPhysics);
return R<void>::Success();
};
@ -369,10 +374,10 @@ void FCarlaServer::FPimpl::BindActions()
RESPOND_ERROR("unable to set actor transform: actor not found");
}
ActorView.GetActor()->SetActorRelativeTransform(
Transform,
false,
nullptr,
ETeleportType::TeleportPhysics);
Transform,
false,
nullptr,
ETeleportType::TeleportPhysics);
return R<void>::Success();
};
@ -545,6 +550,30 @@ void FCarlaServer::FPimpl::BindActions()
return R<void>::Success();
};
BIND_SYNC(apply_bone_control_to_walker) << [this](
cr::ActorId ActorId,
cr::WalkerBoneControl Control) -> R<void>
{
REQUIRE_CARLA_EPISODE();
auto ActorView = Episode->FindActor(ActorId);
if (!ActorView.IsValid())
{
RESPOND_ERROR("unable to apply control: actor not found");
}
auto Pawn = Cast<APawn>(ActorView.GetActor());
if (Pawn == nullptr)
{
RESPOND_ERROR("unable to apply control: actor is not a walker");
}
auto Controller = Cast<AWalkerController>(Pawn->GetController());
if (Controller == nullptr)
{
RESPOND_ERROR("unable to apply control: walker has an incompatible controller");
}
Controller->ApplyWalkerControl(Control);
return R<void>::Success();
};
BIND_SYNC(set_actor_autopilot) << [this](
cr::ActorId ActorId,
bool bEnabled) -> R<void>
@ -707,7 +736,9 @@ void FCarlaServer::FPimpl::BindActions()
return R<void>::Success();
};
BIND_SYNC(show_recorder_file_info) << [this](std::string name, bool show_all) -> R<std::string>
BIND_SYNC(show_recorder_file_info) << [this](
std::string name,
bool show_all) -> R<std::string>
{
REQUIRE_CARLA_EPISODE();
return R<std::string>(Episode->GetRecorder()->ShowFileInfo(
@ -715,7 +746,10 @@ void FCarlaServer::FPimpl::BindActions()
show_all));
};
BIND_SYNC(show_recorder_collisions) << [this](std::string name, char type1, char type2) -> R<std::string>
BIND_SYNC(show_recorder_collisions) << [this](
std::string name,
char type1,
char type2) -> R<std::string>
{
REQUIRE_CARLA_EPISODE();
return R<std::string>(Episode->GetRecorder()->ShowFileCollisions(
@ -724,7 +758,10 @@ void FCarlaServer::FPimpl::BindActions()
type2));
};
BIND_SYNC(show_recorder_actors_blocked) << [this](std::string name, double min_time, double min_distance) -> R<std::string>
BIND_SYNC(show_recorder_actors_blocked) << [this](
std::string name,
double min_time,
double min_distance) -> R<std::string>
{
REQUIRE_CARLA_EPISODE();
return R<std::string>(Episode->GetRecorder()->ShowFileActorsBlocked(
@ -733,7 +770,11 @@ void FCarlaServer::FPimpl::BindActions()
min_distance));
};
BIND_SYNC(replay_file) << [this](std::string name, double start, double duration, uint32_t follow_id) -> R<std::string>
BIND_SYNC(replay_file) << [this](
std::string name,
double start,
double duration,
uint32_t follow_id) -> R<std::string>
{
REQUIRE_CARLA_EPISODE();
return R<std::string>(Episode->GetRecorder()->ReplayFile(
@ -777,18 +818,20 @@ void FCarlaServer::FPimpl::BindActions()
auto command_visitor = carla::Functional::MakeRecursiveOverload(
[=](auto self, const C::SpawnActor &c) -> CR {
auto result = c.parent.has_value() ?
spawn_actor_with_parent(
c.description,
c.transform,
*c.parent,
cr::AttachmentType::Rigid) :
spawn_actor(c.description, c.transform);
if (!result.HasError()) {
spawn_actor_with_parent(
c.description,
c.transform,
*c.parent,
cr::AttachmentType::Rigid) :
spawn_actor(c.description, c.transform);
if (!result.HasError())
{
ActorId id = result.Get().id;
auto set_id = carla::Functional::MakeOverload(
[](C::SpawnActor &) {},
[id](auto &s) { s.actor = id; });
for (auto command : c.do_after) {
for (auto command : c.do_after)
{
boost::apply_visitor(set_id, command.command);
boost::apply_visitor(self, command.command);
}
@ -808,7 +851,9 @@ void FCarlaServer::FPimpl::BindActions()
#undef MAKE_RESULT
BIND_SYNC(apply_batch) << [=](const std::vector<cr::Command> &commands, bool do_tick_cue)
BIND_SYNC(apply_batch) << [=](
const std::vector<cr::Command> &commands,
bool do_tick_cue)
{
std::vector<CR> result;
result.reserve(commands.size());
@ -847,7 +892,12 @@ FDataMultiStream FCarlaServer::Start(uint16_t RPCPort, uint16_t StreamingPort)
{
Pimpl = MakeUnique<FPimpl>(RPCPort, StreamingPort);
StreamingPort = Pimpl->StreamingServer.GetLocalEndpoint().port();
UE_LOG(LogCarlaServer, Log, TEXT("Initialized CarlaServer: Ports(rpc=%d, streaming=%d)"), RPCPort, StreamingPort);
UE_LOG(
LogCarlaServer,
Log,
TEXT("Initialized CarlaServer: Ports(rpc=%d, streaming=%d)"),
RPCPort,
StreamingPort);
return Pimpl->BroadcastStream;
}

View File

@ -0,0 +1,19 @@
// 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 "WalkerBoneControl.generated.h"
USTRUCT(BlueprintType)
struct CARLA_API FWalkerBoneControl
{
GENERATED_BODY()
UPROPERTY(Category = "Walker Bone Control", EditAnywhere, BlueprintReadWrite)
TMap<FString, FTransform> BoneTransforms;
};

View File

@ -7,10 +7,15 @@
#include "Carla.h"
#include "Carla/Walker/WalkerController.h"
#include "Components/PoseableMeshComponent.h"
#include "Components/PrimitiveComponent.h"
#include "Containers/Map.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "GameFramework/Pawn.h"
AWalkerController::AWalkerController(const FObjectInitializer& ObjectInitializer)
#include <boost/variant/apply_visitor.hpp>
AWalkerController::AWalkerController(const FObjectInitializer &ObjectInitializer)
: Super(ObjectInitializer)
{
PrimaryActorTick.bCanEverTick = true;
@ -39,18 +44,107 @@ void AWalkerController::OnPossess(APawn *InPawn)
Character->JumpMaxCount = 2;
}
void AWalkerController::Tick(float DeltaSeconds)
UPoseableMeshComponent *AddNewBoneComponent(AActor *InActor, FVector inLocation, FRotator inRotator)
{
Super::Tick(DeltaSeconds);
UPoseableMeshComponent *NewComp = NewObject<UPoseableMeshComponent>(InActor,
UPoseableMeshComponent::StaticClass());
if (NewComp)
{
NewComp->RegisterComponent();
NewComp->SetWorldLocation(inLocation);
NewComp->SetWorldRotation(inRotator);
NewComp->AttachToComponent(InActor->GetRootComponent(), FAttachmentTransformRules::KeepRelativeTransform);
}
return NewComp;
}
void AWalkerController::ApplyWalkerControl(const FWalkerControl &InControl)
{
Control = InControl;
if (bManualBones)
{
SetManualBones(false);
}
}
void AWalkerController::ApplyWalkerControl(const FWalkerBoneControl &InBoneControl)
{
Control = InBoneControl;
if (!bManualBones)
{
SetManualBones(true);
}
}
void AWalkerController::SetManualBones(const bool bIsEnabled)
{
bManualBones = bIsEnabled;
auto *Character = GetCharacter();
TArray<UPoseableMeshComponent *> PoseableMeshes;
TArray<USkeletalMeshComponent *> SkeletalMeshes;
Character->GetComponents<UPoseableMeshComponent>(PoseableMeshes, false);
Character->GetComponents<USkeletalMeshComponent>(SkeletalMeshes, false);
USkeletalMeshComponent *SkeletalMesh = SkeletalMeshes.IsValidIndex(0) ? SkeletalMeshes[0] : nullptr;
if (SkeletalMesh)
{
if (bManualBones)
{
UPoseableMeshComponent *PoseableMesh =
PoseableMeshes.IsValidIndex(0) ? PoseableMeshes[0] : AddNewBoneComponent(Character,
SkeletalMesh->GetRelativeTransform().GetLocation(),
SkeletalMesh->GetRelativeTransform().GetRotation().Rotator());
PoseableMesh->SetSkeletalMesh(SkeletalMesh->SkeletalMesh);
PoseableMesh->SetVisibility(true);
SkeletalMesh->SetVisibility(false);
}
else
{
UPoseableMeshComponent *PoseableMesh = PoseableMeshes.IsValidIndex(0) ? PoseableMeshes[0] : nullptr;
PoseableMesh->SetVisibility(false);
SkeletalMesh->SetVisibility(true);
}
}
}
void AWalkerController::ControlTickVisitor::operator()(const FWalkerControl &WalkerControl)
{
auto *Character = Controller->GetCharacter();
if (Character != nullptr)
{
Character->AddMovementInput(Control.Direction, Control.Speed / GetMaximumWalkSpeed());
if (Control.Jump)
Character->AddMovementInput(WalkerControl.Direction,
WalkerControl.Speed / Controller->GetMaximumWalkSpeed());
if (WalkerControl.Jump)
{
Character->Jump();
}
}
}
void AWalkerController::ControlTickVisitor::operator()(FWalkerBoneControl &WalkerBoneControl)
{
auto *Character = Controller->GetCharacter();
if (Character == nullptr)
{
return;
}
TArray<UPoseableMeshComponent *> PoseableMeshes;
Character->GetComponents<UPoseableMeshComponent>(PoseableMeshes, false);
UPoseableMeshComponent *PoseableMesh = PoseableMeshes.IsValidIndex(0) ? PoseableMeshes[0] : nullptr;
if (PoseableMesh)
{
for (const TPair<FString, FTransform> &pair : WalkerBoneControl.BoneTransforms)
{
FName BoneName = FName(*pair.Key);
PoseableMesh->SetBoneTransformByName(BoneName, pair.Value, EBoneSpaces::Type::ComponentSpace);
}
WalkerBoneControl.BoneTransforms.Empty();
}
}
void AWalkerController::Tick(float DeltaSeconds)
{
Super::Tick(DeltaSeconds);
AWalkerController::ControlTickVisitor ControlTickVisitor(this);
boost::apply_visitor(ControlTickVisitor, Control);
}

View File

@ -6,11 +6,16 @@
#pragma once
#include "Carla/Walker/WalkerBoneControl.h"
#include "Carla/Walker/WalkerControl.h"
#include "CoreMinimal.h"
#include "GameFramework/Controller.h"
#include <compiler/disable-ue4-macros.h>
#include <boost/variant.hpp>
#include <compiler/enable-ue4-macros.h>
#include "WalkerController.generated.h"
UCLASS()
@ -18,9 +23,27 @@ class CARLA_API AWalkerController : public AController
{
GENERATED_BODY()
private:
class ControlTickVisitor : public boost::static_visitor<>
{
public:
ControlTickVisitor(AWalkerController *Controller)
: Controller(Controller) {}
void operator()(const FWalkerControl &WalkerControl);
void operator()(FWalkerBoneControl &WalkerBoneControl);
private:
AWalkerController *Controller;
};
public:
AWalkerController(const FObjectInitializer& ObjectInitializer);
AWalkerController(const FObjectInitializer &ObjectInitializer);
void OnPossess(APawn *InPawn) override;
@ -34,19 +57,28 @@ public:
}
UFUNCTION(BlueprintCallable)
void ApplyWalkerControl(const FWalkerControl &InControl)
void ApplyWalkerControl(const FWalkerControl &InControl);
void ApplyWalkerControl(const FWalkerBoneControl &InBoneControl);
UFUNCTION(BlueprintCallable)
const FWalkerControl GetWalkerControl() const
{
Control = InControl;
return Control.which() == 0u ? boost::get<FWalkerControl>(Control) : FWalkerControl{};
}
UFUNCTION(BlueprintCallable)
const FWalkerControl &GetWalkerControl() const
const FWalkerBoneControl GetBoneWalkerControl() const
{
return Control;
return Control.which() == 1u ? boost::get<FWalkerBoneControl>(Control) : FWalkerBoneControl{};
}
UFUNCTION(BlueprintCallable)
void SetManualBones(const bool bIsEnabled);
private:
UPROPERTY(VisibleAnywhere)
FWalkerControl Control;
boost::variant<FWalkerControl, FWalkerBoneControl> Control;
bool bManualBones;
};