User:Dz/Avatar Scale

From OpenSimulator

< User:Dz(Difference between revisions)
Jump to: navigation, search
(Added script, animation, and note card information sections)
(Added latest version of the actual code)
Line 27: Line 27:
  
 
<source lang = "lsl">
 
<source lang = "lsl">
 +
//  Avatar proportion scale  BETA
 +
//  based on an "8 head" (Natural) or "9 head" (Heroic) human shape
  
 +
// Doug Osborn  MOSES grid  9/17/2014
 +
// Copyright (c) 2014, Douglas Osborn
 +
// All rights reserved.
 +
 +
// Redistribution and use in source and binary forms, with or without
 +
// modification, are permitted provided that the following conditions are met:
 +
 +
// 1. Redistributions of source code must retain the above copyright notice, this
 +
//    list of conditions and the following disclaimer.
 +
// 2. Redistributions in binary form must reproduce the above copyright notice,
 +
//    this list of conditions and the following disclaimer in the documentation
 +
//    and/or other materials provided with the distribution.
 +
 +
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 +
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 +
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 +
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 +
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 +
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 +
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 +
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 +
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
 +
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 +
 +
//
 +
//  To create your own scale from scratch, 
 +
//      Make a cube to act as your base.  Texture it as you like and turn it phantom.
 +
//      Make 9 more cubes and stack them on top of each other. 
 +
//      Link the 9 cubes together and then link that stack to your "base" cube
 +
//      Copy the [CheckPorportions] animation and the [Proportional Avatar Sizing} notecard into the base
 +
//      Drop this script into your base cube.
 +
//      Your stack of cubes should transform into a "Natural" scale for a 2 meter avatar
 +
//
 +
//
 +
 +
vector MyLocation = <0,0,0>;
 +
rotation MyRotation = ZERO_ROTATION;
 +
 +
list offsetMultiplier = [1,3,5,7,9,11,13,15,17];
 +
 +
string ANIMATION = "CheckProportions";
 +
 +
key avatar = NULL_KEY;
 +
list menuButtons = ["Heroic","Natural","Set Height"];
 +
list heightButtons = [ "Move Up", "Move Down"];
 +
integer menuchannel = -13572468;
 +
integer heightchannel = -13572469;
 +
integer mListener = 0;
 +
integer cListener = 0;
 +
float sitTargetHeight = 0;
 +
integer NaturalMode = TRUE;
 +
string menuStatusInfo = "";
 +
float currentScaleSize = 2.0;
 +
float menuTimeout = 30.0;
 +
vector primColor = <0.0, 0.0, 0.0>;
 +
float panelDivisor = 4.0;
 +
float widthAdjust = 0.02;
 +
 +
setScaleDimensions(float newSize)
 +
{
 +
    integer numlinks = 11;
 +
    panelDivisor = 4.5;
 +
    widthAdjust = 0.05;
 +
   
 +
    if (NaturalMode)
 +
    {
 +
        numlinks = 10;
 +
        panelDivisor = 4;
 +
        widthAdjust = -0.025;
 +
    }
 +
   
 +
    llSay(0, "Resizing scale for a  " +(string) newSize + " meter avatar");
 +
   
 +
    currentScaleSize = newSize;
 +
           
 +
    sitTargetHeight = newSize/2.3;
 +
       
 +
    llSitTarget(< 0,0,sitTargetHeight>,MyRotation*llEuler2Rot(<0,0,PI_BY_TWO>));
 +
       
 +
    integer link = 2;
 +
    while (link < numlinks)
 +
    {
 +
        float heightOffset = (llList2Integer(offsetMultiplier,link-2) * (newSize/(panelDivisor * 4)));
 +
        if (link%2 == 0)
 +
        {
 +
            primColor = <0.75, 0.75, 0.75>;
 +
        }
 +
        else
 +
        {
 +
            primColor = <0.0, 0.0, 0.0>;
 +
        }           
 +
           
 +
        llSetLinkPrimitiveParamsFast( link,[PRIM_SIZE, < newSize/panelDivisor + widthAdjust ,0.02, newSize/(panelDivisor * 2)>,
 +
                                                    PRIM_POS_LOCAL,<0.0 , 0.0, heightOffset>,
 +
                                                    PRIM_ROT_LOCAL,MyRotation, PRIM_PHANTOM,TRUE,
 +
                                                    PRIM_COLOR, ALL_SIDES, primColor, 0.5 ]);
 +
        link ++;
 +
    }
 +
       
 +
    if (NaturalMode)
 +
    {
 +
        llSetLinkPrimitiveParamsFast( link,[PRIM_SIZE, < newSize/panelDivisor + widthAdjust ,0.02, newSize/(panelDivisor * 2)>,
 +
                                                    PRIM_POS_LOCAL,<0.0 , 0.0, 0.0>,
 +
                                                    PRIM_ROT_LOCAL,MyRotation, PRIM_PHANTOM,TRUE,
 +
                                                    PRIM_COLOR, ALL_SIDES, primColor, 0.0]);
 +
    }     
 +
}
 +
 +
