photo

Helicopter cargo sling rope

Recommended Posts

Dear Unigine team,

I'm currently trying to setup properly an HC transporting a cargoH/C > hook > rope > bag
SDK 2.12, Physics::setEnabled(1);

HC can be moving on predefined trajectory, on key pressed or from CIGI remote ctrl.
User can change cone model as rope length pressing + - keys. 
In object tree, the hook is associated to SlingComponent managing different 3D cones and the rope system as bellow described.

#### SlingComponent.h ####

#pragma once
#include <UnigineComponentSystem.h>

#ifdef DLL_EXPORT
#define DLLEI __declspec(dllexport)
#else
#define DLLEI __declspec(dllimport)
#endif

using namespace Unigine;

class DLLEI SlingComponent :
	public Unigine::ComponentBase
{
public:
	COMPONENT(SlingComponent, Unigine::ComponentBase);
	COMPONENT_INIT(init);
	COMPONENT_UPDATE(update);
	COMPONENT_SHUTDOWN(shutdown);

	void init();
	void update();
	void shutdown();

	PROP_NAME("SlingComponent");
	//ig_config.xml described how bellow params are exposed to CIGI
	PROP_PARAM(Float, cable_length, 20, "Cable length");
	PROP_PARAM(Float, bag_weight, 100, "Bag weight");

private:
	Vector<NodePtr> cable_cones; //ObjectMeshStaticPtr
	int cone_idx = 0;
	ObjectDummyPtr hook;
	ObjectMeshDynamicPtr rope;
	ObjectMeshDynamicPtr bag;

	void parameter_changed(PropertyPtr property, int num);
};

#### SlingComponent.cpp ####
  
#include "SlingComponent.h"

#include <UnigineApp.h>
#include <UnigineLog.h>
#include <UnigineNode.h>
#include <UnigineConsole.h>
#include <UniginePrimitives.h>
using namespace Unigine;

#include "../Util.h"
using namespace Math;

REGISTER_COMPONENT(SlingComponent)

/// function, creating a named rope with specified parameters at pos
ObjectMeshDynamicPtr createBodyRope(char* name, float radius, float length, int segments, int slices, const Mat4& tm)
{
	// creating a cylinder dynamic mesh
	ObjectMeshDynamicPtr OMD = Primitives::createCylinder(radius, length, segments, slices);

	// setting parameters
	OMD->setWorldTransform(tm);
	OMD->setMaterial("CableMaterial", "*");
	//OMD->setMaterialParameterFloat4("albedo_color", vec4(0.5f, 0.5f, 0.0f, 1.0f), 0);
	OMD->setName(name);

	//assigning a rope body to the dynamic mesh object and setting rope parameters
	BodyRopePtr body = BodyRope::create(OMD);
	body->setMass(1.0f);
	body->setRadius(0.05f);
	body->setFriction(0.5f);
	body->setRestitution(0.05f);
	body->setRigidity(0.05f);
	body->setLinearStretch(0.0001f);

	return OMD;
}
  
/// function, creating a named dummy body of a specified size at pos
ObjectDummyPtr createBodyDummy(char* name, const vec3& size, const Vec3& pos)
{
	// creating a dummy object
	ObjectDummyPtr OMD = ObjectDummy::create();

	// setting parameters
	OMD->setWorldTransform(Mat4(translate(pos)));
	OMD->setName(name);

	//assigning a dummy body to the dummy object and adding a box shape	to it
	BodyDummy::create(OMD);
	OMD->getBody()->addShape(ShapeBox::create(size)/*, translate(0.2f, 0.2f, 0.2f)*/);

	return OMD;
}

ObjectMeshDynamicPtr createBodyBag(char* name, const vec3& size, const Vec3& pos)
{
	// creating a cylinder dynamic mesh
	ObjectMeshDynamicPtr OMD = Primitives::createBox(size);

	// setting parameters
	OMD->setWorldTransform(Mat4(translate(pos)));
	OMD->setMaterial("mesh_base", "*");
	//OMD->setMaterialParameterFloat4("albedo_color", vec4(0.5f, 0.5f, 0.0f, 1.0f), 0);
	//OMD->setCollision(1,0);
	OMD->setName(name);

	//assigning a box body to the dynamic mesh object and setting box parameters
	auto body = BodyRigid::create(OMD);
	body->addShape(ShapeBox::create(size), translate(vec3(0.0f)));
	//body->setMass(10.0f); 
	OMD->getBody()->getShape(0)->setMass(100.0f);

	return OMD;
}


