Samstag, 10. April 2010

Extended preprocessor script

The simple preprocessor script works fine, but for concrete project it does too much... and too less. What is too much? Rescalling any hair prim sepratelly will easilly destroy the hair style. Also the flower looks mostly odd if rescaled alone. The processor system must just rescale the whole hairinclusive the flower at once.

What is too less? Well, we made use of the letter 'S' in TASC (the preprocessor sends the scale commands) but there are also 'A' and 'C' for alpha and color, we could use, namelly hiding the flower and changing its color would be a nice improvement. In this post we imporve the preprocessor script's abilities but in a way it remains easy adaptable to other concrete projects.

This is how we extend it:
  1. We deactivate the "(this)" and "(all)" buttons in main menu by using constants (homework, task 3).
  2. If we select "hair" prim group, the flower will resize as well.
  3. If we select "flower" prim group, the wearer gets a color menu instead of resize menu.
The messages tutorial gives the required command syntax (this was the housework, task 5).
  • To make the flower semitransparent we send the command "alpha:flower:0.5", The value '0.0' instead of '0.5' hides the flower, the value '1.0' shows it.
  • To make the flower white we send this command: "color:flower:<1.0, 1.0, 1.0>" and to make it red we use the color '<0.5, 0.0, 0.0>'.
The hair must be resizable only, so we do not show the color menu if the group 'hair' is selected and if the group 'flower' is selected, the user sees only the color menu. Now, if we disable the "(all)" button how we rescale the complete hair inclusive flower?

We need the flower to be in the 'hair' group in order it responds to the same commands the hair do. And we need the flower in the 'flower' group in order it responds to color commands. Dilemma? The messages tutorial gives the solution: If we set the flower's description to "hair:flower", we put it in both groups, so it will respond to both commands.

This is all we need to know before to start scripting.

1. Script preamble

integer MACS_CHANNEL   = -1301031903; // MACSC

// Assumed to be distinct
list    SCALE_GROUPS   = ["hair"];
list    COLOR_GROUPS   = ["flower"];

integer NO_THIS_BUTTON = TRUE;
integer NO_ALL_BUTTON  = TRUE;

key     kOwner;

integer gMenuChannel;
integer gMenuListen;
integer gMenuPage;
integer gMenuMode;

list    lPrimGroups;
integer gGroupsNum;

integer gTouchLink     = LINK_SET;
integer gTargetLink    = LINK_SET;
string  sTargetGroup   = "";

integer isClean        = FALSE;

The preamble seems similar to the first preprocessor. New is splitting of the constant PRIM_GROUP in two. Later we will see, if we select a group with a name from SCALE_GROUP, the resize menu will pop up and if we select a prim group with name in COLOR_GROUP, than the color menu will open. That way we can keep the preprocessor script generic.

The list lPrimGroups will merge the both prim group constants, the list is for faster management, we cache the complete groups list and do not calculate it every time we want open the main menu.

The constants NO_XXX_BUTTONS implement the homework's task 2. We disallow here both "(this)" and "(all)" butons in main menu.

2. Restore menu

// [(yes)] [(no)]
openRestoreMenu() {
    llListenRemove(gMenuListen);
    gMenuListen = llListen(gMenuChannel, "", kOwner, "");
    gMenuMode   = -1;
 
    llDialog(kOwner,
        "Reinstall resizer scripts?",
        ["(yes)", "(no)"],
        gMenuChannel);

    llSetTimerEvent(30.0);
}

What hapens here.This is the task 3 of the homework. The menu opens if the scripts are cleaned out and give the chance to abort the scripts installation. The user has 30 sec. to answer.

3. Main menu

// Up to 6 target buttons
// [(this)] [(all)] [(clean)]
// [  <<  ] [     ] [   >>  ]
openMainMenu(integer page, integer refresh) {
    if (page > gGroupsNum-9) page = gGroupsNum-6;
    if (page < 0)            page = 0;
    gMenuPage = page;
    gMenuMode = 0;

    list    btns = llList2List(lPrimGroups, page, page+5);
    integer len  = llGetListLength(btns) % 3;
    if (len > 0) {
        btns = btns + llList2List([" ", " "], 0, 3-len-1);
    }

    string t = " ";
    string a = " ";
    if (!NO_THIS_BUTTON) t = "(this)";
    if (!NO_ALL_BUTTON)  a = "(all)";
 
    list   btna = [t, a, "(clean)"];
    list   btnb = [];
 
    if (page < gGroupsNum-6) {
        if (page > 0)  btnb = ["<<", " ", ">>"];
        else           btnb = [" ", " ", ">>"];
    }
    else if (page > 0) btnb = ["<<", " ", " "];

    if (refresh) {
        llListenRemove(gMenuListen);
        gMenuListen = llListen(gMenuChannel, "", kOwner, "");
    }

    t = "";
    a = "";
    if (!NO_THIS_BUTTON) {
        t = "(this)    - resize touched prim only\n";
    }
    if (!NO_ALL_BUTTON)  {
        a = "(all)       - resize all prims\n";
    }

    llDialog(kOwner,
        "Please select target prims first:\n"+t+a+
        "(clean) - remove resizer scripts",
        btnb + btna + btns,
        gMenuChannel);
 
    llSetTimerEvent(30.0);
}

