Sign in to follow this  
webGLmmk

Understanding "Drag and Drop" Playground sample

Recommended Posts

For now, I'm basically just trying to understand some of the code behind the "drag and drop" sample in the Babylon.js playground.  I would eventually like to create a 3d arcade style game, starting here by being able to move the meshes around WITHOUT the camera moving.

 

But first things first tho, I need to understand the code thats here. I've read as much of the documentation and tutorials as I could, and I didn't see any previous post really related to what I'm looking for. There's are some things I dont get with the event listeners: getGroundPosition(), onPointerDown, onPointerMove.

 

I altered the scene and added it to my hosting. I removed everything but the sphere. http://portfolio.blenderandgame.com/draganddrop.html

 

Posting the code here, I commented the parts of the code I dont get:

 

With getGroundPosition(), I dont get the predicate. Not sure i entirely grasp the concept of "predicate". I know that its a callback that returns a boolean value: returns true if ground was clicked. I just dont know what purpose that boolean serves. What other piece of code is that impacting? Not sure how it ties in.  I notice that if you comment that part out, so that x and y are the only parameters, then the meshes will "sink" in into the ground instead of staying on the surface. I want to understand it on a code level tho.

var getGroundPosition = function () {                var pickinfo = scene.pick(scene.pointerX, scene.pointerY, function (mesh) { return mesh == ground; }); //?? dont understand purpose of predicate                if (pickinfo.hit) {                       return pickinfo.pickedPoint;                }                         return null;            }

I have the  same question about the scene.pick predicate in onPointerDown (this one returns true if the ground was NOT what was clicked on. )

One OTHER question about this:  startingPoint is assigned a value of getGroundPosition(evt). I know its passing the event. But getGroundPosition was defined WITHOUT parameters. So why is a parameter being passed to it? And what difference does that make? I've never seen an example like that with addEventListener.

            var onPointerDown = function (evt) {                if (evt.button !== 0) {                       return;                }                                          var pickInfo = scene.pick(scene.pointerX, scene.pointerY, function (mesh) { return mesh !== ground; }); //dont understand purpose of predicate again                if (pickInfo.hit) {                    currentMesh = pickInfo.pickedMesh;                    startingPoint = getGroundPosition(evt); //dont understand why argument is passed if fucntion was defined with no parameters..                             if (startingPoint) {                          setTimeout(function () {                            camera.detachControl(canvas);                        }, 0);                    }                }            }

         
Because of those two questions. There is a LOT I dont get about onPointerMove. But hopefully once I understand the above, it will help me figure out the rest. Dont want to bombard with my questions here on my first post :)

 

Muchas Gracias to anyone that can help. Can't get any further with this and I dont want to give up and move on.  Everything else I see with regard to movement is actually just moving the camera, not translating a mesh with a static camera view. This is the only sample I've come across that actually disengages the camera (at least only when the mesh is being moved).
 

Share this post


Link to post
Share on other sites

Hi gson78, welcome to the forum!  Good to have you with us.  And may I say, you are doing just fine at having a grasp of this.  In fact, you found a minor bug in our drag'n'drop playground demo.  Well done!  Line 83 and 106 should indeed be getGroundPosition();  No 'evt' needs to be sent to getGroundPosition().  If you remove them, it still works fine, as you likely know.

Yeah, predicates.  Strange word, eh?  *nod*  Simply put, return mesh == ground ensures that ONLY a pickInfo.hit on the mesh reffed in variable 'ground' is allowed/accepted.  If a hit on a mesh-var 'ground' DOESN'T happen, the OTHER return branch is taken... which returns null.  It is important for a dragging feature to know WHERE on the ground that the drag is going to start.  You can see some interesting things, here...

http://playground.babylonjs.com/#279FW9

[i tried to add an initial camera.attachControl(canvas, true); in the line 9 area... failed.  Maybe someone can help ME on that one.  thx!]

 

Back on topic, if you pointerDown on a part of a mesh that is NOT "atop" (in front-of, ray-wise) a section of ground, the getGroundPosition() returns null, and then line 85 fails to trigger.  Then if you move the pointer (while still pointerDown on a ground-miss)... onPointerMOVE is called, but line 102 sees NO ground startingPoint, so it returns without doing anything.

In line 80, the current picked mesh that is NOT the 'ground' mesh... is established.  The fact that pickInfo is spelled differently than it was inside of getGroundPosition()... is inconsequential... it doesn't matter.  Even if they were identical, there would be no variable collision because they each declare their pickinfo/pickInfo variables in different functions, which keeps them isolated from each other.

Ok, the dreaded predicate.  Here is MY understanding of it.  When you click on a mesh to drag it, the FIRST picking point that the picking ray hits... is the picked mesh.  That will make the getGroundPosition() func NOT satisfied... because it's NOT a mesh in a variable named 'ground'.  But, because of the cool way our picking system works, the predicate is saying...  "If the picked mesh is NOT a ground, keep extending the ray until it hits a ground, if possible".

Generally, what we're talking about here... is scene.pick.  Let's take a trip to the src code for Babylon's Scene-class object.

https://github.com/BabylonJS/Babylon.js/blob/master/src/babylon.scene.js

It's a big fat object with the power to take over the world, but that doesn't scare us one bit, eh?  Do a browser search (control f) for "pick" and F3 yourself thru the hits... see what can be learned.   Notice line 360... a predicate func with MANY "requirements" in it.    Line 1641 area... more predicate work.  You can tell that the predicate works as a type of "discriminator" or criteria checker.  Line 1668 says "used to determine eligible meshes".  Yeah, that's another fine way of wording it.  At line 1663 just above it... there it is... the world famous Scene.pick function.

I'm not sure how correct I am about any of this, and I don't know if I have helped with any of your questions.  I hope so.  What I state can be verified/kyboshed with tests.  Others will surely help, too.  Feel free to ask as many questions as you wish... don't worry about that at all.  Don't forget how well a search of our github source code... can bring knowledge, too. 

Tell us about discoveries, projects, anything.  Nice job finding the playground demo mistake, you have good eyes!   Again, welcome aboard and party on!

Share this post


Link to post
Share on other sites
On 9/8/2015 at 7:51 AM, Wingnut said:

Hi gson78, welcome to the forum!  Good to have you with us.  And may I say, you are doing just fine at having a grasp of this.  In fact, you found a minor bug in our drag'n'drop playground demo.  Well done!  Line 83 and 106 should indeed be getGroundPosition();  No 'evt' needs to be sent to getGroundPosition().  If you remove them, it still works fine, as you likely know.

Yeah, predicates.  Strange word, eh?  *nod*  Simply put, return mesh == ground ensures that ONLY a pickInfo.hit on the mesh reffed in variable 'ground' is allowed/accepted.  If a hit on a mesh-var 'ground' DOESN'T happen, the OTHER return branch is taken... which returns null.  It is important for a dragging feature to know WHERE on the ground that the drag is going to start.  You can see some interesting things, here...

http://playground.babylonjs.com/#279FW9

[i tried to add an initial camera.attachControl(canvas, true); in the line 9 area... failed.  Maybe someone can help ME on that one.  thx!]

 

Back on topic, if you pointerDown on a part of a mesh that is NOT "atop" (in front-of, ray-wise) a section of ground, the getGroundPosition() returns null, and then line 85 fails to trigger.  Then if you move the pointer (while still pointerDown on a ground-miss)... onPointerMOVE is called, but line 102 sees NO ground startingPoint, so it returns without doing anything.

In line 80, the current picked mesh that is NOT the 'ground' mesh... is established.  The fact that pickInfo is spelled differently than it was inside of getGroundPosition()... is inconsequential... it doesn't matter.  Even if they were identical, there would be no variable collision because they each declare their pickinfo/pickInfo variables in different functions, which keeps them isolated from each other.

Ok, the dreaded predicate.  Here is MY understanding of it.  When you click on a mesh to drag it, the FIRST picking point that the picking ray hits... is the picked mesh.  That will make the getGroundPosition() func NOT satisfied... because it's NOT a mesh in a variable named 'ground'.  But, because of the cool way our picking system works, the predicate is saying...  "If the picked mesh is NOT a ground, keep extending the ray until it hits a ground, if possible".

Generally, what we're talking about here... is scene.pick.  Let's take a trip to the src code for Babylon's Scene-class object.

https://github.com/BabylonJS/Babylon.js/blob/master/src/babylon.scene.js

...

Thanks for all the help, Wingnut. It took a bit to reply because I wanted to make sure I pretty much understood everything. It was a relief to know that the (evt) passed was a bug and not necessary. Your playground example was a huge help. I'm basically able to read and understand this code now. (mostly...) I can get rid of or  change the size of the ground, change  camera and mesh positions, add a plane, etc. I did check out all your references to the source code. I'd love to be able to confidently read all of it, and be able to get answers from source code in the future. Reading through "You Don't Know JS" right now. 

I also realized in playing around with it that the scene.pick predicate in the onPointerDown function made sure that the ground doesn't move - the  "!mesh==ground;" statement keeps the ground mesh from qualifying as "pickInfo.hit". I had been trying to understand what kept the ground from moving like the other meshes.

There's one last thing, I'm not confident about yet.  In onPointerMove:

var diff = current.subtract(startingPoint);        currentMesh.position.addInPlace(diff);

I know this is what makes the meshes change position when the pointer moves. Not sure if my question comes from a lack of understanding about vectors(probably), or something I dont know about how the vector3 class works:  

1. The initial position is stored in startingPoint when onPointerDown is invoked. (terminology?)  

2. A new pointer position is stored in "current" when the pointer moves, with onPointerMove.

Here's where I get lost: 

3. "startingPoint" is subtracted from "current"  ---  the original "startingPoint" vector is subtracted from the "current" vector and that value is basically a new vector that gets stored in "diff"   ??? 

4. addInPlace: the selected mesh's position is replaced with the new "diff" vector position

I found a site to brush up on vectors.  I didnt have much trouble in math, but I just didn't pursue it as far I could have. (rural high school that wasn't very motivating.) I've been reviewing, but it looks like I'll have to jump ahead and crash course (expected that, just didn't know when.)

Maybe I just dont know how that "subtract" method is working. I'm not sure why the mesh position isn't just directly being reassigned the new pointer position: ("current" i guess). Why is there a need for the "diff"?  

UPDATE: i just tried to do that. In a scene with just the ground and the sphere, commented out the diff line and  passed in current;   addInPlace(current) . The sphere zipped off really quickly into nowhere when I tried to move it, and now I can't find it. Obviously that doesn't work, i just dont know why. 

Thanks again. I hope to build a bunch of 3D arcade style games, include blogs and tutorials on them as I go.

Share this post


Link to post
Share on other sites

Hi again gson78, my pleasure.  You have it figured perfectly... except for...

Quote

4. addInPlace: the selected mesh's position is replaced with the new "diff" vector position

That statement is slightly incorrect, maybe.  It's replaced with diff + current.  Keep in mind that during a drag... many many render frames happen. onPointerMove is called once per frame during a drag, and current is being continuously updated by calls to getGroundPosition()... as the drag happens. 

I used a little playground output reporter... to show how current is continuously changing during onPointerMove.  http://playground.babylonjs.com/#279FW9#4  

(drag some mesh and watch the value of current... constantly changing in the numbers on the PG menu)

Notice line 137.  A NEW startingPoint is set... over and over... from the value of current.  Maybe if you changed the current variable... to be more descriptive... such as naming it currentDragProgressPosition... that might make your life more fun.  :)

