Jump to content

Neo.js - adding some functionality for easier entry


neoRiley
 Share

Recommended Posts

Neo is a class that I've been maintaining to make it easier to deal with Babylon and to add some functionality that other api's have natively.  I'd love to see these considered for babylon, but done "right" or with best practices in mind for performance with babylon.  I'm not even sure I'm handling somethings correctly with Translate/Rotate, so any changes you guys have would be welcomed for sure.  I know I could fork the repo and do a pull request, but I think this is something that needs to be discussed as an approach.  Even though I like this approach and I've actually helped write a 3D api that was very successful, it ultimately is up to Babylon's developers and I really think a discussion is probably needed and I want to be sure to treat the babylon guys with respect and help out where I can in the right manner.

 

That being said, here's what Neo currently adds to BABYLON.Mesh:

  • Translate - pass an axis, distance and Neo.Space.WORLD or Neo.Space.LOCAL to specify local/world coordinate space.
  • Rotate - like Translate, pass the axis, amount (degrees) and coordinate space.
  • DrawAxis - draws a single axis handle
  • DrawAllAxis - draws all 3 axis handles relative to the mesh

Neo also provides simple conversion proprties from degrees > radians and back - it's simple, but its a calculation that doesn't need to be repeated especially in a loop

  • toDEGREES - usage: .78*NEO.toDEGREES; //45 degrees
  • toRADIANS - usage: 45*NEO.toRADIANS; //.78 radians

The class:

var NEO = { REVISION: "0.0.1" };// JavaScript DocumentNEO.Space = Object.freeze({    LOCAL:0,    WORLD:1});NEO.Axis = Object.freeze({    X : new BABYLON.Vector3(1,0,0),    Y : new BABYLON.Vector3(0,1,0),    Z : new BABYLON.Vector3(0,0,1)});/*** Returns a random number between min and max*/NEO.GetRandomInRange = function(min, max){return Math.random() * (max - min) + min;};NEO.toDEGREES = 180/Math.PI;NEO.toRADIANS = Math.PI/180;NEO.JackIntoBabylon = function(){        BABYLON.Mesh.prototype.Translate = function(axis, distance, space)    {        if( space == NEO.Space.LOCAL )        {                var tempV3 = this.getPositionExpressedInLocalSpace().add(axis.scale(distance));            this.setPositionWithLocalVector(tempV3);        }        else        {            this.computeWorldMatrix(true);// without this, the final call in a frame overwrites any other calls to translate            this.setAbsolutePosition(this.getAbsolutePosition().add(axis.scale(distance)));        }    };        BABYLON.Mesh.prototype.Rotate = function(axis, amount, space)    {        var tempV3 = axis.scale(NEO.toRADIANS*amount);        if( space == NEO.Space.LOCAL )        {            this.rotation = this.rotation.add(tempV3);        }        else        {            var rotationToApply = BABYLON.Quaternion.RotationYawPitchRoll(tempV3.y, tempV3.x, tempV3.z);            if( this.rotationQuaternion == null ) this.rotationQuaternion = new BABYLON.Quaternion(0,0,0,1);            this.rotationQuaternion = rotationToApply.multiply(this.rotationQuaternion);        }    };        BABYLON.Mesh.prototype.DrawAllAxis = function(scene, length, headLength, headWidth )    {        // this creates all three axis arrows and adds it to the mesh for th user                if( length === undefined ) length = 5;        if( headLength === undefined ) headLength = 2;        if( headWidth === undefined ) headWidth = 1;        this.DrawAxis(scene, new BABYLON.Vector3(1,0,0), length, new BABYLON.Color3(1,0,0), headLength, headWidth);        this.DrawAxis(scene, new BABYLON.Vector3(0,1,0), length, new BABYLON.Color3(0,1,0), headLength, headWidth);        this.DrawAxis(scene, new BABYLON.Vector3(0,0,1), length, new BABYLON.Color3(0,0,1), headLength, headWidth);    };        BABYLON.Mesh.prototype.DrawAxis = function(scene, dir, length, color, headLength, headWidth)    {        // create an arrow out of cube's and cylinders that faces in the direction specified in world space        // then the user is free to attach to their mesh                if( length === undefined ) length = 5;        if( headLength === undefined ) headLength = 2;        if( headWidth === undefined ) headWidth = 1;        if( color === undefined ) color = new BABYLON.Color3(1,1,0); //yellow                // create the arrow container for the line and head        var arrow = new BABYLON.Mesh("axisArrow", scene);        arrow.isVisible = false;                // create the line        var line = BABYLON.Mesh.CreateBox("arrowLine", 1, scene, true);        var mat = new BABYLON.StandardMaterial("arrowColor", scene);        mat.diffuseColor = color;        mat.ambientColor = color;        mat.emissiveColor = color;        line.material = mat;        line.scaling = new BABYLON.Vector3(.1,.1, length);                // create the head        var head = BABYLON.Mesh.CreateCylinder("arrowHead", headLength, 0, headWidth, 4, scene, true);        head.material = mat;                // set the head in position and rotation        head.Rotate(NEO.Axis.X, -90, NEO.Space.WORLD);        head.Translate(NEO.Axis.Z, ((length*.5) + (headLength*.5)), NEO.Space.WORLD);                // parent the line and head to the arrow, then arrow to the mesh        line.parent = arrow        head.parent = arrow;                arrow.parent = this;                // reset to zero locally after being parented so that we're facing straight ahead on z        arrow.rotation = BABYLON.Vector3.Zero();                        // deal with rotation locally        if( NEO.Axis.X.equals(dir) )        {                arrow.Translate(NEO.Axis.X, length*.5, NEO.Space.LOCAL);            arrow.Rotate(NEO.Axis.Y, 90, NEO.Space.LOCAL);                    }        else if( NEO.Axis.Y.equals(dir) )        {            arrow.Translate(NEO.Axis.Y, length*.5, NEO.Space.LOCAL);            arrow.Rotate(NEO.Axis.X, -90, NEO.Space.LOCAL);                    }        else if( NEO.Axis.Z.equals(dir) )        {            arrow.Translate(NEO.Axis.Z, length*.5, NEO.Space.LOCAL);        }    };}

