VRAY/VRAY_DemoSprite.C

/*
 * Copyright (c) 2013
 *      Side Effects Software Inc.  All rights reserved.
 *
 * Redistribution and use of Houdini Development Kit samples 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. The name of Side Effects Software may not be used to endorse or
 *    promote products derived from this software without specific prior
 *    written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY SIDE EFFECTS SOFTWARE `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 SIDE EFFECTS SOFTWARE 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.
 *
 *----------------------------------------------------------------------------
 * This is a sample procedural DSO which creates sprites on point geometry.
 */

#include "VRAY_DemoSprite.h"

#include <VRAY/VRAY_IO.h>

#include <GU/GU_Detail.h>
#include <GU/GU_PrimPoly.h>

#include <UT/UT_Defines.h>
#include <SYS/SYS_Floor.h>


#define MIN_CHUNK               8
#define SPRITE_LIMIT            1000
#define META_CORRECT            0.5
#define DEFAULT_ATTRIB_PATTERN  ""


namespace HDK_Sample {

class vray_SpriteAttribMap
{
public:
    GB_AttributeRef      myOffset;      // Offset of attrib in template
    int                  myDIndex;      // Index of destination offset
    const GB_Attribute  *myAttrib;      // Source attribute
    int                  myFloats;      // Count of # of floats

    vray_SpriteAttribMap        *myNext;
};

}       // End HDK_Sample namespace

using namespace HDK_Sample;


static vray_SpriteAttribMap *
makeMap(const GU_Detail *tplate, const GB_Attribute *atr)
{
    vray_SpriteAttribMap        *map;
    UT_String                    n;

    map = new vray_SpriteAttribMap();
    map->myOffset = tplate->findPointAttrib(atr);

    map->myDIndex = -1; // Not defined yet
    map->myAttrib = atr;

    if (atr->getType() == GB_ATTRIB_FLOAT)
        map->myFloats = atr->getSize() / sizeof(float);
    else if (atr->getType()==GB_ATTRIB_INT || atr->getType()==GB_ATTRIB_INDEX)
        map->myFloats = atr->getSize() / sizeof(int);
    else if (atr->getType() == GB_ATTRIB_VECTOR)
        map->myFloats = 3;
    else map->myFloats = 0;
    return map;
}

static void
destroyMap(vray_SpriteAttribMap *map)
{
    vray_SpriteAttribMap        *next;
    while (map)
    {
        next = map->myNext;
        delete map;
        map = next;
    }
}

static void
setAttribMap(vray_SpriteAttribMap *&maphead, const GU_Detail *gdp,
             const char *pattern)
{
    const GB_AttributeDict      *dict;
    const GB_Attribute          *atr;
    UT_String                    name;
    vray_SpriteAttribMap        *map;
    int                          index = 0;

    if (!gdp)
        return;

    dict = &gdp->pointAttribs();
    for (atr = dict->getHead(); atr; atr = (const GB_Attribute *)atr->next())
    {
        name = atr->getName();
        if (name.multiMatch(pattern) && atr->getType() != GB_ATTRIB_MIXED)
        {
            map = makeMap(gdp, atr);
            map->myDIndex = index++;
            map->myNext = maphead;
            maphead = map;
        }
    }
}


// Arguments for this procedural:
//
//      -v velocity attribute name
//      -o object name
//      -t time step for motion blur
//      -A attribute pattern
//      -C chunk size
//      -L LOD
//      -M maximum number of sprites to generate from one procedural
static VRAY_ProceduralArg theArgs[] = {
    VRAY_ProceduralArg("velocity",      "string",       "v"),
    VRAY_ProceduralArg("object",        "string",       ""),
    VRAY_ProceduralArg("attribute",     "string",       ""),
    VRAY_ProceduralArg("chunksize",     "int",          "16"),
    VRAY_ProceduralArg("maxsprites",    "int",          "1000"),
    VRAY_ProceduralArg(),
};

// External entry point used to create this procedural
VRAY_Procedural *
allocProcedural(const char *)
{
    return new VRAY_DemoSprite();
}

