use std::convert::TryFrom;
use std::os::unix::ffi::OsStrExt;
use std::{ffi::CString, os::raw::c_int, path::Path, ptr};
use nalgebra::{
DMatrix, Isometry3, Matrix4, Matrix6xX, Point3, Quaternion, Translation3, UnitQuaternion,
Vector3,
};
use self::gui_marker::GuiMarker;
use crate::types::{
AddDebugLineOptions, AddDebugTextOptions, BodyId, ChangeVisualShapeOptions, CollisionId,
ControlModeArray, ExternalForceFrame, GeometricCollisionShape, GeometricVisualShape, Images,
InverseKinematicsParameters, ItemId, Jacobian, JointInfo, JointState, JointType, KeyboardEvent,
LinkState, LoadModelFlags, MouseButtonState, MouseEvent, MultiBodyOptions, SdfOptions,
TextureId, Velocity, VisualId, VisualShapeOptions,
};
use crate::{
BodyInfo, ControlMode, DebugVisualizerFlag, Error, Mode, UrdfOptions, VisualShapeData,
};
use image::{ImageBuffer, Luma, RgbaImage};
use rubullet_sys as ffi;
use rubullet_sys::EnumSharedMemoryServerStatus::{
CMD_ACTUAL_STATE_UPDATE_COMPLETED, CMD_CALCULATED_INVERSE_DYNAMICS_COMPLETED,
CMD_CALCULATED_JACOBIAN_COMPLETED, CMD_CALCULATED_MASS_MATRIX_COMPLETED,
CMD_CAMERA_IMAGE_COMPLETED, CMD_CLIENT_COMMAND_COMPLETED, CMD_CREATE_COLLISION_SHAPE_COMPLETED,
CMD_CREATE_MULTI_BODY_COMPLETED, CMD_CREATE_VISUAL_SHAPE_COMPLETED, CMD_LOAD_TEXTURE_COMPLETED,
CMD_USER_DEBUG_DRAW_COMPLETED, CMD_USER_DEBUG_DRAW_PARAMETER_COMPLETED,
CMD_VISUAL_SHAPE_INFO_COMPLETED, CMD_VISUAL_SHAPE_UPDATE_COMPLETED,
};
use rubullet_sys::{
b3CameraImageData, b3JointInfo, b3JointSensorState, b3KeyboardEventsData, b3LinkState,
b3MouseEventsData, b3SubmitClientCommandAndWaitStatus, B3_MAX_NUM_INDICES, B3_MAX_NUM_VERTICES,
MAX_SDF_BODIES,
};
use std::time::Duration;
type Handle = ffi::b3PhysicsClientHandle;
pub struct PhysicsClient {
handle: Handle,
_gui_marker: Option<GuiMarker>,
}
impl PhysicsClient {
pub fn connect(mode: Mode) -> Result<PhysicsClient, Error> {
let (raw_handle, _gui_marker) = match mode {
Mode::Direct => unsafe { (ffi::b3ConnectPhysicsDirect(), None) },
Mode::Gui => {
let gui_marker = GuiMarker::acquire()?;
let raw_handle = if cfg!(target_os = "macos") {
unsafe {
ffi::b3CreateInProcessPhysicsServerAndConnectMainThread(0, ptr::null_mut())
}
} else {
unsafe { ffi::b3CreateInProcessPhysicsServerAndConnect(0, ptr::null_mut()) }
};
(raw_handle, Some(gui_marker))
}
};
let handle = raw_handle.expect("Bullet returned a null pointer");
let mut client = PhysicsClient {
handle,
_gui_marker,
};
if !client.can_submit_command() {
return Err(Error::new("Physics server is not running"));
}
unsafe {
let command = ffi::b3InitSyncBodyInfoCommand(client.handle);
let status_handle = ffi::b3SubmitClientCommandAndWaitStatus(client.handle, command);
let status_type = ffi::b3GetStatusType(status_handle);
if status_type != ffi::EnumSharedMemoryServerStatus::CMD_SYNC_BODY_INFO_COMPLETED as _ {
return Err(Error::new("Connection terminated, couldn't get body info"));
}
let command = ffi::b3InitSyncUserDataCommand(client.handle);
let status_handle = ffi::b3SubmitClientCommandAndWaitStatus(client.handle, command);
let status_type = ffi::b3GetStatusType(status_handle);
if status_type != ffi::EnumSharedMemoryServerStatus::CMD_SYNC_USER_DATA_COMPLETED as _ {
return Err(Error::new("Connection terminated, couldn't get user data"));
}
}
Ok(client)
}
pub fn reset_simulation(&mut self) {
unsafe {
let command_handle = ffi::b3InitResetSimulationCommand(self.handle);
ffi::b3InitResetSimulationSetFlags(command_handle, 0);
let _status_handle =
ffi::b3SubmitClientCommandAndWaitStatus(self.handle, command_handle);
}
}
pub fn set_time_step(&mut self, time_step: Duration) {
unsafe {
let command = ffi::b3InitPhysicsParamCommand(self.handle);
let _ret = ffi::b3PhysicsParamSetTimeStep(command, time_step.as_secs_f64());
let _status_handle = ffi::b3SubmitClientCommandAndWaitStatus(self.handle, command);
}
}
pub fn set_real_time_simulation(&mut self, enable_real_time_simulation: bool) {
unsafe {
let command = ffi::b3InitPhysicsParamCommand(self.handle);
let _ret = ffi::b3PhysicsParamSetRealTimeSimulation(
command,
enable_real_time_simulation as i32,
);
let _status_handle = ffi::b3SubmitClientCommandAndWaitStatus(self.handle, command);
}
}
pub fn set_additional_search_path<P: AsRef<Path>>(&mut self, path: P) -> Result<(), Error> {
if !self.can_submit_command() {
return Err(Error::new("Not connected to physics server"));
}
let path = CString::new(path.as_ref().as_os_str().as_bytes())
.map_err(|_| Error::new("Invalid path"))?;
unsafe {
let command_handle = ffi::b3SetAdditionalSearchPath(self.handle, path.as_ptr());
let _status_handle =
ffi::b3SubmitClientCommandAndWaitStatus(self.handle, command_handle);
}
Ok(())
}
pub fn set_gravity<GravityVector: Into<Vector3<f64>>>(
&mut self,
gravity: GravityVector,
) -> Result<(), Error> {
let gravity = gravity.into();
if !self.can_submit_command() {
return Err(Error::new("Not connected to physics server"));
}
unsafe {
let command = ffi::b3InitPhysicsParamCommand(self.handle);
let _ret = ffi::b3PhysicsParamSetGravity(command, gravity.x, gravity.y, gravity.z);
let _status_handle = ffi::b3SubmitClientCommandAndWaitStatus(self.handle, command);
}
Ok(())
}
pub fn load_urdf<P: AsRef<Path>, Options: Into<Option<UrdfOptions>>>(
&mut self,
file: P,
options: Options,
) -> Result<BodyId, Error> {
if !self.can_submit_command() {
return Err(Error::new("Not connected to physics server"));
}
let file = CString::new(file.as_ref().as_os_str().as_bytes())
.map_err(|_| Error::new("Invalid path"))?;
let options = options.into().unwrap_or_default();
unsafe {
let command = ffi::b3LoadUrdfCommandInit(self.handle, file.as_ptr());
let _ret = ffi::b3LoadUrdfCommandSetFlags(command, options.flags.bits());
let _ret = ffi::b3LoadUrdfCommandSetStartPosition(
command,
options.base_transform.translation.x,
options.base_transform.translation.y,
options.base_transform.translation.z,
);
let _ret = ffi::b3LoadUrdfCommandSetStartOrientation(
command,
options.base_transform.rotation.coords.x,
options.base_transform.rotation.coords.y,
options.base_transform.rotation.coords.z,
options.base_transform.rotation.coords.w,
);
if let Some(use_maximal_coordinates) = options.use_maximal_coordinates {
if use_maximal_coordinates {
ffi::b3LoadUrdfCommandSetUseMultiBody(command, 0);
} else {
ffi::b3LoadUrdfCommandSetUseMultiBody(command, 1);
}
}
if options.use_fixed_base {
let _ret = ffi::b3LoadUrdfCommandSetUseFixedBase(command, 1);
}
if options.global_scaling > 0.0 {
let _ret =
ffi::b3LoadUrdfCommandSetGlobalScaling(command, options.global_scaling as f64);
}
let status_handle = ffi::b3SubmitClientCommandAndWaitStatus(self.handle, command);
let status_type = ffi::b3GetStatusType(status_handle);
if status_type != ffi::EnumSharedMemoryServerStatus::CMD_URDF_LOADING_COMPLETED as c_int
{
return Err(Error::new("Cannot load URDF file"));
}
Ok(BodyId(ffi::b3GetStatusBodyIndex(status_handle)))
}
}
pub fn load_sdf<P: AsRef<Path>, Options: Into<Option<SdfOptions>>>(
&mut self,
file: P,
options: Options,
) -> Result<Vec<BodyId>, Error> {
if !self.can_submit_command() {
return Err(Error::new("Not connected to physics server"));
}
let file = CString::new(file.as_ref().as_os_str().as_bytes())
.map_err(|_| Error::new("Invalid path"))?;
unsafe {
let command = ffi::b3LoadSdfCommandInit(self.handle, file.as_ptr());
if let Some(options) = options.into() {
if options.use_maximal_coordinates {
ffi::b3LoadSdfCommandSetUseMultiBody(command, 0);
}
if options.global_scaling > 0. {
ffi::b3LoadSdfCommandSetUseGlobalScaling(command, options.global_scaling);
}
}
let status_handle = ffi::b3SubmitClientCommandAndWaitStatus(self.handle, command);
let status_type = ffi::b3GetStatusType(status_handle);
if status_type != ffi::EnumSharedMemoryServerStatus::CMD_SDF_LOADING_COMPLETED as c_int
{
return Err(Error::new("Cannot load SDF file"));
}
let mut body_indices_out = [0; MAX_SDF_BODIES as usize];
let num_bodies = ffi::b3GetStatusBodyIndices(
status_handle,
body_indices_out.as_mut_ptr(),
MAX_SDF_BODIES as i32,
);
if num_bodies as u32 > MAX_SDF_BODIES {
return Err(Error::with(format!(
"SDF exceeds body capacity of {}",
MAX_SDF_BODIES
)));
}
let mut bodies = Vec::<BodyId>::with_capacity(num_bodies as usize);
if num_bodies > 0 && num_bodies <= MAX_SDF_BODIES as i32 {
for i in 0..num_bodies {
bodies.push(BodyId(body_indices_out[i as usize]));
}
}
Ok(bodies)
}
}
pub fn load_mjcf<P: AsRef<Path>, Flags: Into<Option<LoadModelFlags>>>(
&mut self,
file: P,
flags: Flags,
) -> Result<Vec<BodyId>, Error> {
if !self.can_submit_command() {
return Err(Error::new("Not connected to physics server"));
}
let file = CString::new(file.as_ref().as_os_str().as_bytes())
.map_err(|_| Error::new("Invalid path"))?;
unsafe {
let command = ffi::b3LoadMJCFCommandInit(self.handle, file.as_ptr());
if let Some(flags) = flags.into() {
ffi::b3LoadMJCFCommandSetFlags(command, flags.bits());
}
let status_handle = ffi::b3SubmitClientCommandAndWaitStatus(self.handle, command);
let status_type = ffi::b3GetStatusType(status_handle);
if status_type != ffi::EnumSharedMemoryServerStatus::CMD_MJCF_LOADING_COMPLETED as c_int
{
return Err(Error::new("Cannot load .mjcf file"));
}
let mut body_indices_out = [0; MAX_SDF_BODIES as usize];
let num_bodies = ffi::b3GetStatusBodyIndices(
status_handle,
body_indices_out.as_mut_ptr(),
MAX_SDF_BODIES as i32,
);
if num_bodies as u32 > MAX_SDF_BODIES {
return Err(Error::with(format!(
"MuJoCo exceeds body capacity of {}",
MAX_SDF_BODIES
)));
}
let mut bodies = Vec::<BodyId>::with_capacity(num_bodies as usize);
if num_bodies > 0 && num_bodies <= MAX_SDF_BODIES as i32 {
for i in 0..num_bodies {
bodies.push(BodyId(body_indices_out[i as usize]));
}
}
Ok(bodies)
}
}
pub fn step_simulation(&mut self) -> Result<(), Error> {
if !self.can_submit_command() {
return Err(Error::new("Not connected to physics server"));
}
unsafe {
let command = ffi::b3InitStepSimulationCommand(self.handle);
let status_handle = ffi::b3SubmitClientCommandAndWaitStatus(self.handle, command);
let status_type = ffi::b3GetStatusType(status_handle);
if status_type
!= ffi::EnumSharedMemoryServerStatus::CMD_STEP_FORWARD_SIMULATION_COMPLETED as i32
{
return Err(Error::new("Failed to perform forward step"));
}
}
Ok(())
}
pub fn get_base_transform(&mut self, body: BodyId) -> Result<Isometry3<f64>, Error> {
if !self.can_submit_command() {
return Err(Error::new("Not connected to physics server"));
}
unsafe {
let cmd_handle = ffi::b3RequestActualStateCommandInit(self.handle, body.0);
let status_handle = ffi::b3SubmitClientCommandAndWaitStatus(self.handle, cmd_handle);
let status_type = ffi::b3GetStatusType(status_handle);
if status_type
!= ffi::EnumSharedMemoryServerStatus::CMD_ACTUAL_STATE_UPDATE_COMPLETED as c_int
{
return Err(Error::new("Failed to get base transform"));
}
let mut actual_state_q: *const f64 = ptr::null();
ffi::b3GetStatusActualState(
status_handle,
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
&mut actual_state_q,
ptr::null_mut(),
ptr::null_mut(),
);
assert!(!actual_state_q.is_null());
let tx = *actual_state_q;
let ty = *(actual_state_q.offset(1));
let tz = *(actual_state_q.offset(2));
let rx = *(actual_state_q.offset(3));
let ry = *(actual_state_q.offset(4));
let rz = *(actual_state_q.offset(5));
let rw = *(actual_state_q.offset(6));
let tra = Translation3::new(tx, ty, tz);
let rot = Quaternion::new(rw, rx, ry, rz);
Ok(Isometry3::from_parts(
tra,
UnitQuaternion::from_quaternion(rot),
))
}
}
pub fn reset_base_transform(&mut self, body: BodyId, pose: &Isometry3<f64>) {
unsafe {
let command_handle = ffi::b3CreatePoseCommandInit(self.handle, body.0);
ffi::b3CreatePoseCommandSetBasePosition(
command_handle,
pose.translation.x,
pose.translation.y,
pose.translation.z,
);
ffi::b3CreatePoseCommandSetBaseOrientation(
command_handle,
pose.rotation.i,
pose.rotation.j,
pose.rotation.k,
pose.rotation.w,
);
let _status_handle =
ffi::b3SubmitClientCommandAndWaitStatus(self.handle, command_handle);
}
}
pub fn get_base_velocity(&mut self, body: BodyId) -> Result<Velocity, Error> {
let mut base_velocity = [0.; 6];
unsafe {
let cmd_handle = ffi::b3RequestActualStateCommandInit(self.handle, body.0);
let status_handle = ffi::b3SubmitClientCommandAndWaitStatus(self.handle, cmd_handle);
let status_type = ffi::b3GetStatusType(status_handle);
let mut actual_state_qdot: *const f64 = ptr::null();
if status_type != CMD_ACTUAL_STATE_UPDATE_COMPLETED as i32 {
return Err(Error::new("get_base_velocity_failed."));
}
ffi::b3GetStatusActualState(
status_handle,
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
&mut actual_state_qdot,
ptr::null_mut(),
);
let base_velocity_slice = std::slice::from_raw_parts(actual_state_qdot, 6);
base_velocity[..6].clone_from_slice(&base_velocity_slice[..6]);
}
Ok(base_velocity.into())
}
pub fn reset_base_velocity<
IntoVector3: Into<Vector3<f64>>,
Linear: Into<Option<IntoVector3>>,
Angular: Into<Option<IntoVector3>>,
>(
&mut self,
body: BodyId,
linear_velocity: Linear,
angular_velocity: Angular,
) {
let maybe_lin = linear_velocity.into();
let maybe_angular = angular_velocity.into();
unsafe {
let command_handle = ffi::b3CreatePoseCommandInit(self.handle, body.0);
match (maybe_lin, maybe_angular) {
(None, None) => {}
(Some(linear), Some(angular)) => {
let linear: [f64; 3] = linear.into().into();
let angular: [f64; 3] = angular.into().into();
ffi::b3CreatePoseCommandSetBaseLinearVelocity(command_handle, linear.as_ptr());
ffi::b3CreatePoseCommandSetBaseAngularVelocity(
command_handle,
angular.as_ptr(),
);
}
(Some(linear), None) => {
let linear: [f64; 3] = linear.into().into();
ffi::b3CreatePoseCommandSetBaseLinearVelocity(command_handle, linear.as_ptr());
}
(None, Some(angular)) => {
let angular: [f64; 3] = angular.into().into();
ffi::b3CreatePoseCommandSetBaseAngularVelocity(
command_handle,
angular.as_ptr(),
);
}
}
let _status_handle =
ffi::b3SubmitClientCommandAndWaitStatus(self.handle, command_handle);
}
}
pub fn get_link_state(
&mut self,
body: BodyId,
link_index: usize,
compute_link_velocity: bool,
compute_forward_kinematics: bool,
) -> Result<LinkState, Error> {
unsafe {
assert!(body.0 >= 0, "get_link_state failed; invalid BodyId");
let cmd_handle = ffi::b3RequestActualStateCommandInit(self.handle, body.0);
if compute_link_velocity {
ffi::b3RequestActualStateCommandComputeLinkVelocity(cmd_handle, 1);
}
if compute_forward_kinematics {
ffi::b3RequestActualStateCommandComputeForwardKinematics(cmd_handle, 1);
}
let status_handle = ffi::b3SubmitClientCommandAndWaitStatus(self.handle, cmd_handle);
let status_type = ffi::b3GetStatusType(status_handle);
if status_type != CMD_ACTUAL_STATE_UPDATE_COMPLETED as i32 {
return Err(Error::new("getLinkState failed."));
}
let mut link_state = b3LinkState::default();
if ffi::b3GetLinkState(
self.handle,
status_handle,
link_index as i32,
&mut link_state,
) != 0
{
return Ok((link_state, compute_link_velocity).into());
}
}
Err(Error::new("getLinkState failed."))
}
pub fn get_link_states(
&mut self,
body: BodyId,
link_indices: &[usize],
compute_link_velocity: bool,
compute_forward_kinematics: bool,
) -> Result<Vec<LinkState>, Error> {
unsafe {
assert!(body.0 >= 0, "get_link_states failed; invalid BodyId");
let cmd_handle = ffi::b3RequestActualStateCommandInit(self.handle, body.0);
if compute_link_velocity {
ffi::b3RequestActualStateCommandComputeLinkVelocity(cmd_handle, 1);
}
if compute_forward_kinematics {
ffi::b3RequestActualStateCommandComputeForwardKinematics(cmd_handle, 1);
}
let status_handle = ffi::b3SubmitClientCommandAndWaitStatus(self.handle, cmd_handle);
let status_type = ffi::b3GetStatusType(status_handle);
if status_type != CMD_ACTUAL_STATE_UPDATE_COMPLETED as i32 {
return Err(Error::new("getLinkState failed."));
}
let mut link_states = Vec::<LinkState>::with_capacity(link_indices.len());
for &link_index in link_indices.iter() {
let mut link_state = b3LinkState::default();
if ffi::b3GetLinkState(
self.handle,
status_handle,
link_index as i32,
&mut link_state,
) != 0
{
link_states.push((link_state, compute_link_velocity).into());
} else {
return Err(Error::new("getLinkStates failed."));
}
}
Ok(link_states)
}
}
fn can_submit_command(&mut self) -> bool {
unsafe { ffi::b3CanSubmitCommand(self.handle) != 0 }
}
pub fn change_dynamics_linear_damping(&mut self, body: BodyId, linear_damping: f64) {
unsafe {
let command = ffi::b3InitChangeDynamicsInfo(self.handle);
assert!(linear_damping >= 0.);
ffi::b3ChangeDynamicsInfoSetLinearDamping(command, body.0, linear_damping);
ffi::b3SubmitClientCommandAndWaitStatus(self.handle, command);
}
}
pub fn change_dynamics_angular_damping(&mut self, body: BodyId, angular_damping: f64) {
unsafe {
let command = ffi::b3InitChangeDynamicsInfo(self.handle);
assert!(angular_damping >= 0.);
ffi::b3ChangeDynamicsInfoSetAngularDamping(command, body.0, angular_damping);
ffi::b3SubmitClientCommandAndWaitStatus(self.handle, command);
}
}
pub fn get_num_joints(&mut self, body: BodyId) -> usize {
unsafe { ffi::b3GetNumJoints(self.handle, body.0) as usize }
}
pub fn get_joint_info(&mut self, body: BodyId, joint_index: usize) -> JointInfo {
self.get_joint_info_intern(body, joint_index).into()
}
fn get_joint_info_intern(&mut self, body: BodyId, joint_index: usize) -> b3JointInfo {
unsafe {
let mut joint_info = b3JointInfo {
m_link_name: [2; 1024],
m_joint_name: [2; 1024],
m_joint_type: 0,
m_q_index: 0,
m_u_index: 0,
m_joint_index: 0,
m_flags: 0,
m_joint_damping: 0.0,
m_joint_friction: 0.0,
m_joint_lower_limit: 0.0,
m_joint_upper_limit: 0.0,
m_joint_max_force: 0.0,
m_joint_max_velocity: 0.0,
m_parent_frame: [0.; 7],
m_child_frame: [0.; 7],
m_joint_axis: [0.; 3],
m_parent_index: 0,
m_q_size: 0,
m_u_size: 0,
};
ffi::b3GetJointInfo(self.handle, body.0, joint_index as i32, &mut joint_info);
joint_info
}
}
pub fn reset_joint_state<Velocity: Into<Option<f64>>>(
&mut self,
body: BodyId,
joint_index: usize,
value: f64,
velocity: Velocity,
) -> Result<(), Error> {
unsafe {
let joint_index = joint_index as i32;
let num_joints = ffi::b3GetNumJoints(self.handle, body.0);
assert!(joint_index < num_joints, "Joint index out-of-range.");
let command_handle = ffi::b3CreatePoseCommandInit(self.handle, body.0);
ffi::b3CreatePoseCommandSetJointPosition(
self.handle,
command_handle,
joint_index,
value,
);
ffi::b3CreatePoseCommandSetJointVelocity(
self.handle,
command_handle,
joint_index,
velocity.into().unwrap_or(0.),
);
let _handle = ffi::b3SubmitClientCommandAndWaitStatus(self.handle, command_handle);
Ok(())
}
}
pub fn get_joint_state(
&mut self,
body: BodyId,
joint_index: usize,
) -> Result<JointState, Error> {
unsafe {
assert!(body.0 >= 0, "get_joint_state failed; invalid BodyId");
let cmd_handle = ffi::b3RequestActualStateCommandInit(self.handle, body.0);
let status_handle = ffi::b3SubmitClientCommandAndWaitStatus(self.handle, cmd_handle);
let status_type = ffi::b3GetStatusType(status_handle);
if status_type != CMD_ACTUAL_STATE_UPDATE_COMPLETED as i32 {
return Err(Error::new("getJointState failed."));
}
let mut sensor_state = b3JointSensorState::default();
if 0 != ffi::b3GetJointState(
self.handle,
status_handle,
joint_index as i32,
&mut sensor_state,
) {
return Ok(sensor_state.into());
}
}
Err(Error::new("getJointState failed (2)."))
}
pub fn get_joint_states(
&mut self,
body: BodyId,
joint_indices: &[usize],
) -> Result<Vec<JointState>, Error> {
unsafe {
assert!(body.0 >= 0, "get_joint_states failed; invalid BodyId");
let num_joints = self.get_num_joints(body);
if joint_indices.is_empty() {
return Err(Error::new("expected a sequence of joint indices"));
}
let cmd_handle = ffi::b3RequestActualStateCommandInit(self.handle, body.0);
let status_handle = ffi::b3SubmitClientCommandAndWaitStatus(self.handle, cmd_handle);
let status_type = ffi::b3GetStatusType(status_handle);
if status_type != CMD_ACTUAL_STATE_UPDATE_COMPLETED as i32 {
return Err(Error::new("getJointState failed."));
}
let mut result_list_joint_states = Vec::<JointState>::with_capacity(num_joints);
for &joint_index in joint_indices.iter() {
assert!(
joint_index < num_joints,
"get_joint_states failed; invalid joint_index ({}). The robot only has {} joints",
joint_index,
num_joints,
);
let mut sensor_state = b3JointSensorState::default();
if 0 != ffi::b3GetJointState(
self.handle,
status_handle,
joint_index as i32,
&mut sensor_state,
) {
result_list_joint_states.push(sensor_state.into());
} else {
return Err(Error::new("getJointState failed (2)."));
}
}
Ok(result_list_joint_states)
}
}
pub fn calculate_mass_matrix(
&mut self,
body: BodyId,
object_positions: &[f64],
) -> Result<DMatrix<f64>, Error> {
if !object_positions.is_empty() {
let joint_positions = object_positions;
let flags = 0;
unsafe {
let command_handle = ffi::b3CalculateMassMatrixCommandInit(
self.handle,
body.0,
joint_positions.as_ptr(),
joint_positions.len() as i32,
);
ffi::b3CalculateMassMatrixSetFlags(command_handle, flags);
let status_handle =
ffi::b3SubmitClientCommandAndWaitStatus(self.handle, command_handle);
let status_type = ffi::b3GetStatusType(status_handle);
if status_type == CMD_CALCULATED_MASS_MATRIX_COMPLETED as i32 {
let mut dof_count = 0;
ffi::b3GetStatusMassMatrix(
self.handle,
status_handle,
&mut dof_count,
std::ptr::null_mut(),
);
if dof_count != 0 {
let mut mass_vec = vec![0.; (dof_count * dof_count) as usize];
ffi::b3GetStatusMassMatrix(
self.handle,
status_handle,
&mut dof_count,
mass_vec.as_mut_slice().as_mut_ptr(),
);
let mass_mat =
DMatrix::from_vec(dof_count as usize, dof_count as usize, mass_vec);
return Ok(mass_mat);
}
} else {
return Err(Error::new("Internal error in calculateMassMatrix"));
}
}
}
Err(Error::new("error in calculate_mass_matrix"))
}
pub fn calculate_inverse_kinematics(
&mut self,
body: BodyId,
params: InverseKinematicsParameters,
) -> Result<Vec<f64>, Error> {
let solver = params.solver.into();
let end_effector_link_index = params.end_effector_link_index;
let pos = params.target_position;
let ori = params.target_orientation;
let mut sz_lower_limits = 0;
let mut sz_upper_limits = 0;
let mut sz_joint_ranges = 0;
let mut sz_rest_poses = 0;
if let Some(limits) = ¶ms.limits {
sz_lower_limits = limits.lower_limits.len();
sz_upper_limits = limits.upper_limits.len();
sz_joint_ranges = limits.joint_ranges.len();
sz_rest_poses = limits.rest_poses.len();
}
let dof_count = unsafe { ffi::b3ComputeDofCount(self.handle, body.0) } as usize;
let mut has_null_space = false;
let mut has_joint_damping = false;
let mut has_current_positions = false;
let current_positions = params.current_position;
let joint_damping = params.joint_damping;
if dof_count != 0
&& (sz_lower_limits == dof_count)
&& (sz_upper_limits == dof_count)
&& (sz_joint_ranges == dof_count)
&& (sz_rest_poses == dof_count)
{
has_null_space = true;
} else if params.limits.is_some() {
panic!(
"Null space parameter lengths do not match the number DoF! Robot has {} DoF",
dof_count
);
}
if let Some(positions) = current_positions {
assert_ne!(
positions.len(),
dof_count,
"number of current_positions ({}) is not equal to the number of DoF's ({})",
positions.len(),
dof_count
);
has_current_positions = true;
}
if let Some(damping) = joint_damping {
assert_eq!(damping.len(),
dof_count,
"calculateInverseKinematics: the size of input joint damping values ({}) should be equal to the number of degrees of freedom ({})",
damping.len(),
dof_count,
);
has_joint_damping = true;
}
let mut num_pos = 0;
unsafe {
let command = ffi::b3CalculateInverseKinematicsCommandInit(self.handle, body.0);
ffi::b3CalculateInverseKinematicsSelectSolver(command, solver);
if has_current_positions {
ffi::b3CalculateInverseKinematicsSetCurrentPositions(
command,
dof_count as i32,
current_positions.unwrap().as_ptr(),
)
}
if let Some(max_num_iterations) = params.max_num_iterations {
ffi::b3CalculateInverseKinematicsSetMaxNumIterations(
command,
max_num_iterations as i32,
);
}
if let Some(residual_threshold) = params.residual_threshold {
ffi::b3CalculateInverseKinematicsSetResidualThreshold(command, residual_threshold);
}
if has_null_space {
let limits = params.limits.unwrap();
let lower_limits = limits.lower_limits;
let upper_limits = limits.upper_limits;
let joint_ranges = limits.joint_ranges;
let rest_poses = limits.rest_poses;
if let Some(orientation) = ori {
ffi::b3CalculateInverseKinematicsPosOrnWithNullSpaceVel(
command,
dof_count as i32,
end_effector_link_index as i32,
pos.coords.as_ptr(),
orientation.coords.as_ptr(),
lower_limits.as_ptr(),
upper_limits.as_ptr(),
joint_ranges.as_ptr(),
rest_poses.as_ptr(),
);
} else {
ffi::b3CalculateInverseKinematicsPosWithNullSpaceVel(
command,
dof_count as i32,
end_effector_link_index as i32,
pos.coords.as_ptr(),
lower_limits.as_ptr(),
upper_limits.as_ptr(),
joint_ranges.as_ptr(),
rest_poses.as_ptr(),
);
}
} else if let Some(orientation) = ori {
ffi::b3CalculateInverseKinematicsAddTargetPositionWithOrientation(
command,
end_effector_link_index as i32,
pos.coords.as_ptr(),
orientation.coords.as_ptr(),
);
} else {
ffi::b3CalculateInverseKinematicsAddTargetPurePosition(
command,
end_effector_link_index as i32,
pos.coords.as_ptr(),
);
}
if has_joint_damping {
ffi::b3CalculateInverseKinematicsSetJointDamping(
command,
dof_count as i32,
joint_damping.unwrap().as_ptr(),
)
}
let status_handle = ffi::b3SubmitClientCommandAndWaitStatus(self.handle, command);
let mut result_body_index: c_int = 0;
let result = ffi::b3GetStatusInverseKinematicsJointPositions(
status_handle,
&mut result_body_index,
&mut num_pos,
std::ptr::null_mut(),
);
if result != 0 && num_pos != 0 {
let mut ik_output_joint_pos = vec![0.; num_pos as usize];
ffi::b3GetStatusInverseKinematicsJointPositions(
status_handle,
&mut result_body_index,
&mut num_pos,
ik_output_joint_pos.as_mut_slice().as_mut_ptr(),
);
return Ok(ik_output_joint_pos);
}
}
Err(Error::new("Error in calculateInverseKinematics"))
}
pub fn calculate_inverse_dynamics(
&mut self,
body: BodyId,
object_positions: &[f64],
object_velocities: &[f64],
object_accelerations: &[f64],
) -> Result<Vec<f64>, Error> {
let flags = 0;
assert_eq!(object_velocities.len(),
object_accelerations.len(),
"number of object velocities ({}) should be equal to the number of object accelerations ({})",
object_velocities.len(),
object_accelerations.len(),
);
unsafe {
let command_handle = ffi::b3CalculateInverseDynamicsCommandInit2(
self.handle,
body.0,
object_positions.as_ptr(),
object_positions.len() as i32,
object_velocities.as_ptr(),
object_accelerations.as_ptr(),
object_velocities.len() as i32,
);
ffi::b3CalculateInverseDynamicsSetFlags(command_handle, flags);
let status_handle =
ffi::b3SubmitClientCommandAndWaitStatus(self.handle, command_handle);
let status_type = ffi::b3GetStatusType(status_handle);
if status_type == CMD_CALCULATED_INVERSE_DYNAMICS_COMPLETED as i32 {
let mut body_unique_id = 0;
let mut dof_count = 0;
ffi::b3GetStatusInverseDynamicsJointForces(
status_handle,
&mut body_unique_id,
&mut dof_count,
std::ptr::null_mut(),
);
if dof_count != 0 {
let mut joint_forces_output = vec![0.; dof_count as usize];
ffi::b3GetStatusInverseDynamicsJointForces(
status_handle,
&mut 0,
&mut 0,
joint_forces_output.as_mut_slice().as_mut_ptr(),
);
return Ok(joint_forces_output);
}
}
}
Err(Error::new(
"Error in calculateInverseDynamics, please check arguments.",
))
}
pub fn calculate_jacobian<LocalPosition: Into<Translation3<f64>>>(
&mut self,
body: BodyId,
link_index: usize,
local_position: LocalPosition,
object_positions: &[f64],
object_velocities: &[f64],
object_accelerations: &[f64],
) -> Result<Jacobian, Error> {
assert_eq!(
object_velocities.len(),
object_positions.len(),
"object_velocities (size: {}) has not the same size as object_positions (size: {})",
object_velocities.len(),
object_positions.len(),
);
assert_eq!(
object_accelerations.len(),
object_positions.len(),
"object_accelerations (size: {}) has not the same size as object_positions (size: {})",
object_accelerations.len(),
object_positions.len(),
);
let num_joints = self.get_num_joints(body);
let mut dof_count_org = 0;
for j in 0..num_joints {
let joint_type =
JointType::try_from(self.get_joint_info_intern(body, j).m_joint_type).unwrap();
match joint_type {
JointType::Revolute | JointType::Prismatic => {
dof_count_org += 1;
}
JointType::Spherical => {
return Err(Error::new(
"Spherical joints are not supported in the rubullet binding",
))
}
JointType::Planar => {
return Err(Error::new(
"Planar joints are not supported in the rubullet binding",
))
}
_ => {}
}
}
if dof_count_org != 0 && dof_count_org == object_positions.len() {
let joint_positions = object_positions;
let joint_velocities = object_velocities;
let joint_accelerations = object_accelerations;
unsafe {
let command_handle = ffi::b3CalculateJacobianCommandInit(
self.handle,
body.0,
link_index as i32,
local_position.into().vector.as_ptr(),
joint_positions.as_ptr(),
joint_velocities.as_ptr(),
joint_accelerations.as_ptr(),
);
let status_handle =
ffi::b3SubmitClientCommandAndWaitStatus(self.handle, command_handle);
let status_type = ffi::b3GetStatusType(status_handle);
if status_type == CMD_CALCULATED_JACOBIAN_COMPLETED as i32 {
let mut dof_count = 0;
ffi::b3GetStatusJacobian(
status_handle,
&mut dof_count,
std::ptr::null_mut(),
std::ptr::null_mut(),
);
if dof_count != 0 {
let mut linear_jacobian = vec![0.; 3 * dof_count as usize];
let mut angular_jacobian = vec![0.; 3 * dof_count as usize];
ffi::b3GetStatusJacobian(
status_handle,
&mut dof_count,
linear_jacobian.as_mut_slice().as_mut_ptr(),
angular_jacobian.as_mut_slice().as_mut_ptr(),
);
let mut jacobian = linear_jacobian;
jacobian.append(&mut angular_jacobian);
let jacobian_mat = Matrix6xX::from_row_slice(&jacobian);
return Ok(Jacobian {
jacobian: jacobian_mat,
});
}
}
}
}
Err(Error::new("Error in calculateJacobian"))
}
#[doc(alias = "set_joint_motor_control_2")]
#[doc(alias = "setJointMotorControl2")]
pub fn set_joint_motor_control(
&mut self,
body: BodyId,
joint_index: usize,
control_mode: ControlMode,
maximum_force: Option<f64>,
) {
let force = maximum_force.unwrap_or(100000.);
let kp = 0.1;
let kd = 1.0;
let target_velocity = 0.;
unsafe {
let command_handle =
ffi::b3JointControlCommandInit2(self.handle, body.0, control_mode.get_int());
let info = self.get_joint_info_intern(body, joint_index);
match control_mode {
ControlMode::Position(target_position) => {
ffi::b3JointControlSetDesiredPosition(
command_handle,
info.m_q_index,
target_position,
);
ffi::b3JointControlSetKp(command_handle, info.m_u_index, kp);
ffi::b3JointControlSetDesiredVelocity(
command_handle,
info.m_u_index,
target_velocity,
);
ffi::b3JointControlSetKd(command_handle, info.m_u_index, kd);
ffi::b3JointControlSetMaximumForce(command_handle, info.m_u_index, force);
}
ControlMode::Pd {
target_position: pos,
target_velocity: vel,
position_gain: kp,
velocity_gain: kd,
maximum_velocity: max_vel,
}
| ControlMode::PositionWithPd {
target_position: pos,
target_velocity: vel,
position_gain: kp,
velocity_gain: kd,
maximum_velocity: max_vel,
} => {
if let Some(max_vel) = max_vel {
ffi::b3JointControlSetMaximumVelocity(
command_handle,
info.m_u_index,
max_vel,
);
}
ffi::b3JointControlSetDesiredPosition(command_handle, info.m_q_index, pos);
ffi::b3JointControlSetKp(command_handle, info.m_u_index, kp);
ffi::b3JointControlSetDesiredVelocity(command_handle, info.m_u_index, vel);
ffi::b3JointControlSetKd(command_handle, info.m_u_index, kd);
ffi::b3JointControlSetMaximumForce(command_handle, info.m_u_index, force);
}
ControlMode::Velocity(vel) => {
ffi::b3JointControlSetDesiredVelocity(command_handle, info.m_u_index, vel);
ffi::b3JointControlSetKd(command_handle, info.m_u_index, kd);
ffi::b3JointControlSetMaximumForce(command_handle, info.m_u_index, force);
}
ControlMode::Torque(f) => {
ffi::b3JointControlSetDesiredForceTorque(command_handle, info.m_u_index, f);
}
}
let _status_handle =
ffi::b3SubmitClientCommandAndWaitStatus(self.handle, command_handle);
}
}
pub fn set_joint_motor_control_array(
&mut self,
body: BodyId,
joint_indices: &[usize],
control_mode: ControlModeArray,
maximum_force: Option<&[f64]>,
) -> Result<(), Error> {
let alloc_vec;
let forces;
match maximum_force {
None => {
alloc_vec = vec![100000.; joint_indices.len()];
forces = alloc_vec.as_slice();
}
Some(max_forces) => {
forces = max_forces;
}
}
assert_eq!(forces.len(),
joint_indices.len(),
"number of maximum forces (size: {}) should match the number of joint indices (size: {})",
forces.len(),
joint_indices.len(),
);
let kp = 0.1;
let kd = 1.0;
let num_joints = self.get_num_joints(body);
unsafe {
let command_handle =
ffi::b3JointControlCommandInit2(self.handle, body.0, control_mode.get_int());
for &joint_index in joint_indices.iter() {
assert!(
joint_index < num_joints,
"Joint index ({}) out-of-range. Robot has a total number of {} joints",
joint_index,
num_joints,
);
}
match control_mode {
ControlModeArray::Positions(target_positions) => {
assert_eq!(target_positions.len(),
joint_indices.len(),
"number of target positions ({}) should match the number of joint indices ({})",
target_positions.len(),
joint_indices.len(),
);
for i in 0..target_positions.len() {
let info = self.get_joint_info_intern(body, joint_indices[i]);
ffi::b3JointControlSetDesiredPosition(
command_handle,
info.m_q_index,
target_positions[i],
);
ffi::b3JointControlSetKp(command_handle, info.m_u_index, kp);
ffi::b3JointControlSetDesiredVelocity(command_handle, info.m_u_index, 0.);
ffi::b3JointControlSetKd(command_handle, info.m_u_index, kd);
ffi::b3JointControlSetMaximumForce(
command_handle,
info.m_u_index,
forces[i],
);
}
}
ControlModeArray::Pd {
target_positions: pos,
target_velocities: vel,
position_gains: pg,
velocity_gains: vg,
}
| ControlModeArray::PositionsWithPd {
target_positions: pos,
target_velocities: vel,
position_gains: pg,
velocity_gains: vg,
} => {
assert_eq!(pos.len(),
joint_indices.len(),
"number of target positions ({}) should match the number of joint indices ({})",
pos.len(),
joint_indices.len(),
);
assert_eq!(vel.len(),
joint_indices.len(),
"number of target velocities ({}) should match the number of joint indices ({})",
vel.len(),
joint_indices.len(),
);
assert_eq!(pg.len(),
joint_indices.len(),
"number of position gains ({}) should match the number of joint indices ({})",
pg.len(),
joint_indices.len(),
);
assert_eq!(vg.len(),
joint_indices.len(),
"number of velocity gains ({}) should match the number of joint indices ({})",
vg.len(),
joint_indices.len(),
);
for i in 0..pos.len() {
let info = self.get_joint_info_intern(body, joint_indices[i]);
ffi::b3JointControlSetDesiredPosition(
command_handle,
info.m_q_index,
pos[i],
);
ffi::b3JointControlSetKp(command_handle, info.m_u_index, pg[i]);
ffi::b3JointControlSetDesiredVelocity(
command_handle,
info.m_u_index,
vel[i],
);
ffi::b3JointControlSetKd(command_handle, info.m_u_index, vg[i]);
ffi::b3JointControlSetMaximumForce(
command_handle,
info.m_u_index,
forces[i],
);
}
}
ControlModeArray::Velocities(vel) => {
assert_eq!(vel.len(),
joint_indices.len(),
"number of target velocities ({}) should match the number of joint indices ({})",
vel.len(),
joint_indices.len(),
);
for i in 0..vel.len() {
let info = self.get_joint_info_intern(body, joint_indices[i]);
ffi::b3JointControlSetDesiredVelocity(
command_handle,
info.m_u_index,
vel[i],
);
ffi::b3JointControlSetKd(command_handle, info.m_u_index, kd);
ffi::b3JointControlSetMaximumForce(
command_handle,
info.m_u_index,
forces[i],
);
}
}
ControlModeArray::Torques(f) => {
assert_eq!(f.len(),
joint_indices.len(),
"number of target torques ({}) should match the number of joint indices ({})",
f.len(),
joint_indices.len(),
);
for i in 0..f.len() {
let info = self.get_joint_info_intern(body, joint_indices[i]);
ffi::b3JointControlSetDesiredForceTorque(
command_handle,
info.m_u_index,
f[i],
);
}
}
}
let _status_handle =
ffi::b3SubmitClientCommandAndWaitStatus(self.handle, command_handle);
}
Ok(())
}
pub fn compute_view_matrix<Point: Into<Point3<f32>>, Vector: Into<Vector3<f32>>>(
camera_eye_position: Point,
camera_target_position: Point,
camera_up_vector: Vector,
) -> Matrix4<f32> {
let mut view_matrix: Matrix4<f32> = Matrix4::zeros();
unsafe {
ffi::b3ComputeViewMatrixFromPositions(
camera_eye_position.into().coords.as_ptr(),
camera_target_position.into().coords.as_ptr(),
camera_up_vector.into().as_slice().as_ptr(),
view_matrix.as_mut_ptr(),
);
view_matrix
}
}
pub fn compute_view_matrix_from_yaw_pitch_roll<Point: Into<Point3<f32>>>(
camera_target_position: Point,
distance: f32,
yaw: f32,
pitch: f32,
roll: f32,
z_axis_is_up: bool,
) -> Matrix4<f32> {
let mut view_matrix: Matrix4<f32> = Matrix4::zeros();
let up_axis_index = match z_axis_is_up {
true => 2,
false => 1,
};
unsafe {
ffi::b3ComputeViewMatrixFromYawPitchRoll(
camera_target_position.into().coords.as_ptr(),
distance,
yaw,
pitch,
roll,
up_axis_index,
view_matrix.as_mut_ptr(),
);
view_matrix
}
}
pub fn compute_projection_matrix(
left: f32,
right: f32,
bottom: f32,
top: f32,
near_val: f32,
far_val: f32,
) -> Matrix4<f32> {
let mut projection_matrix = Matrix4::zeros();
unsafe {
ffi::b3ComputeProjectionMatrix(
left,
right,
bottom,
top,
near_val,
far_val,
projection_matrix.as_mut_ptr(),
);
projection_matrix
}
}
pub fn compute_projection_matrix_fov(
fov: f32,
aspect: f32,
near_val: f32,
far_val: f32,
) -> Matrix4<f32> {
let mut projection_matrix = Matrix4::zeros();
unsafe {
ffi::b3ComputeProjectionMatrixFOV(
fov,
aspect,
near_val,
far_val,
projection_matrix.as_mut_ptr(),
);
projection_matrix
}
}
pub fn get_camera_image(
&mut self,
width: i32,
height: i32,
view_matrix: Matrix4<f32>,
projection_matrix: Matrix4<f32>,
) -> Result<Images, Error> {
unsafe {
let command = ffi::b3InitRequestCameraImage(self.handle);
ffi::b3RequestCameraImageSetPixelResolution(command, width, height);
ffi::b3RequestCameraImageSetCameraMatrices(
command,
view_matrix.as_ptr(),
projection_matrix.as_ptr(),
);
if self.can_submit_command() {
let status_handle = ffi::b3SubmitClientCommandAndWaitStatus(self.handle, command);
let status_type = ffi::b3GetStatusType(status_handle);
if status_type == CMD_CAMERA_IMAGE_COMPLETED as i32 {
let mut image_data = b3CameraImageData::default();
ffi::b3GetCameraImageData(self.handle, &mut image_data);
let buffer = std::slice::from_raw_parts(
image_data.m_rgb_color_data,
(width * height * 4) as usize,
);
let depth_buffer = std::slice::from_raw_parts(
image_data.m_depth_values,
(width * height) as usize,
);
let segmentation_buffer = std::slice::from_raw_parts(
image_data.m_segmentation_mask_values,
(width * height) as usize,
);
let rgba: RgbaImage =
ImageBuffer::from_vec(width as u32, height as u32, buffer.into()).unwrap();
let depth = ImageBuffer::<Luma<f32>, Vec<f32>>::from_vec(
width as u32,
height as u32,
depth_buffer.into(),
)
.unwrap();
let segmentation = ImageBuffer::<Luma<i32>, Vec<i32>>::from_vec(
width as u32,
height as u32,
segmentation_buffer.into(),
)
.unwrap();
return Ok(Images {
rgba,
depth,
segmentation,
});
}
}
Err(Error::new("getCameraImage failed"))
}
}
pub fn configure_debug_visualizer(&mut self, flag: DebugVisualizerFlag, enable: bool) {
unsafe {
let command_handle = ffi::b3InitConfigureOpenGLVisualizer(self.handle);
ffi::b3ConfigureOpenGLVisualizerSetVisualizationFlags(
command_handle,
flag as i32,
enable as i32,
);
ffi::b3SubmitClientCommandAndWaitStatus(self.handle, command_handle);
}
}
pub fn add_user_debug_line<
Options: Into<Option<AddDebugLineOptions>>,
Start: Into<Point3<f64>>,
End: Into<Point3<f64>>,
>(
&mut self,
line_from_xyz: Start,
line_to_xyz: End,
options: Options,
) -> Result<ItemId, Error> {
unsafe {
let options = options.into().unwrap_or_default();
let command_handle = ffi::b3InitUserDebugDrawAddLine3D(
self.handle,
line_from_xyz.into().coords.as_ptr(),
line_to_xyz.into().coords.as_ptr(),
options.line_color_rgb.as_ptr(),
options.line_width,
options.life_time,
);
if let Some(parent) = options.parent_object_id {
let parent_link_index = match options.parent_link_index {
None => -1,
Some(index) => index as i32,
};
ffi::b3UserDebugItemSetParentObject(command_handle, parent.0, parent_link_index);
}
if let Some(replacement) = options.replace_item_id {
ffi::b3UserDebugItemSetReplaceItemUniqueId(command_handle, replacement.0);
}
let status_handle =
ffi::b3SubmitClientCommandAndWaitStatus(self.handle, command_handle);
let status_type = ffi::b3GetStatusType(status_handle);
if status_type == CMD_USER_DEBUG_DRAW_COMPLETED as i32 {
let debug_item = ItemId(ffi::b3GetDebugItemUniqueId(status_handle));
return Ok(debug_item);
}
Err(Error::new("Error in addUserDebugLine."))
}
}
pub fn add_user_debug_parameter<'a, Text: Into<&'a str>>(
&mut self,
param_name: Text,
range_min: f64,
range_max: f64,
start_value: f64,
) -> Result<ItemId, Error> {
unsafe {
let param_name = CString::new(param_name.into().as_bytes()).unwrap();
let command_handle = ffi::b3InitUserDebugAddParameter(
self.handle,
param_name.as_ptr(),
range_min,
range_max,
start_value,
);
let status_handle =
ffi::b3SubmitClientCommandAndWaitStatus(self.handle, command_handle);
let status_type = ffi::b3GetStatusType(status_handle);
if status_type == CMD_USER_DEBUG_DRAW_COMPLETED as i32 {
let debug_item_unique_id = ffi::b3GetDebugItemUniqueId(status_handle);
return Ok(ItemId(debug_item_unique_id));
}
Err(Error::new("Error in addUserDebugParameter."))
}
}
pub fn read_user_debug_parameter(&mut self, item: ItemId) -> Result<f64, Error> {
unsafe {
let command_handle = ffi::b3InitUserDebugReadParameter(self.handle, item.0);
let status_handle =
ffi::b3SubmitClientCommandAndWaitStatus(self.handle, command_handle);
let status_type = ffi::b3GetStatusType(status_handle);
if status_type == CMD_USER_DEBUG_DRAW_PARAMETER_COMPLETED as i32 {
let mut param_value = 0.;
let ok = ffi::b3GetStatusDebugParameterValue(status_handle, &mut param_value);
if ok != 0 {
return Ok(param_value);
}
}
Err(Error::new("Failed to read parameter."))
}
}
pub fn add_user_debug_text<
'a,
Text: Into<&'a str>,
Position: Into<Point3<f64>>,
Options: Into<Option<AddDebugTextOptions>>,
>(
&mut self,
text: Text,
text_position: Position,
options: Options,
) -> Result<ItemId, Error> {
unsafe {
let options = options.into().unwrap_or_default();
let text = CString::new(text.into().as_bytes()).unwrap();
let command_handle = ffi::b3InitUserDebugDrawAddText3D(
self.handle,
text.as_ptr(),
text_position.into().coords.as_ptr(),
options.text_color_rgb.as_ptr(),
options.text_size,
options.life_time,
);
if let Some(parent_object) = options.parent_object_id {
let parent_link_index = match options.parent_link_index {
None => -1,
Some(index) => index as i32,
};
ffi::b3UserDebugItemSetParentObject(
command_handle,
parent_object.0,
parent_link_index,
);
}
if let Some(text_orientation) = options.text_orientation {
ffi::b3UserDebugTextSetOrientation(
command_handle,
text_orientation.coords.as_ptr(),
);
}
if let Some(replacement_id) = options.replace_item_id {
ffi::b3UserDebugItemSetReplaceItemUniqueId(command_handle, replacement_id.0);
}
let status_handle =
ffi::b3SubmitClientCommandAndWaitStatus(self.handle, command_handle);
let status_type = ffi::b3GetStatusType(status_handle);
if status_type == CMD_USER_DEBUG_DRAW_COMPLETED as i32 {
let debug_item_id = ItemId(ffi::b3GetDebugItemUniqueId(status_handle));
return Ok(debug_item_id);
}
}
Err(Error::new("Error in add_user_debug_text"))
}
pub fn remove_user_debug_item(&mut self, item: ItemId) {
unsafe {
let command_handle = ffi::b3InitUserDebugDrawRemove(self.handle, item.0);
let status_handle =
ffi::b3SubmitClientCommandAndWaitStatus(self.handle, command_handle);
let _status_type = ffi::b3GetStatusType(status_handle);
}
}
pub fn remove_all_user_debug_items(&mut self) {
unsafe {
let command_handle = ffi::b3InitUserDebugDrawRemoveAll(self.handle);
let status_handle =
ffi::b3SubmitClientCommandAndWaitStatus(self.handle, command_handle);
let _status_type = ffi::b3GetStatusType(status_handle);
}
}
pub fn set_debug_object_color<Link: Into<Option<usize>>, Color: Into<Option<[f64; 3]>>>(
&mut self,
body: BodyId,
link_index: Link,
object_debug_color: Color,
) {
unsafe {
let link_index = match link_index.into() {
None => -1,
Some(index) => index as i32,
};
let command_handle = ffi::b3InitDebugDrawingCommand(self.handle);
if let Some(color) = object_debug_color.into() {
ffi::b3SetDebugObjectColor(command_handle, body.0, link_index, color.as_ptr());
} else {
ffi::b3RemoveDebugObjectColor(command_handle, body.0, link_index);
}
ffi::b3SubmitClientCommandAndWaitStatus(self.handle, command_handle);
}
}
pub fn get_keyboard_events(&mut self) -> Vec<KeyboardEvent> {
unsafe {
let mut keyboard_events = b3KeyboardEventsData::default();
let command_handle = ffi::b3RequestKeyboardEventsCommandInit(self.handle);
ffi::b3SubmitClientCommandAndWaitStatus(self.handle, command_handle);
ffi::b3GetKeyboardEventsData(self.handle, &mut keyboard_events);
let mut events =
Vec::<KeyboardEvent>::with_capacity(keyboard_events.m_numKeyboardEvents as usize);
let data = std::slice::from_raw_parts_mut(
keyboard_events.m_keyboardEvents,
keyboard_events.m_numKeyboardEvents as usize,
);
for &event in data.iter() {
events.push(KeyboardEvent {
key: std::char::from_u32(event.m_keyCode as u32).expect("Got invalid key code"),
key_state: event.m_keyState,
});
}
events
}
}
pub fn get_mouse_events(&mut self) -> Vec<MouseEvent> {
unsafe {
let mut mouse_events = b3MouseEventsData::default();
let command_handle = ffi::b3RequestMouseEventsCommandInit(self.handle);
ffi::b3SubmitClientCommandAndWaitStatus(self.handle, command_handle);
ffi::b3GetMouseEventsData(self.handle, &mut mouse_events);
let mut events =
Vec::<MouseEvent>::with_capacity(mouse_events.m_numMouseEvents as usize);
let data = std::slice::from_raw_parts_mut(
mouse_events.m_mouseEvents,
mouse_events.m_numMouseEvents as usize,
);
for &event in data.iter() {
if event.m_eventType == 1 {
events.push(MouseEvent::Move {
mouse_pos_x: event.m_mousePosX,
mouse_pos_y: event.m_mousePosY,
});
} else if event.m_eventType == 2 {
events.push(MouseEvent::Button {
mouse_pos_x: event.m_mousePosX,
mouse_pos_y: event.m_mousePosY,
button_index: event.m_buttonIndex,
button_state: MouseButtonState {
flag: event.m_buttonState,
},
});
}
}
events
}
}
pub fn apply_external_force<
Force: Into<Vector3<f64>>,
Position: Into<Point3<f64>>,
Link: Into<Option<usize>>,
>(
&mut self,
body: BodyId,
link_index: Link,
force_object: Force,
position_object: Position,
flags: ExternalForceFrame,
) {
let link_index = match link_index.into() {
None => -1,
Some(index) => index as i32,
};
unsafe {
let command = ffi::b3ApplyExternalForceCommandInit(self.handle);
ffi::b3ApplyExternalForce(
command,
body.0,
link_index,
force_object.into().as_ptr(),
position_object.into().coords.as_ptr(),
flags as i32,
);
let _status_handle = ffi::b3SubmitClientCommandAndWaitStatus(self.handle, command);
}
}
pub fn apply_external_torque<Torque: Into<Vector3<f64>>, Link: Into<Option<usize>>>(
&mut self,
body: BodyId,
link_index: Link,
torque_object: Torque,
flags: ExternalForceFrame,
) {
let link_index = match link_index.into() {
None => -1,
Some(index) => index as i32,
};
unsafe {
let command = ffi::b3ApplyExternalForceCommandInit(self.handle);
ffi::b3ApplyExternalTorque(
command,
body.0,
link_index,
torque_object.into().as_ptr(),
flags as i32,
);
let _status_handle = ffi::b3SubmitClientCommandAndWaitStatus(self.handle, command);
}
}
pub fn enable_joint_torque_sensor(
&mut self,
body: BodyId,
joint_index: usize,
enable_sensor: bool,
) -> Result<(), Error> {
unsafe {
let command_handle = ffi::b3CreateSensorCommandInit(self.handle, body.0);
ffi::b3CreateSensorEnable6DofJointForceTorqueSensor(
command_handle,
joint_index as i32,
enable_sensor as i32,
);
let status_handle =
ffi::b3SubmitClientCommandAndWaitStatus(self.handle, command_handle);
let status_type = ffi::b3GetStatusType(status_handle);
if status_type == CMD_CLIENT_COMMAND_COMPLETED as i32 {
return Ok(());
}
Err(Error::new("Error creating sensor."))
}
}
pub fn create_collision_shape(
&mut self,
shape: GeometricCollisionShape,
frame_offset: Isometry3<f64>,
) -> Result<CollisionId, Error> {
unsafe {
let mut shape_index = -1;
let command_handle = ffi::b3CreateCollisionShapeCommandInit(self.handle);
match shape {
GeometricCollisionShape::Sphere { radius } if radius > 0. => {
shape_index = ffi::b3CreateCollisionShapeAddSphere(command_handle, radius);
}
GeometricCollisionShape::Box { half_extents } => {
shape_index =
ffi::b3CreateCollisionShapeAddBox(command_handle, half_extents.as_ptr());
}
GeometricCollisionShape::Capsule { radius, height }
if radius > 0. && height >= 0. =>
{
shape_index =
ffi::b3CreateCollisionShapeAddCapsule(command_handle, radius, height);
}
GeometricCollisionShape::Cylinder { radius, height }
if radius > 0. && height >= 0. =>
{
shape_index =
ffi::b3CreateCollisionShapeAddCylinder(command_handle, radius, height);
}
GeometricCollisionShape::HeightfieldFile {
filename,
mesh_scaling,
texture_scaling,
} => {
let file = CString::new(filename.into_os_string().as_bytes()).unwrap();
shape_index = ffi::b3CreateCollisionShapeAddHeightfield(
command_handle,
file.as_ptr(),
mesh_scaling
.unwrap_or_else(|| Vector3::from_element(1.))
.as_ptr(),
texture_scaling,
);
}
GeometricCollisionShape::Heightfield {
mesh_scaling,
texture_scaling: heightfield_texture_scaling,
data: mut heightfield_data,
num_rows: num_heightfield_rows,
num_columns: num_heightfield_columns,
replace_heightfield,
} => {
if num_heightfield_columns > 0 && num_heightfield_rows > 0 {
let num_height_field_points = heightfield_data.len();
assert_eq!( num_heightfield_rows * num_heightfield_columns,
num_height_field_points,
"Size of heightfield_data ({}) doesn't match num_heightfield_columns * num_heightfield_rows = {}",
num_height_field_points,
num_heightfield_rows * num_heightfield_columns,
);
shape_index = ffi::b3CreateCollisionShapeAddHeightfield2(
self.handle,
command_handle,
mesh_scaling
.unwrap_or_else(|| Vector3::from_element(1.))
.as_ptr(),
heightfield_texture_scaling,
heightfield_data.as_mut_slice().as_mut_ptr(),
num_heightfield_rows as i32,
num_heightfield_columns as i32,
replace_heightfield.unwrap_or_else(|| CollisionId(-1)).0,
);
}
}
GeometricCollisionShape::MeshFile {
filename,
mesh_scaling,
flags,
} => {
let file = CString::new(filename.into_os_string().as_bytes()).unwrap();
shape_index = ffi::b3CreateCollisionShapeAddMesh(
command_handle,
file.as_ptr(),
mesh_scaling
.unwrap_or_else(|| Vector3::from_element(1.))
.as_ptr(),
);
if shape_index >= 0 {
if let Some(flags) = flags {
ffi::b3CreateCollisionSetFlag(command_handle, shape_index, flags);
}
}
}
GeometricCollisionShape::Mesh {
vertices,
indices,
mesh_scaling,
} => {
if vertices.len() > B3_MAX_NUM_VERTICES {
return Err(Error::new("Number of vertices exceeds the maximum."));
}
let mut new_vertices = Vec::<f64>::with_capacity(vertices.len() * 3);
for vertex in vertices.iter() {
new_vertices.extend_from_slice(vertex);
}
if let Some(indices) = indices {
if indices.len() > B3_MAX_NUM_INDICES {
return Err(Error::new("Number of indices exceeds the maximum."));
}
shape_index = ffi::b3CreateCollisionShapeAddConcaveMesh(
self.handle,
command_handle,
mesh_scaling
.unwrap_or_else(|| Vector3::from_element(1.))
.as_ptr(),
new_vertices.as_slice().as_ptr(),
vertices.len() as i32,
indices.as_slice().as_ptr(),
indices.len() as i32,
);
} else {
shape_index = ffi::b3CreateCollisionShapeAddConvexMesh(
self.handle,
command_handle,
mesh_scaling
.unwrap_or_else(|| Vector3::from_element(1.))
.as_ptr(),
new_vertices.as_slice().as_ptr(),
vertices.len() as i32,
);
}
}
GeometricCollisionShape::Plane { plane_normal } => {
let plane_constant = 0.;
shape_index = ffi::b3CreateCollisionShapeAddPlane(
command_handle,
plane_normal.as_ptr(),
plane_constant,
);
}
_ => {}
}
if shape_index >= 0 {
let position_vector = &frame_offset.translation.vector;
let rotation = &frame_offset.rotation;
let position_array = [position_vector.x, position_vector.y, position_vector.z];
let rotation_array = [rotation.i, rotation.j, rotation.k, rotation.w];
ffi::b3CreateCollisionShapeSetChildTransform(
command_handle,
shape_index,
position_array.as_ptr(),
rotation_array.as_ptr(),
);
}
let status_handle =
ffi::b3SubmitClientCommandAndWaitStatus(self.handle, command_handle);
let status_type = ffi::b3GetStatusType(status_handle);
if status_type == CMD_CREATE_COLLISION_SHAPE_COMPLETED as i32 {
let uid = ffi::b3GetStatusCollisionShapeUniqueId(status_handle);
return Ok(CollisionId(uid));
}
Err(Error::new("create_collision_shape failed."))
}
}
pub fn create_visual_shape(
&mut self,
shape: GeometricVisualShape,
options: VisualShapeOptions,
) -> Result<VisualId, Error> {
unsafe {
let mut shape_index = -1;
let command_handle = ffi::b3CreateVisualShapeCommandInit(self.handle);
match shape {
GeometricVisualShape::Sphere { radius } if radius > 0. => {
shape_index = ffi::b3CreateVisualShapeAddSphere(command_handle, radius);
}
GeometricVisualShape::Box { half_extents } => {
shape_index =
ffi::b3CreateVisualShapeAddBox(command_handle, half_extents.as_ptr());
}
GeometricVisualShape::Capsule { radius, length } if radius > 0. && length > 0. => {
shape_index =
ffi::b3CreateVisualShapeAddCapsule(command_handle, radius, length);
}
GeometricVisualShape::Cylinder { radius, length } => {
shape_index =
ffi::b3CreateVisualShapeAddCylinder(command_handle, radius, length);
}
GeometricVisualShape::MeshFile {
filename,
mesh_scaling,
} => {
let file = CString::new(filename.into_os_string().as_bytes()).unwrap();
shape_index = ffi::b3CreateVisualShapeAddMesh(
command_handle,
file.as_ptr(),
mesh_scaling
.unwrap_or_else(|| Vector3::from_element(1.))
.as_ptr(),
);
}
GeometricVisualShape::Mesh {
mesh_scaling,
vertices,
indices,
uvs,
normals,
} => {
let mut new_vertices = Vec::<f64>::with_capacity(vertices.len() * 3);
let mut new_normals = Vec::<f64>::with_capacity(vertices.len() * 3);
let mut new_uvs = Vec::<f64>::with_capacity(vertices.len() * 2);
if vertices.len() > B3_MAX_NUM_VERTICES {
return Err(Error::new("Number of vertices exceeds the maximum."));
}
for vertex in vertices.iter() {
new_vertices.extend_from_slice(vertex);
}
if indices.len() > B3_MAX_NUM_INDICES {
return Err(Error::new("Number of indices exceeds the maximum."));
}
let new_indices = indices;
if let Some(uvs) = uvs {
if uvs.len() > B3_MAX_NUM_VERTICES {
return Err(Error::new("Number of uvs exceeds the maximum."));
}
for uv in uvs.iter() {
new_uvs.extend_from_slice(uv);
}
}
if let Some(normals) = normals {
if normals.len() > B3_MAX_NUM_VERTICES {
return Err(Error::new("Number of normals exceeds the maximum."));
}
for normal in normals.iter() {
new_normals.extend_from_slice(normal);
}
}
shape_index = ffi::b3CreateVisualShapeAddMesh2(
self.handle,
command_handle,
mesh_scaling
.unwrap_or_else(|| Vector3::from_element(1.))
.as_ptr(),
new_vertices.as_slice().as_ptr(),
new_vertices.len() as i32 / 3,
new_indices.as_slice().as_ptr(),
new_indices.len() as i32,
new_normals.as_slice().as_ptr(),
new_normals.len() as i32 / 3,
new_uvs.as_slice().as_ptr(),
new_uvs.len() as i32 / 2,
);
}
GeometricVisualShape::Plane { plane_normal } => {
let plane_constant = 0.;
shape_index = ffi::b3CreateVisualShapeAddPlane(
command_handle,
plane_normal.as_ptr(),
plane_constant,
);
}
_ => {}
}
if shape_index >= 0 {
if let Some(flags) = options.flags {
ffi::b3CreateVisualSetFlag(command_handle, shape_index, flags);
}
ffi::b3CreateVisualShapeSetRGBAColor(
command_handle,
shape_index,
options.rgba_colors.as_ptr(),
);
ffi::b3CreateVisualShapeSetSpecularColor(
command_handle,
shape_index,
options.specular_colors.as_ptr(),
);
let position_vector = &options.frame_offset.translation.vector;
let rotation = &options.frame_offset.rotation;
let position_array = [position_vector.x, position_vector.y, position_vector.z];
let rotation_array = [rotation.i, rotation.j, rotation.k, rotation.w];
ffi::b3CreateVisualShapeSetChildTransform(
command_handle,
shape_index,
position_array.as_ptr(),
rotation_array.as_ptr(),
);
}
let status_handle =
ffi::b3SubmitClientCommandAndWaitStatus(self.handle, command_handle);
let status_type = ffi::b3GetStatusType(status_handle);
if status_type == CMD_CREATE_VISUAL_SHAPE_COMPLETED as i32 {
let uid = ffi::b3GetStatusVisualShapeUniqueId(status_handle);
if uid == -1 {
return Err(Error::new("create visual Shape failed."));
}
return Ok(VisualId(uid));
}
Err(Error::new("create visual Shape failed."))
}
}
pub fn create_multi_body(
&mut self,
base_collision_shape: CollisionId,
base_visual_shape: VisualId,
options: MultiBodyOptions,
) -> Result<BodyId, Error> {
unsafe {
assert!(
options.link_masses.len() == options.link_collision_shapes.len()
&& options.link_masses.len() == options.link_visual_shapes.len()
&& options.link_masses.len() == options.link_poses.len()
&& options.link_masses.len() == options.link_joint_types.len()
&& options.link_masses.len() == options.link_joint_axis.len()
&& options.link_masses.len() == options.link_inertial_frame_poses.len()
&& options.link_masses.len() == options.link_collision_shapes.len(),
"All link arrays need to be same size."
);
let command_handle = ffi::b3CreateMultiBodyCommandInit(self.handle);
let position_vector = &options.base_pose.translation.vector;
let rotation = &options.base_pose.rotation;
let base_position_array = [position_vector.x, position_vector.y, position_vector.z];
let base_rotation_array = [rotation.i, rotation.j, rotation.k, rotation.w];
let position_vector = &options.base_inertial_frame_pose.translation.vector;
let rotation = &options.base_inertial_frame_pose.rotation;
let base_inertial_position_array =
[position_vector.x, position_vector.y, position_vector.z];
let base_inertial_rotation_array = [rotation.i, rotation.j, rotation.k, rotation.w];
let _base_index = ffi::b3CreateMultiBodyBase(
command_handle,
options.base_mass,
base_collision_shape.0,
base_visual_shape.0,
base_position_array.as_ptr(),
base_rotation_array.as_ptr(),
base_inertial_position_array.as_ptr(),
base_inertial_rotation_array.as_ptr(),
);
if let Some(batch_positions) = options.batch_positions {
let mut new_batch_positions = Vec::<f64>::with_capacity(batch_positions.len() * 3);
for pos in batch_positions.iter() {
new_batch_positions.extend_from_slice(pos.coords.as_slice());
}
ffi::b3CreateMultiBodySetBatchPositions(
self.handle,
command_handle,
new_batch_positions.as_mut_slice().as_mut_ptr(),
batch_positions.len() as i32,
);
}
for i in 0..options.link_masses.len() {
let link_mass = options.link_masses[i];
let link_collision_shape_index = options.link_collision_shapes[i].0;
let link_visual_shape_index = options.link_visual_shapes[i].0;
let position_vector = &options.link_poses[i].translation.vector;
let rotation = &options.link_poses[i].rotation;
let link_position = [position_vector.x, position_vector.y, position_vector.z];
let link_orientation = [rotation.i, rotation.j, rotation.k, rotation.w];
let link_joint_axis: [f64; 3] = options.link_joint_axis[i].into();
let position_vector = &options.link_inertial_frame_poses[i].translation.vector;
let rotation = &options.link_inertial_frame_poses[i].rotation;
let link_inertial_frame_position =
[position_vector.x, position_vector.y, position_vector.z];
let link_inertial_frame_orientation =
[rotation.i, rotation.j, rotation.k, rotation.w];
let link_parent_index = options.link_parent_indices[i];
let link_joint_type = options.link_joint_types[i] as i32;
ffi::b3CreateMultiBodyLink(
command_handle,
link_mass,
link_collision_shape_index as f64,
link_visual_shape_index as f64,
link_position.as_ptr(),
link_orientation.as_ptr(),
link_inertial_frame_position.as_ptr(),
link_inertial_frame_orientation.as_ptr(),
link_parent_index,
link_joint_type,
link_joint_axis.as_ptr(),
);
}
if options.use_maximal_coordinates {
ffi::b3CreateMultiBodyUseMaximalCoordinates(command_handle);
}
if let Some(flags) = options.flags {
ffi::b3CreateMultiBodySetFlags(command_handle, flags.bits());
}
let status_handle = b3SubmitClientCommandAndWaitStatus(self.handle, command_handle);
let status_type = ffi::b3GetStatusType(status_handle);
if status_type == CMD_CREATE_MULTI_BODY_COMPLETED as i32 {
let uid = ffi::b3GetStatusBodyIndex(status_handle);
return Ok(BodyId(uid));
}
}
Err(Error::new("create_multi_body failed."))
}
pub fn change_visual_shape<Link: Into<Option<usize>>>(
&mut self,
body: BodyId,
link_index: Link,
options: ChangeVisualShapeOptions,
) -> Result<(), Error> {
let link_index = match link_index.into() {
None => -1,
Some(index) => index as i32,
};
unsafe {
let command_handle =
ffi::b3InitUpdateVisualShape2(self.handle, body.0, link_index, options.shape.0);
if let Some(texture_id) = options.texture_id {
ffi::b3UpdateVisualShapeTexture(command_handle, texture_id.0);
}
if let Some(specular) = options.specular_color {
ffi::b3UpdateVisualShapeSpecularColor(command_handle, specular.as_ptr());
}
if let Some(rgba) = options.rgba_color {
ffi::b3UpdateVisualShapeRGBAColor(command_handle, rgba.as_ptr());
}
if let Some(flags) = options.flags {
ffi::b3UpdateVisualShapeFlags(command_handle, flags);
}
let status_handle =
ffi::b3SubmitClientCommandAndWaitStatus(self.handle, command_handle);
let status_type = ffi::b3GetStatusType(status_handle);
if status_type != CMD_VISUAL_SHAPE_UPDATE_COMPLETED as i32 {
return Err(Error::new("Error resetting visual shape info"));
}
}
Ok(())
}
pub fn get_visual_shape_data(
&mut self,
body: BodyId,
request_texture_id: bool,
) -> Result<Vec<VisualShapeData>, Error> {
unsafe {
let command_handle = ffi::b3InitRequestVisualShapeInformation(self.handle, body.0);
let status_handle =
ffi::b3SubmitClientCommandAndWaitStatus(self.handle, command_handle);
let status_type = ffi::b3GetStatusType(status_handle);
if status_type == CMD_VISUAL_SHAPE_INFO_COMPLETED as i32 {
let mut visual_shape_info = ffi::b3VisualShapeInformation::default();
ffi::b3GetVisualShapeInformation(self.handle, &mut visual_shape_info);
let mut visual_shapes: Vec<VisualShapeData> =
Vec::with_capacity(visual_shape_info.m_numVisualShapes as usize);
let data = std::slice::from_raw_parts_mut(
visual_shape_info.m_visualShapeData,
visual_shape_info.m_numVisualShapes as usize,
);
for i in 0..visual_shape_info.m_numVisualShapes {
let mut shape_data: VisualShapeData = data[i as usize].into();
if request_texture_id {
shape_data.texture_id = Some(TextureId(data[i as usize].m_textureUniqueId));
}
visual_shapes.push(shape_data);
}
return Ok(visual_shapes);
}
}
Err(Error::new("Error receiving visual shape info"))
}
pub fn load_texture<File: AsRef<Path>>(&mut self, file: File) -> Result<TextureId, Error> {
unsafe {
let cfilename = CString::new(file.as_ref().as_os_str().as_bytes()).unwrap();
let command_handle = ffi::b3InitLoadTexture(self.handle, cfilename.as_ptr());
let status_handle =
ffi::b3SubmitClientCommandAndWaitStatus(self.handle, command_handle);
let status_type = ffi::b3GetStatusType(status_handle);
if status_type == CMD_LOAD_TEXTURE_COMPLETED as i32 {
let texture_id = TextureId(ffi::b3GetStatusTextureUniqueId(status_handle));
return Ok(texture_id);
}
}
Err(Error::new("Error loading texture"))
}
pub fn remove_body(&mut self, body: BodyId) {
unsafe {
assert!(body.0 >= 0, "Invalid BodyId");
assert!(
self.can_submit_command(),
"Internal Error: Can not submit command!",
);
let status_handle = ffi::b3SubmitClientCommandAndWaitStatus(
self.handle,
ffi::b3InitRemoveBodyCommand(self.handle, body.0),
);
let _status_type = ffi::b3GetStatusType(status_handle);
}
}
pub fn get_body_info(&mut self, body: BodyId) -> Result<BodyInfo, Error> {
let mut body_info_c = ffi::b3BodyInfo {
m_baseName: [0; 1024],
m_bodyName: [0; 1024],
};
unsafe {
if ffi::b3GetBodyInfo(self.handle, body.0, &mut body_info_c) != 0 {
return Ok(body_info_c.into());
}
}
Err(Error::new("Couldn't get body info"))
}
pub fn get_num_bodies(&mut self) -> usize {
unsafe { ffi::b3GetNumBodies(self.handle) as usize }
}
}
impl Drop for PhysicsClient {
fn drop(&mut self) {
unsafe { ffi::b3DisconnectSharedMemory(self.handle) }
}
}
mod gui_marker {
use std::sync::atomic::{AtomicBool, Ordering};
static GUI_EXISTS: AtomicBool = AtomicBool::new(false);
pub struct GuiMarker {
_unused: (),
}
impl GuiMarker {
pub fn acquire() -> Result<GuiMarker, crate::Error> {
match GUI_EXISTS.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) {
Ok(false) => Ok(GuiMarker { _unused: () }),
_ => Err(crate::Error::new(
"Only one in-process GUI connection allowed",
)),
}
}
}
impl Drop for GuiMarker {
fn drop(&mut self) {
GUI_EXISTS.store(false, Ordering::SeqCst)
}
}
}