Usage:  include the Neo.js file in your project, then call:

Neo.JackIntoBabylon(); 

The Translate/Rotate methods are still being worked on and I'm probably going to add other methods, but for now, this works very well and mirrors other api's that I've used and rotations work in degrees and convert to radians:

mesh.Translate(NEO.Axis.Z, length*.5, NEO.Space.LOCAL);mesh.Rotate(NEO.Axis.Y, 90, NEO.Space.LOCAL);    

And if you're not used to using radians (like most lower level users of 3D api's are) but you're having to provide radians to deal with a babylon method, you can simply use NEO.toRADIANS to convert from your easy to read degrees to radians.  Personally, I'd love to see if babylon could consider having a boolean flag on the engine that allows someone to choose using radians or degrees. This is something we did in Papervision and it went over fairly well with the developers:

this.sunLight = new BABYLON.DirectionalLight("Sun", new BABYLON.Vector3(-22*NEO.toRADIANS,-90*NEO.toRADIANS, 0), this.scene);

The DrawAxis/DrawAllAxis is very easy to use - all you have to do is provide the scene in the arguments for DrawAllAxis.  For debugging, its obviously essential and easy to use.   

// draw all 3 axis handles for a meshthis.mesh = BABYLON.Mesh.CreateSphere("entryPoint", 10, 1.0,scene);this.mesh.DrawAllAxis(scene);// draw a single axis handle for a meshthis.mesh.DrawAxis(scene, NEO.Axis.Z);

2014-01-31_12-19-10.png

 

Side note:  I was the first contributor to the Papervision3D (Flash 3D api back in the day) and did a lot of work on the forward facing/public api to make it not only friendly, but to work with the nomenclature of AS3 and flash developers coming in for the first time to use a 3D api - I have a lot of experience with this.  We *could* assume a user understands programing in 3D space (api), but *had* to assume they did not understand programming 3D space (the engine), and that's what made PV3D so easy to use and get into in my opinion.

 

Anyway, I hope this helps others who are struggling with getting into Babylon and I hope we can get some of these things added to babylon and implemented the way babylon would do it.  The developers have been super nice about questions, answers and requests, and that's a huge credit to this api

 

