SIM/SIM_SolverHair.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 "SIM_SolverHair.h"
#include <UT/UT_DSOVersion.h>
#include <GU/GU_PrimPoly.h>
#include <PRM/PRM_Include.h>
#include <SIM/SIM_DopDescription.h>
#include <SIM/SIM_GeometryCopy.h>
#include <SIM/SIM_DataFilter.h>
#include <SIM/SIM_Object.h>
#include <SIM/SIM_ObjectArray.h>
#include <SIM/SIM_Engine.h>
#include <SIM/SIM_Force.h>

using namespace HDK_Sample;

void
initializeSIM(void *)
{
    IMPLEMENT_DATAFACTORY(SIM_SolverHair);
}

namespace HDK_Sample {

class SIM_HairForceCallback : public SIM_PointForceCallback,
                              public SIM_EachDataCallback
{
public:
                         SIM_HairForceCallback(GU_Detail &gdp,
                                        const SIM_Object &object,
                                        const UT_DMatrix4 &xform,
                                        fpreal timestep,
                                        const GB_AttributeRef &massoffset,
                                        const GB_AttributeRef &veloffset);
    virtual             ~SIM_HairForceCallback();

    virtual void         callbackConst(const SIM_Data *data,
                                       const char *name);
    virtual void         forceCallback(int ptnum,
                                       const UT_Vector3 &force,
                                       const UT_Vector3 &torque);

private:
    GU_Detail                           &myGdp;
    const SIM_Object                    &myObject;
    const UT_DMatrix4                    myTransform;
    fpreal                               myTimestep;
    GB_AttributeRef                      myMassOffset;
    GB_AttributeRef                      myVelOffset;
};

} // namespace HDK_Sample

SIM_HairForceCallback::SIM_HairForceCallback(GU_Detail &gdp,
                                             const SIM_Object &object,
                                             const UT_DMatrix4 &xform,
                                             fpreal timestep,
                                             const GB_AttributeRef &massoffset,
                                             const GB_AttributeRef &veloffset)
 : myGdp(gdp),
   myObject(object),
   myTransform(xform),
   myTimestep(timestep),
   myMassOffset(massoffset),
   myVelOffset(veloffset)
{
}

SIM_HairForceCallback::~SIM_HairForceCallback()
{
}

void
SIM_HairForceCallback::callbackConst(const SIM_Data *data, const char *)
{
    const SIM_Force     *force = SIM_DATA_CASTCONST(data, SIM_Force);
    GU_DetailHandle      gdh;

    gdh.allocateAndSet(&myGdp, false);
    force->getPointForces(*this, myObject, gdh, myTransform, 0, 0, false);
}

void
SIM_HairForceCallback::forceCallback(int ptnum,
                                     const UT_Vector3 &force,
                                     const UT_Vector3 &)
{
    GEO_Point   *ppt = myGdp.points()(ptnum);
    float        mass;

    mass = ppt->getValue<float>(myMassOffset);
    ppt->setValue<UT_Vector3>(myVelOffset,
            ppt->getValue<UT_Vector3>(myVelOffset) + (force/mass)*myTimestep);
}

SIM_SolverHair::SIM_SolverHair(const SIM_DataFactory *factory)
    : BaseClass(factory),
      SIM_OptionsUser(this)
{
}

SIM_SolverHair::~SIM_SolverHair()
{
}

const SIM_DopDescription *
SIM_SolverHair::getSolverHairDopDescription()
{
    static PRM_Template          theTemplates[] = {
        PRM_Template()
    };

    static SIM_DopDescription    theDopDescription(true,
                                                   "hdk_hairsolver",
                                                   "Hair Solver",
                                                   SIM_SOLVER_DATANAME,
                                                   classname(),
                                                   theTemplates);

    return &theDopDescription;
}

