vujadin / BabylonHx

Port of Babylon.js 3D engine to Haxe.
http:/paradoxplay.com/babylonhx
Apache License 2.0
189 stars 43 forks source link

Change gravity based on accelerometer values (on mobile) #133

Closed ramsestom closed 7 years ago

ramsestom commented 7 years ago

On android, with openfl, I tried to update the world gravity based on accelerometer values. But I can't make it work... Basically what I do is:

if (Accelerometer.isSupported) 
{
    _sensor = new Accelerometer();
    _sensor.addEventListener(AccelerometerEvent.UPDATE, updateGravityCallback);
}

private function updateGravityCallback(Event:AccelerometerEvent):Void 
{
        #if android
        scene.getPhysicsEngine()._setGravity(new Vector3(Event.accelerationX, -Event.accelerationY, Event.accelerationZ));
        #else //Values on iOS and BlackBerry are inverted
        scene.getPhysicsEngine()._setGravity(new Vector3(-Event.accelerationX, Event.accelerationY, -Event.accelerationZ));
        #end

        trace("Gravity physics: " + scene.getPhysicsEngine().gravity);
}

and in my world, I have a sphere with a non null mass. I use the Oimo plugin as a physics engine.

If I don't try to set the gravity based on accelerometer values, the sphere fall along the y axis, as expected (by default, the gravity is set as a (0,-1,0) vector in Oimo ). But as soon as I listen to accelerometer events and update the gravity vector accordingly, the ball stop to move, even if the gravity applied on at leat one of the 3D axis is strong.

Any idea why it doesn't work and how to make it work?

EDIT: Actually, the problem seems to be that the physical world stop to work at the first collision (my ball is actually in a 3D maze and with accelerometer activated, it is so close to a wall at start that most of the time it touch it "immediately" whereas when I fix a default (0,-1,0) gravity vector, the ball fall until it touch a vertical wall)

ramsestom commented 7 years ago

I made a simple example to show the issue:

package;

import com.babylonhx.cameras.FreeCamera;
import com.babylonhx.lights.DirectionalLight;
import com.babylonhx.lights.HemisphericLight;
import com.babylonhx.lights.PointLight;
import com.babylonhx.materials.MultiMaterial;
import com.babylonhx.materials.PBRMaterial;
import com.babylonhx.materials.textures.RawTexture;
import com.babylonhx.math.Color3;
import com.babylonhx.mesh.MeshBuilder;
import com.babylonhx.mesh.SubMesh;
import com.babylonhx.mesh.VertexBuffer;
import com.babylonhx.mesh.primitives.Box;
import com.babylonhx.physics.PhysicsBodyCreationOptions;
import com.babylonhx.physics.plugins.OimoPlugin;
import com.babylonhx.tools.EventState;
import com.babylonhx.tools.Tools;
import com.babylonhx.utils.Image;
import com.babylonhx.utils.typedarray.UInt8Array;
import com.babylonhx.cameras.ArcRotateCamera;
import com.babylonhx.Engine;
import com.babylonhx.materials.Material;
import com.babylonhx.materials.StandardMaterial;
import com.babylonhx.materials.textures.Texture;
import com.babylonhx.math.Vector3;
import com.babylonhx.math.Space;
import com.babylonhx.mesh.Mesh;
import com.babylonhx.Scene; 
import openfl.Assets;
import openfl.display.BitmapData;
import openfl.display.Sprite;
import openfl.events.Event;
import openfl.events.MouseEvent;
import openfl.geom.Point;
import openfl.geom.Vector3D;
import openfl.geom.Matrix3D;
import openfl.Lib;
import openfl.Vector;
import com.babylonhx.physics.PhysicsEngine;

class GravityTest extends Sprite {

    private var scene:Scene;
    private var engine:Engine;

    private var mouse_clicked_start_time:Int;
    private var mouse_clicked_start_pos:Point;

    public function new() 
    {
        super();

        stage.addChild(this);

        engine = new Engine(this, false);   
        scene = new Scene(engine);

        engine.width = stage.stageWidth;
        engine.height = stage.stageHeight;

        scene.enablePhysics(new Vector3(0, -1, 0), new OimoPlugin());
        trace("Scene Gravity: " + scene.gravity);
        trace("Physics Gravity: " + scene.getPhysicsEngine().gravity);

        //Create camera and light
        var camera = new ArcRotateCamera("Camera", 0, 0, 0, Vector3.Zero(), scene);
        camera.setPosition(new Vector3(0, 0, -300*2.5));
        camera.attachControl();

        var light = new PointLight("Omni", new Vector3( 0, 0, -300 * 2.5), scene);

        //create a sphere with some mass
        var ball:Mesh = Mesh.CreateSphere("ball", 20, 50, scene);

        var ball_material = new StandardMaterial("ballmat", scene);
        ball_material.diffuseColor=new Color3(1,0,0);
        ball.material = ball_material;

        ball.position = new Vector3(50,150,0);

        var bphysOpt = new PhysicsBodyCreationOptions();
        bphysOpt.mass = 1;
        bphysOpt.friction = 0.5;
        bphysOpt.restitution = 0.5;
        ball.setPhysicsState(PhysicsEngine.SphereImpostor, bphysOpt);

        //create a box obstacle
        var cube:Mesh = Mesh.CreateBox("cube", 1, scene, false, Mesh.DOUBLESIDE);
        cube.scaling = new Vector3(250,10,50);
        cube.showBoundingBox=false;
        var cm=new StandardMaterial("material0",scene);
        cm.diffuseColor=new Color3(0,1,0);
        cube.material = cm;

        cube.position = new Vector3(0,0,-20);

        var cphysOpt = new PhysicsBodyCreationOptions();
        cphysOpt.mass = 0;
        cphysOpt.friction = 0.5;
        cphysOpt.restitution = 0.5;
        cube.setPhysicsState(PhysicsEngine.BoxImpostor, cphysOpt);

        stage.addEventListener(Event.RESIZE, resize);
        stage.addEventListener(Event.ENTER_FRAME, update);
        stage.addEventListener (MouseEvent.MOUSE_DOWN, onMouseDown);
        stage.addEventListener (MouseEvent.MOUSE_UP, onMouseUp);

        scene.registerBeforeRender(function(scene:Scene, ?es:EventState) {
            light.position = camera.position;
        });

        scene.getEngine().runRenderLoop(function () {
            scene.render();
        });

        enableOpenFL2D();
    }