Thanks guys,

 

John

Neo.js.zip

Link to comment
Share on other sites

Thanks very much Alvin!

 

I have an update to Rotate, that fixes world rotation:

BABYLON.Mesh.prototype.Rotate = function(axis, amount, space)	{		var tempV3 = axis.scale(NEO.toRADIANS*amount);		if( space == NEO.Space.LOCAL )		{			this.rotation = this.rotation.add(tempV3);		}		else		{			var rotationToApply = BABYLON.Quaternion.RotationYawPitchRoll(tempV3.y, tempV3.x, tempV3.z);			if( this.rotationQuaternion == null ) this.rotationQuaternion = new BABYLON.Quaternion(0,0,0,1);			this.rotationQuaternion = this.rotationQuaternion.multiply(rotationToApply);		}	};

There's still an issue with setting local rotation and I'm waiting for an answer on how to properly set that.  I'll add that as soon as it's available, but for now, I've attached the latest version.  I'll have to start versioning the file name ;)

 

NOTE:  If you set WORLD rotation, local rotation calls will not work.  This is due to the fact that Mesh.rotation is abandoned if Mesh.rotationQuaternion is not null.  When you set WORLD rotation on a Mesh, rotationQuaternion is used and there's the issue.  

Neo.js.zip

Link to comment
Share on other sites

Definitely would love to have this added to babylon as that was my intention from the beginning - thanks for allowing me to contribute!

 

I would love to have help Deltakosh - you mention disposing and since I don't know the Mesh class (or engine) that well, it'd probably be prudent to have you integrate.  The helper enums/consts were definitely meant to be changed over to a babylone specific signature, I just wasn't sure how you prefer to implement such things. 

 

The one thing I want to get resolved before integration, however, is the rotation issue - right now, I need help setting rotationQuaternion in LOCAL space.  Right now, as I was saying earlier, if someone starts out using Mesh.rotation, and then sets WORLD rotation, they cannot return to using Mesh.rotation.  So, we *have* to provide code to setting local rotation with quaternions before integrating Neo's logic into babylon.

Link to comment
Share on other sites

But this is working no?

var rotationToApply = BABYLON.Quaternion.RotationYawPitchRoll(tempV3.y, tempV3.x, tempV3.z);            if( this.rotationQuaternion == null ) this.rotationQuaternion = new BABYLON.Quaternion(0,0,0,1);            this.rotationQuaternion = this.rotationQuaternion.multiply(rotationToApply);
Link to comment
Share on other sites

Ok here's the latest, and please note that babylon has been updated as well thanks to David!  We've been working hard on getting things ironed out as far having dedicated properties represent local and world space exclusively for euler and quaternion rotation.

 

New to babylon's api:

 

Mesh.rotation = local euler rotation

Mesh.worldRotation = world euler rotation

Mesh.rotationQuaternion = local quaternion rotation

Mesh.worldRotationQuaternion = world quaternion rotation

 

Please note: we are still looking at some sync issues between local and world eulers.  I'll be posting a question after this to see if someone has time to look at why.

 

Neo has been updated on how it handles eulers and quats with degree's being passed:

 

If you want to rotate using Eulers, use RotateEulers():

// degrees are converted to radiansRotateEulers(axis, amount, space) // axis: Vector3, amount:degrees, NEO.Space.WORLD/LOCAL

If you want to rotate using quaternions, use Rotate()

// degrees are converted to radiansRotate(axis, amount, space) // axis: Vector3, amount:degrees, NEO.Space.WORLD/LOCAL

If you want to set Euler rotation to a specific rotation - ie: setting a mesh's rotation to another's:

SetEulerRotation(vector3, space); // vector3:radians, NEO.Space.WORLD/LOCAL// usage:container.SetEulerRotation(this.entryPoint.rotation, NEO.Space.WORLD); // since Mesh.rotation is a Vector3 expressed in radians

So far, this has worked in dealing with parented meshes translating and rotating locally and the question above of how to set WORLD quaternion values has been dealt with ;)

 

The latest Neo.js is attached

 

I would like to add methods for dealing in radians without the degree conversion, I'm just trying to be careful about the nomenclature and what will be added to Babylon later.

Neo.js.zip

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.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...