// External entry point used to create this procedural's arguments
const VRAY_ProceduralArg *
getProceduralArgs(const char *)
{
    return theArgs;
}

VRAY_DemoSprite::VRAY_DemoSprite()
{
    myBox.initBounds(0, 0, 0);
    myVelBox = myBox;
    myParms = 0;
}

// We get a rough bounding box for the sprite by expanding the box around
// the position by the largest component of the scale (assuming it were
// rotated 45 degrees).
static void
getRoughSpriteBox(UT_BoundingBox &box, UT_BoundingBox &vbox,
                  const GEO_Point *point, const UT_Vector3 &sprite_scale,
                  const GB_AttributeRef &voff,
                  fpreal tscale, const UT_Matrix4 &xform)
{
    fpreal              maxradius;
    static fpreal       isin45 = 1.0F / SYSsin(M_PI/4);
    UT_Vector3          pt;

    maxradius = SYSmax(sprite_scale.x(), sprite_scale.y()) * isin45 * 0.5F;

    pt = UT_Vector3(-maxradius, -maxradius, 0)*xform; box.initBounds(pt);
    pt = UT_Vector3(-maxradius,  maxradius, 0)*xform; box.enlargeBounds(pt);
    pt = UT_Vector3( maxradius, -maxradius, 0)*xform; box.enlargeBounds(pt);
    pt = UT_Vector3( maxradius,  maxradius, 0)*xform; box.enlargeBounds(pt);

    box.translate(point->getPos());
    vbox = box;

    if (voff.isValid())
    {
        UT_Vector3      vel;
        int             i;
        fpreal          amount;

        vel = point->getValue<UT_Vector3>(voff);
        for (i = 0; i < 3; i++)
        {
            amount = vel(i) * tscale;
            if (amount < 0)
                 vbox.vals[i][1] -= amount;
            else vbox.vals[i][0] -= amount;

        }
    }
}

static inline void
clampBox(UT_BoundingBox &from, const UT_BoundingBox &to)
{
    if (from.vals[0][0] < to.vals[0][0]) from.vals[0][0] = to.vals[0][0];
    if (from.vals[1][0] < to.vals[1][0]) from.vals[1][0] = to.vals[1][0];
    if (from.vals[2][0] < to.vals[2][0]) from.vals[2][0] = to.vals[2][0];
    if (from.vals[0][1] > to.vals[0][1]) from.vals[0][1] = to.vals[0][1];
    if (from.vals[1][1] > to.vals[1][1]) from.vals[1][1] = to.vals[1][1];
    if (from.vals[2][1] > to.vals[2][1]) from.vals[2][1] = to.vals[2][1];
}

static inline int
testClampBox(const UT_BoundingBox &from, const UT_BoundingBox &to)
{
    if (from.vals[0][0] < to.vals[0][0]) return 1;
    if (from.vals[1][0] < to.vals[1][0]) return 1;
    if (from.vals[2][0] < to.vals[2][0]) return 1;
    if (from.vals[0][1] > to.vals[0][1]) return 1;
    if (from.vals[1][1] > to.vals[1][1]) return 1;
    if (from.vals[2][1] > to.vals[2][1]) return 1;
    return 0;
}

int
VRAY_DemoSprite::initChild(VRAY_DemoSprite *sprite, const UT_BoundingBox &box)
{
    int                  i, first, idx;
    UT_BoundingBox       tbox, tvbox;
    const GEO_Point     *ppt;
    GU_Detail           *gdp;
    UT_Matrix4           xform;

    myParms = sprite->myParms;
    myParms->myRefCount++;

    gdp = getPointGdp();

    UT_Vector3  sprite_scale(0.1, 0.1, 0.1);


    // Initially, we divide the points based solely on the actual 
    // point positions.  We then compute the actual bounding box of
    // this division.  This ensures that each point is only in
    // one procedural.
    myPointList.resize(sprite->myPointList.entries());
    for (i = sprite->myPointList.entries(); i-- > 0; )
    {
        idx = sprite->myPointList(i);
        ppt = gdp->points()(idx);
        if (box.isInside(ppt->getPos()))
        {
            myPointList.append(idx);
        }
    }
    myPointList.resize(myPointList.entries());

    first = 1;
    xform = myParms->myXformInverse;
    for (i = myPointList.entries(); i-- > 0; )
    {
        idx = myPointList(i);
        ppt = gdp->points()(idx);

        if (myParms->mySpriteScaleOff.isValid())
            sprite_scale = ppt->getValue<UT_Vector3>(myParms->mySpriteScaleOff);

        getRoughSpriteBox(tbox, tvbox, ppt, sprite_scale,
                          myParms->myVelOff, myParms->myTimeScale, xform);
    
        if (first)
        {
            myBox = tbox;
            myVelBox = tvbox;
            first = 0;
        }
        else
        {
            myBox.enlargeBounds(tbox);
            myVelBox.enlargeBounds(tvbox);
        }
    }

//    printf("init child with %d entries\n", myPointList.entries());
    if (first)
        return 0;
    clampBox(myBox, box);
    return 1;
}

VRAY_DemoSprite::~VRAY_DemoSprite()
{
    myParms->myRefCount--;
    if (!myParms->myRefCount)
    {
        destroyMap(myParms->myAttribMap);
        delete myParms;
    }
}

const char *
VRAY_DemoSprite::getClassName()
{
    return "VRAY_DemoSprite";
}

int
VRAY_DemoSprite::initialize(const UT_BoundingBox *box)
{
    void                *handle;
    const char          *name;
    GEO_Point           *ppt;
    int                  i, first;
    UT_BoundingBox       tbox, tvbox;
    GU_Detail           *gdp;
    UT_Matrix4           xform;
    UT_String            str;
    int                  vblur;

    myParms = new VRAY_DemoSpriteParms;
    myParms->myGdp = 0;
    myParms->myVelOff.clear();
    myParms->mySpriteScaleOff.clear();
    myParms->mySpriteRotOff.clear();
    myParms->mySpriteShopOff.clear();
    myParms->mySpriteTexOff.clear();
    myParms->myChunkSize = MIN_CHUNK * 2;
    myParms->mySpriteLimit = SPRITE_LIMIT;
    myParms->myRefCount = 1;
    myParms->myAttribMap = 0;

    //
    // First, find the geometry object we're supposed to render
    name = 0;
    if (import("object", str))
        name = str.isstring() ? (const char *)str : 0;
    handle = queryObject(name);
    if (!handle)
    {
        VRAYerror("%s couldn't find object '%s'", getClassName(), name);
        return 0;
    }
    name = queryObjectName(handle);
    gdp = myParms->myGdp = (GU_Detail *)queryGeometry(handle, 0);
    if (!gdp)
    {
        VRAYerror("%s object '%s' has no geometry", getClassName(), name);
        return 0;
    }

    // Retrieve the velocity scale for use in velocity motion blur
    // calculations
    if (!import("object:velocityscale", &myParms->myTimeScale, 1))
        myParms->myTimeScale = 0.5F / 24.0F;

    vblur = 0;
    import("object:velocityblur", &vblur, 0);

    myParms->myXformInverse = queryTransform(handle, 0);
    myParms->myXformInverse.invert();

    myParms->mySpriteScaleOff = gdp->findPointAttrib("spritescale",
            sizeof(float) * 3, GB_ATTRIB_FLOAT);
    myParms->mySpriteRotOff = gdp->findPointAttrib("spriterot",
            sizeof(float), GB_ATTRIB_FLOAT);
    myParms->mySpriteShopOff = gdp->findPointAttrib("spriteshop",
            sizeof(int), GB_ATTRIB_INDEX);
    myParms->mySpriteTexOff = gdp->findPointAttrib("spriteuv",
            sizeof(float) * 4, GB_ATTRIB_FLOAT);

    //
    // Now, find the velocity attribute (if it's there)
    if (vblur)
    {
        str = 0;
        import("velocity", str);
        if (str.isstring())
        {
            myParms->myVelOff = gdp->findPointAttrib(str, sizeof(UT_Vector3),
                                              GB_ATTRIB_VECTOR);
            if (myParms->myVelOff.isInvalid())
                myParms->myVelOff = gdp->findPointAttrib(str, sizeof(UT_Vector3),
                                                  GB_ATTRIB_FLOAT);
            if (myParms->myVelOff.isInvalid())
                VRAYwarning("%s object (%s) couldn't find the '%s' attribute",
                        getClassName(), name, (const char *)str);
        }
    }

    UT_Vector3  sprite_scale(0.1, 0.1, 0.1);

    first = 1;
    xform = myParms->myXformInverse;
    for (i = gdp->points().entries(); i-- > 0; )
    {
        ppt = gdp->points()(i);
        if (myParms->mySpriteScaleOff.isValid())
            sprite_scale = ppt->getValue<UT_Vector3>(myParms->mySpriteScaleOff);

        getRoughSpriteBox(tbox, tvbox, ppt, sprite_scale,
                          myParms->myVelOff, myParms->myTimeScale, xform);
        myPointList.append(i);
        if (first)
        {
            myBox = tbox;
            myVelBox = tvbox;
            first = 0;
        }
        else
        {
            myBox.enlargeBounds(tbox);
            myVelBox.enlargeBounds(tvbox);
        }
    }
    if (first)
    {
        VRAYwarning("%s found no points in %s", getClassName(), name);
        return 0;
    }
    if (!myPointList.entries())
        return 0;

    str = 0;
    import("attribute", str);
    if (str.isstring())
        setAttribMap(myParms->myAttribMap, gdp, str);
    import("chunksize", &myParms->myChunkSize, 1);
    if (myParms->myChunkSize < MIN_CHUNK)
        myParms->myChunkSize = MIN_CHUNK;
    import("maxsprites", &myParms->mySpriteLimit, 1);
    if (myParms->mySpriteLimit < SPRITE_LIMIT)
        myParms->mySpriteLimit = SPRITE_LIMIT;

    if (box)
    {
        if (myParms->myVelOff.isValid())
        {
            if (testClampBox(myBox, *box) || testClampBox(myVelBox, *box))
                VRAYwarning("%s[%s] cannot render a partial box %s",
                        getClassName(), name, "with motion blur");
        }
        else
        {
            clampBox(myBox, *box);
            clampBox(myVelBox, *box);
        }
    }

    return 1;
}

