Child facing problem

(disclaimer: this might not be directly related to zay-es, but since the code is in zay-es notation, probably better to post it here)

for( Entity e : entities ) 
{
    EntityId coreId = ed.getComponent(e.getId(), PartOf.class).getCore();
    Pose corePose = ed.getComponent(coreId, Pose.class);
    Pose childPose = ed.getComponent(e.getId(), Pose.class);
    Vector3f childOffset = ed.getComponent(e.getId(), PartOf.class).getOffset();
    Quaternion childFacing = childPose.getFacing();
                   
    Vector3f newOffVec = corePose.getFacing().clone().mult(childOffset);
    Vector3f loc = corePose.getLocation().clone().addLocal(newOffVec);
    Quaternion facing = corePose.getFacing().clone().multLocal(childFacing);
    
    Pose newPose = new Pose(loc,facing);
    ed.setComponent(e.getId(), newPose);
}

So parent rotation is ok, child position is ok and child facing is a spinning madness. What did I miss?

Facing is supposed to be the direction forward?

If so just do
forward = rotation.multLocal(Vector(0,0,1));

Hmm well facing is a Quaternion that represents rotations for both parent and child, so I’m trying to get child facing with respect to parent’s. They’re both then set in (courtesy of pspeed):

protected void updateModelSpatial( Entity e, Spatial s ) {
        Position p = e.get(Position.class);
        s.setLocalTranslation(p.getLocation());
        s.setLocalRotation(p.getFacing());
    }

Can you describe at a higher level what effect you actually want? Are these parent/child entities not also parent/child spatials?

No they are not. They’re created as separate entities with separate spatials factory feeds them with (it’s pretty much your Asteroid Panic model, I basically took general purpose classes from there w/o changes). During creation, hierarchy is created by attaching PartOf (or ChildOf if you like) component. Then, I have a system that keeps compound model altogether - which code I posted above. It takes all components that have Pose and PartOf and does what’s put above. Then, I have another system that does changes to Pose of parent entity (let’s name it PropulsionSystem), but never touches children (means PartOf ones), that are supposed to be updated by compound system taking current location/facing from parent.

And what I want is my turrets to rotate together with their parent model. They do it in terms of location, but their facing goes nuts somehow.

Wouldn’t the whole thing be easier just to let pose by relative to parent (in the case of children) and use PartOf to parent the child spatial to the parent spatial?

As you have it, it’s possible that your children can slide around.

Anyway, your issue is that child position is built only from parent pose + childOffset… but there is no equivalent of childOffset for rotation. Your rotation is built from parent rotation and last rotation… which is of course going to include last rotation and the previous rotation and the previous rotation, etc…

To continue like you are, PartOf basically has to include everything Pose has. Alternately, you can add four or five lines of code to your model state to manage a PartOf entity set and then get rid of all of this other stuff (and the extra redundant fields on PartOf). Though I guess it would mean that everywhere you need a real Pose for a child you’d have to do that calculation also.

So anyway, to continue like you are just make PartOf have offset rotation also.

No, no, no, sir, don’t think I have plenty of solutions on my table and I pick the hardest one just because I want it to be like that. If you expand your first sentence about relative placement in more detail, I will only appreciate it!

p.s. and as far as I realize, in addition to “Math for Dummies” there is a need in “Math for Complete Idiots” topic.

Well, there is nothing wrong with your approach as long as PartOf has rotation also… but if you want to let Pose be relative…

Give your model state a second EntitySet that is for PartOf… or perhaps PartOf, Pose, and whatever else models normally have. When you see something added to this set, find the spatial for its parent and the spatial for the child and attach them. When you see things removed from this set, find the child spatial and call removeFromParent() on it.

The only down side of this approach is that everything that needs to know the child’s real world position will have to know its parent also.

By now I got it to work only with one parent-child depth level so I can’t post final solution yet, but some intermediate thoughts that possibly can be useful for someone who might follow my way:
First, I tried the relative approach and finally decided not to use it, at least right now, cause it implies several questionable (for me) things. In short: while keeping things at lowest possible level (model state in this case) seems to be the best way at first, there are drawbacks resulting from the fact that Node is a subtype of Spatial, not vice versa. So I can’t just attach to spatial, I need to create Node first. Which means either update-time Node creation and disposal which I am not sure is good for performance (besides losing the uniformity of processing entities as they can be of different type) or creating a Node for every single Entity which means overhead. The other drawback already mentioned above is the need to do additional calculations to get world pose. So I decided to keep it in different system and will probably ask my friend (who is way smarter than me when it comes to quaternions) to help me with processing hierarchies recursively to support arbitrary tree depths.

In my case, all of my entities are already nodes so I wouldn’t have had this issue.

For me, it’s incredibly rare that a particular model is just one Geometry, already positioned exactly as required relative to 0, etc…

As to your other problem, it’s easy… if you need to do the same thing the scene graph is doing then you will end up just doing the same thing the scene graph is doing.

Find the root, traverse down the children and use a Transform to keep track of all of the relative positions and rotations. Every time something in the hierarchy moves, you will need to recalculate all of the children… and you will have to take care to always do all of your updates in a single frame so that children don’t slide around relative to their parents.

1 Like

Thanks, Paul, not that I’m saying my solution is the best for all the possible contexts, not at all. Just it seems less redesigning right now, and it is easier for me to look at my single system to make changes than to look into modelstate as well. Anyway all that you have said is very helpful, that’s only my incompetence that prevents me from completely redesigning model state. As it’s now very transparent and works well, and I can occasionally add some mess I will suffer refining then.

Yeah, I was just hinting that with your current approach… look at what the scene graph is already doing to figure out what you need to do in your child positioning system.

So final code to keep them all together looks like this:

EntityId coreId = ed.getComponent(e.getId(), PartOf.class).getCore();
Pose corePose = ed.getComponent(coreId, Pose.class);
Vector3f childOffset = ed.getComponent(e.getId(), PartOf.class).getOffset();

Vector3f newOffVec = corePose.getFacing().mult(childOffset);
Vector3f loc = corePose.getLocation().clone().addLocal(newOffVec);
Vector3f dir = new Vector3f(Vector3f.UNIT_Z);
               
dir = corePose.getFacing().clone().multLocal(dir);
               
Vector3f up = new Vector3f(Vector3f.UNIT_Y);
               
up = corePose.getFacing().clone().multLocal(up);

Quaternion newFacing;
               
// check for override of facing by aiming system
if (ed.getComponent(e.getId(), AimLtd.class) == null)
{
      newFacing = new Quaternion();
      newFacing.lookAt(dir, up);
} else
{
      newFacing = ed.getComponent(e.getId(), Pose.class).getFacing();
}
// overriding pose
Pose newPose = new Pose(loc, newFacing);
ed.setComponent(e.getId(), newPose);

First confusing moment is that I’m hundred percent sure I WAS trying to calculate rotations this way at first and it didn’t work. Maybe somewhere was mult instead of multlocal or vice versa (subtle enough thing btw, always read javadoc five times before use). Second moment I’m not that happy with is that there’s concurrent system dependency and I have to keep track of which overrides what manually. Not the best design probably, but so far it works and I can go further at least. To my dream of higher level logic lol.

Yeah, sometimes it’s best to move onto other problems at some point anyway.

…then the answer will hit you in the shower in 4 weeks when you are deep into something else and don’t have time to work on it. :slight_smile: