GR/GR_SimpleNoise.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.
 *
 *----------------------------------------------------------------------------
 */

#include <UT/UT_DSOVersion.h>

#include <RE/RE_Render.h>

#include <GEO/GEO_Primitive.h>
#include <GEO/GEO_PrimPoly.h>

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

#include <GR/GR_Detail.h>
#include <GR/GR_RenderHook.h>
#include <GR/GR_RenderTable.h>


namespace HDK_Sample {
class GR_SimpleNoise : public GR_RenderHook
{
public:
    GR_SimpleNoise() {}
    virtual ~GR_SimpleNoise() {}

    int          getWireMask(GU_Detail * /*gdp*/,
                             const GR_DisplayOption * /*dopt*/) const
    {
        // We want to tell the renderer to ignore all polygons
        // as we'll be handling them.
        return ~GEOPRIMPOLY;
    }

    virtual void renderWire(GU_Detail *gdp,
                        RE_Render &ren,
                        const GR_AttribOffset &ptinfo,
                        const GR_DisplayOption *dopt,
                        float lod,
                        const GU_PrimGroupClosure *hidden_geometry);

    int          getShadedMask(GU_Detail * /*gdp*/,
                             const GR_DisplayOption * /*dopt*/) const
    {
        // We want to tell the renderer to ignore all polygons
        // as we'll be handling them.
        return ~GEOPRIMPOLY;
    }

    virtual void renderShaded(GU_Detail *gdp,
                        RE_Render &ren,
                        const GR_AttribOffset &ptinfo,
                        const GR_DisplayOption *dopt,
                        float lod,
                        const GU_PrimGroupClosure *hidden_geometry);

    virtual const char *getName() const { return "GR_SimpleNoise"; }

protected:
    // This does a simple random fractal on the given line.
    void         fillLineArray(UT_Vector3 *line, int step, unsigned seed);

    // This builds the seed & properly orients the line array.
    void         fillReversedLineArray(UT_Vector3 *line, int step,
                            GEO_Point *pt1, GEO_Point *pt2);

    // Draws the line between the two given points.
    void         renderLine(RE_Render &ren,
                            GEO_Point *pt1, GEO_Point *pt2,
                            float lod);

    // Draws an interpolated quad between the four given lines.
    void         renderQuad(RE_Render &ren, const UT_Vector3 &nml,
                            UT_Vector3 *left, UT_Vector3 *right,
                            UT_Vector3 *top, UT_Vector3 *bot,
                            int step);
};
} // End HDK_Sample namespace
using namespace HDK_Sample;

void
GR_SimpleNoise::fillLineArray(UT_Vector3 *line, int step, unsigned seed)
{
    // Determine new point.
    UT_Vector3          mid;
    float               dist;

    if (step < 2)
        return;

    dist = distance3d(line[0], line[step]);

    dist *= 0.1;
    
    mid = line[0];
    mid += line[step];
    mid /= 2.0f;

    mid.x() += dist * (UTrandom(seed) - 0.5f);
    mid.y() += dist * (UTrandom(seed) - 0.5f);
    mid.z() += dist * (UTrandom(seed) - 0.5f);

    step >>= 1;
    line[step] = mid;

    if (step > 1)
    {
        fillLineArray(line, step, seed);
        fillLineArray(&line[step], step, seed + step);
    }
}

void
GR_SimpleNoise::fillReversedLineArray(UT_Vector3 *line, int step,
                        GEO_Point *pt1, GEO_Point *pt2)
{
    int         n1, n2, i;
    unsigned    seed;
    GEO_Point   *tmp;
    bool        reverse = false;
    UT_Vector3  v3;

    n1 = pt1->getNum();
    n2 = pt2->getNum();

    if (n1 > n2)
    {
        reverse = true;
        tmp = pt1;
        pt1 = pt2;
        pt2 = tmp;

        n1 = pt1->getNum();
        n2 = pt2->getNum();
    }

    line[0] = pt1->getPos();
    line[step] = pt2->getPos();

    // n2 >= n1 is guaranteed, so this will match fillLineArray.
    seed = UTwang_inthash(n1) ^ n2;

    fillLineArray(line, step, seed);

    // We may have to reverse it...
    if (reverse)
    {
        for (i = 0; i < step / 2; i++)
        {
            v3 = line[i];
            line[i] = line[step - i];
            line[step - i] = v3;
        }
    }
            
}

void
GR_SimpleNoise::renderLine(RE_Render &ren,
                            GEO_Point *pt1, GEO_Point *pt2,
                            float lod)
{
    // We always draw from the least numbered point to the highest.
    if (pt1->getNum() > pt2->getNum())
    {
        GR_SimpleNoise::renderLine(ren, pt2, pt1, lod);
        return;
    }

    unsigned            seed;

    UT_Vector3          linearray[33];
    int                 i;
    int                 step;

    step = (int)UTrint(lod * 2);
    if (step > 5)
        step = 5;
    step = 1 << step;
    
    linearray[0] = pt1->getPos();
    linearray[step] = pt2->getPos();
    
    seed = UTwang_inthash(pt1->getNum()) ^ pt2->getNum();

    fillLineArray(linearray, step, seed);

    ren.beginLine();

    for (i = 0; i <= step; i++)
    {
        ren.vertex3DW(linearray[i].x(), linearray[i].y(), linearray[i].z());
    }

    ren.endLine();
}