The main menu is almost the same. New is disabling buttons and hiding the message parts accordant to the preference constants.

4. Resize menu

// [ + 1% ] [+ 5%] [ + 10% ]
// [ - 1% ] [- 5%] [ - 10% ]
// [(main)] [    ] [(reset)]
openResizeMenu() {
    gMenuMode = 1;
 
    string trg;
    if (sTargetGroup != "*")          trg = sTargetGroup;
    else if (gTargetLink == LINK_SET) trg = "(all prims)";
    else                              trg = "(this prim)";

    llDialog(kOwner,
        "Resize "+trg+" or return:\n"+
        "(main)  - open main menu\n"+
        "(reset) - reset prim size to rezzing state",
        ["(main)", " ", "(reset)",
        "- 1%", "- 5%", "- 10%",
        "+ 1%", "+ 5%", "+ 10%"],
        gMenuChannel);

    llSetTimerEvent(30.0);
}

The resize menu is the same as in the first preprocessor.

5. Color and Transparency menu 

// [white ] [black] [red ]
// [yellow] [blue ] [pink]
// [(main)] [hide ] [show]
openColorMenu() {
    gMenuMode = 2;
 
    string trg;
    if (sTargetGroup != "*")          trg = sTargetGroup;
    else if (gTargetLink == LINK_SET) trg = "(all prims)";
    else                              trg = "(this prim)";

    llDialog(kOwner,
        "Hide or recolor "+trg+":\n"+
        "(main)  - open main menu",
        ["(main)", "hide", "show",
        "yellow", "blue", "pink",
        "white", "black", "red"],
        gMenuChannel);

    llSetTimerEvent(30.0);
}

This menu is very simillar to the resize menu. The menu buttons are also hard-coded and give a few usefull colors. The colors are given by names also, the color values are used on the other place.

6. Closing menu handles

closeMenu() {
    llSetTimerEvent(0.0);
    llListenRemove(gMenuListen);
}

The same as last time, the listen handles are closed and the timer stops.

7. Command helper

send(string cmd, string value) {
    cmd += ":"+sTargetGroup;
    if (value != "") cmd += ":"+value;
    llMessageLinked(gTargetLink, MACS_CHANNEL, cmd, NULL_KEY);
}

This function is used to send the transformation command to the processor. It makes the handling code simpler and easier to understand.

8. Handling of menu command 

handleMenu(string cmd) {
    if (gMenuMode == -1) {
        closeMenu();
        if (cmd == "(yes)") restoreScripts();
    }
 
    else if (gMenuMode == 0) {
        if      (cmd == "<<") openMainMenu(gMenuPage-6, FALSE);
        else if (cmd == ">>") openMainMenu(gMenuPage+6, FALSE);
        else if (cmd == "(this)") {
            gTargetLink  = gTouchLink;
            sTargetGroup = "*";
            openResizeMenu();
        }
        else if (cmd == "(all)") {
            gTargetLink  = LINK_SET;
            sTargetGroup = "*";
            openResizeMenu();
        }
        else if (cmd == "(clean)") {
            isClean = TRUE;
            llMessageLinked(
                LINK_THIS, MACS_CHANNEL, "fix", NULL_KEY);
            closeMenu();
        }
        else if (llListFindList(SCALE_GROUPS, [cmd]) > -1) {
            gTargetLink  = LINK_SET;
            sTargetGroup = cmd;
            openResizeMenu();
        }
        else if (llListFindList(COLOR_GROUPS, [cmd]) > -1) {
            gTargetLink  = LINK_SET;
            sTargetGroup = cmd;
            openColorMenu();
        }
        else {
            closeMenu();
        }
    }

    else if (gMenuMode == 1) {  
        if (cmd == "(main)") {
            openMainMenu(gMenuPage, FALSE);
        }
        else {
            if (cmd == "(reset)")    send("size", "");

            else if (cmd == "+ 1%")  send("size", "1.01");
            else if (cmd == "+ 5%")  send("size", "1.05");
            else if (cmd == "+ 10%") send("size", "1.1");

            else if (cmd == "- 1%")  send("size", "0.9901");
            else if (cmd == "- 5%")  send("size", "0.95238");
            else if (cmd == "- 10%") send("size", "0.90909");
            else {
                closeMenu();
                return;
            }

            openResizeMenu();
        }
    } 
 
    else if (gMenuMode == 2) {  
        if (cmd == "(main)") {
            openMainMenu(gMenuPage, FALSE);
        }
        else {
            if      (cmd == "hide")   send("alpha", "0.0");
            else if (cmd == "show")   send("alpha", "1.0");

            else if (cmd == "yellow") 
                send("color", "<0.8, 0.8, 0.2>");
            else if (cmd == "blue")   
                send("color", "<0.1, 0.1, 0.9>");
            else if (cmd == "pink")   
                send("color", "<0.9, 0.1, 0.4>");
            else if (cmd == "white")  
                send("color", "<1.0, 1.0, 1.0>");
            else if (cmd == "black")  
                send("color", "<0.3, 0.3, 0.3>");
            else if (cmd == "red")    
                send("color", "<0.6, 0.1, 0.1>");
            else {
                closeMenu();
                return;
            }

            openColorMenu();
        }
    } 
}