void SlingComponent::init()
{
	Log::message("SlingComponent init from root node %s\n ",
		node->getRootNode()->getName());

	Util::find_nodes_in_children(node, Node::OBJECT_MESH_STATIC, "cone", &cable_cones);

	getProperty()->addCallback(
		Property::CALLBACK_PARAMETER_CHANGED,
		MakeCallback(this, &SlingComponent::parameter_changed));
}

void SlingComponent::update()
{
	bool modelUp = false, modelDown = false;

	if (!Console::isActive()) {
		modelUp = (App::clearKeyState('+') == 1);
		modelDown = (App::clearKeyState('-') == 1);
	}

	if (modelUp || modelDown)
	{
		if (modelUp && cone_idx < cable_cones.size()) //-> over means no mesh display, let user check fps impact
			cone_idx++;
		if (modelDown && cone_idx > -1) //-> under means no mesh display, let user check fps impact
			cone_idx--;

		for (int i = 0; i < cable_cones.size(); i++)
			cable_cones[i]->setEnabled(i == cone_idx);

		if (modelUp)
			cable_length = cable_length + 10.f;
		else if (modelDown)
			cable_length = cable_length - 10.f;
	}
	else {}//NTD
}

void SlingComponent::shutdown()
{
	Log::message("SlingComponent shutdown from root node %s\n",
		node->getRootNode()->getName());
}

void SlingComponent::parameter_changed(PropertyPtr property, int num)
{
	if (cable_length.getID() == num)
	{
		auto value = property->getParameterPtr(num)->getValueFloat();
		Log::message("parameter_changed node %s cable length %f\n", node->getName(), value);

		//remove if already exists
		if (hook && rope && bag)
		{
			node->removeChild(hook);
			hook->deleteForce();
			rope->deleteForce();
			bag->deleteForce();
		}

		//add
		const float hlength = cable_length.get(); //< horizontal length of the cable
		const float radius = 0.15 / 2.; //< radius of the cable
		const int slices = 10; //< nb of slices of the cable (higher=rounder)
		const int segments = min(100, max(10, int(hlength / 5))); // number of segments in the cable (min 10, max 100)
		hook = createBodyDummy("slingHook", vec3(0.05f, 0.05f, 0.05f), Vec3::ZERO);
		rope = createBodyRope("slingRope", radius, hlength, segments, slices, Mat4(translate(0.0f, 0.0f, -hlength / 2)));
		bag = createBodyBag("slingBag", vec3(1.f, 1.f, 1.f), Vec3(0.0f, 0.0f, -hlength));
		JointParticles::create(hook->getBody(), rope->getBody(), hook->getPosition(), vec3(0.55f));
		JointParticles::create(bag->getBody(), rope->getBody(), bag->getPosition(), vec3(0.55f));
		node->addChild(hook);
	}
	else if (bag_weight.getID() == num)
	{
		bag->getBody()->getShape(0)->setMass(bag_weight.get());
	}
}

The result is already interesting.. but still not satisfying / realistic:
- I can't get proper init position for the bag, seem to be at origin whereas it should be at the end of the rope
- How to set proper elasticity? Should be nearly none, even if the bag mass can reach 1 tonne
- Is it better way to set up everything?
-- Hook is the only child of HC
-- Between rope and bag, it is JointParticles instead of JointBall 

Kind regards,
Charles

Share this post


Link to post

Dear Unigine team,

Any feedback?
Support would be appreciated on this ;-)

Kind regards,
Charles

Share this post


Link to post

Hi Charles,

sorry for the silence. We're still looking into this. Will answer by the end of the week, earlier if possible.

Share this post


Link to post

Hi,

So, sorry but I still don't get it working properly..

Let's consider simpler system, with a bag fixed onto a hook under the HC:

  • HC is a node
    • sling is a node with SlingComponent associated
      from SlingComponent method (init, update or paramChanged), I do node->addChild(hook); 
      • hook is ObjectDummyPtr
        bag is ObjectMeshDynamicPtr
        both are joint with JointFixed

So, in bellow code, what am I doing wrong?
Even with fixed HC in world, I can't get right init position and stable system... is it normal?