I didn't explain this very well... but maybe enough.  :)  Generally speaking, try to understand that a drag (an onPointerMove) is a sequence of many little moves... done once per frame.

Nice job with the code analysis!  You have it figured-out quite well... good work.  Don't hesitate to ask more questions and report your discoveries.  Essentially, you have written a pretty good tutorial about dragging.  Maybe we can add some of that information to our help files... based-upon your discoveries.  Be well, good luck.

Share this post


Link to post
Share on other sites
On 9/13/2015 at 9:06 AM, Wingnut said:

Hi again gson78, my pleasure.  You have it figured perfectly... except for...

That statement is slightly incorrect, maybe.  It's replaced with diff + current.  Keep in mind that during a drag... many many render frames happen. onPointerMove is called once per frame during a drag, and current is being continuously updated by calls to getGroundPosition()... as the drag happens. 

I used a little playground output reporter... to show how current is continuously changing during onPointerMove.  http://playground.babylonjs.com/#279FW9#4  

(drag some mesh and watch the value of current... constantly changing in the numbers on the PG menu)

Notice line 137.  A NEW startingPoint is set... over and over... from the value of current.  Maybe if you changed the current variable... to be more descriptive... such as naming it currentDragProgressPosition... that might make your life more fun.  :)

I didn't explain this very well... but maybe enough.  :)  Generally speaking, try to understand that a drag (an onPointerMove) is a sequence of many little moves... done once per frame.

