Hey guys.. I'm trying to implement snapping one object to another. Sort of garry's mod style. Only I can't seem to get the math reliably correct. My issue is kind of hard to explain, but here's how it works. Each object has a bunch of "snap points" on them. These are 4x4 matrix transforms in object space, so they basically define where on the surface of the object the other object will snap to. But when snapping an object, I don't just want to snap the second object's position to that transform, what I want to do is snap the second objects selected snap point to the first objects snap point and then get the position of the second object and fix it in place. Currently I'm using: Object A transform * snaptransform of A * snaptransform of B.inverse Which theoretically gives me the world-space position of object B. Only, it doesn't do it reliably, because I have some snaptransform's that are at different angles (imagine snap points on top and bottom of a box) and I'm trying to understand WHY it isn't reliable and failing I made a quick picture to try and explain better than this text. Hope someone has some thoughts!! https://www.dropbox.com/s/unelku3ddfmw7iz/snaps.jpg
I used to be a wiz at math, acing calculus I, II, III, etc... but alas it is true what they say... if you don't use it you lose it. Although not math related, whenever I've had problems of unreliability its because I did a copy and paste and forgot to change one of the x's in my calculations to y's or something like that. Those are sometimes the hardest to catch without doing a thorough code review.
This is probably more like a mental blindspot on my part. I know and understand what I think SHOULD happen, but I get some edge cases where some snap points don't snap as expected. So I suspect that in some cases there is a problem with concatenation or some such.
snaptransform of B.inverse doesn't look consistent with snaptransform of A (looks like snapstransform is defined in the object space and not the world space), why don't you have Object B.inverse in your equation too? JC
I do. Heres the actual code: snapmat = getParentGob()->getTransform() * GetSnapTransform(snapindex) * snap->GetSnapTransform(othersnapindex).inverted(); The snapmat is the final world space matrix I am setting object B to. getParentGob() gets the gameobject this component is attached to (in this case, object A), GetSnapTransform() gets the object space transform of the snap point (in this case of object A) and then multiplies that with the inverse of the snap transform of B. It works for certain cases of snap orientation. But not all of them. Which is where I'm having problems visualizing the issue. To my mind this should just work and yet it doesn't. I tried setting up a unit test for it and it does indeed have some issues. But I cant for the life of me think what is wrong. I need a maths hero hahahaha
I'm certainly not a maths hero but your logic looks OK to me. I suppose you've tried multiplying your matrices in the opposite order already? Have you checked your matrix invert works correctly (I've seen maths libs where invert works only for special cases, like no scaling or uniform scaling)? Otherwise, I'd divide and conquer, render something at "getParentGob()->getTransform() * GetSnapTransform(snapindex)" and make sure it sticks correctly as the object rotates. After you're confident of that you can concentrate on the other bit.
Yeah, I rendered something at the snap point and it works fine. So its really something to do with the way that the transform and inverse is working I guess. But I dont think I've actually drawn a debug position there. So maybe I'll try and see how that works. From what I can tell, there is some combination of transforms where one essentially inverts the other.
Yeah, you should definitely display graphically each step. Decompose your whole transformation into all the step. Think of it this way: how can I move my obj into the final metrics and decompose each step. Thinking of it, you don't need the original position of objB, you could assume it is at the origin and apply each transformation. Looking at your drawing, I am a bit confused about the snap point for Object B, shouldn't they go inside the cube and not outside. Think of it as you need to align them with the one on ObjA. If you align them in your drawing, both cubes will be at the same pos. hope it make senses. JC
Yeah, you ignore the original position of object B. The task is to compute the position and orientation of it so that it is correctly "snapped". Then apply the rigid joint to the world-space objects so they stick together. No, the snaps always go on the outside (they represent the UP vector for the snap position, so like a press-stud kind of arrangement), so you should be always be able to align one snap with the other (the snaps up should always be pointing in the opposite direction). I think whats happening is in some configurations the snaps UP vector are being aligned when they should be opposite. Phil.
Not sure I am completely following you but when you compute the inverse of snapB, it isn't really the inverse of snap B, it should be the inverse of the opposite vector. How do you do that? Are you sure this step is correct? JC
Yeah, it just occurred to me that it might need to be rotated 180 degrees so that the snaps are properly aligned. But then why would it work SOMETIMES? Imagine a box with a snap at +1 with 0 rotation and a snap at -1 with a 180 rotation (degress), then snap two of them together. So box A pos = 0.0.0 and then you add transform (vec(0,1,0),rot(0)) so then you have the world space position and orientation of the snap, you then snap B onto it from pos(0,1,0)rot(0) onto it. So the inverse gives us a position of pos(0,2,0)rot(0), which is as you would expect (snap one box on top of another). Then do the BOTTOM snap of B onto A the same way. a = pos(0,0,0)rot(0) + a.snap pos(0,1,0) rot(0) + ?? and here is the tricky bit.. the inverse snap b pos(0,-1,0) rot(180) = pos(0,1,0) rot (-180) = pos(0,-1,0) so we add that on and we get pos(0,0,0) So I think the problem is that the rotation on the negative snap point inverts the translation and therefore you get the wrong position? Does that make sense? If I applied a rotation to get the opposite direction of the snap, I'd basically get the first B snap point inverted and still have the same problem (just the other way round).
> If I applied a rotation to get the opposite direction of the snap, I'd basically get the first B snap point inverted and still have the same problem (just the other way round). Not at all. before doing your rotation you have to go back to the origin, then do your rotation and then go back to the real position. JC
If this is the result you get, then it's wrong. If you snap box B to A, where both boxes got the same snap->object transformation (e.g. pos(0,1,0)rot(0)), then B should have the same object->world transformation as A: objB_to_world=objA_to_world*snap1_to_obj*inv(snap0_to_obj) i.e. snap1_to_obj*inv(snap0_to_obj) will result identity matrix if those snap matrices are equal, so your both boxes will end up overlapping. This is the result that's correct, but not the result you want. To get the result you want, your other snap->obj transform needs to be rotated so that the surface normal points to the opposite direction, i.e. multiply Z (assuming this is your snap normal vector) and X (or Y) vectors by -1. Cheers, Jarkko
Oh man, do I feel silly right now. As I was looking at this again, I noticed that the debug mesh I had added to the snap points wasnt showing +y as the out direction, which it should have been. Turns out I was specifying the orientations of the snap points wrong (I'd chosen the wrong axis to rotate) and that was what was causing the issue!!! way to non-fix a problem I'd un-broken already Anyway, thanks for the moral support here guys. Now works. So I can bin it in the knowledge it didn't beat me (I don't like how it was working, so going to do something else).