void
VRAY_DemoSprite::getBoundingBox(UT_BoundingBox &box)
{
    // We need to return the maximum area that the primitive is defined over.
    box = myVelBox;
}

#define DEFAULT_SIZE    0.05F

#define SPRITE_SIZE(xsize, ysize, size) \
    { xsize = src_point->getValue<float>(parms.mySpriteScaleOff, 0) * 0.5F; \
    ysize = src_point->getValue<float>(parms.mySpriteScaleOff, 1) * 0.5F; }

#define SPRITE_TEXTURE(u1, v1, u2, v2, txt) \
    { UT_Vector3 txt = src_point->getValue<UT_Vector3>(parms.mySpriteTexOff); \
    u1 = txt.x(); v1 = txt.y(); u2 = txt.x()+txt.z(); v2 = txt.y()+txt.z(); }

#define ASSIGN_VERTEX(poly, ppt, i, XDELTA, YDELTA, u, v) \
    ppt = poly->getVertex(i).getPt(); \
    ppt->getPos() = src_point->getPos(); \
    ppt->getPos().x() XDELTA; ppt->getPos().y() YDELTA; \
    if (txt_off >= 0) \
        ppt->setValue<UT_Vector3>(txt_off, UT_Vector3(u, v, 0));

static void
applyMapToPrimitive(vray_SpriteAttribMap *map,
        const UT_RefArray<GB_AttributeRef> &dest_offsets,
        GEO_Primitive *dest, const GEO_Point *src_point)
{
    float        float_buffer[128];
    float       *abuf = NULL;
    float       *fdata;
    int          asize = 0;

    while (map)
    {
        if (map->myFloats)
        {
            if (map->myFloats < 128)
                fdata = float_buffer;
            else
            {
                if (map->myFloats > asize)
                {
                    delete [] abuf;
                    asize = map->myFloats + 1024;
                    abuf = new float[asize];
                }
                fdata = abuf;
            }
            src_point->get<float>(map->myOffset, fdata, map->myFloats);
            dest->set<float>(dest_offsets(map->myDIndex), fdata, map->myFloats);
        }
        map = map->myNext;
    }
    delete [] abuf;
}