    private function enableOpenFL2D():Void {
        var openflCameraMask:Int = 0xF0E1D2;
        var mainCamera = scene.activeCamera;
        if (scene.activeCameras.indexOf(mainCamera) == -1) {
            scene.activeCameras.push(mainCamera);
        }
        var openflCamera = new FreeCamera("openfl_nme_dummycamera", new Vector3(Math.POSITIVE_INFINITY, Math.POSITIVE_INFINITY, Math.POSITIVE_INFINITY), scene);
        openflCamera.fov = 0;
        openflCamera.layerMask = openflCameraMask; 
        var openflDummyMesh = Mesh.CreatePlane("openfl_nme_dummymesh", 0.1, scene);
        var openflDummyMaterial = new StandardMaterial("openfl_nme_DummyMaterial", scene);
        openflDummyMaterial.backFaceCulling = false;
        openflDummyMesh.material = openflDummyMaterial;
        openflDummyMesh.layerMask = openflCameraMask;
        scene.activeCameras.push(openflCamera);
    }

    private function onMouseDown(event:MouseEvent):Void 
    {

        mouse_clicked_start_pos = new Point(event.stageX, event.stageY);
        mouse_clicked_start_time = Lib.getTimer();
    }

    private function onMouseUp(event:MouseEvent):Void 
    {
        if (mouse_clicked_start_time != 0)
        {
            var deltatime:Int = Lib.getTimer() -  mouse_clicked_start_time;
            if (deltatime > 0 && deltatime < 1000)
            {
                var deltaX:Float = mouse_clicked_start_pos.x - event.stageX;
                var deltaY:Float = mouse_clicked_start_pos.y - event.stageY;

                if (Math.abs(deltaX) > Math.abs(deltaY))
                {
                    if (Math.abs(deltaX)>(Lib.current.stage.stageWidth*0.1)) 
                    {
                        if (deltaX > 0) {scene.getPhysicsEngine()._setGravity(new Vector3(1, 0, 0));}
                        else {scene.getPhysicsEngine()._setGravity(new Vector3(-1, 0, 0)); }
                    }   
                }
                else 
                {
                    if (Math.abs(deltaY)>(Lib.current.stage.stageHeight*0.1)) 
                    {
                        if (deltaY > 0) {scene.getPhysicsEngine()._setGravity(new Vector3(0, -1, 0));}
                        else {scene.getPhysicsEngine()._setGravity(new Vector3(0, 1, 0)); }
                    }   
                }
            }
        }
        mouse_clicked_start_time = 0;

    }

    function resize(e) {
        engine.width = stage.stageWidth;
        engine.height = stage.stageHeight;
    }

    function update(e) {
        engine._renderLoop();
    }

}

To allow to test it on any platform, I replaced the gravity change on accelerometer change by a gravity change on mouse swipe. So to test it, let the red ball fall on the green box. Then swipe horizontally left or right to apply an horizontal gravity. Normally, the ball should follow this gravity and roll on the left or the right. But it do not work. If you swipe left or right BEFORE the red ball collide with the green box, then the ball is correctly affected by the horizontal gravity.

So there is clearly a problem. The collision between the ball and the box makes the world physics to stop....

vujadin commented 7 years ago

I've tried your example and for me gravity change works until the ball stops (falls on box). My guess is that the problem is in ball goes to 'sleep' (most physics engines that I know of have this feature to save processing time by excluding non-active object from computation). But this is just a guess. You might try to set objects sleeping property to false to wake it up (or whatever the api is - if it exists :) or something like that...

ramsestom commented 7 years ago

Preventing the ball to sleep fixed the problem. Thanks. didn't knew objects where going to sleep after the first collision in babylon by default...

vujadin commented 7 years ago

this has nothing to do with babylon but with oimo physics engine. its up to physics engine to decide when object goes to sleep. babylon simply renders the object based on info it gets from oimo

vujadin commented 7 years ago

I guess you can turn off this feature completely in oimo or at least mark objects you don't want to never go to sleep. this is standard feature supported by a lot of physics engines, both 2D and 3D