[sldev] Investigating how the viewer creates object (Re: New
Feature: Mute Visibility)
Able Whitman
able.whitman at gmail.com
Sun Jun 3 16:42:59 PDT 2007
With an eye towards figuring out how to implement visual object muting
(VWR-1017), I decided to look into how the viewer handles sim notifications
of new objects for it to render. I don't know how interesting or useful this
is, but I thought I'd share what I've learned. If it would be helpful, I'd
be happy to make a wiki entry with this information.
In summary, the process by which the viewer handles new object notifications
(as well as existing object updates) works like this:
* register_viewer_callbacks() in llstartup.cpp registers an handler for the
ObjectUpdate message (defined in message_template.msg)
* the viewer receives an ObjectUpdate message from a sim
* the message is handled by process_object_update() in llviewermessage.cpp
* the message is passed to gObjectList.processObjectUpdate() in
llviewerobjectlist.cpp
* if the object is a new object, gObjectList.createObject() is called
- the object params are passed to the static
LLViewerObject::createObject() in llviewerobject.cpp
- LLViewerObject::createObject() examines the type of object to create,
and calls a specific constructor of a descendant of LLViewerObject
- for prims, flexi-prims, and sculpties, the class in question is
LLVOVolume
* gObjectList.processObjectUpdate() passes the message to
processUpdateCore()
* processUpdateCore() passes the message to processUpdateMessage() on the
instance returned by LLViewerObject::createObject()
- for all LLViewerObject objects:
. the region and coordinates for the object are obtained
. object params like scale, material, touch (click) actions, and
attached sounds are processed
. object rotation, velocity, acceleration, angular velocity, hover text,
particle system settings, parent object, etc. are processed
- for LLVOVolume objects:
. scuplted prim settings and texture are processed
. animated textures are processed
. prim parameters like hollow, shear, twist, taper, skew, etc. are
processed by the static LLVolumeMessage::unpackVolumeParams()
. prim textures are updated by LLVOVolume::updateTEData()
* processUpdateCore() adds the object to the list of active viewer objects,
and also to the render pipeline via gPipeline.addObject()
The process by which the viewer handles object deletion notifications works
like this:
* register_viewer_callbacks() in llstartup.cpp registers an handler for the
KillObject message (defined in message_template.msg)
* the viewer receives a KillObject message from a sim
* the message is handled by process_kill_object() in llviewermessage.cpp,
which loops through all objects to be killed as listed in the message
* if an object to be killed is selected, it is unselected via
gSelectMgr->removeObjectFromSelections()
* the viewer object to be killed is found via gObjectList.findObject()
* the object is killed by calling gObjectList.killObject()
Thoughts on Implementing Object Muting:
It seems like the straightforward approach would be something like this:
* add a flag to the LLVOVolume class (perhaps BOOL mIsVisuallyMuted or
similar) which defaults to FALSE
* in LLVOVolume::processUpdateMessage(), use the prim region, coordinates,
object id, and owner id to determine whether to flag the object as muted or
not
* if the object is muted, override the object audio and click (touch)
settings so that the viewer's version of the object doesn't play sound or
respond to touch events
* hook into LLVOVolume::updateTextures() to force the object to use the
default texture for all faces if the object is muted
* also hook into LLPrimitive::unpackTEMessage() to force any texture updates
for the object sent from the sim result in "new" textures also being forced
to be the default texture
Note that the default texture is the static LLViewerImage*
LLViewerImage::sDefaultImagep, which uses the IMG_DEFAULT texture LLUUID
(d2114404-dd59-4a4d-8e6c-49359e91bbf0, which seems to be the default grey
texture).
I make a quick-and-dirty hack similar to the above to the LLVOAvatar class,
which forced every avatar to have their skin and clothing rendered with the
default grey texture. It was in the process of making this hack that I
discovered that hooking updateTextures() is not enough, and that
unpackTEMessage() was also necessary.
Open issues I haven't investigated yet:
* how to store and persist the list of filters to use to determine which
objects are muted
* how to hook into the viewer UI to enable users to mark object as muted or
unmuted
* how to determine whether a given region and coordinate, whether that point
exists within a given parcel or not
I plan to look into how the existing muting mechanism works, to see how
feasible it would be to extend this to support visual muting of object as
well. I'm also going to look into how the viewer determines the boundaries
for parcels in a region, to see if there is a way to quickly and efficiently
check whether a point exists within a given parcel or not, since I believe
that muting object by parcel is the most effective way to choose objects to
mute.
More Details on Object Creation:
The viewer is notified buy the simulator of new objects to draw by a variety
of messages, most of them similar to the ObjectUpdate message (defined in
message_template.msg). These are the messages that appear to be related to
sim notifications of object that need to be drawn by the viewer:
* ObjectUpdate
- handled by process_object_update() in llviewermessage.cpp
* ObjectUpdateCompressed
- handled by process_compressed_object_update() in llviewermessage.cpp
* ObjectUpdateCached
- handled by process_cached_object_update() in llviewermessage.cpp
* ImprovedTerseObjectUpdate
- handled by process_terse_object_update() in llviewermessage.cpp
- essentially the same handling code as ObjectUpdateCompressed
* KillObject
It's not entirely clear to me what what specific circumstances cause the
various ObjectUpdate messages to be generated, but for now I'm mostly
concerned with the general process for creating and updating objects.
Message handlers are configured by register_viewer_callbacks() in
llstartup.cpp. All of the ObjectUpdate-related messages call through to
LLViewerObjectList::processObjectUpdate() in llviewerobjectlist.cpp with a
little or no message-specific preprocessing.
In LLViewerObjectList::processObjectUpdate():
* the handle for the region that the object is in is decoded
* this handled is validated against a list of regions known to the viewer
* the viewer looks up the object in its list of known objects
- the global viewer object list is an LLViewerObjectList named gObjectList
- lookups for known objects via gObjectList->findObject()
* if the object already exists, the viewer updates its localid (which I
think is session-specific)
- the localid will also be updated if the object has moved from one region
to another
* if the object doesn't exist, the viewer creates it
Object creation occurrs in LLViewerObjectList::createObject(), which just
does some pre-processing and calls down to the static method
LLViewerObject::createObject() in llviewerobject.cpp. Based on the type of
object specified in the ObjectUpdate message, createObject() call
constructors for one of the following classes:
LLVOVolume, LLVOAvatar, LLVOGrass, LLVOTree, LLVOTextBubble, LLVOClouds,
LLVOSurfacePatch, LLVOSky, LLVOStars, LLVOWater, LLVOGround, LLVOPartGroup
All of these classes derive (eventually) from LLViewerObject, which itself
derives from LLPrimitive. Of these classes, the most interesting one is
LLBVOVolume, which is the class that encapsulates what we generally think of
as "prims". This includes all basic prim types, plus flexi-prims and
sculpted prims. Once the existing LLViewerObject is updated, or a new one is
created, processUpdateObject() calls into
LLViewerObjectList::processUpdateCore() in llviewerobjectlist.cpp.
The first thing processUpdateCore() does is call the instance method
processUpdateMessage() on the LLViewerObject instance returned from
LLViewerObject::createObject(). (If the object already existed, this method
is called on the existing instance of the object found via
gObjectList->findObject().)
In the case of prim objects, the method called is
LLVOVolume::processUpdateMessage(), which immediately calls into the base
implementation on LLViewerObject::processUpdateMessage().
The base implmenentation of processUpdateMessage():
* first determines the region the object is in and its coordinates in that
region
* processes properties common to all the different ObjectUpdate-related
messages: object id, parent id, material, attached sound, click (touch)
action, scale, etc.
* processes properties specific to the different types of
ObjectUpdate-related messages
There is rather a mess of code which handles the special cases of
ObjectUpdate messages--cached, compressed, terse, or improved terse message
types. The special cases are handled by nested switch and if/then blocks,
with a lot of magic numbers and a good deal of code specific to more-derived
LLViewerObject types. This section of code alone is more than 1,000 lins
long, and parts of it probably should be moved into the derived
implementations of processUpdateMessage(). (The whole base implementation of
this method is about 1,200 lines, so the special-cased section is likely
ripe for refactoring.)
The derived LLVOVolume::processUpdateMessage():
* processes scuplted prim settings and textures
* processes animated textures
* processes prim parameters like hollow, shear, twist, taper, skew, etc. via
the static method LLVolumeMessage::unpackVolumeParams()
* updates prim textures via LLVOVolume::updateTEData()
Finally, processUpdateCore() adds the prim to the list of active viewer
object (if the object is indeed active) and then calls the updateTextures()
method on the instance of LLViwerObject for the object. Then the object is
added to the render pipeline by calling gPipeline.addObject().
--Able
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.secondlife.com/pipermail/sldev/attachments/20070603/874e855b/attachment-0001.htm
More information about the SLDev
mailing list