default
 +
{
 +
    state_entry()
 +
    {
 +
        llSay(0, "OpenSimian Avatar scale is ready to use");
 +
        MyLocation = llGetRootPosition();
 +
        MyRotation = llGetRootRotation();
 +
        setScaleDimensions(currentScaleSize);     
 +
    }
 +
   
 +
    touch_start(integer numTouches)
 +
    {
 +
        string mode = "Heroic";
 +
        if(NaturalMode)
 +
            mode = "Natural";
 +
           
 +
        menuStatusInfo = "\nCurrent mode : " + mode + "\nAvatar height: " + llGetSubString((string) currentScaleSize, 0, 4) +"(meters)\n \nMake a selection";     
 +
 +
        if (avatar != NULL_KEY)
 +
        {
 +
            cListener = llListen( heightchannel, "", "", "");
 +
            llDialog(llDetectedKey(0), menuStatusInfo,  heightButtons, heightchannel);
 +
            llSetTimerEvent(menuTimeout);
 +
        }
 +
        else
 +
        {
 +
            mListener = llListen( menuchannel, "", "", "");     
 +
            llDialog(llDetectedKey(0), menuStatusInfo,  menuButtons, menuchannel);
 +
            llSetTimerEvent(menuTimeout);
 +
        }   
 +
    }
 +
   
 +
    listen(integer channel, string name, key id, string message)
 +
    {
 +
        if(channel == menuchannel)
 +
        {
 +
            llListenRemove(mListener);
 +
            llSetTimerEvent(0);
 +
           
 +
            if (message == "Heroic")
 +
            {
 +
                if ( NaturalMode )
 +
                {
 +
                    NaturalMode = FALSE;
 +
                    llSay(0,"Resetting to Heroic Mode");
 +
                    setScaleDimensions(currentScaleSize);   
 +
                }
 +
                else
 +
                {
 +
                    llSay(0,"Already in Heroic Mode");
 +
                }
 +
            }
 +
           
 +
            if (message == "Natural")
 +
            {
 +
                if ( NaturalMode )
 +
                {
 +
                    llSay(0,"Already in Natural Mode");
 +
                }
 +
                else
 +
                {
 +
                    NaturalMode = TRUE;
 +
                    llSay(0,"Resetting to Natural Mode");
 +
                    setScaleDimensions(currentScaleSize);   
 +
                }
 +
            }           
 +
                   
 +
            if (message == "Set Height")
 +
            {
 +
                cListener = llListen( heightchannel, "", "", "");
 +
                llTextBox(id, "Enter the total Height of the avatar in METERS", heightchannel);
 +
            }
 +
            if (message == "Move Up")
 +
            {
 +
                sitTargetHeight += .025;
 +
                llSetLinkPrimitiveParams(llGetNumberOfPrims(),[PRIM_POS_LOCAL,<0,0,sitTargetHeight>]);
 +
                mListener = llListen( menuchannel, "", "", "");       
 +
                llDialog(id, "Select an Option",  heightButtons, menuchannel);                 
 +
            }
 +
            if (message == "Move Down")
 +
            {
 +
                sitTargetHeight -= .025;
 +
                llSetLinkPrimitiveParams(llGetNumberOfPrims(),[PRIM_POS_LOCAL,<0,0,sitTargetHeight>]);
 +
                mListener = llListen( menuchannel, "", "", "");       
 +
                llDialog(id, "Select an Option",  heightButtons, menuchannel);                 
 +
            }
 +
        }           
 +
        if(channel== heightchannel)
 +
        {
 +
            llListenRemove(cListener);
 +
            setScaleDimensions((float) message);         
 +
        }
 +
    }
 +
   
 +
    changed(integer change)
 +
    {
 +
        if(change & CHANGED_LINK)
 +
        {
 +
            avatar = llAvatarOnSitTarget();
 +
            if(avatar != NULL_KEY)
 +
            {
 +
                llRequestPermissions(avatar,  PERMISSION_TRIGGER_ANIMATION);
 +
            }
 +
            else
 +
            {
 +
                if (llGetPermissionsKey() != NULL_KEY)
 +
                {
 +
                    llStopAnimation(ANIMATION);
 +
                    llSitTarget(< 0,0,sitTargetHeight>,MyRotation*llEuler2Rot(<0,0,PI_BY_TWO>));
 +
                    avatar = NULL_KEY;
 +
                }
 +
            }
 +
        }
 +
        if(change & CHANGED_INVENTORY) { llResetScript(); }
 +
        if(change & CHANGED_OWNER)    { llResetScript(); }
 +
    }
 +
 +
 +
    run_time_permissions(integer perm)
 +
    {
 +
        if(perm & PERMISSION_TRIGGER_ANIMATION) {
 +
            llStopAnimation("sit");
 +
            llStartAnimation(ANIMATION);
 +
            mListener = llListen( menuchannel, "", "", "");       
 +
            llDialog(avatar, "Select an Option",  heightButtons, menuchannel);   
 +
        }
 +
    }
 +
   
 +
    timer()
 +
    {
 +
        llSay(0, "Menu selection time limit ( 30 seconds ) exceeded, please touch the scale again for a new menu");
 +
        llListenRemove(mListener);
 +
        llSetTimerEvent(0);
 +
    }
 +
}
 