void
SIM_SolverHair::solveHair(SIM_GeometryCopy &hairgeo,
                          const SIM_ObjectArray &srcobjs,
                          const SIM_Object &object,
                          const SIM_Time &timestep) const
{
    GU_DetailHandleAutoWriteLock         gdl(hairgeo.lockGeometry());
    GU_Detail           &gdp = *gdl.getGdp();
    UT_DMatrix4          xform;
    UT_Vector4           startpos, midline;
    UT_Vector3           dp, dv;
    const UT_Vector3     zero(0.0, 0.0, 0.0);
    const float          one = 1.0;
    GEO_Primitive       *hairpoly;
    GEO_Point           *hairpt0, *hairpt1, *hairpt2;
    GB_AttributeRef      massoffset, veloffset;
    int                  primnum, ptnum, geonum, numhairprims;

    hairgeo.getTransform(xform);
    primnum = 0;
    numhairprims = gdp.primitives().entries();
    for( geonum = 0; geonum < srcobjs.entries(); geonum++ )
    {
        const SIM_Object *sourceobj = srcobjs(geonum);
        const SIM_Geometry *sourcegeo = sourceobj->getGeometry();
        if(!sourcegeo || sourcegeo->getGeometry().isNull())
            continue;

        GU_DetailHandleAutoReadLock sourcegdl(sourcegeo->getGeometry());
        const GU_Detail         *sourcegdp = sourcegdl.getGdp();
        const GEO_Point         *sourcept;
        UT_DMatrix4              sourcexform;
        
        SIMgetGeometryTransform(sourcexform, *sourceobj);
        massoffset = gdp.addPointAttrib(
                    gdp.getStdAttributeName(GEO_ATTRIBUTE_MASS),
                    sizeof(float), GB_ATTRIB_FLOAT, &one);
        veloffset = gdp.addPointAttrib(
                    gdp.getStdAttributeName(GEO_ATTRIBUTE_VELOCITY),
                    sizeof(UT_Vector3), GB_ATTRIB_VECTOR, &zero);

        // Apply all forces and move the hairs according to those forces.
        SIM_HairForceCallback    callback(gdp, object, xform, timestep,
                                          massoffset, veloffset);
        object.forEachConstSubData(callback,
                                   SIM_DataFilterByType("SIM_Force"),
                                   SIM_FORCES_DATANAME,
                                   SIM_DataFilterNone());

        // Integrate our velocities.
        FOR_ALL_GPOINTS((&gdp), hairpt0)
        {
            dp = hairpt0->getValue<UT_Vector3>(veloffset);
            dp *= timestep;

            hairpt0->getPos() = hairpt0->getPos() + dp;
        }
        
        // Now pull all the points along to make sure the hair length
        // remains constant.
        FOR_ALL_GPOINTS(sourcegdp, sourcept)
        {
            if( primnum >= numhairprims )
                break;

            startpos = sourcept->getPos();
            startpos *= sourcexform;
            hairpoly = gdp.primitives()(primnum);
            if( hairpoly->getPrimitiveId() == GEOPRIMPOLY &&
                hairpoly->getVertexCount() == 10 )
            {
                UT_Vector3      vel;
                hairpt0 = hairpoly->getVertex(0).getPt();
                hairpt1 = hairpoly->getVertex(1).getPt();
                // Calculate the change in position we need to enforce
                // our length constraints. Then calculate the change in
                // velocity inherent in this change in position, using
                // dp = vt, we get dv = dp/t.
                dp = startpos - hairpt0->getPos();
                dv = dp / timestep;
                vel = hairpt0->getValue<UT_Vector3>(veloffset);
                vel += dv;
                // Apply drag. Just scale down the velocity.
                vel *= 0.8;
                hairpt0->setValue<UT_Vector3>(veloffset, vel);
                hairpt0->getPos() = hairpt0->getPos() + dp;
                for( ptnum = 1; ptnum < 10; ptnum++ )
                {
                    if( ptnum < 9 )
                        hairpt2 = hairpoly->getVertex(ptnum + 1).getPt();
                    midline = hairpt2->getPos() - hairpt0->getPos();
                    // Calculate the change in position we need to enforce
                    // our length constraints. Then calculate the change in
                    // velocity inherent in this change in position, using
                    // dp = vt, we get dv = dp/t.
                    dp = midline * (0.1 / midline.length()) +
                         hairpt0->getPos() - hairpt1->getPos();
                    dv = dp / timestep;
                    vel = hairpt1->getValue<UT_Vector3>(veloffset);
                    vel += dv;
                    // Apply drag. Just scale down the velocity.
                    vel *= 0.8;
                    hairpt1->setValue<UT_Vector3>(veloffset, vel);
                    hairpt1->getPos() = hairpt1->getPos() + dp;
                    hairpt0 = hairpt1;
                    hairpt1 = hairpt2;
                }
            }
            primnum++;
        }
    }

    hairgeo.releaseGeometry();
}