void
GR_SimpleNoise::renderQuad(RE_Render &ren, const UT_Vector3 &nml,
                            UT_Vector3 *left, UT_Vector3 *right,
                            UT_Vector3 *top, UT_Vector3 *bot,
                            int step)
{
    int         i, j;
    float       u, v, ustep, vstep, mixfactor;
    float       n[3];

    n[0] = -nml.x();
    n[1] = -nml.y();
    n[2] = -nml.z();

    // The coordinates interpolated from left to right and top to bottom.
    UT_Vector3  lr, tb;
    UT_Vector3  v3;

    ustep = vstep = 1.0f / (float) step;

    u = 0.0f;
    for (i = 0; i < step; i++)
    {
        ren.beginQuadStrip();

        v = 0.0f;
        for (j = 0; j <= step; j++)
        {
            tb = bot[i] * (1.0f - v) + top[i] * v;
            lr = left[j] * (1.0f - u) + right[j] * u;

            // Note that while this mixing ensures we interpolate the
            // edges, it introduces discontinuities in the interior.
            if (fabs(u - 0.5f) > fabs(v - 0.5f))
            {
                // u is closer to edge than v.
                mixfactor = fabs(u - 0.5f) * 2;
            }
            else
            {
                mixfactor = 1.0f - fabs(v - 0.5f) * 2;
            }
            
            v3 = tb * (1.0f - mixfactor) + lr * mixfactor;

            ren.n3DW(n);
            ren.vertex3DW(v3.x(), v3.y(), v3.z());

            tb = bot[i+1] * (1.0f - v) + top[i+1] * v;
            lr = left[j] * (1.0f - u - ustep) + right[j] * (u + ustep);
            
            if (fabs(u + ustep - 0.5f) > fabs(v - 0.5f))
            {
                // u is closer to edge than v.
                mixfactor = fabs(u + ustep - 0.5f) * 2;
            }
            else
            {
                mixfactor = 1.0f - fabs(v - 0.5f) * 2;
            }
            v3 = tb * (1.0f - mixfactor) + lr * mixfactor;

            ren.n3DW(n);
            ren.vertex3DW(v3.x(), v3.y(), v3.z());

            v += vstep;
        }

        ren.endQuadStrip();

        u += ustep;
    }
}

void
GR_SimpleNoise::renderWire(GU_Detail *gdp,
                    RE_Render &ren,
                    const GR_AttribOffset & /*ptinfo*/,
                    const GR_DisplayOption * /*dopt*/,
                    float lod,
                    const GU_PrimGroupClosure *hidden_geometry)
{
    int                  i, nprim, nvtx, j, lj;
    GEO_Primitive       *prim;

    nprim = gdp->primitives().entries();
    for (i = 0; i < nprim; i++)
    {
        prim = gdp->primitives()(i);

        // Ignore hidden geomtry.
        if (hidden_geometry && hidden_geometry->containsPrim(prim))
            continue;

        // Only deal with polygons.
        if (!(prim->getPrimitiveId() & GEOPRIMPOLY))
            continue;
        
        nvtx = prim->getVertexCount();
        if (((GEO_PrimPoly *)prim)->isClosed())
        {
            j = 0;
            lj = nvtx-1;
        }
        else
        {
            j = 1;
            lj = 0;
        }
        for (; j < nvtx; j++)
        {
            renderLine(ren, 
                        prim->getVertex(lj).getPt(),
                        prim->getVertex(j).getPt(),
                        lod);
            lj = j;
        }
    }
}

void
GR_SimpleNoise::renderShaded(GU_Detail *gdp,
                    RE_Render &ren,
                    const GR_AttribOffset & /*ptinfo*/,
                    const GR_DisplayOption * /*dopt*/,
                    float lod,
                    const GU_PrimGroupClosure *hidden_geometry)
{
    int                  i, nprim, nvtx;
    GEO_Primitive       *prim;

    nprim = gdp->primitives().entries();
    for (i = 0; i < nprim; i++)
    {
        prim = gdp->primitives()(i);

        // Ignore hidden geomtry.
        if (hidden_geometry && hidden_geometry->containsPrim(prim))
            continue;

        // Only deal with polygons.
        if (!(prim->getPrimitiveId() & GEOPRIMPOLY))
            continue;

        nvtx = prim->getVertexCount();
        // Only deal with quads.
        if (nvtx != 4 ||
            !((GEO_PrimPoly *)prim)->isClosed())
        {
            continue;
        }

        UT_Vector3      top[33], bot[33], left[33], right[33];
        int             step;
        UT_Vector3      nml;

        nml = ((GEO_PrimPoly *)prim)->computeNormal();

        step = (int)UTrint(lod * 2);
        if (step > 5)
            step = 5;
        step = 1 << step;

        fillReversedLineArray(bot, step,
                prim->getVertex(0).getPt(),
                prim->getVertex(1).getPt());
        fillReversedLineArray(top, step,
                prim->getVertex(3).getPt(),
                prim->getVertex(2).getPt());
        fillReversedLineArray(left, step,
                prim->getVertex(0).getPt(),
                prim->getVertex(3).getPt());
        fillReversedLineArray(right, step,
                prim->getVertex(1).getPt(),
                prim->getVertex(2).getPt());

        renderQuad(ren, nml, left, right, top, bot, step);
    }
}

void
newRenderHook(GR_RenderTable *table)
{
    GR_SimpleNoise *hook = new GR_SimpleNoise;
    
    table->addHook(hook, GR_RENDER_HOOK_VERSION);
}

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