auto transform = node->getWorldTransform(); // hc > sling
auto hook = createBodyDummy("slingHook", vec3(0.05f, 0.05f, 0.05f), transform * Mat4(translate(0.0f, 0.0f, -1.f))); 
auto bag = createBodyBag("slingBag", vec3(1.f, 1.f, 1.f), transform * Mat4(translate(0.0f, 0.0f, -10.f)));
if (hook && bag)
{
	auto jf = JointFixed::create(hook->getBody(), bag->getBody(), Vec3(0.0f, 0.0f, -10.0f));
	jf->setNumIterations(10);
	node->addChild(hook);
}

with 

ObjectDummyPtr createBodyDummy(char* name, const vec3& size, const Mat4& transform)
{
	// creating a dummy object
	ObjectDummyPtr OMD = ObjectDummy::create();

	// setting parameters
	OMD->setWorldTransform(transform);
	OMD->setName(name);

	//assigning a dummy body to the dummy object and adding a box shape	to it
	BodyDummy::create(OMD);
	OMD->getBody()->addShape(ShapeBox::create(size), translate(0.0f, 0.0f, 0.0f));

	return OMD;
}

ObjectMeshDynamicPtr createBodyBag(char* name, const vec3& size, const Mat4& transform)
{
	// creating a cylinder dynamic mesh
	ObjectMeshDynamicPtr OMD = Primitives::createBox(size);

	// setting parameters
	OMD->setWorldTransform(transform);
	OMD->setMaterial("mesh_base", "*");
	OMD->setName(name);

	//assigning a box body to the dynamic mesh object and setting box parameters
	auto body = BodyRigid::create(OMD);
	body->addShape(ShapeBox::create(size), translate(vec3(0.0f)));
	OMD->getBody()->getShape(0)->setMass(100.0f);

	return OMD;
}

Kind regards,
Charles

Share this post


Link to post

Hi,

Any feedback?
First post is 1 week ago, and still blocked on it..
At least previous post simplified setup... what am I doing wrong?

Kind regards,
Charles

Share this post


Link to post

Hi Charles,

We're sorry it takes too long. Rope setup isn't that easy and my colleague keeps experimenting to find a better behavior. I believe he'll answer you by tomorrow.

Share this post


Link to post

Hi,

Ok, thanks for investigating.

What about the easier (easiest) setup:

  • HC is a node
    • sling is a node with SlingComponent associated
      from SlingComponent method (init, update or paramChanged), I do node->addChild(hook); 
      • hook is ObjectDummyPtr
        bag is ObjectMeshDynamicPtr
        both are joint with JointFixed

I don't get the bag fixed at proper position -10m under the H/C... it is properly init, but after that start to orbit with weird trajectory :-o

Kind regards,
Charles

Share this post


Link to post

Hi Charles,

I don't get the bag fixed at proper position -10m under the H/C... it is properly init, but after that start to orbit with weird trajectory
I tried to reproduce this. I saw a problem with the correct position. But I didn't see the "weird trajectory" movement. It's fixed in my case. I created a new world, added a NodeDummy with the component, placed it at (0, 0, 12). Your code was added to a init() method.

About the proper position:
Instead of this: node->addChild(hook); use this: node->addWorldChild(hook).
What's the difference?
- addWorldChild() doesn't move your new child in the world (it recalculates the local transform, the world transform would be the same as before).
- addChild() does nothing with the local transform, it's the same as before, but as a result, world transform changes (objects change their position relative to the new parent).
So, in your sample you change the hook's position right after creating the fixed joint.

About the weird trajectory...
Maybe it's a side effect of addChild(), maybe there are surface collisions with scaled objects, maybe the reason is in the physics settings... Who knows. Can you send your project if the problem is not the addChild()?

Between rope and bag, it is JointParticles instead of JointBall.
I think you can't create a realistic sling rope (with dynamic length) by using BodyRope / JointParticles. It can be done via JointBalls at the moment.

How to set proper elasticity? Should be nearly none, even if the bag mass can reach 1 tonne.
In case of JointBalls increase both joint's number of iterations and restitution.

 

I've finally made a sample and attached it to this post. Just create a new C++ project (double precision, SDK 2.12.0.2) and apply to it files from the attach.
Run the "sling_rope" world.
Controls:
J and L - rotate the crane
I and K - increase or decrease length of the rope
API gives you possibility to attach or detach the bag.
In this sample the rope is a set of BodyRigids + ShapeCapsules + JointBalls. It can collide with the world, dynamically generate its mesh with corrected uv, can change its length during runtime.
изображение.pngизображение.pngизображение.pngизображение.png

