splash27

Wind Implementation

Recommended Posts

Hi, everyone!

I want to share my wind implementation in BabylonJS with explanations.

It may to seem obvious. Wind is just an adding the vector to objects position. But if we'll go this way, then result will be boring. We'll just get sliding objects. Our goal is to perform the storm with bouncing and collisions.

First of all we need to enable collisions for all meshes and camera. We' ll also configure gravity here.
 

scene.gravity = new BABYLON.Vector3(0, -0.9, 0);

scene.collisionsEnabled = true;

camera.checkCollisions = true;
camera.applyGravity = true;
camera._needMoveForGravity = true;

ground.checkCollisions = true;
box.checkCollisions = true;
c1.checkCollisions = true;
c2.checkCollisions = true;
c3.checkCollisions = true;

Then we need to implement a kind of objects pushing. When camera can pushes objects. I decided to mark meshes that can be pushed by camera with special property "pushable". Here is implementation.

var physicsPlugin = new BABYLON.CannonJSPlugin();
scene.enablePhysics();

ground.physicsImpostor = new BABYLON.PhysicsImpostor(ground, BABYLON.PhysicsImpostor.BoxImpostor, { mass: 0, restitution: 0.9 }, scene);
box.physicsImpostor = new BABYLON.PhysicsImpostor(box, BABYLON.PhysicsImpostor.BoxImpostor, { mass: 10, restitution: 0.1 }, scene);
c1.physicsImpostor = new BABYLON.PhysicsImpostor(c1, BABYLON.PhysicsImpostor.BoxImpostor, { mass: 20, restitution: 0.1 }, scene);
c2.physicsImpostor = new BABYLON.PhysicsImpostor(c2, BABYLON.PhysicsImpostor.BoxImpostor, { mass: 20, restitution: 0.1 }, scene);
c3.physicsImpostor = new BABYLON.PhysicsImpostor(c3, BABYLON.PhysicsImpostor.BoxImpostor, { mass: 150, restitution: 0.1 }, scene);

//handling when camera pushes other meshes
var playerImpulse = 30; //we can use fixed value, because player mass and velocity is constant in most cases

camera.onCollide = function (mesh) {
    if (mesh.pushable) {
        var givenVelocity = playerImpulse / mesh.physicsImpostor.mass;
        var camPosition = camera.position.clone();
        camPosition.y -= camera.ellipsoid.y * 0.80; //kick height
        var movementDirectionVector = mesh.getAbsolutePosition().subtract(camPosition);                
        mesh.physicsImpostor.setLinearVelocity(movementDirectionVector.scale(givenVelocity));
    }
};

Ok, when we "touch" a mesh we detect if it's pushable, then calculate direction using camera and mesh positions. Thanks @Wingnut for tips with this part. Also, I correct direction at y property. It needed to make things look like the player applies force using his leg. So I correct the height at which the force will be applied. I don't use addImpulse method, because CannonJS accumulate impulses and we can boost meshes to incredible velocities by several touches. It's better to set the final velocities.

And now the wind. We need some things to keep in mind to procced.

  • Wind should preserve current meshes velocities. It should not to override, for example, a gravity effect.
  • Wind should be applied to camera also for best user's experience. But we know that camera has now physicsImpostor.
  • We must provide safe places, where we can hide from the wind, for example, in a cave or behind the building. Should work for meshes and camera.

And now the implementation goes.

    var wb1 = BABYLON.MeshBuilder.CreateBox("wb1", {width: 8, height: 8, depth: 8}, scene);
    var wbMat = new BABYLON.StandardMaterial("wbmat", scene);
    wbMat.wireframe = true;
    wb1.position = new BABYLON.Vector3(55, -6, 2);
    wb1.material = wbMat;

    var wb2 = BABYLON.MeshBuilder.CreateBox("wb2", {width: 8, height: 8, depth: 8}, scene);
    wb2.position = new BABYLON.Vector3(40, -6, 20);
    wb2.material = wbMat;

    function pointIntersectsAny(volumes, point) {
        for (var k = volumes.length - 1; k >= 0; k--) {
            if (volumes[k].intersectsPoint(point)) {
                return true;
            }
        }
        return false;
    }

    function meshIntersectsAny(volumes, mesh) {
        for (var k = volumes.length - 1; k >= 0; k--) {
            if (volumes[k].intersectsMesh(mesh, false)) {
                return true;
            }
        }
        return false;
    }

    //wind processor, should be called from gaming loop
    function processWind (scene, camera, vector, meshWindScale, windBlocks) {
        var scale = meshWindScale || 1;
        var windBlocks = windBlocks || [];

        if (!pointIntersectsAny(windBlocks, camera.position)){
            camera.position = camera.position.add(vector);
        }

        var meshes = scene.meshes;
        for (var i = meshes.length - 1; i>=0; i--) {
            if (meshes[i].pushable && meshes[i].physicsImpostor) {
                if (!meshIntersectsAny(windBlocks, meshes[i])) {
                    meshes[i].physicsImpostor.setLinearVelocity(meshes[i].physicsImpostor.getLinearVelocity().add(vector.scale(scale)));
                }
            }
        }
    }

Ok, wb1 and wb2 are safe volumes where is no wind at any conditions. I use a wireframe to display, later these meshes can be set as invisible. pointIntersectsAny and meshIntersectsAny are helpers and pretty self-explanatory.

The main action is inside processWind function. We handle the camera and meshes separately. Actually, we don't see the player outside of the camera, so, we can manipulate it's position directly. What concerns the meshes, we should apply linearVelocity to them to make CannonJS to handle their movements in physical manner. Look how we preserved the initial linearVelocity using getLinearVelocity. Of cource the wind can't move the heavy objects if wind's strength is not big enough.

If a camera or meshes are inside the safe volume then no wind will be applied to it.

I also added an ability to scale the wind applied to meshes. It could be useful for some effects. For example, when player has a special boots with high friction value. So the player can resist the wind.

Also important to say, that wind could be applied only to "pushable" meshes that have physicsImpostors.

And all we need is just launch whe wind.

var windAllowed = false;

//press "f" to launch the wind
document.addEventListener("keydown", function (e) {
    if (e.keyCode == 70) {
        windAllowed = true;
    }
});

scene.registerBeforeRender(function () {
    if (windAllowed) {
        processWind(scene, scene.cameras[0], new BABYLON.Vector3(0.1, 0, 0.1), 2, [wb1, wb2]);
    }
});

Here is playground: http://www.babylonjs-playground.com/#3CPL8T#1

I also implemented jumping, croaching and free mouse-look just for fun. =)

Yeah, there are still some issues. For example, camera affected by the wind can flow through objects. It could be fixed by checking onCollide event.

Another issue, this wind acts like constant force. It looks like spaceship depressurization than like actual wind. But you can try to add flurries using f(t) = |six(t)| function or something else.

Feel free to tune my approach for your needs or share your implementations.

If you want to use this in your project, the credits will be much appreciated but it's not required.

 

 

Share this post


Link to post
Share on other sites

Too cool, Splash!   'C' for crouch, space for jump, 'F' to Flow the winds.  WASD and arrow keys active.  Don't get 'blown away'.  :)

Fun idea/project! 

Tornadoes, next?  Hard, huh?  A vortex of invisible circular intersection zones, and the direction of impulses is dependent-upon which "ring of impulsing influence" that a mesh's absolutePosition is located-within (and where within the ring, degrees/radially, too). 

Wow!  My brain hurts just thinking about it.  :)  It makes me ponder... self-impulsing smart-mesh, which monitor their 'world' and always know how to pulse themselves.  hmm.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.