</source >
 
</source >
  

Revision as of 12:13, 21 September 2014

Contents

Avatar Scale

This project was the result of a recent discussion on the difficulty of creating realistically proportioned avatars in OpenSimulator. With 7 years of experience "tweaking" my own shapes, and helping friends with "body makeovers", it is clear that the process of using the sliders in [edit appearance] to create a reasonable shape is not a simple task for most. Being able to generate realistic or even attractive shapes can be frustrating, and new users who are "stuck" using avatars they think look "weird" are less likely to continue experimenting and improving this important skill.

Unfortunately, I cannot change the process of how avatar shapes are modified, But what I can give you is a way to measure the results of your shape changing efforts. This scale is not the "silver bullet" to making attractive avatars, but it has been a useful technique I have used to generate the "rough shapes" I build my avatars around. This project consist of 3 parts. An LSL script, a very basic BVH animation file, and a note card describing the features and some web references that might be useful for future investigation.

As with all OpenSimian projects posted here, You are free to copy/modify/and IMPROVE the functionality to fit your needs.

The scale relies on the ability to move a seated avatar via script. This functionality has been the focus of a number of MANTIS reports, and may not be working unless you are using a very recent release. It is still possible to use the script but you will have to edit the script and adjust the calculation that sets the initial sit position.

If you have feedback on script errors, please post it to the page discussion User_talk:Dz/Avatar Scale


The script

I'm not going to write a lot about this script. It is relatively simple and I haven't really spent a ton of time refining it. Read the comments for information about use and distribution..

//  Avatar proportion scale   BETA
//  based on an "8 head" (Natural) or "9 head" (Heroic) human shape
 
// Doug Osborn   MOSES grid   9/17/2014
// Copyright (c) 2014, Douglas Osborn
// All rights reserved.
 
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
 
