Numa Posted March 27, 2016 Share Posted March 27, 2016 Hi there, I'm working on a little project where I do a lot of parenting and swapping parents/children around, etc.. Is there an easy way to preserve a child's world position when changing its parent? I'm really new to Babylon so I don't always know what's available to me. If this doesn't exist how would I go about it? Inverse the parent's world matrix and apply to it its child? If someone has an example handy I'd appreciate Thanks! Quote Link to comment Share on other sites More sharing options...
GameMonetize Posted March 27, 2016 Share Posted March 27, 2016 Hello, we've got you covered ;D Just use mesh.getAbsolutePosition() before removing the parent and then mesh.setAbsolutePosition() after setting the new parent Quote Link to comment Share on other sites More sharing options...
Numa Posted March 28, 2016 Author Share Posted March 28, 2016 Great, that works, thanks! Quote Link to comment Share on other sites More sharing options...
Numa Posted April 6, 2016 Author Share Posted April 6, 2016 2 questions on this: 1. Do I need to call updateWorldMatrix() after setting the parent or after setting the new position? on both child and parent? 2. What about rotation and scale? There is no getAbsoluteRotation() / Scaling Is there an easy trick to only change the parenting without moving the object at all? Would multiplying the child's world matrix by the the inverted parent's world matrix do it? Thanks! (also, a little bool to preserve original matrix or not when parenting would be great :D) Quote Link to comment Share on other sites More sharing options...
adam Posted April 6, 2016 Share Posted April 6, 2016 12 minutes ago, Numa said: Would multiplying the child's world matrix by the the inverted parent's world matrix? have you tried it? Quote Link to comment Share on other sites More sharing options...
Numa Posted April 6, 2016 Author Share Posted April 6, 2016 Yes I did and it didn't seem to work. I'm only asking for someone to confirm the theory here. If I tried it and it didn't work, does it mean my code is wrong? or is there a bug in Babylon? or is it not the way to go at all? I'm very new to Babylon and Coming from Unity I make a lot of assumptions about how things work And later realize I need to do some special handling when working at a lower level. So, is that the way to go? Quote Link to comment Share on other sites More sharing options...
adam Posted April 6, 2016 Share Posted April 6, 2016 10 hours ago, Numa said: Yes I did and it didn't seem to work. I'm only asking for someone to confirm the theory here. It sounds right to me. Can you create a PG? GameMonetize 1 Quote Link to comment Share on other sites More sharing options...
Numa Posted April 7, 2016 Author Share Posted April 7, 2016 I'm looking into it, I think it'll work. I'm wondering why there is a getAbsolutePosition, but nothing for rotation and scale... instead I have to get the world matrix and decompose it because there is a getWorldMatrix but no setter, so I have to set each component manually. I'll post updates later, I'm onto a different problem now Quote Link to comment Share on other sites More sharing options...
GameMonetize Posted April 7, 2016 Share Posted April 7, 2016 Hello NUma, getAbsolutePosition is fast and can be called often. In the contrary, extracting rotation and scaling is slow, time and memory intensive so I prefer not expose a simple function Quote Link to comment Share on other sites More sharing options...
Numa Posted April 7, 2016 Author Share Posted April 7, 2016 That makes sense, how would I go about getting and setting the absolute rotation and scale then? Or even better, is there a way to set the world matrix directly? I tried doing myMesh._worldMatrix = myWorldMatrix but it didn't seem to have an effect even if I called computeWorldMatrix(true) Quote Link to comment Share on other sites More sharing options...
adam Posted April 7, 2016 Share Posted April 7, 2016 Please create a PG of your failed attempt. Edit: You can't set the world matrix directly for meshes. You can with bones. Quote Link to comment Share on other sites More sharing options...
Numa Posted April 11, 2016 Author Share Posted April 11, 2016 Fixed it, the problem was that computeWorldMatrix() doesn't go down the hierarchy (I assumed it would), so after changing parents none of the children would be aware of the change. I had to add something like: function UpdateWorldMatrix(object) { object.computeWorldMatrix(true); for (var i = 0; i < object.getChildren().length; i++) { UpdateWorldMatrix(object.getChildren()[i]); } } note that you need to check that the hierarchy is valid beforehand to avoid running into infinite loops in some sneaky scenarios like (A---> B ---> C ---> A). That's an area where Babylon could really use some improvement beside the .parent = , everything has to be handled manually. I might contribute some code later when I feel more comfortable with the library All it needs is a object.Attach(parent, keepWorldTransform) function that handles the attaching / detaching / propagating updated matrices, and lets you choose whether you want your child to keep its world or local transform. Here is what the matrix operations look like if you want to preserve the world position after changing parents: function Attach(object, parent) { object.parent = parent; // Get parent's inverse matrix var invertParentWorldMatrix = object.parent.getWorldMatrix().clone(); invertParentWorldMatrix.invert(); // Combine it with the child's var newWorldMatrix = object.getWorldMatrix().multiply(invertParentWorldMatrix); // Apply it var components = decomposeTranslationRotationScalingMatrix(newWorldMatrix); object.position = components.translation; object.rotationQuaternion = components.rotation; object.scaling = components.scaling; // Update matrix UpdateWorldMatrix(object); } function decomposeTranslationRotationScalingMatrix(matrix) { var innerMatrix = matrix.clone(); // Translation var positionX = innerMatrix.m[12]; var positionY = innerMatrix.m[13]; var positionZ = innerMatrix.m[14]; var translation = new BABYLON.Vector3(positionX, positionY, positionZ); var translationMatrixInv = BABYLON.Matrix.Translation(-positionX, -positionY, -positionZ); // // Scaling var scalingX = Math.sqrt(innerMatrix.m[0] * innerMatrix.m[0] + innerMatrix.m[1] * innerMatrix.m[1] + innerMatrix.m[2] * innerMatrix.m[2]); var scalingY = Math.sqrt(innerMatrix.m[4] * innerMatrix.m[4] + innerMatrix.m[5] * innerMatrix.m[5] + innerMatrix.m[6] * innerMatrix.m[6]); var scalingZ = Math.sqrt(innerMatrix.m[8] * innerMatrix.m[8] + innerMatrix.m[9] * innerMatrix.m[9] + innerMatrix.m[10] * innerMatrix.m[10]); var scaling = new BABYLON.Vector3(scalingX, scalingY, scalingZ); // // Rotation var rotationMatrix = innerMatrix.multiply(translationMatrixInv); // Normalize to remove scaling. if (scalingX) { rotationMatrix.m[0] /= scalingX; rotationMatrix.m[1] /= scalingX; rotationMatrix.m[2] /= scalingX; } if (scalingY) { rotationMatrix.m[4] /= scalingY; rotationMatrix.m[5] /= scalingY; rotationMatrix.m[6] /= scalingY; } if (scalingZ) { rotationMatrix.m[8] /= scalingZ; rotationMatrix.m[9] /= scalingZ; rotationMatrix.m[10] /= scalingZ; } // var rotationX = Math.asin(-rotationMatrix.m[9]); var rotationY = Math.atan2(rotationMatrix.m[8], rotationMatrix.m[10]); var rotationZ = Math.atan2(rotationMatrix.m[1], rotationMatrix.m[5]); var quaternion = new BABYLON.Quaternion.FromRotationMatrix(rotationMatrix); var result = { translation: translation, scaling: scaling, rotation: quaternion }; return result; } function UpdateWorldMatrix(object) { object.computeWorldMatrix(true); for (var i = 0; i < object.getChildren().length; i++) { UpdateWorldMatrix(object.getChildren()[i]); } } Thanks for your help everyone Numa dbawel 1 Quote Link to comment Share on other sites More sharing options...
GameMonetize Posted April 11, 2016 Share Posted April 11, 2016 Actually there is no need to go down the hiearchy. All matrices are rebuilt (if needed) on every frame. If you want to force a complete update of a specific mesh, you can just do: scene.incrementRenderId(); object.computeWorldMatrix(true); incrementRenderId just simulates a new frame to invalidate all caches Madclaws 1 Quote Link to comment Share on other sites More sharing options...
Numa Posted April 11, 2016 Author Share Posted April 11, 2016 Oh ahah perfect very nice trick! There is no way I could have found out on my own though I needed to refresh some matrices early because I'm doing several coordinates calculations within one frame before I can render it. All good now, I'll remember that trick next time! Thanks Quote Link to comment Share on other sites More sharing options...
GameMonetize Posted April 11, 2016 Share Posted April 11, 2016 My pleasure Quote Link to comment Share on other sites More sharing options...
satguru Posted April 17, 2016 Share Posted April 17, 2016 @Numa Trying to do something similar myself, for my Vishva project. I found there is a Matrix.decompose() function available http://doc.babylonjs.com/classes/2.3/Matrix#decompose-scale-rotation-translation-rarr-boolean Something like below seems to work for me function Attach(object, parent) { var invParentMatrix = Matrix.Invert(parent.getWorldMatrix()); var newMatrix = object.getWorldMatrix().multiply(invParentMatrix); object.parent = parent; newMatrix.decompose(object.scaling, object.rotationQuaternion, object.position); } Numa 1 Quote Link to comment Share on other sites More sharing options...
Numa Posted April 17, 2016 Author Share Posted April 17, 2016 Nice, didn't know about that one. Quote Link to comment Share on other sites More sharing options...
satguru Posted May 2, 2016 Share Posted May 2, 2016 @Numa Did you run into this issue? http://www.html5gamedevs.com/topic/22271-matrix-decompose-issue/ Quote Link to comment Share on other sites More sharing options...
Numa Posted May 3, 2016 Author Share Posted May 3, 2016 damn, no I haven't but I definitely will one day. Did the last proposed solution work for you? I'm not using the decompose function, I'm using my own which basically does the same thing but returns all 3 components separately. I then assign them back to the object. (see snippet in one of my posts above) Maybe you could do that and assign the correct scale separately? I don't know if that would work. Quote Link to comment Share on other sites More sharing options...
adam Posted May 5, 2016 Share Posted May 5, 2016 I'm posting the latest solution here to make it easy for others who have this issue: http://www.babylonjs-playground.com/#28IXSE#13 The parentMesh function only adds the extra mesh to the parent if the parent has been scaled. Numa 1 Quote Link to comment Share on other sites More sharing options...
Sigmus Posted March 10, 2017 Share Posted March 10, 2017 This seems like the most fitting thread I found for the problem I'm currently having, so here it goes: I'm currently building an object viewer with an ArcRotateCamera. I want the user to be able to position two of the light sources I have in the scene (one point light and one spot light) by attaching them to the camera and detaching them with a button click. Being the BabylonJS-Newbie that I am (actually this whole 3D stuff is quite new to me, I'm a web developer and this is my first project using any 3D engine at all) I assumed that simply assigning null to the parent would do the trick but of course that wasn't the case, as the light immediately jumped back to its initial position after it was detached from the camera. A quick google search led me here and the content of this thread helped me to achive what I wanted for the point light like this: var pos = myPointLight.getAbsolutePosition(); myPointLight.parent = null; myPointLight.position = pos; setAbsolutePosition wasn't available, by the way. I assumed that is the case because I'm working with a light here and not with a mesh, so I simply assiged it to position and it worked, the point light now gets correctly returned to the position it had when it was last attached. But when I tried it for the spot light, things got very weird, as I wasn't really sure what to do to correctly read and then restore its rotation. Normally, the spot light recieves a direction and not a rotation, but grabbing that before the null assignment and re-applying it didn't work. Same for rotation or rotationQuaternion. So apparently it won't work without doing some serious math. The last time I actually had to do those kind of calculations was 15 years ago at my university of applied sciences and I'm afraid I don't remember much of it. So, any help would be greatly appreciated. Oh, and I hope this is the correct place to ask this question. Most of the answers in this thread apply to problems with meshes in parent/child relations and I assume things work quite a bit different with lights, but since my problem still fits this thread's title, it didn't seem appropriate to open a new discussion for this... Quote Link to comment Share on other sites More sharing options...
GameMonetize Posted March 10, 2017 Share Posted March 10, 2017 Hello and welcome..this is the perfect place for it We will help you find a solution. Can you please create a simple playground that highlights your issue? http://www.babylonjs-playground.com Quote Link to comment Share on other sites More sharing options...
Sigmus Posted March 10, 2017 Share Posted March 10, 2017 Hi Deltakosh. Here it is: http://www.babylonjs-playground.com/#1EZHCT#0 As you can see, the scene has a point light and a spot light attached to the camera. The first right-click in the scene drops the point light at its current position. The second right-click is supposed to do the same for the spot light, but instead it shows the weirdest behavior, totally unpredictable. It only works if the camera wasn't moved at all. Quote Link to comment Share on other sites More sharing options...
adam Posted March 10, 2017 Share Posted March 10, 2017 http://www.babylonjs-playground.com/#1EZHCT#1 GameMonetize 1 Quote Link to comment Share on other sites More sharing options...
GameMonetize Posted March 10, 2017 Share Posted March 10, 2017 Thanks @adam 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.