How to add own internal function


Why it is introduced

   One of the most interesting features in POV-Ray 3.5 are functions. They are powered by virtual machine. They are really fast but complex calculations are still a little faster when compiled directly into executable file. My idea was to show how one can add own internal (precompiled) function to the list of internal functions already builded in POV 3.5. As an example I choosed f_triangle function. This function will return distance to specified triangle. First I have designed this function in POV-Ray SDL. You can find it in iso_CSG library as IC_Triangle macro. Then I have started rewrite it as internal function.

f_triangle usage examples
f_triangle function as pattern f_triangle isosurface displaced with bozo
pattern isosurface
 

How it is implemented

   Here are steps necessary to introduce own internal function. First you have to add your function to the list of definition in Global functions block in fnintern.cpp file.

/*****************************************************************************
* Global functions
******************************************************************************/

DBL f_algbr_cyl1(DBL *ptr, unsigned int fn); // 0
:
:
DBL f_noise_generator(DBL *ptr, unsigned int fn); // 78
DBL f_triangle(DBL *ptr, unsigned int fn); // 79

Now we have to add this function to the list POVFPU_TrapTable. The that second part of this addition 10 + 3 means that ther would be 13 parameters to this function: 3 for x,y,z and 10 for additional parameters - in this case coordinates of triangle vertices and last for triangle thickness. Additionally POVFPU_TrapTableSize have to be increased to point last (NULL) entry in list of internal functions.

/*****************************************************************************
* Global variables
******************************************************************************/

Trap POVFPU_TrapTable[] =
{
	{ f_algbr_cyl1,              5 + 3 }, // 0
        :
        :
	{ f_triangle,               10 + 3 }, // 79
	{ NULL, 0 }
};

TrapS POVFPU_TrapSTable[] =
{
	{ f_pigment,                 0 + 3 }, // 0
	{ f_transform,               0 + 3 }, // 1
	{ f_spline,                  0 + 1 }, // 2
	{ NULL, 0 }
};

unsigned int POVFPU_TrapTableSize = 80;
unsigned int POVFPU_TrapSTableSize = 3;

Final step is just to write content for f_triangle function. To make this code shorter I have defined 3 additional definitions. Place it in any place where they will be visible for compiler, for example in vectors.h

// macro to prevent zero-lenth normalizing
#define VNormalizeEqNotZeroLength(a,len) \
        { \
                len=sqrt((a)[X]*(a)[X]+(a)[Y]*(a)[Y]+(a)[Z]*(a)[Z]); \
                if(len<=0) \
                        Error( "Can't normalize zero length vector" ); \
                (a)[X]/=len; \
                (a)[Y]/=len; \
                (a)[Z]/=len; \
        }

// macro to prevent zero-lenth normalized cross product
#define VNormalizeCrossNotZeroLength(a,v1,v2) \
      { \
                DBL d_temp; \
                \
                VCross(a,v1,v2); \
                VNormalizeEqNotZeroLength(a,d_temp); \
      }
        
// macro to calculate appearance for one edge
#define DistanceToEdge( Dist , Edge , End , Point ) \
        { \
                DBL Edge_To_Point; \
                DBL Edge_Length,Rd; \
                \
                VNormalizeEqNotZeroLength(Edge,Edge_Length); \
                VDot(Rd,Edge,End); \
                VDot(Edge_To_Point,Edge,Point); \
                Edge_To_Point=max(min(Edge_To_Point-Rd,Edge_Length),0.0); \
                VSub(v_temp,Point,End); \
                VSubScaledEq(v_temp,Edge_To_Point,Edge); \
                VLength(Dist,v_temp); \
        }

So we have now everything to code f_triangle in fnintern.cpp file. All parameters passed to this function are available via macros PARAM... in DBL *ptr parameter. We have to only calculate and return result.

DBL f_triangle(DBL *ptr, unsigned int) // 79
{
        // PARAM_X  .. PARAM_Z  - x,y,z function parameters
        // PARAM(0) .. PARAM(2) - first vertex coordinates
        // PARAM(3) .. PARAM(5) - second vertex coordinates
        // PARAM(6) .. PARAM(8) - third vertex coordinates
        // PARAM(9)             - thickness

        VECTOR v_temp,N,R1,R2,R3,E;
        DBL Nd,Ed,El;
        DBL Distance = 0.0;
        VECTOR P0 = { PARAM_X, PARAM_Y, PARAM_Z };
        VECTOR P1 = { PARAM(0), PARAM(1), PARAM(2) };
        VECTOR P2 = { PARAM(3), PARAM(4), PARAM(5) };
        VECTOR P3 = { PARAM(6), PARAM(7), PARAM(8) };

        VSub(R1,P2,P1);
        VSub(R2,P3,P2);
        VSub(R3,P1,P3);
        VNormalizeCrossNotZeroLength(N,R2,R3);
        VNormalizeCrossNotZeroLength(E,N,R1);
        VDot(Ed,E,P1);
        VDot(El,E,P0);
        if ( Ed < El )
        {
                VNormalizeCrossNotZeroLength(E,N,R2);
                VDot(Ed,E,P2);
                VDot(El,E,P0);
                if ( Ed < El )
                {
                        VNormalizeCrossNotZeroLength(E,N,R3);
                        VDot(Ed,E,P3);
                        VDot(El,E,P0);
                        if ( Ed < El )
                        {
                                VDot(Nd,N,P1);
                                VDot(Distance,N,P0);
                                Distance = fabs( Distance-Nd );
                        }
                        else
                        {
                                DistanceToEdge( Distance , R3 , P3 , P0 );
                        }
                }
                else
                {
                        DistanceToEdge( Distance , R2 , P2 , P0 );
                }
        }
        else
        {
                DistanceToEdge( Distance , R1 , P1 , P0 );
        }

        return ( Distance - (PARAM(9)/2) ) ;
}

After compilation you can use this function in your POV scripts this way:

#declare f_triangle=function{internal(79)};

#declare P1=-x;
#declare P2=y;
#declare P3=x-y;

#macro List(P)
        #local Px=P.x;
        #local Py=P.y;
        #local Pz=P.z;
        Px,Py,Pz
#end

#declare Pattern = function{f_triangle(x,y,z,List(P1),List(P2),List(P3),.1)};

Notes

   Do you want to know how to use other internals in your own function? Do you want to know how to use patterns there to speedup isosurface calculations ? Ask me for next tutorial. I want to know that somebody needs it :-).


Contact

This is an unofficial addition to POV-Ray. Do not ask the POV-Team for help with this. Feel free to send all opinions, suggestions, corrections and thanks :-) connected with this site to me
Copyright 2002 by Wlodzimierz ABX Skiba

Valid HTML 4.01!   Valid CSS!   Bobby WorldWide Approved AAA
If a thing is worth doing, it is worth doing well.