Jump to content

Parenting Physics Objects to Platforms


SpiderPig
 Share

Recommended Posts

I want to be able have physics working on various different platforms that each have there own velocity and rotations happening.  (In this example I'm just turning the floor object, so that might be the wrong way to do that.)

On each platform I'd want to have physics working normally as though each platform is it's own little physics world, oblivious to the fact that it's platform is moving about.  Just like we can walk on Earth and not feel like were hurtling around the sun and around the Earths axis. ;)

 

I guess this is similar to parenting a player controller to an elevator that moves left and right so the player doesn't slide off the platform as it moves.

As the GIF shows, simply parenting the box to the floor breaks the physics sim I think.  But the concept is what I'm trying to achieve.

The same goes for parenting the player controller to the floor.

It looks like it wants to work the way I intend, it just keeps bouncing back and forth.

Any one know if this might be possible?

 

ParentedPhysicsObject.gif.dcf746d27915661e131f0af1f500e64b.gif

#include "UltraEngine.h"

using namespace UltraEngine;

Vec3 mousepos = Vec3(0.0f);
float move_adjustment = 0.1f;
float move_speed = 1.0f;
float lookspeed = 0.1f;
float looksmoothing = 0.5f;
bool enable_camera = true;

int main(int argc, const char* argv[])
{
    auto displays = GetDisplays();
    auto window = CreateWindow("Ultra Engine", 0, 0, 1280, 720, displays[0], WINDOW_CENTER | WINDOW_TITLEBAR);
    auto world = CreateWorld();
    auto framebuffer = CreateFramebuffer(window);

    auto camera = CreateCamera(world);
    camera->SetClearColor(0.125);
    camera->SetFov(70);
    camera->SetPosition(0, 10, -5);

    auto light = CreateDirectionalLight(world);
    light->SetRotation(35, 45, 0);

    auto floor = CreateBox(world, 10.0f, 0.1f, 10.0f);
    floor->SetPosition(0.0f, -3.0f, 0.0f);
    floor->SetMaterial(LoadMaterial("Materials\\Developer\\bluegrid.mat"));

    auto box = CreateBox(world);
    box->SetMass(1.0f);
    box->SetPosition(2.0f, 2.0f, 0.0f);
    box->SetParent(floor);

    auto player = CreateCylinder(world);
    player->SetPhysicsMode(PHYSICS_PLAYER);
    player->SetPosition(0.0f, 5.0f, 0.0f);
    player->SetMass(1.0f);

    auto cam_pivot = CreatePivot(world);
    cam_pivot->SetParent(player);
    cam_pivot->SetPosition(0.0f, 1.8f, 0.0f);

    camera->SetParent(cam_pivot);
    camera->SetPosition(0.0f, 0.0f, -3.0f);
    camera->SetDebugPhysicsMode(true);

    bool stage = 0;
    while (window->Closed() == false and window->KeyDown(KEY_ESCAPE) == false)
    {
        if (window->KeyHit(KEY_F2) == true) { camera->SetWireframe(!camera->GetWireframe()); }
        if (window->KeyHit(KEY_F3) == true) { camera->SetDebugPhysicsMode(!camera->GetDebugPhysicsMode()); }
        if (window->KeyHit(KEY_F4) == true) { enable_camera = !enable_camera; }

        if (enable_camera) {
            auto _displaySize = window->GetSize();
            float cx = Round((float)_displaySize.x / 2.0f);
            float cy = Round((float)_displaySize.y / 2.0f);

            auto mpos = Vec3(window->GetMousePosition().x, window->GetMousePosition().y, window->GetMousePosition().z);
            window->SetMousePosition(cx, cy);
            mpos = mpos * looksmoothing + mousepos * (1 - looksmoothing);
            auto dx = (mpos.x - cx) * lookspeed;
            auto dy = (mpos.y - cy) * lookspeed;


            auto camrot = cam_pivot->GetRotation();
            camrot.x += dy;
            camrot.y += dx;
            cam_pivot->SetRotation(camrot);
            mousepos = mpos;
        }

        floor->Turn(0.0f, 0.1f, 0.0f);
        
        auto y_angle = cam_pivot->GetRotation().y;
        auto move = 0.0f, strafe = 0.0f;
        if (window->KeyDown(KEY_W)) { move = 1.0f; }
        else if (window->KeyDown(KEY_S)) { move = -1.0f; }
        if (window->KeyDown(KEY_A)) { strafe = -1.0f; }
        else if (window->KeyDown(KEY_D)) { strafe = 1.0f; }
        player->SetInput(y_angle, move, strafe);

        world->Update();
        world->Render(framebuffer);
    }
    return 0;
}

 

Link to comment
Share on other sites

I think I actually might have solved this.  I'm reluctant to party until I've tested it further but I can use a 2nd world for physics that has non-moving platforms, then I can transform positions and rotations into the main world which is rendered.  Should work for controllers and multiple gravity directions I would think... I wonder if there is a downside to doing it this way...?

ParentedPhysicsObject_002.gif.d055740bc8e90ba080338110127be7e0.gif

#include "UltraEngine.h"

using namespace UltraEngine;

Vec3 mousepos = Vec3(0.0f);
float move_adjustment = 0.1f;
float move_speed = 1.0f;
float lookspeed = 0.1f;
float looksmoothing = 0.5f;
bool enable_camera = true;

int main(int argc, const char* argv[])
{
    auto displays = GetDisplays();
    auto window = CreateWindow("Ultra Engine", 0, 0, 1280, 720, displays[0], WINDOW_CENTER | WINDOW_TITLEBAR);
    auto world = CreateWorld();
    auto framebuffer = CreateFramebuffer(window);

    auto camera = CreateCamera(world);
    camera->SetClearColor(0.125);
    camera->SetFov(70);
    camera->SetPosition(0, 10, -5);

    auto light = CreateDirectionalLight(world);
    light->SetRotation(35, 45, 0);

    auto floor = CreateBox(world, 10.0f, 0.1f, 10.0f);
    floor->SetPosition(0.0f, -3.0f, 0.0f);
    floor->SetMaterial(LoadMaterial("Materials\\Developer\\bluegrid.mat"));

    auto box = CreateBox(world);
    box->SetCollider(nullptr);
   // box->SetMass(1.0f);
    box->SetPosition(2.0f, 2.0f, 0.0f);
   // box->SetParent(floor);

    auto player = CreateCylinder(world);
    player->SetPhysicsMode(PHYSICS_PLAYER);
    player->SetPosition(0.0f, 5.0f, 0.0f);
    player->SetMass(1.0f);

    auto cam_pivot = CreatePivot(world);
    cam_pivot->SetParent(player);
    cam_pivot->SetPosition(0.0f, 1.8f, 0.0f);

    camera->SetParent(cam_pivot);
    camera->SetPosition(0.0f, 0.0f, -3.0f);
    camera->SetDebugPhysicsMode(true);

    auto world_2 = CreateWorld();
    auto f2 = CreateBox(world_2, 10.0f, 0.1f, 10.0f);
    f2->SetPosition(0, -3, 0);

    auto b2 = CreateBox(world_2);
    b2->SetMass(1.0f);
    b2->SetPosition(2.0f, 2.0f, 0.0f);

    world_2->Update();//No random crash with this?
    bool stage = 0;
    while (window->Closed() == false and window->KeyDown(KEY_ESCAPE) == false)
    {
        if (window->KeyHit(KEY_F2) == true) { camera->SetWireframe(!camera->GetWireframe()); }
        if (window->KeyHit(KEY_F3) == true) { camera->SetDebugPhysicsMode(!camera->GetDebugPhysicsMode()); }
        if (window->KeyHit(KEY_F4) == true) { enable_camera = !enable_camera; }

        if (enable_camera) {
            auto _displaySize = window->GetSize();
            float cx = Round((float)_displaySize.x / 2.0f);
            float cy = Round((float)_displaySize.y / 2.0f);

            auto mpos = Vec3(window->GetMousePosition().x, window->GetMousePosition().y, window->GetMousePosition().z);
            window->SetMousePosition(cx, cy);
            mpos = mpos * looksmoothing + mousepos * (1 - looksmoothing);
            auto dx = (mpos.x - cx) * lookspeed;
            auto dy = (mpos.y - cy) * lookspeed;


            auto camrot = cam_pivot->GetRotation();
            camrot.x += dy;
            camrot.y += dx;
            cam_pivot->SetRotation(camrot);
            mousepos = mpos;
        }

        floor->Turn(0.0f, 0.1f, 0.0f);
        
        auto y_angle = cam_pivot->GetRotation().y;
        auto move = 0.0f, strafe = 0.0f;
        if (window->KeyDown(KEY_W)) { move = 1.0f; }
        else if (window->KeyDown(KEY_S)) { move = -1.0f; }
        if (window->KeyDown(KEY_A)) { strafe = -1.0f; }
        else if (window->KeyDown(KEY_D)) { strafe = 1.0f; }
        player->SetInput(y_angle, move, strafe);

        if (window->KeyHit(KEY_P)) { b2->AddForce(0,200,0); }
        world_2->Update();

        auto p = b2->GetPosition();
        auto r = b2->GetRotation();

        auto matrix = floor->GetMatrix();
        matrix = matrix.Inverse();
        matrix.t = Vec4(0, 0, 0, 1);

        p = TransformPoint(p, Mat4(), matrix);
        auto rr = TransformRotation(r, Mat4(), matrix);

        box->SetPosition(p);
        box->SetRotation(rr);

        world->Update();
        world->Render(framebuffer);
    }
    return 0;
}

 

Link to comment
Share on other sites

I think the Physicsmatrix is only updated for the box itself, not taking the parent into account. You can see this in the first sample: While the box is moving  you can actually move through it even though the collision shape is displayed correct. When you move to the original Position the collison will occur.

  • Windows 10 Pro 64-Bit-Version
  • NVIDIA Geforce 1080 TI
Link to comment
Share on other sites

yes, but that would mean, that the floor itself needs a mass ;) otherwise the force methods will have no impact. 

