Prim and Object Hierarchy
This article is a feature request
Linden Lab position on object/model hierarchies
In the Google video presentation Glimpse Inside a Metaverse: The Virtual World of Second Life at 30:11 to 31:00 Phillip Rosedale says that one of the first design decisions he made when Second Life was first created, was that skeletal models and object hierarchies would not be implemented.
There appears to be broad support from Linden developers for this concept, as noted in many an Office Hours discussion.
One of the greatest constraints in SL is that there is no hierarchical object composition: you cannot buy two opaque objects X and Y and create a composite object Z out of them, which can then be used as a component object by someone else.
Object composition is the bedrock of engineering and the key to exponential progress. Without it, all products are limited by the capability of individual producers working from raw materials instead of from pre-existing components. In other words, there can be no riding on the shoulders of giants. Instead of building an ever-rising pyramid, all growth is in breadth-only, always out of raw materials, so progress is severely limited.
Support for hierarchical object composition needs to be added at an infrastructure level to be effective. This means being able to create, refer to and manipulate a composite object as if it were a monolithic one, and being able to attach inner components without first having to break the containing object apart into prims. SL currently supports only one-level linking of primitive objects into linksets, not hierarchical object composition. Linksets are severely limited as they cannot be used to build a new component out of existing protected 3rd party components, except as unhelpful captive objects.
Another way of saying the same thing is that every object should be a container for other objects, if needed.
The problem of lack of hierarchical composition is often illustrated through the need for hierarchy in jointed constructs. The concept is however far more general than that, and applies equally well to any construction in which one or more discrete inner objects are components of an outer one, recursively.
Lets say that in SL we would want to make a human skeleton. When we want to do such correctly, we need to think about the bones and how they work together. Very soon we realize that if one bone moves, the bones which are connected to it and the bones connected to them, will follow the movement. Of cause, in the real world, this is only logical, but with a computer, this is a different story. It actually needs program code to make sure that the bones of the wrist, hand and fingers move along if the arm moves. When you think about it, you see that the upper arm depends on the movement of the shoulder; the lower arm depends on the upper arm; the wrist depends on the lower arm, etc.. Clearly, there is a relation between these bones. This relation is often referred to as child and parent. The shoulder in this example is the parent for the upper arm. The upper arm is itself then the parent to the lower arm, which is the parent for the wrist bone.
In SL there is not such a relation between prims. It is possible to put several prims together in a set of linked prims, but if we would want to make something which needs a child-parent relation between prims, then the designer of the object needs to do this him or herself in LSL code, which is a difficult job that takes a lot of time and adds some extra code which makes the script less maintainable.
The solution is probably fairly easier for the user to understand than it is to make the changes to the viewer and server. For an easy solution to the above example of a human skeleton, a prim could receive an extra flag indicating that it acts like a bone. If this flag is set (the box is ticked), then the prim has a few extra properties. Among which are a parent prim and child prim, but both are optional. In script some extra commands would be available:
prim llGetPrimParent(prim p); // This returns a pointer (be it a key, name or other) to the parent of the given prim. integer llGetPrimChildCount(prim p); // Returns the number of child prims linked to the given parent. prim llGetPrimChild(prim p, integer n); // Similar to llGetPrimParent(), but returns a pointer to the n'th child. llSetPrimParent(prim child, prim parent); // This command takes two prims and links them by way of script. This is usefull for prims that are rezzed by script. // This sets the parent of the given child prim. Of cause it also sets the child of the given parent to the given child. llAddPrimChild(prim child, prim parent); llRemovePrimChild(prim child, prim parent); // Adds or removes a given child prim to the given parent prim. This seems to do the same as llSetPrimParent, // but there is a subtle difference. While a prim can only have one parent, it can have multiple children. // It is true that these can be used for most of the linkage, but the first prim in a chain never has a parent and to set this, // llSetPrimParent() is needed. llSetJointRotation(prim p, rotation rot); // This would set the angle between the given prim and it's parent. Thereby rotating it around the point where it meets it's parent. llSetSkinPoseRotation(prim p, rotation rot); // This would set a special angle value for the joint. It is called the skin pose, which is the rotation of the joint in a rest. // One might see the skin pose as the initial rotation or a rotation where you want the joint to reset to. llSetSkinPoseRotationAndPosition(prim p, rotation rot, vector pos, bool StretchConnectedPrims, bool MoveConnectedPrims); // Sometimes, it might be needed to also set the position of a child prim and stretch or move other prims accordingly. llSetSkinPose(prim p); // This is a command travels through the entire hierarchy and resets all the joints to their skin pose. // This should be just a bit faster than explicitly setting each joint to its skin pose from a script loop. llSetJointOmega(prim p, vector v, float speed, float time); // Like llSetTargetOmega(), but this one takes into account, the rotation of all the child prims. // By keeping two different commands for this, and the parent-child relationship needing probably a lot of extra code, it is // probably better to keep the function this one offers seperated from the existing llSetTargetOmega().
More such commands could probably be invented. This allows for easy multi axis rotation of objects like disco lights and lots of other designs which need extra flexibility. -Xoren Raymaker
Unscripted object composition
Note that the above example provided an ambitious use case for hierarchical objects in which each level of the hierarchy is associated with scripted articulation.
In contrast, the most common use case for hierarchical objects would not involve articulation at all, but merely hierarchical containment. The resulting component assemblies would be rigid, just like unscripted linksets, but much more powerful because each hierarchical object would be an extensible tree of components, and the component parts held in this tree would be individually accessible and replaceable.
To graft hierarchy retrospectively onto the existing one-level linksets could quite easily break existing content. Consequently, extending linksets is not a recommended design path. Instead, one of two alternative and much safer approaches are suggested, as follows:
- Either: create a whole new set of prims derived from the existing ones but with hierarchical linking behaviour instead of linkset linking behaviour. Old content that uses linksets would be unaffected, and linksets would continue to be available for simple construction.
- Or: create only one new object type, the hierarchical container, into which any of the existing prim types or linksets or further (child) hierarchical containers can be placed. The legacy behaviour of linksets would not be affected because the new hierarchical operations would apply to the containers at each level, and not to the objects they contain. (It would require careful design to present this distinction unambiguously at the user-level.)
The second approach is more difficult to develop, but it would be much more powerful since all future object types would automatically be available for use as internal components, without separate linkset and hierarchical versions needing to be maintained.
Jiras and other references
- MISC-2237 - Requiered additions and changes to objects and scripting, for improving content-creation capabilities (Meta issue)
- MISC-428 - Hierarchical linking of prim/objects
- MISC-1434 - Add an orientable linking capability
- ISC-227 - Implement optional prim-joint hierachy
- SVC-93 - llSetPrimitiveParams PRIM_ROTATION and llSetRot incorrectly implemented for child prims