HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Writing Solvers for DOPs

Introduction

It is possible to use the HDK to write your own solver. This solver will have access to the all the objects and data in your simulation. Your solver can interact with other, existing solvers in Houdini.

Usage

The easiest way to get started writing your own solver is to derive from SIM_SingleSolver:

class SIM_MyOwnSolver : public SIM_SingleSolver,
{
public:
GETSET_DATA_FUNCS_F("yourownaccuracy", MyOwnAccuracy);
protected:
// This implements your own solver step
SIM_Engine& engine, SIM_Object& object,
SIM_ObjectArray& feedback_to_objects,
const SIM_Time& time_step,
bool object_is_new
);
private:
static const SIM_DopDescription* getMyOwnSolverDescription();
DECLARE_DATAFACTORY(SIM_MyOwnSolver, "Your Own Solver",
getMyOwnSolverDescription());
};

The main thing to do is to write your own implementation of solveSingleObjectSubclass. A typical implementation of a custom solver that derives from SIM_SingleSolver looks like this:

SIM_Result SIM_MyOwnSolver::solveSingleObjectSubclass(
SIM_Engine& engine,
SIM_Object& object,
SIM_ObjectArray& feedback_to_objects,
const SIM_Time& time_step,
bool object_is_new
)
{
// Get the object's last state before this time step
const SIM_Geometry*const geometry(object.getGeometry());
... // extract simulation state from geometry
... // integrate simulation state forward by time step
... // using the solver parameters, e.g., getMyOwnAccuracy()
if( something went wrong )
return SIM_SOLVER_FAIL;
SIM_GeometryCopy* geometry_copy(
)
)
... // store the integrated simulation state in geometry_copy
return SIM_SOLVER_SUCCESS;
}

If you want to integrate your simulation objects simultaneously, then you need to derive your custom solver directly from SIM_Solver. In that case you would have to implement your own version of the member solveObjectsSubclass, which takes an SIM_ObjectArray of objects, instead of a single object.

Reading DOP Constraints

It is possible for your own solver work with any of the constraints that are specified in a DOP network. The responsibility for enforcing constraints lies with the solver; if you don't explicitly implement support for the constraints, then they will not affect your custom solver's objects.

Pin constraints can be used to ensure that certain specified points of simulation objects don't move, or follow an animated trajectory. The code to read, update, and apply pin constraints in your solver would look like this:

SIM_Engine engine; // passed to solveObjectsSubclass
SIM_Object* object; // one of objects passed to solveObjectsSubclass
...
const SIM_DataFilterByType hard_filter = "SIM_ConRelHard";
const SIM_DataFilterByType pin_filter = "SIM_ConAnchorObjPointPos";
const SIM_DataFilterByType goal_filter = "SIM_ConAnchorWorldSpacePos";
const fpreal t_end = engine.getSimulationTime();
object, &hard_filter, &pin_filter, &goal_filter, t_end
);
for( ; !it.atEnd(); it.advance() )
{
SIM_ConRel& conrel(*constraint_it.getConRel());
conrel.stateErrorCode(SIM_CONRELERR_NONE);
const SIM_ConAnchorObjPointPos* object_anchor(
);
const SIM_ConAnchorWorldSpacePos* anchor_goal(
);
const int anchor_pid = object_anchor->getPoint();
const UT_Vector3 pin_position = anchor_goal->getPosition(t_end);
... // Ensure that your own simulation model keeps the point of
... // object with index anchor_pid pinned to pin_position
}

Reading External Forces

It is possible to make your own solver react to the forces that are applied to its objects in a DOP network. Examples of these forces are gravity, wind, drag, and fan. Each of these forces is represented by a SIM_Force object. For each object in the objects array you will need to process all SIM_Force objects that affect it. You will want to evaluate these SIM_Force objects for each of the points in your simulation object's geometry. In general, the force direction and magnitude will vary from point to point. These point forces depend on the point positions as well as the point velocities. The drag force is an example of a force that is velocity-dependent. In general, the mass of each point is also needed. The gravity force is an example of this.

The code for extracting the per-point forces forces for a simulation object looks like this:

const SIM_Geometry* geometry = object.getGeometry();
// Get conversion from geometry coordinates to sim coordinates
UT_DMatrix4 object_to_world;
geometry->getTransform(object_to_world);
const SIM_Position* sim_position_object = object.getPosition();
UT_DMatrix4 world_to_sim;
sim_position_object->getTransform(world_to_sim);
UT_DMatrix4 object_to_sim = object_to_world * world_to_sim;
// Get the velocity attribute from the geometry
GA_ROHandleV3 att_point_velocity(&detail, GA_ATTRIB_POINT,
if( !att_point_velocity.isValid() )
{
.. // Handle missing attribute
}
object.filterConstSubData(
sim_forces, 0,
SIM_DataFilterByType("SIM_Force"),
);
const UT_Vector irrelevant_angular_velocity_pid(0, 0, 0);
UT_Vector irrelevant_torque(0, 0, 0);
UT_Vector3 position_pid;
UT_Vector3 velocity_pid;
for( int fi = 0; fi < sim_forces.entries(); ++fi )
{
const SIM_Force* force = SIM_DATA_CAST_CONST(forces(fi), SIM_Force);
for( int pid = 0; pid < npids; ++pid )
{
GA_Offset point = detail.pointOffset(pid);
position_pid = rowVecMult(detail.getPos(point), object_to_sim);
velocity_pid = rowVecMult3(
att_point_velocity.get(point), object_to_sim
);
.. // Assign a mass mass_pid for point pid (e.g., mass attribute)
UT_Vector3 force_pid;
force->getForce(
object,
position_pid, velocity_pid, irrelevant_angular_velocity_pid,
mass_pid,
force_pid,
irrelevant_torque
);
}
}

This code uses the getForce method on SIM_Force, which assumes that all the mass in your physical model is concentrated in the query points. Note that, in addition, the SIM_Force takes an angular velocity, and returns a torque. These parameters are legacy; they may be removed from the SIM_Force::getForce interface in future versions of the HDK. Dummy variables are passed in for both the angular velocity and torque.

Examples

Examples of custom solvers can be found in SIM/SIM_SolverSNOW::h and SIM/SIM_SolverHair.h. Both these examples derive from SIM_SingleSolver (as opposed to deriving from SIM_Solver directly).