Nice job with the code analysis!  You have it figured-out quite well... good work.  Don't hesitate to ask more questions and report your discoveries.  Essentially, you have written a pretty good tutorial about dragging.  Maybe we can add some of that information to our help files... based-upon your discoveries.  Be well, good luck.

Actually that was perfect, thanks again. I was forgetting about the game loop and frames per second. That makes sense of the difference/ subtract on line 129.  We've stored the pointer position, but the mesh position needs to be updated in each frame; thus the position.addInPlace. I need to create a tutorial page on my blog, so maybe I'll start with what I've learned from this. I think I have a somewhat unique "newbie self-taught" perspective that could be pretty useful if I document the things that confuse me and how I sorted them out.

Share this post


Link to post
Share on other sites

Hi Wingnut:

Following on from this thread, let me raise a question about the proper way to implement pointer event handling in BJS.

The PG example (http://www.babylonjs-playground.com/#279FW9#33) is showing what a think can be the preferred way (with the current version of the engine, 3.0) to tackle this kind of functionalities:

  • not to use HTML5 canvas event handling mechanism (canvas.addEventListener()) at all in order to avoid cross-browsing inconsistencies;
  • implement "pointerdown" and "pointerup" by means of (powerful and high-level) Action Managers as it provides a convenient mesh-oriented functionality (making the use of scene.pick not necessary and by the way cursor-pointer change for free);
  • because of the lack of "pointermove" implementation with Action Managers, to use an Observable (scene-oriented) to manage it.

Obviously this is a working solution, by I find it not very elegant, as we are mixing two functionalities in the very same "block of code": Action Managers with Observables.

I would be grateful if you were to share your opinion about this.

Best regards.

Share this post


Link to post
Share on other sites

Hi PaleRider, thanks for the fine comments/questions.  I hope you allow others to help me answer, because I'm certainly no expert.  I am not the author of our drag'n'drop demo, and I believe it was created BEFORE observables and actionManagers existed in BJS.  It would be a fun challenge to try to reproduce the same drag'n'drop functionality... using all actionManagers.

I haven't had morning coffee yet, but I "suspect" that actionManager.onEveryFrameTrigger COULD be used as an onPointerMove trigger.  First line of our handler might be...  if (scene.pointerX == previousPointerX && scene.pointerY == previousPointerY) return;  If it falls-thru that, we have a moved pointer, yes?

Here's an unloved basic drag'n'drop PG we can mess-with:  https://www.babylonjs-playground.com/#17EGSU#3

Let's make it the "Monday Challenge".  The first person to create an all-actionManager drag'n'drop demo... wins a BRAND NEW... umm... level of admiration from fellow forumers.  :)

.onEveryFrameTrigger used for a pointerMove checker?  Seems goofy, eh?  But why not?  Let's see what blows-up.  :)

Again, great questions and thoughts.  PR, I hope you're cool-with welcoming comments/ideas from everyone.  I'm not overly-qualified to answer these things.  We need all the power experts we can get, for this one.

Share this post


Link to post
Share on other sites

Hi Deltakosh:

Well, I didn't hear nothing about "behaviors" in BJS until now, but reviewing the new v3.1 additions in the docs (Behaviors doc) I can see this is a (let say) high-level functionality in the line a modern and productive (code less do more) engine must have.

Of course I will try it when available.

Thanks for your continuous enhancements.

Share this post


Link to post
Share on other sites

Yay!  Hi-tech drag'n'drop!

"behavior"... what a nasty word to type.  :)  Can't we call them "jigs"?  Jigs and rigs!  How cool is that?  ;)

