Re: Fade Out without artifacts

New Message Reply Date view Thread view Subject view Author view

From: Simon Mills (simon++at++wgs.estec.esa.nl)
Date: 06/16/2000 00:22:11


ross::barna wrote:
>
> > If it helps, I also implemented this but I altered all pfGeoSet alpha
> > values under the given node. I did this rather than change the
> > pfMaterial alpha values because materials may be shared with other
> > geometry and that would have adverse effects. I also had to take care in
> > the case that the geometry already had some transparent materials so I
> > had to combine any material alpha with my overall fade factor. Oh yes,
> > and it had to work on most hardware i.e. not iR specific so I couldn't
> > use multisample masks.
>
> do you have any sample code that i can read through?
> i would also like to implement fading...

OK, here's my test code as a follow up to my previous posting. I my case
I was using it to fade models made in Multigen on O2/Octane/Onyx2. As
usual, any comments or improvements are welcome ;-).

Regards, Simon
________________________________________________________________________

Simon Mills
Silicon Worlds S.A.
c/o Modelling & Simulation Section (TOS-EMM) Tel: +31 (0)71 565 3725
European Space Agency (ESA/ESTEC) Fax: +31 (0)71 565 5419
Postbus 299, 2200AG Noordwijk e-mail: simon++at++wgs.estec.esa.nl
The Netherlands http://www.estec.esa.nl/wmwww/EMM
________________________________________________________________________

/*
 * fade.c++
 *
 * Fade a node or sub-hierarchy i.e vary it's transparency in real-time.
 *
 * Simon Mills ESA/ESTEC/TOS-EMM
 * 01-Mar-99
 *
 * Notes:
 *
 * Modifies the node's draw traversal mask. Any changes from the default will
 * be overriden.
 *
 * The changes to be alpha values in the geosets should use pfFlux to be
 * frame accurate. The direct values are used at present. IF the change is
 * slow this inaccuracy is not noticeable.
 */

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <Performer/pf.h>
#include <Performer/pfui.h> // for processKeybd()
#include <Performer/pfutil.h>
#include <Performer/pf/pfNode.h>
#include <Performer/pf/pfScene.h>
#include <Performer/pf/pfGeode.h>
#include <Performer/pf/pfTraverser.h>
#include <Performer/pr/pfType.h>
#include <Performer/pr/pfGeoSet.h>
#include <Performer/pr/pfGeoState.h>
#include <Performer/pr/pfMaterial.h>

#include "fly.h"

/******************************************************************************
 *
 * APPLICATION PROCESS ROUTINES
 *
 ******************************************************************************/

/* Part of scene to fade */
static pfNode *Node;

/*****************************************************************************/

/* Lookup node of given type by name */

static pfNode *
lookupNode(pfNode *root, pfType *type, const char *name)
{
    pfNode *node;
    
    node = root->lookup(name, type);
    if (node == NULL)
        pfNotify(PFNFY_WARN, PFNFY_PRINT, "Fade: Node '%s' not found", name);
    else
        pfNotify(PFNFY_INFO, PFNFY_PRINT,
                     "Fade: found %s '%s'", type->getName(), name);

    return node;
}

void
PostInitApp(int argc, char *argv[])
{
    pfNotify(PFNFY_INFO, PFNFY_PRINT, "Fade: Press 'P' key to fade node on/off");

    if (argc != 2) {
            pfNotify(PFNFY_WARN, PFNFY_PRINT, "Fade: must give the name of node");
            return;
    }
    
    /* Get part of scene by name */
    Node = lookupNode((pfNode *)ViewState->scene,
                              pfNode::getClassType(), argv[1]);
}

/**************************** UPDATE SECTION *******************************/

static void
setGeodeAlpha(pfGeode *geode, float alpha)
{
    int nrGSets = geode->getNumGSets();
    pfGeoSet *gset;
    int nColors;
    int minIndex, maxIndex; // dummy
    pfVec4 *clist;
    ushort *ilist; // dummy
    pfGeoState *gstate;
    pfMaterial *mat;
    float matAlpha, finalAlpha;
    
    // For each geoset in the geode
    for (int g = 0; g < nrGSets; g++) {
            gset = geode->getGSet(g);
            
            // Check we have a color array bound
            if (gset->getAttrBind(PFGS_COLOR4) == PFGS_OFF)
                pfNotify(PFNFY_WARN, PFNFY_PRINT,
                            "Fade: no color array to modify - giving up");
            else {
            
                // Get geostate material otherwise get global material
                gstate = gset->getGState();
                mat = (pfMaterial *) gstate->getAttr(PFSTATE_FRONTMTL);
                if (!mat) {
                        mat = pfGetCurMtl(PFMTL_FRONT);
                }

                // Get material's alpha and use this as basis
                if (mat)
                        matAlpha = mat->getAlpha();
               else
                        matAlpha = 1.0f;

                // Get geoset color array
                nColors = gset->getAttrRange(PFGS_COLOR4, &minIndex, &maxIndex);
                gset->getAttrLists(PFGS_COLOR4, (void **)&clist, &ilist);

                // Override transparency in the color arrays.
                // Assuming there is no transparency set in the colour arrays
                // N.B. This should use pfFluxes to be frame accurate!
                finalAlpha = matAlpha * alpha;
                for (int c = 0; c < nColors; c++)
                    clist[c][3] = finalAlpha;
                
                // Place geoset into transparent bin if requires blending.
                // This is to avoid rendering artifacts during fade.
                if (alpha < 1.0f)
                        gset->setDrawBin(PFSORT_TRANSP_BIN);
                else
                        gset->setDrawBin(-1);
                
                // Check if geoset GL display list needs recompiling (for iR)
                if (gset->getDrawMode(PFGS_COMPILE_GL) == PF_ON)
                        gset->setDrawMode(PFGS_COMPILE_GL, PF_ON);
            }
    }
}

