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.
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.
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.
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);
// check for override of facing by aiming system
if (ed.getComponent(e.getId(), AimLtd.class) == null)
newFacing = new Quaternion();
newFacing = ed.getComponent(e.getId(), Pose.class).getFacing();
// overriding pose
Pose newPose = new Pose(loc, newFacing);
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.