To make it physically correct, ( i believe i read that somewhere on the Newtondynamics forums back in Blitzmax times ) Would be to turn off gravity at all and for each object apply a force pointing to the center of your "planet" or maybe in this case just downwards. Of course in the case of a plane you need to conquer the forces applied to the plane to make it stable.

  • Windows 10 Pro 64-Bit-Version
  • NVIDIA Geforce 1080 TI
Link to comment
Share on other sites

Got it working with the player controller.  I'm going to expand on this and make a complex level with a lot going on to see if it holds up.  You can see in the animation that the physics objects don't rotate and none of the rendered objects have colliders.  It's just a matter of getting the physics matrix's and transforming them based on what platform is moving.

ParentedPhysicsObject_003.gif.1f40aa464c6d8112dce93aaedd50917c.gif

Link to comment
Share on other sites

This is what I'm doing to transform the objects.

void UpdatePhysics(shared_ptr<Entity> source, shared_ptr<Entity> target, shared_ptr<Entity> platform = nullptr) {
    auto pos = source->GetPosition();
    auto rot = source->GetRotation();

    if (platform != nullptr) {
        auto matrix = platform->GetMatrix();
        matrix = matrix.Inverse();
        matrix.t = Vec4(0, 0, 0, 1);

        pos = TransformPoint(pos, Mat4(), matrix);
        rot = TransformRotation(rot, Mat4(), matrix);
    }

    target->SetPosition(pos);
    target->SetRotation(rot);
}

This may yet not work for all cases, but I wonder if a simple callback system between the physics thread and the rendered world could do this?  Basically it would mean we could offset the rendered object away from it's collider.  Just thinking out loud. :)

Link to comment
Share on other sites

If i remember correctly (@Josh correct me if i am wrong) , the gravity is applied to each object in the "NewtonBodySetForceAndTorqueCallback" which means, that while currently only a fixed gravity force is applied, this could be extended to add several "Attractors" or planets. So in theory you could simulate whole a whole universe with planets. and correct gravity. Maybe it would be an option to open up the Callback to override the force calculation for gravity? 

 

Maybe something like this (more or less pseudo code):

vec3 world_gravity_callback(vec3 pos)
{
	vec3 force = vec3(0);
	for(auto p : planets)
	{
		float distanceToPlanet = calcDistance(pos);
		force += calcForce(pos, distanceToPlanet); // as Newton engine uses real world units, you can actually use the real physics equations here
	}
	return force;
}

void cb_applyForce(const NewtonBody* const body, dFloat timestep, int threadIndex)
{
  // Fetch user data and body position.
  UserData *mydata = (UserData*)NewtonBodyGetUserData(body);
  dFloat pos[4];
  NewtonBodyGetPosition(body, pos);

  // Apply gravity.
  dFloat force[3] = world->Gravity;
  if(world->HasGravityCallback) // if custom gravity is used calculate this instead of the fixed gravity
  {
	force = world->CalculateGravityForPosition(pos); 
  }
  NewtonBodySetForce(body, force);
}

 

  • Upvote 1
  • Windows 10 Pro 64-Bit-Version
  • NVIDIA Geforce 1080 TI
