Adam Frisby

Archive for the ‘sceneobject’ tag

OpenSim Internals: Rebuilding SceneObjectGroup & co.

with one comment

Since we are on the verge of tagging the 0.6 OpenSim release, there’s a number of fundemental architectural changes we are investigating internally towards a path of more vendor neutrality. One of the changes I am proposing we make is to restructure SceneObjectGroup into part of the ClientStack heirachy rather than make assumptions about client representation models.

This is a proposal - it is not beyond reproach or comment.

The Current Model: SceneObjectGroup

Let’s begin by describing the current model - the largest limitation enforced on the current model is it dictates a method of heirachy which is fairly constrained. You have a parent who contains all the properties of the children, plus a few extra, then zero or more child primitives who are linked directly to the parent. The combined whole forms the ‘object’.

When you wish to modify a group property from a child, you need to check if you have a parent, if you do, modify the parent - but beware, certain things like the Position property mean different things as a child or root (why we also have AbsolutePosition and OffsetPosition properties to avoid confusion).

Current SceneObjectGroup

The obvious downsides of this approach are - we’ve made decisions that indicate that heirachical linking will never happen (something clients such as VastPark support via their nesting methods), neither is it possible to not have linking at all (ie say a standard Torque game). We’ve also got a mess of message routing which results in a very large number of if-null checks that perform different tasks depending on the outcome.

What should a revision also contain?

As part of the opportunity to fix this set of classes, we’ve got a chance to fix up some problems with SceneObjectGroup and SceneObjectPart - among them are, Vendor Neutrality (SOG/SOP is highly vendor specific), Multiple Linking models (ie not just Root->Children)

Strengthen IEntity - Derive More.

My personal favoured solution to this problem is to go back to IEntity and strengthen it up a bit. Right now, IEntity is a very minimalist interface dictating almost nothing about entities within the Scene, but it doesnt nessecarily need to be.

There are a number of properties which we can dictate as being universal about every entity regardless of the virtual world platform - these properties can be deemed as appropriate for a core interface, off the top of my head we can say the following are true of every type of ‘Entity’ we want to implement.

  • ID - The internal number used to indicate the object ID.
  • Position - The position of the entity inworld.
  • Serialised Version - A version of the object which can be imported/exported easily (XML?)
  • Physics Shell - The bounding box or triangle collection of this collection.
  • Name?
  • Scale?
  • Rotation?
  • BoundingBox?
  • etc…

In the case of the Linden model specified above (SceneObjectGroup), the container becomes harder - we access our information about the object collection via the container. Physics shells are given for the container rather than every object inside, etc.

It also becomes possible to have multiple seperate models sharing the same scene simultaneously, each shares and communicates via the properties on IEntity. As a result, the scenegraph then resembles something like this.

New SceneObjectGroup

A part overview

IEntity - Basic building block of objects contained in virtual worlds, it contains only properties which can be considered common to every virtual world platform.

LLEntity - This forms a replaceemnt for the current SceneObjectGroup, it contains properties which are applicable to the entire object, such as Owner, Permissions, Position and others. It contains a list of Children in the form of LLPrimitive. Unlike the current SceneObjectGroup, we do not utilise the .Root property from children to store group parameters. For instance, LLEntity.Root.OffsetPosition does not dictate where the entire object is located - as the root object, it will always be [0,0,0].

LLPrimitive - This contains a replacement for SceneObjectPart, unlike the current SOP it contains properties and methods only availible unique to the individual part. Properties which are shared among the group should be members of LLEntity instead. If you wish to access a group property it is always and only availible via LLEntity, and there are never members of LLPrimitive which simply act as ‘pass-thru’s to LLEntity. If you wish to access LLPrimitive - you must always access it via the parent group, it cannot be accessed via Entities directly.

XYZEntity - This is another entity from another world platform, it may have a heirachical linking situation where multiple entities in scene are chained together and share properties between each other internally. To the OpenSim perspective however we only see multiple seperate objects.

Potential Other IEntity Derivatives