The differences to the first preprocessor is:
  • The restoring menu (mode is -1) is handled by calling the function if the answer was "yes".
  • By handling the main menu we call either the resize or color menu depends on what prim group was selected.
  • The handling of the resize menu is changed a bit by calling the helper function
  • and we added the handling of color menu. The handling is similar to the resize menu, just the command and value is different.
9. Reinstalling scripts 

restoreScripts() {
    isClean = FALSE;
    llMessageLinked(
        LINK_THIS, MACS_CHANNEL, "unfix", NULL_KEY);
}

The same as in first preprocessor, the reinstallation command is sent and noticed the scripts are no more cleaned out.

10. Default state 

default {
    state_entry() {
        gMenuChannel = (integer)llFrand(100000.0)-2100000000;
        kOwner       = llGetOwner();
  
        lPrimGroups  = COLOR_GROUPS+SCALE_GROUPS;
        gGroupsNum   = llGetListLength(lPrimGroups);
    }

    on_rez(integer start_param) {
        closeMenu();

        gMenuChannel = (integer)llFrand(100000.0)-2100000000;
        gTargetLink  = LINK_SET;
        sTargetGroup = "";
        kOwner       = llGetOwner();
    }

    touch_start(integer total_number) {
        integer i;
        for (i = 0 ; i < total_number ; ++i) {
            if (llDetectedKey(0) == kOwner) {
                if (isClean) {
                    openRestoreMenu();
                }
                else {
                    gTouchLink = llDetectedLinkNumber(i);
                    openMainMenu(0, TRUE);
                }
                return;
            }
        }
    }

    listen(integer chan, string name, key agent, string msg) {
        handleMenu(msg);
    }

    timer() {
        closeMenu();
    }
}

Almost the same like in the first preprocessor. The difference: The scripts are nor reinstalled immediatelly if the hair is klicked but the reinstallation menu is open to confirm the installation intends.

Installation the new preprocessor

The installation is simple: Edit hair, remove the old preprocessor and put new one (you have to copy and paste the code into a LSL script, compile it in mono please).

While the edit window is open, check the box "edit linked parts" and select the flower prim. Than go to the general tab and change it's description from "flower" to "hair:flower", picture 1. This puts it into the both groups and allows to rescale it together with the hair. It belongs to hair, so it is stil correct semantically.

Picture 1: The flower goes in both, 'hair' and 'flower' groups.

Now detach and reattach the hair or pick it from the world and rezz again, depends how you edit it. This is neccessary after changing the flower's description, the processor in it has to know about the new group. Now it is ready for final test.

The new menus

Touch the hair, we get a menu with the both group names but without the generic buttons we deactivate. If we select "hair" groupd we get the resize menus, that works for the whole hair inclusive the flower - as intended. If we select the "flower" group, we get a color menu instead of the resize menu - as intended, too. picture 2.

Picture 2: Menus of the processor system.

In the color menu we are able to hide the flower or change its colors - also as intended, picture 3.

Picture 3: The flower is hidden, turn white or red again.

Test pass.

Afterword

We did everything reasonable with the choosen hair. We could change also textures and also have more prim groups and also use face replacement technique to change only cerain faces of the modell. But it is too much for this one. I hope just, you can easilly adapt the preprocessor script for that tasks for your choosen models.

Thank you for reading

Keine Kommentare:

Kommentar veröffentlichen

Dear creators, internet connection is not a premisse

Hello everyone reading :) This post targets all the people who design and create all the fancy things, be it in hand or on screen. Pleas...