// 1. Redistributions of source code must retain the above copyright notice, this
//    list of conditions and the following disclaimer. 
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS   
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 
//
//   To create your own scale from scratch,  
//      Make a cube to act as your base.  Texture it as you like and turn it phantom.
//      Make 9 more cubes and stack them on top of each other.   
//      Link the 9 cubes together and then link that stack to your "base" cube
//      Copy the [CheckPorportions] animation and the [Proportional Avatar Sizing} notecard into the base
//      Drop this script into your base cube. 
//      Your stack of cubes should transform into a "Natural" scale for a 2 meter avatar
//
//
 
vector MyLocation = <0,0,0>;
rotation MyRotation = ZERO_ROTATION;
 
list offsetMultiplier = [1,3,5,7,9,11,13,15,17];
 
string ANIMATION = "CheckProportions";
 
key avatar = NULL_KEY;
list menuButtons = ["Heroic","Natural","Set Height"]; 
list heightButtons = [ "Move Up", "Move Down"];
integer menuchannel = -13572468;
integer heightchannel = -13572469;
integer mListener = 0;
integer cListener = 0;
float sitTargetHeight = 0;
integer NaturalMode = TRUE;
string menuStatusInfo = "";
float currentScaleSize = 2.0;
float menuTimeout = 30.0;
vector primColor = <0.0, 0.0, 0.0>;
float panelDivisor = 4.0;
float widthAdjust = 0.02;
 
setScaleDimensions(float newSize)
{
    integer numlinks = 11;
    panelDivisor = 4.5;
    widthAdjust = 0.05;
 
    if (NaturalMode)
    {
        numlinks = 10;
        panelDivisor = 4;
        widthAdjust = -0.025;
    }
 
    llSay(0, "Resizing scale for a  " +(string) newSize + " meter avatar");
 
    currentScaleSize = newSize;
 
    sitTargetHeight = newSize/2.3;
 
    llSitTarget(< 0,0,sitTargetHeight>,MyRotation*llEuler2Rot(<0,0,PI_BY_TWO>));
 
    integer link = 2;
    while (link < numlinks)
    {
        float heightOffset = (llList2Integer(offsetMultiplier,link-2) * (newSize/(panelDivisor * 4)));
        if (link%2 == 0)
        { 
            primColor = <0.75, 0.75, 0.75>;
        }
        else
        {
            primColor = <0.0, 0.0, 0.0>;
        }            
 
        llSetLinkPrimitiveParamsFast( link,[PRIM_SIZE, < newSize/panelDivisor + widthAdjust ,0.02, newSize/(panelDivisor * 2)>, 
                                                    PRIM_POS_LOCAL,<0.0 , 0.0, heightOffset>,
                                                    PRIM_ROT_LOCAL,MyRotation, PRIM_PHANTOM,TRUE,
                                                    PRIM_COLOR, ALL_SIDES, primColor, 0.5 ]);
        link ++;
    } 
 
    if (NaturalMode)
    {
        llSetLinkPrimitiveParamsFast( link,[PRIM_SIZE, < newSize/panelDivisor + widthAdjust ,0.02, newSize/(panelDivisor * 2)>, 
                                                    PRIM_POS_LOCAL,<0.0 , 0.0, 0.0>,
                                                    PRIM_ROT_LOCAL,MyRotation, PRIM_PHANTOM,TRUE, 
                                                    PRIM_COLOR, ALL_SIDES, primColor, 0.0]);
    }       
}
 