Is this what you are trying to achieve?


Best regards,
Alexander

sling_rope.zip

Share this post


Link to post

Hi,

Thanks a lot for this dedicated and clear sample!
To change in RT rope length is really cool.

Still adapting it to our context, little difference / difficulties already:

  • Crane is actually moving since it is helicopters
  • Attache / detach bag requires add/removeChild or world version.. right? otherwise (with moving crane) you don't get independent falling trajectory.. 
  • Bag doesn't have any mass here, only rope segment, already with 100Kg I get some weird behaviour (kept your join params)
  • With geodetic pivot, is there any special setWorldPosition to perform at init?

Kind regards,
Charles

Share this post


Link to post

Hello Charles,

Attache / detach bag requires add/removeChild or world version.. right? otherwise (with moving crane) you don't get independent falling trajectory.. 
Don't use add/removeChild between helicopters and bags. Physical relationship should only be built through physical joints. Node hierarchy should be "flat". The bag should never be a child to the helicopter.

Bag doesn't have any mass here, only rope segment, already with 100Kg I get some weird behaviour (kept your join params)
In my sample the bag weights 10Kg (BodyRigid -> ShapeBox -> Mass):изображение.png

Try to increase iterations and softness (a little bit), but decrease restitution (a little bit) in the SlingRope component.


With geodetic pivot, is there any special setWorldPosition to perform at init?
No, everything should work fine.

Best regards,
Alexander

Share this post


Link to post

Hi,

Thanks, fixes make sens, and it starts to work fine.

For the mass, have you tried with 100kg? 1 tone?
10kg is fine, rope is stable and nice.
Already with 100kg the rope is dislocated..
To prospect on rope params, is there a way to use script in editor with physic activated maybe?

Geodetic pivot itself is maybe not the issue, but CIGI init seems to disturb a bit...

Here is init with fix HC in world with geodetic pivot -> nice (with 10kg bag...)

UNG-bag-world.PNG.f522571c689081fe791ec3493b34d5d3.PNG

Here is same world, with HC loaded from CIGI.
The HC is red circle on the right, whereas the bag is on the left.
Detach / attach bag not helping, change rop length same thing.

 UNG-bag-CIGI.PNG.04a2856a62ba30cdc2032604fb05fce7.PNG

Kind regards,
Charles

Share this post


Link to post

For the mass, have you tried with 100kg? 1 tone?
10kg is fine, rope is stable and nice.
Already with 100kg the rope is dislocated..

Because maximum value of the joint's restitution is 1, all you can do is to increase "Segment Mass" value.
If your bag weights 100kg then use 2.5kg as the "segment mass".
If your bag weights 1000kg, use 25kg.

To prospect on rope params, is there a way to use script in editor with physic activated maybe?
Only if you rewrite SlingRope.cpp to UnigineScript. ;-)

Here is same world, with HC loaded from CIGI.
Is HC a entity? Helicopter and HC are different entities? Is the Bag an entity?
I think I don't undestrand what exactly is going on in this case.

Best regards,
Alexander

Share this post


Link to post

Hi,

Sorry, HC is helicopter.
The bag is simply a node of the loaded world, as you explain.
Works fine if HC is loaded directly in the world as well, but face some issue if it is loaded from CIGI.

Bellow you can see the hook (or crane in  your code) is just under the HC loaded / moving with CIGI..
But the rope is somehow stuck at the origin, and segment are build only between bag and this origin.

UNG-bag-CIGI2.PNG.7e743469e1f5df936493839dd8c62455.PNG

Otherwise bag / segment masses seem to work, thanks!

Kind regards,
Charles
 

Share this post


Link to post

Hi! 
there are several problems
1. in AppWorldLogic::init() enable physics


int AppWorldLogic::init()
{
	// init systems, do some optimizations for IG
	Game::setEnabled(1);
	Physics::setEnabled(1); // switch on
	Render::setMotionBlur(0);
	Render::setLightsLensFlares(0);
//....
}

2. when you create an entity by CIGI, you need the colision detection enable to always be on (in EntityControl packet)

3. entity creating in 0.0.0 and the length of the rope is calculated immediately (in SlingRope::init). you need recalculate rope length again when you picking up weight. For example:

rope_component->setLength(static_cast<float>(length(rope_component->bag_node->getWorldPosition() - rope_component->getNode()->getWorldPosition())));

Thanks!

Share this post


Link to post