void
SIM_SolverHair::createHairFromSource(SIM_GeometryCopy &hairgeo,
                                     const SIM_ObjectArray &srcobjs) const
{
    GU_DetailHandleAutoWriteLock         gdl(hairgeo.lockGeometry());
    GU_Detail           &gdp = *gdl.getGdp();
    UT_Vector4           startpos;
    GEO_Point           *newpt;
    GEO_Face            *newpoly;
    int                  i, geonum;

    for( geonum = 0; geonum < srcobjs.entries(); geonum++ )
    {
        const SIM_Object *sourceobj = srcobjs(geonum);
        const SIM_Geometry *sourcegeo = sourceobj->getGeometry();
        if(!sourcegeo || sourcegeo->getGeometry().isNull())
            continue;

        GU_DetailHandleAutoReadLock sourcegdl(sourcegeo->getGeometry());
        const GU_Detail         *sourcegdp = sourcegdl.getGdp();
        const GEO_Point         *sourcept;
        UT_DMatrix4              sourcexform;

        SIMgetGeometryTransform(sourcexform, *sourceobj);
        FOR_ALL_GPOINTS(sourcegdp, sourcept)
        {
            startpos = sourcept->getPos();
            startpos *= sourcexform;
            newpoly = GU_PrimPoly::build(&gdp, 0, GU_POLY_OPEN, 0);
            for( i = 0; i < 10; i++ )
            {
                newpt = gdp.appendPoint();
                newpt->getPos().assign(startpos.x(),
                                       startpos.y() + (fpreal)i * 0.1,
                                       startpos.z(),
                                       1.0);
                newpoly->appendVertex(newpt);
            }
        }
    }

    hairgeo.releaseGeometry();
}

SIM_Solver::SIM_Result
SIM_SolverHair::solveSingleObjectSubclass(SIM_Engine & /*engine*/,
                                          SIM_Object &object,
                                          SIM_ObjectArray &,
                                          const SIM_Time &timestep,
                                          bool newobject)
{
    SIM_GeometryCopy                    *hairgeo = 0;
    SIM_ObjectArray                      sourceobjects;

    if( newobject )
    {
        if( !SIM_DATA_GET(object, SIM_GEOMETRY_DATANAME, SIM_GeometryCopy) )
        {
            hairgeo = SIM_DATA_CREATE(object, SIM_GEOMETRY_DATANAME,
                                      SIM_GeometryCopy, 0);
            object.getAffectors(sourceobjects, "SIM_RelationshipSource");
            if( hairgeo && sourceobjects.entries() > 0 )
                createHairFromSource(*hairgeo, sourceobjects);
        }
    }
    else
    {
        hairgeo = SIM_DATA_GET(object, SIM_GEOMETRY_DATANAME, SIM_GeometryCopy);
        if( hairgeo )
        {
            object.getAffectors(sourceobjects, "SIM_RelationshipSource");
            if( hairgeo && sourceobjects.entries() > 0 )
                solveHair(*hairgeo, sourceobjects, object, timestep);
        }
    }

    return hairgeo ? SIM_SOLVER_SUCCESS : SIM_SOLVER_FAIL;
}


Generated on Mon Jan 28 00:45:44 2013 for HDK by  doxygen 1.5.9