default
{
    state_entry()
    {
        llSay(0, "OpenSimian Avatar scale is ready to use");
        MyLocation = llGetRootPosition(); 
        MyRotation = llGetRootRotation(); 
        setScaleDimensions(currentScaleSize);      
    }
 
    touch_start(integer numTouches)
    {
        string mode = "Heroic";
        if(NaturalMode)
            mode = "Natural";
 
        menuStatusInfo = "\nCurrent mode : " + mode + "\nAvatar height: " + llGetSubString((string) currentScaleSize, 0, 4) +"(meters)\n \nMake a selection";      
 
        if (avatar != NULL_KEY)
        {
            cListener = llListen( heightchannel, "", "", ""); 
            llDialog(llDetectedKey(0), menuStatusInfo,  heightButtons, heightchannel);
            llSetTimerEvent(menuTimeout); 
        } 
        else
        { 
            mListener = llListen( menuchannel, "", "", "");       
            llDialog(llDetectedKey(0), menuStatusInfo,  menuButtons, menuchannel);
            llSetTimerEvent(menuTimeout);
        }     
    }
 
    listen(integer channel, string name, key id, string message)
    {
        if(channel == menuchannel)
        {
            llListenRemove(mListener);
            llSetTimerEvent(0);
 
            if (message == "Heroic")
            {
                if ( NaturalMode )
                {
                    NaturalMode = FALSE;
                    llSay(0,"Resetting to Heroic Mode");
                    setScaleDimensions(currentScaleSize);    
                }
                else
                {
                    llSay(0,"Already in Heroic Mode");
                }
            }
 
            if (message == "Natural")
            {
                if ( NaturalMode )
                {
                    llSay(0,"Already in Natural Mode");
                }
                else
                {
                    NaturalMode = TRUE;
                    llSay(0,"Resetting to Natural Mode");
                    setScaleDimensions(currentScaleSize);    
                }
            }            
 
            if (message == "Set Height")
            {
                cListener = llListen( heightchannel, "", "", ""); 
                llTextBox(id, "Enter the total Height of the avatar in METERS", heightchannel);
            }
            if (message == "Move Up")
            {
                sitTargetHeight += .025;
                llSetLinkPrimitiveParams(llGetNumberOfPrims(),[PRIM_POS_LOCAL,<0,0,sitTargetHeight>]); 
                mListener = llListen( menuchannel, "", "", "");         
                llDialog(id, "Select an Option",  heightButtons, menuchannel);                  
            }
            if (message == "Move Down")
            {
                sitTargetHeight -= .025;
                llSetLinkPrimitiveParams(llGetNumberOfPrims(),[PRIM_POS_LOCAL,<0,0,sitTargetHeight>]); 
                mListener = llListen( menuchannel, "", "", "");         
                llDialog(id, "Select an Option",  heightButtons, menuchannel);                  
            }
        }            
        if(channel== heightchannel)
        {
            llListenRemove(cListener);
            setScaleDimensions((float) message);          
        }
    }
 
    changed(integer change) 
    {
        if(change & CHANGED_LINK) 
        {
            avatar = llAvatarOnSitTarget();
            if(avatar != NULL_KEY)
            {
                llRequestPermissions(avatar,  PERMISSION_TRIGGER_ANIMATION);
            }
            else
            {
                if (llGetPermissionsKey() != NULL_KEY)
                { 
                    llStopAnimation(ANIMATION); 
                    llSitTarget(< 0,0,sitTargetHeight>,MyRotation*llEuler2Rot(<0,0,PI_BY_TWO>));
                    avatar = NULL_KEY;
                }
            }
        }
        if(change & CHANGED_INVENTORY) { llResetScript(); }
        if(change & CHANGED_OWNER)     { llResetScript(); }
    }
 
 
    run_time_permissions(integer perm) 
    {
        if(perm & PERMISSION_TRIGGER_ANIMATION) {
            llStopAnimation("sit");
            llStartAnimation(ANIMATION);
            mListener = llListen( menuchannel, "", "", "");         
            llDialog(avatar, "Select an Option",  heightButtons, menuchannel);    
        }
    }
 
    timer()
    {
        llSay(0, "Menu selection time limit ( 30 seconds ) exceeded, please touch the scale again for a new menu");
        llListenRemove(mListener); 
        llSetTimerEvent(0);
    }
}

CheckProportions BVH file

This is a very basic animation that stands the avatar upright with feet together and hands hanging down. It is a close enough match to the references included in the note card. You should be able to upload this file as an animation file and use it with most avatar models that the scale is appropriate for. The script expects the uploaded animation to be named "CheckProportions". Feel free to change it, but then you will also need to change the script.

I recommend that you set the animation priority to 4 and set the animation to LOOP when uploaded. This will help insure it remains active while the avatar is on the scale.

 

Information Notecard

This is an optional add-on. It is not required, but I have included it to help explain the use to a novice user. I recommend that you at least READ through the note card before you attempt to use the scale for yourself. If you intend to re-distribute the scale, Please Please Please include this or something like it to minimize the support questions that may result.

 
Personal tools
General
About This Wiki