In line with the above stated goal of supporting greater vendor neutrality I would like to propose some examples of other representations used today, and how we would be able to implement them under the new scheme mentioned above.

AWEntity (ActiveWorlds™) - While I’m not proposing to actually implement a AW Clientview, it’s representation of objects in scenegraph is fairly common and very similar to how game engines handle them. In ActiveWorlds, the world is a semi-finite plane divided into ‘cells’, each cell contains a number of objects however objects cannot link and have no notion of heirachy.

To implement AWEntity - we would implement all the common IEntity interfaces such as Position, Rotation, ID, etc. Then we would in addition implement some properties for the AW object parameters (Mesh Name, Object Name, Description, Client Script, etc).

We do not need to implement anything complex in terms of parents or children and the classes are without such properties. They contain a fairly ‘bare minimum’ scenegraph representation. (Just to repeat above, I have no plans to implement any such AW entity - this is just a hypothetical example using real world object representations)

Heirachical Entity (VastPark, Melanies Proposal, etc) - In this proposal, the same object consists of multiple independent parts. Each part has a collection of parents and children - however to Scene they are all independent seperate objects.

Each presents a seperate physics shell, each presents seperate property name and ID descriptions - however in special cases such as position or rotation, updating one reflects an update on the entire set simultaneously (or at least any descendants).

Handling the new objects internally

One of the largest problems of doing this (or any change) is that SceneObjectGroup is currently referenced in a very large number of places, making changes to it is likely going to result in a large number of structural changes needing to be done.

As an example of one of those changes - the following is a proposal on how to handle a single event in IClientAPI - OnNewEntity. The following represents the path that two independent IClientAPI implementations would follow in processing the same path.

Client API Overview

The goal of this structure is to allow each client the opportunity to attempt to parse foreign object formats - if LLClientView understands XYZEntity enough to write a converter, then it can do so and send the converted result onto the Linden Viewer end user.

Likewise, if XYZClient understands Linden entities, then it can in turn write a converter to it’s internal packet representation and send the same object to it’s viewer.

In practice such converters will not be particularly easy to write - most Virtual Worlds use meshes in a variety of formats, so such converters may need to convert texture formats, mesh formats, etc. For LLClientView converters, it may simply be impossible to convert certain outside representations to something the viewer is capable of displaying and simply choose to display a lower detail representation, such as a bounding box of where the object should be.

To follow this process in detail, we’ll examine the LLClientView’s core processes.

Linden Clientview

In the first step (1), the Scene fires the OnNewEntity event which each client is listening for - as part of the event payload, the new entity is attached. This gets transmitted as a IEntity interface directly into the clientview.

Here the clientview needs to work out what kind of entity it is dealing with, it may do something like “if(IEntity is LLEntity) { … }” and handle appropriately (2).

As the LLClientStack is innately familiar with the structure of LLEntity, it can pretty much directly package it up to packets and prepare to send it to the end user (3).

However when it sees XYZEntity - XYZEntity is known to the ClientView, so it is capable of understanding it and potentially writing a converter (4) to the own internal packet formats. If we assume for a moment that XYZEntity is a AWEntity described above, then it may choose to create a temporary LLEntity which forms just a single box primitive and then package that up to the viewer.

Finally we’ve got something either in packets or close to it that we can quickly and easily send to the viewer (5). They are capable of seeing ’something’ for each Object in the scene, but naturally you need to be on a suitable client to see ever feature that platform supports.

Things unmentioned

One of the things I have left unmentioned is how to handle certain common tasks such as saving and restoring from the database. For the most part the infrastructure remains fairly similar - we can do a check ‘Is LLEntity’ in the DB adapters and follow the current path, or if we dont have one, we can use the IEntity.Serialise() method to get a text string for each entity which can be saved in the DB fairly easily.

In the case of restoring from the database however we need to in addition implement a IEntityFactory which can take a set of data (such as a serialised version) and restore it to an object, these EntityFactories could be installed via a Application or Region module.

Written by Adam Frisby

November 8th, 2008 at 10:07 am