static int
makeSpritePoly(GU_Detail *gdp, GU_Detail *src, UT_IntArray &points,
               const VRAY_DemoSpriteParms &parms, const char *srcpath)
{
    int                  i;
    GB_AttributeRef      shop_off;
    GB_AttributeRef      txt_off;
    fpreal               xSize = DEFAULT_SIZE;
    fpreal               ySize = DEFAULT_SIZE;
    fpreal               uMin = 0.0F;
    fpreal               vMin = 0.0F;
    fpreal               uMax = 1.0F;
    fpreal               vMax = 1.0F;
    GU_PrimPoly         *poly;
    const GEO_Point     *src_point;
    GEO_Point           *ppt;
    UT_Matrix4           xform;
    UT_Matrix4           view_inverse;
    UT_RefArray<GB_AttributeRef>        dest_offsets;
    static int           num_polys = 0;
    
    view_inverse = parms.myXformInverse;

    if (points.entries())
    {
        if (parms.mySpriteShopOff.isValid())
        {
            static int  minus1 = -1;
            shop_off = gdp->addPrimAttrib("shop_vm_surface", sizeof(int),
                                          GB_ATTRIB_INDEX, (void *)&minus1);
            GB_Attribute *shop_attr =
                gdp->primitiveAttribs().find("shop_vm_surface", sizeof(int),
                                              GB_ATTRIB_INDEX);
            const GB_Attribute *sprite_shop_attr 
                = src->pointAttribs().find("spriteshop", sizeof(int),
                                           GB_ATTRIB_INDEX);
            UT_ASSERT(shop_attr && sprite_shop_attr);
            if (shop_attr && sprite_shop_attr)
            {
                UT_String        path, fullpath;
                int              numidx;

                numidx = sprite_shop_attr->getIndexSize();
                for( i = 0; i < numidx; i++ )
                {
                    path = sprite_shop_attr->getIndex(i);
                    if( path.isstring() && path(0) != '/' )
                    {
                        fullpath = srcpath;
                        fullpath += "/";
                        fullpath += path;
                        fullpath.collapseAbsolutePath();
                    }
                    else
                        fullpath = path;
                    shop_attr->addIndex(fullpath);
                }
            }
            if (parms.mySpriteTexOff.isValid())
            {
                txt_off = gdp->addTextureAttribute(GEO_POINT_DICT);
            }
        }

        // Create the attributes that we wish to copy from the points.
        if (parms.myAttribMap)
        {
            vray_SpriteAttribMap        *map;
            const GB_Attribute          *atr;

            dest_offsets.resize(parms.myAttribMap->myDIndex+1);
            dest_offsets.entries(parms.myAttribMap->myDIndex+1);

            for (map = parms.myAttribMap; map; map = map->myNext)
            {
                atr = map->myAttrib;
                dest_offsets(map->myDIndex) = gdp->addPrimAttrib(atr);
                UT_ASSERT(dest_offsets(map->myDIndex).isValid());
                if (atr->getType() == GB_ATTRIB_INDEX)
                    gdp->primitiveAttribs().findClone(atr)->mergeIndex(atr);
            }
        }
    }

    // It is important to note that the order of the vertices is reversed,
    // making this a backfacing polygon.  We do this to make sure that the
    // s, t coordinates run correctly for when we're not bothering with a
    // texture attribute.
    for (i = 0; i < points.entries(); i++)
    {
        poly = GU_PrimPoly::build(gdp, 4, GU_POLY_CLOSED, 1);

        src_point = src->points()(points(i));
        if (parms.mySpriteScaleOff.isValid())
        {
            SPRITE_SIZE(xSize, ySize, ssize);
        }
        if (parms.mySpriteTexOff.isValid())
        {
            SPRITE_TEXTURE(uMin, vMin, uMax, vMax, txt);
        }

        xform.identity();
        xform.translate(src_point->getPos().x(),
                        src_point->getPos().y(),
                        src_point->getPos().z());
//      xform *= view_inverse;
        xform.leftMult(view_inverse);
        if (parms.mySpriteRotOff.isValid())
        xform.prerotate(UT_Axis3::ZAXIS, UTdegToRad(
                    src_point->getValue<float>(parms.mySpriteRotOff)));

        ppt = poly->getVertex(2).getPt();
//      ppt->getPos() = src_point->getPos();
        ppt->getPos().x() += xSize; ppt->getPos().y() += ySize;
        ppt->getPos() *= xform;
        if (txt_off.isValid())
        {
            ppt->setValue<UT_Vector3>(txt_off, UT_Vector3(uMax, vMax, 0));
        }

        ppt = poly->getVertex(3).getPt();
//      ppt->getPos() = src_point->getPos();
        ppt->getPos().x() -= xSize; ppt->getPos().y() += ySize;
        ppt->getPos() *= xform;
        if (txt_off.isValid())
        {
            ppt->setValue<UT_Vector3>(txt_off, UT_Vector3(uMin, vMax, 0));
        }

        ppt = poly->getVertex(0).getPt();
//      ppt->getPos() = src_point->getPos();
        ppt->getPos().x() -= xSize; ppt->getPos().y() -= ySize;
        ppt->getPos() *= xform;
        if (txt_off.isValid())
        {
            ppt->setValue<UT_Vector3>(txt_off, UT_Vector3(uMin, vMin, 0));
        }

        ppt = poly->getVertex(1).getPt();
//      ppt->getPos() = src_point->getPos();
        ppt->getPos().x() += xSize; ppt->getPos().y() -= ySize;
        ppt->getPos() *= xform;
        if (txt_off.isValid())
        {
            ppt->setValue<UT_Vector3>(txt_off, UT_Vector3(uMax, vMin, 0));
        }

        if (shop_off.isValid())
        {
            poly->setValue<int>(shop_off,
                src_point->getValue<int>(parms.mySpriteShopOff));
        }

        // Apply the attribute map if we actually have one.
        if (parms.myAttribMap)
            applyMapToPrimitive(parms.myAttribMap, dest_offsets,
                                (GEO_Primitive *)poly->castToGeo(), src_point);
    }
    num_polys += gdp->primitives().entries();
//    printf("converted %d / %d\n", num_polys, src->points().entries());

    return gdp->primitives().entries();
}

static void
velocityMove(GU_Detail *gdp2, GU_Detail *gdp1, GU_Detail *src,
             const UT_IntArray &points, const GB_AttributeRef &off,
             fpreal scale)
{
    int                  i;
    GEO_Point           *ppt;
    const GEO_Point     *spt;
    UT_Vector3           vel;

    gdp2->merge(*gdp1);
    for (i = points.entries(); i-- > 0;)
    {
        spt = src->points()(points(i));
        vel = spt->getValue<UT_Vector3>(off);

        ppt = gdp2->points()(i*4);
        ppt->getPos() += UT_Vector4(scale * vel.x(),
                                    scale * vel.y(),
                                    scale * vel.z(),
                                    0);
        ppt = gdp2->points()(i*4+1);
        ppt->getPos() += UT_Vector4(scale * vel.x(),
                                    scale * vel.y(),
                                    scale * vel.z(),
                                    0);
        ppt = gdp2->points()(i*4+2);
        ppt->getPos() += UT_Vector4(scale * vel.x(),
                                    scale * vel.y(),
                                    scale * vel.z(),
                                    0);
        ppt = gdp2->points()(i*4+3);
        ppt->getPos() += UT_Vector4(scale * vel.x(),
                                    scale * vel.y(),
                                    scale * vel.z(),
                                    0);
    }
}

