Posted by: Peter Quirk | December 31, 2008

Tutorial: realXtend’s powerful configuration management for models without notecards

One of the most annoying aspects of supplying parameters to scripted objects in Second Life is the need to use notecards. Notecards have a variety of annoying restrictions, and LSL lacks decent parsing tools for dealing with parameters on notecards. In contrast, RealXtend has a remarkable, but up till now undocumented facility for supplying configuration data to models in any format you like with virtually no size restrictions on the data. When combined with your favorite object serialization library in Python, a fantastic world of possibilities for initializing and persisting object data appears.

I’ve just started to explore the potential of this approach, so I’ll give you one working example to get you started. This example demonstrates solutions to a few problems that bug builders from time to time:

  • How to switch units from meters to cm, feet or inches
  • How to specify colors by name using the W3C / X11 color names
  • How to automatically place cloned objects on the ground when the ground is uneven

The example uses the Python ConfigObj library by Michael Foord and Nichola Larosa. ConfigObj implements a read/write (round-trip) handler for .ini files. Grab a copy of ConfigObj and store it in the rexserverScriptenginesLib folder. (By rights, it should be stored in site-packages, but the IronPython doesn’t seem to look there.)

We will use ConfigObj to read configuration data for a prim that looks like this:

[Units]
length=m
[Main]
color = SteelBlue
height = 10
width = 1
depth = .5

The [Units] section presently supports one key – length – which specifies what our unit of length is. The sample code handles “m” (meters), “cm” (centimeters), “ft” (feet) and “in” (inches). You can add conversions for other units.

The [Main] section lets you override the color, height, width and depth of a prim. I am being deliberately vague about the meaning of width and depth. At some point in the future I plan to determine which face of the prim is facing the avatar and define the width of that face as the width of the prim. (Of course it’s quite meaningless for a sphere or torus, but this is just a toy example.)

The configuration data is stored in the Data sub-panel of the reX tab in the object editor.

Data panel

The data is processed by the DisplayConfig class in the Python script called sampleconfig.py. You indicate this by setting the class on the Misc sub-panel of the reX tab.

Misc panel

The key ideas in the code are the following.

1.  At the head of the code we import two Python libraries:

from configobj import ConfigObj
import StringIO

2. In the EventTouch handler we instantiate a ConfigObj and pass it the data from the reX Data panel via a StringIO object. The rexGetPrimFreeData() method returns the blob of data as a string. It could have been an XML data structure, a Python Pickle object, or the URL of an external (and possibly dynamic) data source.

    def EventTouch(self,vAvatar):
        configstring = StringIO.StringIO(self.rexGetPrimFreeData())
        config = ConfigObj(configstring)

The config variable is assigned a hierarchical dictionary of results which is easily indexed.

        length = “m”
        if config.has_key(‘Units’):
            section_units = config[‘Units’]
            if section_units.has_key(‘length’):
                length = section_units[‘length’]
        if length == “m”:
            length_conversion = 1.0
        elif length == “ft”:
            length_conversion = 1.0 / 3.2808398950131
        elif length == “cm”:
            length_conversion = 0.01
        elif length == “in”:
            length_conversion = 1.0 / 39.370078740157
        else:
            self.llSay(0,”Unknown Unit: ” + length)
            return false

You can also iterate over the keys. Here we process the keys in the [Main] section.

        section = config[‘Main’]
        for k in section.iterkeys():
            key = k.lower()
            if key == “height”:
                # set height of prim
                targetheight = float(section[k]) * length_conversion
                my_scale = self.llGetScale()
                x = my_scale.x
                y = my_scale.y
                z = targetheight
                # adjust the height of the object so it sits on the ground
                land = self.llGround( Vector3(0.0, 0.0, 0.0) )
                pos = self.llGetPos()
                self.llSetPos(Vector3(pos.x, pos.y, land + (targetheight / 2.0)))
                self.llSetScale(Vector3(x,y,z))
            elif key == “width”:
                # set width of prim
                targetwidth = float(section[k]) * length_conversion
                my_scale = self.llGetScale()
                x = my_scale.x
                z = my_scale.z
                y = targetwidth
                self.llSetScale(Vector3(x,y,z))
            elif key == “depth”:
                # set depth of prim
                targetdepth = float(section[k]) * length_conversion
                my_scale = self.llGetScale()
                y = my_scale.y
                z = my_scale.z               
                x = targetdepth
                self.llSetScale(Vector3(x,y,z))
            elif key == “color”:
                # set color of prim
                targetcolor = section[k]
                if DisplayConfig.color_LUT.has_key(targetcolor):
                    v = DisplayConfig.color_LUT[targetcolor]
                    ALL_SIDES = -1
                    color = Vector3(v[0]/255.0, v[1]/255.0, v[2]/255.0)
                    self.llSetColor(color,ALL_SIDES)
                else:
                    self.llSay(0,” Invalid color – check spelling & case: ” + targetcolor)

The color Lookup Table (DisplayConfig,color_LUT) is initialized in the EventCreated method. Here are a few lines from the hundreds that define the colors.

class DisplayConfig(rxactor.Actor):
    color_LUT = {} # a dictionary object
    def GetScriptClassName():
        return “sampleconfig.DisplayConfig”

    def EventCreated(self):
        super(self.__class__,self).EventCreated()
        print “DisplayConfig EventCreated”
        # Red Colors
        DisplayConfig.color_LUT[‘IndianRed’] = (205,92,92)
        DisplayConfig.color_LUT[‘LightCoral’] = (240,128,128)
        DisplayConfig.color_LUT[‘Salmon’] = (250,128,114)
        DisplayConfig.color_LUT[‘DarkSalmon’] = (233,150,122)
        DisplayConfig.color_LUT[‘LightSalmon’] = (255,160,122)

Lastly, here’s a quick video of me creating a default cubic object and assigning it the configuration data above. The text is hard to read and you can’t see the touch event. However, you will see the object changes size and color to match the configuration data. When cloned by SHIFT-dragging, the copy is left above the ground, but when clicked, it attaches itself to the ground. I’ll try to replace this one with a higher quality recording in the next day or so.

Happy New Year!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Categories

%d bloggers like this: