Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding in a Networked World Transform Option and Example #223

Conversation

PlumCantaloupe
Copy link

@PlumCantaloupe PlumCantaloupe commented Jul 23, 2020

I have been playing with being able to pick up and release other elements for a few months and always wrestled with how networked-aframe currently only syncs via local space so that if you are trying to sync a nested component (e.g. one parented to the camera) the expected transforms will not be reflected in other users' views.

In this PR I am proposing and showing an example of a new networked property called "synchWorldTransforms" in networked.js

This property defaults to false so that most use-cases that do not require world transforms do not incur the overhead of calculating them, defaulting to traditional methods of copying each property over.

An example of what this would look on an element that can be picked up below

*new* world-space.html in examples

    <a-entity
        class="interactive"
        networked="template:#interactive-geo-template; attachTemplateToLocal:true; synchWorldTransforms:true;"
        pickupable
        spawn-in-circle="radius:5"
        geometry="primitive:dodecahedron; radius:1.0;"
        animation="property:rotation.y; to:360; dur:15000; easing:linear; loop:true">
    </a-entity>

Note that the added code in networked.js is not necessarily the most performant as it will likely calculate the object3D's world matrices more than once per frame; but it felt a fair trade-off so that the code was not too violently inserted into the existing foundation. Maybe I have overlooked an easier way to do this with greater performance :)

networked.js

      //before for-loop. Doing this here once should reduce matrixWorld updates
      if (this.data.synchWorldTransforms === true) {
        euler_w = new THREE.Euler();
        pos_w = new THREE.Vector3();
        quat_w = new THREE.Quaternion();
        scale_w = new THREE.Vector3();

        //doing this here once so we don't have to do it multiple times later
        this.el.object3D.updateMatrixWorld( true );
        this.el.object3D.matrixWorld.decompose( pos_w, quat_w, scale_w );
      }

     /*** moving down a few code lines ***/

     //in component for-loop
     if (this.data.synchWorldTransforms === true) {
        if (componentName === 'position') {
          componentData = pos_w.clone();
        }
        else if (componentName === 'rotation') {
          euler_w.setFromQuaternion(quat_w, this.conversionEuler.order);
          componentData = {x:DEG2RAD * euler_w.x, y:DEG2RAD * euler_w.y, z:DEG2RAD * euler_w.z}; 
        }
        else if (componentName === 'scale') {
          componentData = scale_w.clone();
        }
      }

Here is a video of the example here in action: https://twitter.com/PlumCantaloupe/status/1286145807784321027?s=20

Screen Shot 2020-07-22 at 11 48 02 PM

@PlumCantaloupe
Copy link
Author

Have updated to only update worldMatrices once per update now.

}
else {
//pick-up
self.player.object3D.attach(self.el.object3D);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you attach it the the camera in the first place? The whole PR exists only because of it.
You shouldn't need to detach and attach the object when it's grabbed.
You should look at the https://github.com/wmurphyrd/aframe-super-hands-component library instead.

euler_w = new THREE.Euler();
pos_w = new THREE.Vector3();
quat_w = new THREE.Quaternion();
scale_w = new THREE.Vector3();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are creating those objects each time gatherComponentsData is called, not cool for the garbage collector. You may lose some fps when the garbage collector is triggered.

Copy link
Member

@vincentfretin vincentfretin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR and the added example, but I really think this is not the proper way to do.
See https://github.com/wmurphyrd/aframe-super-hands-component a great library to grab objects.

@PlumCantaloupe
Copy link
Author

PlumCantaloupe commented Mar 6, 2021

Unfortunate.

Super-hands is a huge dependency to put into a project If all you need is a way to adjust adjust scene hierarchy while still maintaining appropriate transform synchs on other client. Attaching/detaching appears to be a valid form to do so in three.js without relying on a physics-based constraint.

Being able to pick up and release an object just seemed the simplest and most intuitive way to demonstrate this.

Thanks for checking it out anyhow.

@vincentfretin
Copy link
Member

Do you really need physics with super-hands? I thought you could use some components without physics.
Looking at the grabbable component https://github.com/wmurphyrd/aframe-super-hands-component/blob/master/reaction_components/grabbable.js#L40-L70 I may be wrong.
I think you can create your pickable component based on some of the code of the grabbable component I highlighted above, and without any changes in networked-aframe.
First you need to define the temporary Vector3 and Quaternion you use outside the function (look at the tick function) so the same instance is used on each tick and not recreating it. grabber is your hand here I think, you apply the world quaternion of the hand, and the world position of the hand and set the result on the element (pickable) position this.el.setAttribute('position', this.destPosition)
If you or anyone else can create such simple component that can be used with laser-controls for example, I'll gladly use it in an example. #258

@vincentfretin
Copy link
Member

Ok I understand now that your pickable component is using the camera position, not the cursor position, this is why I found it was weird in your video.

@PlumCantaloupe
Copy link
Author

Just a note that I am continuing to use an updated version of this fork as it is so handy to be able to create networked entities that appear to obey the scene graph when synched i.e., below having an empty parent entity holding a bunch of locally transformed spheres that will all show up on the remote side with the correct placement.

So can be useful beyond just having a simple "carrying" mechanic ...

Screen Shot 2021-06-08 at 8 02 39 PM

@vincentfretin
Copy link
Member

I guess you have a networked component only on the children? Is that right?
If you add a networked component to the parent entity, can't you have the same effect? (like with the player and hands in the tracked-controllers example)
It may depend on your use case, with a networked component on the parent, if some children doesn't move often and only the parent entity moves, only the parent position will be sent. With your fork with world transform, the position of each child will be sent, 8 more messages.

@vincentfretin
Copy link
Member

vincentfretin commented Jun 9, 2021

My previous comment may be wrong, it depends when and how often the children updates their position. If the children update their world position because the parent position changed, so in the same frame, instead of the 9 u messages, you may have a single um message sent, but it will always be 9 more in bytes sent than a single component update of the parent. I guess there are also more computations, so more CPU, with your fork. It may be interesting to do a simple chrome profiling with both solutions.

@PlumCantaloupe
Copy link
Author

Yeah, I am pretty sure this solution could probably be better optimized, so I only set the ‘syncWorldTranforms’ to try where needed :/

But it is just a strange case where I need to control color/visibility of each target/sphere but I need the entire group to move together.

I think I would need to create another NAF template otherwise which I guess is a possibility also. I’ll keep poking thanks.

@vincentfretin
Copy link
Member

If the spheres doesn't move, I'll write a schema for the parent with position/rotation, and schema with material.color/visible for the spheres. I don't see anything wrong with it. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants