Splines under 3DSMAX

The most convenient way to manipulate an object made of several splines is to use the SplineShape class. Each object of this class has a BezierShape* public member (shape) that handles the splines themselves (Spline3D**)

The spline used in the examples is decribed in an XML file as following:

<!DOCTYPE CurveSetXML>
<animation nb_curves="1" image_height="512" isWorldCoord="1" image_width="512" nb_frames="1"
              zmin="-7.63998" ymin="-16.638" xmin="-8.10264"
              zmax="-5.35857" ymax="-13.4332" xmax="-5.0602" >
 <camera_info up_x="0" up_y="0" up_z="1" pos_x="-60" pos_y="-360" pos_z="0"
                 view_x="-60" view_y="100" view_z="0" />
 <curve_set frame_id="0" zmin="5.24285" ymin="-16.1648" xmin="-7.63998"
               zmax="8.19698" ymax="-13.4332" xmax="-5.0602" >
  <curve curve_id="0" nb_control_points="5">
   <control_points_set>
    <control_point x="1.0" y="0.0" z="2.0" />
    <control_point x="2.0" y="0.0" z="4.0" />
    <control_point x="3.0" y="0.0" z="4.0" />
    <control_point x="5.0" y="0.0" z="3.0" />
    <control_point x="4.0" y="0.0" z="0.0" />
   </control_points_set>
  </curve>
 </curve_set>
</animation>

Create the object then add the splines:

SplineShape* hair = (SplineShape *)ip->CreateInstance(SHAPE_CLASS_ID, splineShapeClassID);
Spline3D* spline = hair->shape.NewSpline();
for(int i=0;i<nbPoints;i++)
   //read ith point from file
   spline->AddKnot(SplineKnot(KTYPE_AUTO, LTYPE_LINE, pt, Point3(0,0,0),Point3(0,0,0)));
spline->SetClosed(0); // opened spline
spline->ComputeBezPoints();

The following table gives the values retreived by Spline3D methods spline->GetKnotPoint(k), spline->GetInVec(k), spline->GetOutVec(k) which respectively returns the Point3 kth knot, inward tangent and outrward tangent and the resulting curve.

pt0 : knot(1,0000, 0,0000, 2,0000)
      inVec(1,0000, 0,0000, 2,0000)
      outVec(1,3333, 0,0000, 3,0000)
pt1 : knot(2,0000, 0,0000, 4,0000)
      inVec(1,6667, 0,0000, 3,6667)
      outVec(2,3333, 0,0000, 4,3333)
pt2 : knot(3,0000, 0,0000, 4,0000)
      inVec(2,500000, 0,0000, 4,1667)
      outVec(3,500000, 0,0000, 3,8333)
pt3 : knot(5,0000, 0,0000, 3,0000)
      inVec(4,8333, 0,0000, 3,6667)
      outVec(5,1667, 0,0000, 2,3333)
pt4 : knot(4,0000, 0,0000, 0,0000)
      inVec(4,8333, 0,0000, 1,3333)
      outVec(4,0000, 0,0000, 0,0000)

AddKnot adds a SplineKnot which is constructed giving the knot type, the segment type, the knot itself wich is a point ON the spline, the inward tangent and the outward tangent.

The knot type can be:

The segment type can be:

ComputeBezPoints updates the spline internal structure when points are modified using the GUI for example.

Note: the Spline3D method GetVert can be misunderstood: it returns knot inVec or Outvec according the index:
spline->GetVert(0) = spline->GetInVec(0)
spline->GetVert(1) = spline->GetKnotPoint(0)
spline->GetVert(2) = spline->GetOutVec(0)

The following examples show the use of different types, insterpolation step is 1.

spline->AddKnot(SplineKnot(KTYPE_BEZIER, LTYPE_LINE, pt, Point3(0,0,0),Point3(0,0,0)));
pt0 : knot(1,0000, 0,0000, 2,0000)
      inVec(0,0000, 0,0000, 0,0000)
      outVec(0,0000, 0,0000, 0,0000)
pt1 : knot(2,0000, 0,0000, 4,0000)
      inVec(0,0000, 0,0000, 0,0000)
      outVec(0,0000, 0,0000, 0,0000)
pt2 : knot(3,0000, 0,0000, 4,0000)
      inVec(0,0000, 0,0000, 0,0000)
      outVec(0,0000, 0,0000, 0,0000)
pt3 : knot(5,0000, 0,0000, 3,0000)
      inVec(0,0000, 0,0000, 0,0000)
      outVec(0,0000, 0,0000, 0,0000)
pt4 : knot(4,0000, 0,0000, 0,0000)
      inVec(0,0000, 0,0000, 0,0000)
      outVec(0,0000, 0,0000, 0,0000)
spline->AddKnot(SplineKnot(KTYPE_AUTO, LTYPE_CURVE, pt, Point3(0,0,0),Point3(0,0,0)));
pt0 : knot(1,0000, 0,0000, 2,0000)
      inVec(1,0000, 0,0000, 2,0000)
      outVec(1,3333, 0,0000, 3,0000)
pt1 : knot(2,0000, 0,0000, 4,0000)
      inVec(1,6667, 0,0000, 3,6667)
      outVec(2,3333, 0,0000, 4,3333)
pt2 : knot(3,0000, 0,0000, 4,0000)
      inVec(2,500000, 0,0000, 4,1667)
      outVec(3,500000, 0,0000, 3,8333)
pt3 : knot(5,0000, 0,0000, 3,0000)
      inVec(4,8333, 0,0000, 3,6667)
      outVec(5,1667, 0,0000, 2,3333)
pt4 : knot(4,0000, 0,0000, 0,0000)
      inVec(4,8333, 0,0000, 1,3333)
      outVec(4,0000, 0,0000, 0,0000)

Now we introduce tangent information in the XML format as following:

<!DOCTYPE CurveSetXML>
<animation nb_curves="1" image_height="512" isWorldCoord="1" image_width="512" nb_frames="1"
              zmin="-7.63998" ymin="-16.638" xmin="-8.10264"
              zmax="-5.35857" ymax="-13.4332" xmax="-5.0602" >
 <camera_info up_x="0" up_y="0" up_z="1" pos_x="-60" pos_y="-360" pos_z="0"
                 view_x="-60" view_y="100" view_z="0" />
 <curve_set frame_id="0" zmin="5.24285" ymin="-16.1648" xmin="-7.63998"
               zmax="8.19698" ymax="-13.4332" xmax="-5.0602" >
  <curve curve_id="0" steps="1" nb_control_points="5" nb_in_tangents="5" nb_out_tangents="5">
   <control_points_set >
    <control_point x="1.0" y="0.0" z="2.0" itx="0.0" ity="0.0" itz="-1.0"
                      otx="0.0" oty="0.0" otz="1.0"/>
    <control_point x="2.0" y="0.0" z="4.0" itx="-1.0" ity="0.0" itz="-1.0"
                      otx="1.0" oty="0.0" otz="1.0"/>
    <control_point x="3.0" y="0.0" z="4.0" itx="-1.0" ity="0.0" itz="0.0"
                      otx="1.0" oty="0.0" otz="0.0"/>
    <control_point x="5.0" y="0.0" z="3.0" itx="0.0" ity="0.0" itz="1.0"
                      otx="0.0" oty="0.0" otz="-1.0"/>
    <control_point x="4.0" y="0.0" z="0.0" itx="1.0" ity="0.0" itz="1.0"
                      otx="-1.0" oty="0.0" otz="-1.0"/>
   </control_points_set>
  </curve>
 </curve_set>
</animation>
spline->AddKnot(SplineKnot(KTYPE_BEZIER, LTYPE_CURVE, pt, inVec, outVec);
pt0 : knot(1,0000, 0,0000, 2,0000)
      inVec(0,0000, 0,0000, -1,0000)
      outVec(0,0000, 0,0000, 1,0000)
pt1 : knot(2,0000, 0,0000, 4,0000)
      inVec(-1,0000, 0,0000, -1,0000)
      outVec(1,0000, 0,0000, 1,0000)
pt2 : knot(3,0000, 0,0000, 4,0000)
      inVec(-1,0000, 0,0000, 0,0000)
      outVec(1,0000, 0,0000, 0,0000)
pt3 : knot(5,0000, 0,0000, 3,0000)
      inVec(0,0000, 0,0000, 1,0000)
      outVec(0,0000, 0,0000, -1,0000)
pt4 : knot(4,0000, 0,0000, 0,0000)
      inVec(1,0000, 0,0000, 1,0000)
      outVec(-1,0000, 0,0000, -1,0000)

!! inVec and outVec in AddKnot give tangents extremities in absolute coordinates!!!! Use the following methods to describe tangents as local vectors:

 //jth spline knot     
spline->AddKnot(SplineKnot(KTYPE_BEZIER, LTYPE_CURVE, pt, Point3(0,0,0), Point3(0,0,0)));
spline->SetRelInVec(j, inVec);
spline->SetRelOutVec(j, outVec);
pt0 : knot(1,0000, 0,0000, 2,0000)
      inVec(1,0000, 0,0000, 1,0000)
      outVec(1,0000, 0,0000, 3,0000)
pt1 : knot(2,0000, 0,0000, 4,0000)
      inVec(1,0000, 0,0000, 3,0000)
      outVec(3,0000, 0,0000, 5,0000)
pt2 : knot(3,0000, 0,0000, 4,0000)
      inVec(2,0000, 0,0000, 4,0000)
      outVec(4,0000, 0,0000, 4,0000)
pt3 : knot(5,0000, 0,0000, 3,0000)
      inVec(5,0000, 0,0000, 4,0000)
      outVec(5,0000, 0,0000, 2,0000)
pt4 : knot(4,0000, 0,0000, 0,0000)
      inVec(5,0000, 0,0000, 1,0000)
      outVec(3,0000, 0,0000, -1,0000)

Changing steps value, give the following results:

steps = 1steps = 3steps = 5

To load an animation, the following code is used:

int cti = 1;
for(int i=0;i<nbPoints;i++)  {
  //read ith point and tangents at this point from file			
  hair->cont[cti]->SetValue(GetTicksPerFrame() * frame, pt);
  hair->cont[cti-1]->SetValue(GetTicksPerFrame() * frame, inVec);
  hair->cont[cti+1]->SetValue(GetTicksPerFrame() * frame, outVec);
  cti+=3;
}

hair->cont is the Point3 controllers array where frames are stored for each point (points are inVec, knot, outVec ordered sequentially) and values are set for each frame using SetValue method. !!!! inVec and outVec set up this way must be absolute coordinates!