static int
cbUpdateAlpha(pfuTraverser *trav)
{
    pfNode *node = trav->node;
    float alpha = *((float *) trav->data);
    
    /* Update geode transparency */
    if (node->isOfType(pfGeode::getClassType())) {
            setGeodeAlpha((pfGeode *)node, alpha);
    }
    
    return PFTRAV_CONT;
}

static int
setTransparencyOn(pfTraverser *trav, void *data)
{
    // Override transparency and don't draw pixels with < 1% alpha
    pfTransparency(PFTR_HIGH_QUALITY);
    pfAlphaFunc(0.01f, PFAF_GEQUAL);
    pfOverride(PFSTATE_TRANSPARENCY | PFSTATE_ALPHAFUNC, PF_ON);

    return PFTRAV_CONT;
}

static int
setTransparencyOff(pfTraverser *trav, void *data)
{
    // Remove override on transparency and alpha function
    pfOverride(PFSTATE_TRANSPARENCY | PFSTATE_ALPHAFUNC, PF_OFF);
    pfAlphaFunc(0.0f, PFAF_OFF);
    pfTransparency(PFTR_OFF);

    return PFTRAV_CONT;
}

/* Set transparency of all geometry under node */
static void
updateAlpha(pfNode *node, float alpha)
{
    pfuTraverser trav;

    // Check node is valid
    if (!node) {
            pfNotify(PFNFY_WARN, PFNFY_PRINT, "UpdateAlpha: node is NULL");
            return;
    }

    // Install or clear node pre-, post-callbacks
    if (alpha < 1.0f)
            node->setTravFuncs(PFTRAV_DRAW, setTransparencyOn, setTransparencyOff);
    else
            node->setTravFuncs(PFTRAV_DRAW, NULL, NULL);
    
    // If alpha is 0 just disable drawing
    // N.B. This overrides any draw trav mask that was set!
    if (alpha == 0.0f)
            node->setTravMask(PFTRAV_DRAW, 0x00000000, PFTRAV_SELF, PF_SET);
    else
            node->setTravMask(PFTRAV_DRAW, 0xFFFFFFFF, PFTRAV_SELF, PF_SET);
    
    // Traverse all geosets under node and update transparency
    pfuInitTraverser(&trav);
    trav.preFunc = cbUpdateAlpha;
    trav.data = &alpha;
    pfuTraverse(node, &trav);
}

/*
 * Just for testing
 */

static int FadeOn = FALSE;

void
ProcessKeyboard(pfuEventStream *events) /* process keyboard in addition to Fly standard keys */
{
    int i;
    int j;
    int key;
    int dev;
    int val;
    
    /*
     * N.B. Only alter the queue if you wish to disable Fly functions
     */
    for (j = 0; j < events->numDevs; j++)
    {
        dev = events->devQ[j];
        val = events->devVal[j];

        if (events->devCount[dev] > 0)
        {
            switch(dev)
            {
                
            /* Main keyboard */
            case PFUDEV_KEYBD:
                for (i = 0; i < events->numKeys; i++)
                {
                    key = events->keyQ[i];
                    if (events->keyCount[key])
                    {
                        switch(key)
                        {
                        case 'p': /* extra command */
                            printf("p key pressed\n");
                            FadeOn = !FadeOn;
                            break;
                        }
                    }
                }
                break;
            }
        }
    }
}

/* Make function which "stops" fading of geode to do tidying up etc. ? */

void
PostUpdateApp(void)
{
    float alpha;
    static int prevFadeOn = FALSE;
    double currTime = pfGetTime();
    static double startTime = 0.0;
    static int fading = FALSE;
    float fadeDuration = 2.0f;
    
    //alpha = ( 1.0f + cosf(2.0f * (float)pfGetTime()) ) / 2.0f;

    // Detect start of fading
    if ((FadeOn && !prevFadeOn) || (!FadeOn && prevFadeOn)) {
            startTime = currTime;
            fading = TRUE;
    }
    
    // During fading (1 sec) compute and set new transparency
    if (fading) {
        if (FadeOn) {
                alpha = PF_MAX2(0.0f, 1.0f - (float) (currTime - startTime)/fadeDuration);
                if (alpha == 0.0f)
                        fading = FALSE;
        }
        else {
                alpha = PF_MIN2(1.0f, (float) (currTime - startTime)/fadeDuration);
                if (alpha == 1.0f)
                        fading = FALSE;
            }
            
        printf("Alpha = %f\n", alpha);

        if (Node)
                updateAlpha(Node, alpha);
    }
    
    prevFadeOn = FadeOn;
}


New Message Reply Date view Thread view Subject view Author view

This archive was generated by hypermail 2b29 : Fri Jun 16 2000 - 00:22:23 PDT

This message has been cleansed for anti-spam protection. Replace '++at++' in any mail addresses with the '@' symbol.