Let's see how it "sounds".  BouncingJig, AutoRotationJig, FramingJig...  not bad.

JumpJig, ScaleJig, BounceJig, PositionJig, NavJig, MotorJig, ColorJig, FaderJig, ParticleJig, GodraysJig, MythrosSlipperySlopeJig.  :)

YEAH!  heh.  "Jig" is probably not "industry standard" though, huh?

In a way, that's what my bobsled's "flyerframe" is supposed to be... a jig.  Parent ANY mesh to the white box, and suddenly it becomes a 24 thruster micro-grav physics-active spacecraft.  UncleWingysPhysicsFlyerJig  :) 

And the small-but-dedicated group that cares-for all our jigs?  The Jig-Pigs.  :rolleyes:

Ahhh, maybe not.

Share this post


Link to post
Share on other sites

I know this is an old thread in an old forum, but since this thematic fits in here, I didn't want to open a new thread in the new forum and trying my luck here now:

https://playground.babylonjs.com/#279FW9#44

I want to create a mesh with a button, attache it to the cursor and drop it, when clicked again.
It works fine, except that, when I create another mesh, the position of the old mesh will also be set again, so I have meshes on the same position and it seems, that it is only one mesh.
How can I manage, to let the first mesh be fixed on its position?

Even a reference with scene.getMeshByName won't work.
I hope anyone will see me here :D

Share this post


Link to post
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...
Sign in to follow this  

  • Recently Browsing   0 members

    No registered users viewing this page.