ProfessorF Posted January 8, 2014 Author Share Posted January 8, 2014 Two quick questions. If I want to use a transformation matrix on a mesh (instead of using the mesh members: position, translation, scale), 1) After I create the transformation matrix (ex: mat) do I use mesh.setPivotMatrix(mat)?2) Matrix.m is a single-dimensional array. Is the order 0=m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, 15=m44? Thanks in advance Quote Link to comment Share on other sites More sharing options...
GameMonetize Posted January 9, 2014 Share Posted January 9, 2014 1. Yes it should work if your mesh has no parent2. Correct Quote Link to comment Share on other sites More sharing options...
gwenael Posted January 9, 2014 Share Posted January 9, 2014 1. Just also be sure that position = (0,0,0), rotation = (0,0,0) and scaling = (1,1,1) otherwise the corresponding matrix (equals mesh._worldMatrix if the mesh doesn't have a parent) will be different than Identity and will be "added" to your pivot matrix to get the world matrix. 2. Be aware in BabylonJS, like in the XNA framework, matrices use a row vector layout. Whereas you do A * B when matrices use a column vector layout (what we use in France in mathematics), you have to do B * A when they use a row vector layout. That's why in BabylonJS B * A = B.multiply(A) (A * B for french maths). For example, wolrdMatrix = localWorld.multiply(parent.worldMatrix) (wm = lw * pwm whereas in french maths it would be wm = pwm * lw) ProfessorF 1 Quote Link to comment Share on other sites More sharing options...
ProfessorF Posted January 9, 2014 Author Share Posted January 9, 2014 Thanks Gwenael. I did a matrix version of Mesh.LookAt and I wasted a day because I was thinking in terms of column layout! Quote Link to comment Share on other sites More sharing options...
ProfessorF Posted January 10, 2014 Author Share Posted January 10, 2014 Sorry for the delay in implementing this, I want it to be fast! I have two algorithms and any feedback would be appreciated. I'm actually leaning towards the slow version because it's less complex, and "plays nicely" with the other mesh variables. The fast version sets the pivot matrix and is all vector math. Anyway, here they are. 1. Algorithm one is based on Euler angles and works flawlessly for static and skinned models BUT is slow because it uses trig functions. // This Euler version works flawlessly. Haven't made into a function yet, so// Globals: looker mesh, target mesh, cor-rection for rotationvar dv = target.position.subtract(looker.position);var yaw = -Math.atan2(dv.z, dv.x)-cor; // -because rots are oppositevar len = Math.sqrt(dv.x * dv.x + dv.z * dv.z);var roll = Math.atan2(dv.y, len); //-any corrections if mesh is vertical TBI, similar to Vector.Uplooker.rotationQuaternion = BABYLON.Quaternion.RotationYawPitchRoll(yaw, 0, roll);2. Algorithm two is vector based (transforming the basis vectors, similar to GLULookAt in OpenGL), is fast, works as expected with static models, but is wonky when it comes to skinned models. "Wonky" means it works, but not in a way I expect, which bothers me. I'm pretty sure the problem is that I need to transform the skeleton as well. Also, since I am using the pivot matrix, I need to store and reset: position, rotation, and scale before using this code.var v3up = new BABYLON.Vector3(0, 1, 0);var v3look = new BABYLON.Vector3(lpx, lpy, lpz); // lpx,lpy,lpz=looker.position, can't use .position because using pivotMatrixvar v3newz = target.position.subtract(v3look);v3newz.normalize();var v3newx = BABYLON.Vector3.Cross(v3newz, v3up);v3newx.normalize();var v3newy = BABYLON.Vector3.Cross(v3newx, v3newz);v3newy.normalize();var mat = BABYLON.Matrix.Identity();mat.m = [ v3newx.x, v3newx.y, v3newx.z, 0, v3newy.x, v3newy.y, v3newy.z, 0, -v3newz.x, -v3newz.y, -v3newz.z, 0, 0, 0, 0, 1];looker.setPivotMatrix(BABYLON.Matrix.RotationY(cor).multiply(mat).multiply(BABYLON.Matrix.Scaling(scl,scl,scl).multiply(BABYLON.Matrix.Translation(lpx,lpy,lpz)))); // works, but have to fold back in position, rotation, and scale Quote Link to comment Share on other sites More sharing options...
GameMonetize Posted January 10, 2014 Share Posted January 10, 2014 I clearly prefer the first one because it does not need a trick(do not touching position, scaling and rotation for the 2nd one) ProfessorF 1 Quote Link to comment Share on other sites More sharing options...
gwenael Posted January 11, 2014 Share Posted January 11, 2014 1. Just also be sure that position = (0,0,0), rotation = (0,0,0) and scaling = (1,1,1) otherwise the corresponding matrix (equals mesh._worldMatrix if the mesh doesn't have a parent) will be different than Identity and will be "added" to your pivot matrix to get the world matrix. To clearify my previous post (please Deltakosh, correct me if I'm wrong): position, rotation and scaling are actually the vectors (expressed in the parent space of the mesh) used to compute the matrix which represents the pivot space of the mesh (you multiply by this matrix to pass from the pivot space to the parent space) and pivotMatrix is the matrix which expresses the mesh in this pivot space. In 2D + + M + + + + ++ + ++ + ++ + + +PO O: origin of the parent space (or world if no parent)P: pivot of the mesh: mesh.position = (3,0,0), mesh.rotation = (0,0,PI/4), mesh.scaling = (1.3, 1.3, 1.3)M: set by pivotMatrix: (1/1.3,a,b,c, d,1/1.3,e,f, g,h,1/1.3,0, 1.25,0,0,1) where a,b,c,d,e,f,g,h are the coefficients for a z rotation of -PI/4 Quote Link to comment Share on other sites More sharing options...
gwenael Posted January 14, 2014 Share Posted January 14, 2014 Other maths that could help: http://www.html5gamedevs.com/topic/3083-spaces-world-parent-pivot-local/?p=19938 Quote Link to comment Share on other sites More sharing options...
neoRiley Posted January 18, 2014 Share Posted January 18, 2014 Any idea when LookAt will be officially added/released for us to use? Thanks for all your help, John Quote Link to comment Share on other sites More sharing options...
ProfessorF Posted January 18, 2014 Author Share Posted January 18, 2014 I actually finished the code a while ago, I just haven't finished the documentation. The documentation is necessary because, unlike camera.lookAt, a mesh.lookAt has to take into account the different ways people can model meshes. For example: if someone models a spaceship or character pointing to the right (>), then mesh.lookAt(point) works fine, because zero degrees is defined relative to a vector pointing to the right. But if someone models a spaceship or character pointing towards the screen (V), then you need to add a yaw correction: mesh.lookAt(point, -90 degrees) And to make matters even more complex, you can correct for pitch and roll as well. Anyway, I feel that if I don't provide the documentation and a working sample, then DeltaKosh and team will be deluged with bug reports. Once I'm finished with the documentation/sample code, I will push and issue a merge request. Quote Link to comment Share on other sites More sharing options...
neoRiley Posted January 19, 2014 Share Posted January 19, 2014 Just as a test, I went ahead and implemented your first code for LookAt with a custom Atan2 method. It works well and I wonder if what the performance difference would be with that custom atan2 method. I haven't done any tests with it, but it does work and thought I'd throw it out there: Atan2 method:NEO.Coeff_1 = Math.PI / 4.0;NEO.Coeff_2 = 3.0 * (Math.PI / 4.0);NEO.atan2 = function(y, x) { var abs_y = y < 0 ? y*-1 : y; // abs sub var angle; if (x >= 0) { var r = (x - abs_y) / (x + abs_y); angle = NEO.Coeff_1 - NEO.Coeff_1 * r; } else { var r = (x + abs_y) / (abs_y - x); angle = NEO.Coeff_2 - NEO.Coeff_1 * r; } return y < 0 ? -angle : angle;}and here's the method implemented:BABYLON.Mesh.prototype.LookAt = function (target, adjustment){ adjustment = adjustment == null ? 0 : adjustment; var dv = target.getAbsolutePosition().subtract(this.position); var yaw = -NEO.atan2(dv.z, dv.x) - adjustment; // -because rots are opposite var len = Math.sqrt(dv.x * dv.x + dv.z * dv.z); var roll = NEO.atan2(dv.y, len); //-any corrections if mesh is vertical TBI, similar to Vector.Up this.rotationQuaternion = BABYLON.Quaternion.RotationYawPitchRoll(yaw, 0, roll);};sample call:box.LookAt(moon);Check it out:http://www.rockonflash.com/webGL/babylon/demo/index.html ProfessorF and Dad72 2 Quote Link to comment Share on other sites More sharing options...
gwenael Posted January 19, 2014 Share Posted January 19, 2014 The following line implies that your mesh doesn't have a parent.var dv = target.getAbsolutePosition().subtract(this.position); Quote Link to comment Share on other sites More sharing options...
ProfessorF Posted January 19, 2014 Author Share Posted January 19, 2014 Okay, this is what I submitted a pull request for. As Gwenael, pointed out to me in e-mail, this assumes that the targetPoint is in the same space as the Mesh.Typical usage would be mesh..lookAt(targetPoint), But I added optional parameters yawCor, pitchCor, rollCor -- to give you the flexibility to adjust the model after it orients to the targetPoint. For example, in neoRiley's cool demo, the square can be rolling while it follows the orbiting planet, by doing mesh.lookAt(targetPoint,0,0,++roll) -- assuming roll is a global. BABYLON.Mesh.prototype.lookAt = function (targetPoint, yawCor, pitchCor, rollCor) { /// <summary>Orients a mesh towards a target point. Mesh must be drawn facing user.</summary> /// <param name="targetPoint" type="BABYLON.Vector3">The mesh to look at</param> /// <param name="yawCor" type="Number">optional yaw (y-axis) correction in radians</param> /// <param name="pitchCor" type="Number">optional pitch (x-axis) correction in radians</param> /// <param name="rollCor" type="Number">optional roll (z-axis) correction in radians</param> /// <returns>Mesh oriented towards targetMesh</returns> yawCor = yawCor || 0; // default to zero if undefined pitchCor = pitchCor || 0; rollCor = rollCor || 0; var dv = targetPoint.subtract(this.position); var yaw = -Math.atan2(dv.z, dv.x) - Math.PI / 2; var len = Math.sqrt(dv.x * dv.x + dv.z * dv.z); var pitch = Math.atan2(dv.y, len); this.rotationQuaternion = BABYLON.Quaternion.RotationYawPitchRoll(yaw + yawCor, pitch + pitchCor, rollCor); };I'll post two working samples shortly. Quote Link to comment Share on other sites More sharing options...
ProfessorF Posted January 19, 2014 Author Share Posted January 19, 2014 NeoRiley, that is pretty cool! GameMonetize 1 Quote Link to comment Share on other sites More sharing options...
GameMonetize Posted January 19, 2014 Share Posted January 19, 2014 No need for a pull request for this one, I integrate it directly. Thank you for your appreciated work! Quote Link to comment Share on other sites More sharing options...
neoRiley Posted January 19, 2014 Share Posted January 19, 2014 You're right about using getAbsolute! Sorry about that - the reason I did that was because when I was trying to get world coordinates by accessing target.position, it was returning the same value which seemed to be the local position in the log and so the cube would do the initial look at the starting point of the moon, but would not rotate because it was not returning updated world coordinates. Using getAbsolutePosition() gave me the updated world coordinates of the moon. Why would that be? Quote Link to comment Share on other sites More sharing options...
ProfessorF Posted January 19, 2014 Author Share Posted January 19, 2014 Okay I finished a tutorial for mesh.lookAt that combines movement (w,a,s,d,f,v moves the cube, spacebar moves the spaceship) Video: Code:<!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml"><head> <title>Tutorial - mesh.lookAt - ProfessorF </title> <script src="js/babylon.js"></script> <script src="js/hand.js"></script> <script> window.addEventListener("load", start); // 1st line of code executed window.addEventListener("keydown", handleKeyDown); window.addEventListener("change", handleChange); var canvas, engine, scene, light, light2, camera; // Engine Globals var looker, yawCor, rollCor, pitchCor, moveLooker; // Looker Globals var target; function start() { canvas = document.getElementById("cvRAVE"); engine = new BABYLON.Engine(canvas, true); scene = new BABYLON.Scene(engine); light = new BABYLON.DirectionalLight("sun", new BABYLON.Vector3(1, -1, 1), scene); light2 = new BABYLON.DirectionalLight("sun2", new BABYLON.Vector3(0, -1, 0), scene); light2.intensity = 0.5; camera = new BABYLON.FreeCamera("cam", new BABYLON.Vector3(0, 50, -50), scene); camera.rotation.x = Math.PI / 4; // look down 45 degrees ground = new BABYLON.Mesh.CreateGround("ground", 1024, 1024, 1, scene); ground.material = new BABYLON.StandardMaterial("texGround", scene); ground.material.diffuseTexture = new BABYLON.Texture("geotrigtexture.png", scene); BABYLON.SceneLoader.ImportMesh("", "assets/pointer/", "pointer.babylon", scene, function (meshes, particles, skeletons) { looker = meshes[0]; looker.position = new BABYLON.Vector3(-5, 5, 5); // you can set this yourself yawCor =0; pitchCor = 0; rollCor = 0; scl = 1; moveLooker = false; }); target = BABYLON.Mesh.CreateBox("orangeBox", 5, scene); target.position.x = 20; target.position.z = 0; target.position.y = 0; target.material = new BABYLON.StandardMaterial("orangeMaterial", scene); target.material.diffuseColor = new BABYLON.Color3(1, .5, 0); scene.activeCamera.attachControl(canvas); engine.runRenderLoop(update); } function update() { if (target && looker) { looker.lookAt(target.position); if (moveLooker) { var dv = target.position.subtract(looker.position); if (dv.length() > 1) { // if distance from target is > epsilon, move closer dv.normalize(); // otherwise you jump to the position looker.position = looker.position.add(dv); } else moveLooker = false; } } scene.render(); } function handleKeyDown(event) { ch = String.fromCharCode(event.keyCode); if (ch == "W") target.position.z += 1; if (ch == "S") target.position.z -= 1; if (ch == "D") target.position.x += 1; if (ch == "A") target.position.x -= 1; if (ch == "F") target.position.y += 1; if (ch == "V") target.position.y -= 1; if (ch == " ") { moveLooker = true; } } function handleChange(event) { var x = event.target; if (looker) looker.dispose(); if (x.value == "Kesha") { BABYLON.SceneLoader.ImportMesh("", "assets/pointer/", "pointer.babylon", scene, function (meshes, particles, skeletons) { looker = meshes[0]; looker.position = new BABYLON.Vector3(-5, 5, 5); yawCor = 0; pitchCor = 0; rollCor = 0; }); } else { BABYLON.SceneLoader.ImportMesh("him", "assets/dude/", "dude.babylon", scene, function (meshes, particles, skeletons) { looker = meshes[0]; looker.position = new BABYLON.Vector3(-5, 0, 5); yawCor = 0; pitchCor = 0; rollCor = 0; scl = .25; looker.scaling = new BABYLON.Vector3(scl, scl, scl); scene.beginAnimation(skeletons[0], 0, 100, true, 1.0); }); } } </script> <style> html, body { width: 100%; height: 100%; padding: 0; margin: 0; } #cvRAVE { width: 100%; height: 90%; } #dvMenu { width: 100%; height: 10%; font:10pt arial; } </style></head><body> <div id="dvMenu"> Target Keys: W,A,S,D,F (up),V (down)<br /> Looker Keys: SPACE BAR (moves towards target)<br /> Model: <select id="seLooker"><option>Kesha</option><option>Dude</option></select><br /> </div> <canvas id="cvRAVE"></canvas></body></html>I attached a zip of the complete code w/ all assets. I hope this will be useful. Any new changes will be at: https://github.com/professorf/MeshLookAt Good luck!MeshLookAtTutorial.zip HugoMcPhee 1 Quote Link to comment Share on other sites More sharing options...
ProfessorF Posted January 19, 2014 Author Share Posted January 19, 2014 No need for a pull request for this one, I integrate it directly. Thank you for your appreciated work!Thanks! Do you know when it will be integrated into 1.8.5 (I didn't see it in the latest). Quote Link to comment Share on other sites More sharing options...
GameMonetize Posted January 20, 2014 Share Posted January 20, 2014 It is on github right now ProfessorF 1 Quote Link to comment Share on other sites More sharing options...
neoRiley Posted January 20, 2014 Share Posted January 20, 2014 Thanks for adding this feature and taking the time to work on it with us, I really appreciate your time! I did pull the latest and updated my demo, and I'm still hitting the same issue with moon.position not being updated (moon is parented to earth btw). For the demo to work, I have to use moon.getAbsolutePosition() instead of moon.position like you show in your demo code above. Here's my simple render loop - any idea why moon.position's value isn't being updated?engine.runRenderLoop(function () { earth.rotation.y += .01; //box.lookAt(moon.position); // is not updating as parent rotates box.lookAt(moon.getAbsolutePosition()); // works scene.render();}); Quote Link to comment Share on other sites More sharing options...
gwenael Posted January 20, 2014 Share Posted January 20, 2014 "moon is parented to earth btw" You have your answer . You don't move moon, you rotate its parent that generates a movement for it in world space but not in local space since its position relative to its parent hasn't changed. More about it here http://www.html5gamedevs.com/topic/3083-spaces-world-parent-pivot-local/?p=19938 Quote Link to comment Share on other sites More sharing options...
ProfessorF Posted January 20, 2014 Author Share Posted January 20, 2014 Gwenael is correct. I think I will create a Wiki documentation page (orientation & movement) for the different scenarios. Quote Link to comment Share on other sites More sharing options...
gwenael Posted January 20, 2014 Share Posted January 20, 2014 I'll be glad to contribute once I've found time. Deltakosh asked me to write a wiki page talking about spaces http://www.html5gamedevs.com/topic/3083-spaces-world-parent-pivot-local/ Quote Link to comment Share on other sites More sharing options...
neoRiley Posted January 20, 2014 Share Posted January 20, 2014 "moon is parented to earth btw" You have your answer . You don't move moon, you rotate its parent that generates a movement for it in world space but not in local space since its position relative to its parent hasn't changed. More about it here http://www.html5gamedevs.com/topic/3083-spaces-world-parent-pivot-local/?p=19938 Thanks gwenael, I appreciate the link and the clarification with your link I was confused because I'd used the getPositionExpressedInLocalSpace() method previously, so my assumption was that "position" was always world space. The docs don't specify either local or world space for the "position" property on the Mesh object. Thanks for the help gwenael, John gwenael 1 Quote Link to comment Share on other sites More sharing options...
ProfessorF Posted January 20, 2014 Author Share Posted January 20, 2014 I'll be glad to contribute once I've found time. Deltakosh asked me to write a wiki page talking about spaces http://www.html5gamedevs.com/topic/3083-spaces-world-parent-pivot-local/ You're the space expert! I really like your jsdiff. But yeah, that's the problem--finding time. Especially now that the semester has started for me. 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.