Link to comment
Share on other sites

small experiment, i couldn't resist ;)

image.thumb.gif.768a10902fea2bf5b51142ff5dba960b.gif

#include "UltraEngine.h"
#include "Components/Player/CameraControls.hpp"

using namespace UltraEngine;

const float G = 667.4f;
const float planet_mass = 1000.0;

Vec3 calculateForce(shared_ptr<Entity> planet, shared_ptr<Entity> target)
{
    Vec3 direction = (planet->GetPosition() - target->GetPosition());
    float distance = direction.Length();
    if (distance == 0)
        return Vec3(0.0);

    float forceMagnitude = G * (target->GetMass() * planet_mass) / pow(distance, 2);
    Vec3 force = direction.Normalize() * forceMagnitude;

    return force;
}

int main(int argc, const char* argv[])
{
    auto displays = GetDisplays();
    auto window = CreateWindow("Ultra Engine", 0, 0, 1280, 720, displays[0], WINDOW_CENTER | WINDOW_TITLEBAR);
    auto world = CreateWorld();
    auto framebuffer = CreateFramebuffer(window);

    auto camera = CreateCamera(world);
    camera->SetClearColor(0.125);
    camera->SetFov(70);
    camera->SetPosition(0, 10, -5);

    auto light = CreateDirectionalLight(world);
    light->SetRotation(35, 45, 0);

    world->SetGravity(0.0, 0.0, 0.0);
   
    auto planet = CreateSphere(world, 100.0, 128);
    planet->SetMass(FLT_MAX - 1);
    planet->SetPosition(0.0f, -100.0f, 0.0f);
    planet->SetMaterial(LoadMaterial("Materials\\Developer\\bluegrid.mat"));

    camera->SetPosition(0.0f, 0.0f, -3.0f);
    camera->AddComponent<CameraControls>();
    camera->SetDebugPhysicsMode(true);

    vector<shared_ptr<Entity>> boxes;

    auto box_main = CreateBox(world, 4.0);
    for(int i = 0; i < 200; i ++)
    { 
        auto box = box_main->Instantiate(world);
        box->SetMass(1.0);
        box->SetColor(1.0, 0.0, 0.0, 1.0);
        box->SetPosition(Random(120.0, -120.0), Random(120.0, -120.0), Random(120.0, -120.0));
        box->Move(0.0, -100.0, 0.0);
        boxes.push_back(box);
    }

    box_main = NULL;

    bool stage = 0;
    while (window->Closed() == false and window->KeyDown(KEY_ESCAPE) == false)
    {
        if (window->KeyHit(KEY_F2) == true) { camera->SetWireframe(!camera->GetWireframe()); }
        if (window->KeyHit(KEY_F3) == true) { camera->SetDebugPhysicsMode(!camera->GetDebugPhysicsMode()); }

        bool hit_space = window->KeyHit(KEY_SPACE);
        for each (auto box in boxes)
        {
            box->AddForce(calculateForce(planet, box));

            if (hit_space)
            {
                Vec3 direction = (planet->GetPosition() - box->GetPosition()).Normalize();
                box->AddForce(-direction * 2500.0);
            }
        }

        world->Update();
        world->Render(framebuffer);
    }
    return 0;
}

 

  • Like 3
  • Windows 10 Pro 64-Bit-Version
  • NVIDIA Geforce 1080 TI
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...