neoRiley Posted January 31, 2014 Share Posted January 31, 2014 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 handleDrawAllAxis - draws all 3 axis handles relative to the meshNeo 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 looptoDEGREES - usage: .78*NEO.toDEGREES; //45 degreestoRADIANS - usage: 45*NEO.toRADIANS; //.78 radiansThe 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); 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, JohnNeo.js.zip Alvin and Dad72 2 Quote Link to comment Share on other sites More sharing options...
Alvin Posted January 31, 2014 Share Posted January 31, 2014 Great idea, and your project looks really nice Quote Link to comment Share on other sites More sharing options...
neoRiley Posted February 2, 2014 Author Share Posted February 2, 2014 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 Quote Link to comment Share on other sites More sharing options...
GameMonetize Posted February 2, 2014 Share Posted February 2, 2014 Hello neo, do you mind contributing these features to babylon.js ? Quote Link to comment Share on other sites More sharing options...
GameMonetize Posted February 2, 2014 Share Posted February 2, 2014 For DrawAxis, I suggest creating a new BABYLON.Axis object that can be attached (or removed) to a mesh (just for the sake of objects tracking ) Quote Link to comment Share on other sites More sharing options...
GameMonetize Posted February 2, 2014 Share Posted February 2, 2014 (I can do the integration if you wish, I do not want to bother you)Or perhaps we couldjust have a mesh.showAxis(true) which can create the axis or remove it. In this way the mesh.dispose could also dispose the axis Quote Link to comment Share on other sites More sharing options...
Dad72 Posted February 2, 2014 Share Posted February 2, 2014 It would be indeed well to integrate it in Babylon. Quote Link to comment Share on other sites More sharing options...
neoRiley Posted February 2, 2014 Author Share Posted February 2, 2014 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. Quote Link to comment Share on other sites More sharing options...
GameMonetize Posted February 2, 2014 Share Posted February 2, 2014 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); Quote Link to comment Share on other sites More sharing options...
neoRiley Posted February 4, 2014 Author Share Posted February 4, 2014 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 rotationMesh.worldRotation = world euler rotationMesh.rotationQuaternion = local quaternion rotationMesh.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/LOCALIf you want to rotate using quaternions, use Rotate()// degrees are converted to radiansRotate(axis, amount, space) // axis: Vector3, amount:degrees, NEO.Space.WORLD/LOCALIf 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 radiansSo 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 Dad72 1 Quote Link to comment Share on other sites More sharing options...
Dad72 Posted February 4, 2014 Share Posted February 4, 2014 Thank you for this job neoRiley 1 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.