static inline int
computeDivs(fpreal inc, fpreal min)
{
    int divs = (int)SYSceil(inc / min);
         if (divs < 1) divs = 1;
    else if (divs > 4) divs = 4;
    return divs;
}

void
VRAY_DemoSprite::render()
{
    GU_Detail           *gdp, *vgdp;
    fpreal               max;
    UT_BoundingBox       kidbox;
    VRAY_DemoSprite     *kid;
    int                  dogeo;
    int                  nx, ny, nz;
    int                  ix, iy, iz;
    fpreal               xinc, yinc, zinc, factor;
    fpreal               xv, yv, zv;
    fpreal               dfactor;
    int                  sprite_limit = myParms->mySpriteLimit;
    fpreal               lod;

    dogeo = 1;
    // Compute LOD without regards to motion blur
    lod = getLevelOfDetail(myBox);
    if (lod > myParms->myChunkSize && myPointList.entries() > sprite_limit)
    {
        // Split into further procedurals
        dogeo = 0;
        xinc = myBox.sizeX();
        yinc = myBox.sizeY();
        zinc = myBox.sizeZ();

        max = myBox.sizeMax();
        dfactor = (xinc+yinc+zinc)/max;
        factor = SYSpow((fpreal)myPointList.entries() / sprite_limit,
                      1.0F/dfactor);
        if (factor > 4)
            factor = 4;
        max /= factor;
        //printf("Preparing to split %d points with %g lod [%g %g %g]\n",
        //      myPointList.entries(), lod,
        //      myBox.sizeX(), myBox.sizeY(), myBox.sizeZ());

        nx = ::computeDivs(xinc, max);
        ny = ::computeDivs(yinc, max);
        nz = ::computeDivs(zinc, max);

        if (nx == 1 && ny == 1 && nz == 1)
        {
            if (xinc > yinc)
            {
                if (xinc > zinc) nx = 2;
                else             nz = 2;
            }
            else
            {
                if (yinc > zinc) ny = 2;
                else             nz = 2;
            }
        }
        xinc /= (fpreal)nx;
        yinc /= (fpreal)ny;
        zinc /= (fpreal)nz;
        //printf("breaking up into: %dx%dx%d\n", nx, ny, nz);

        for (iz = 0, zv = myBox.vals[2][0]; iz < nz; iz++, zv += zinc)
        {
            for (iy = 0, yv = myBox.vals[1][0]; iy < ny; iy++, yv += yinc)
            {
                for (ix = 0, xv = myBox.vals[0][0]; ix < nx; ix++, xv += xinc)
                {
                    kidbox.initBounds(xv, yv, zv);
                    kidbox.enlargeBounds(xv+xinc, yv+yinc, zv+zinc);
                    kid = new VRAY_DemoSprite();
                    if (!kid->initChild(this, kidbox))
                        delete kid;
                    else
                    {
                        if (openProceduralObject())
                        {
                            addProcedural(kid);
                            closeObject();
                        }
                        else
                        {
                            dogeo = 1;
                            break;
                        }
                    }
                }
                if (dogeo) break;
            }
            if (dogeo) break;
        }
    }

    if (dogeo)
    {
        if (lod < 3) lod = 3;
        gdp = allocateGeometry();

        // Don't turn backface culling on.  We attempt to reduce data
        // size by using the st parameterization of the quad instead
        // of texture coordinates when we can, and when doing this, the
        // quad actually turns out to be backfacing.

        if (!makeSpritePoly(gdp, getPointGdp(), myPointList, *myParms,
                            queryRootName()))
            freeGeometry(gdp);
        else
        {
            if (myParms->myVelOff.isValid())
            {
                vgdp = allocateGeometry();
                velocityMove(vgdp, gdp, getPointGdp(), myPointList,
                             myParms->myVelOff, getTime());
            }
            else vgdp = 0;
            openGeometryObject();
            addGeometry(gdp, 0);
            if (vgdp)
            {
                addGeometry(vgdp, 1);
            }
            closeObject();
        }
    }
}

Generated on Mon Jan 28 00:26:27 2013 for HDK by  doxygen 1.5.9