POV-Ray : Newsgroups : povray.binaries.tutorials : Making POV Patches(first draft) - attached files (1/1) Server Time
3 Feb 2023 07:58:38 EST (-0500)
  Making POV Patches(first draft) - attached files (1/1) (Message 1 to 2 of 2)  
From: Chris Huff
Subject: Making POV Patches(first draft) - attached files (1/1)
Date: 5 Dec 1999 13:11:18
Message: <chrishuff_99-5A3D7B.13112905121999@news.povray.org>
Here it is, a very early draft version. It contains 2 tutorials, one for 
adding a solid pattern and the other for adding a noise3d() function.
Please tell me what you think.
Because of the differences with line endings for Mac and PC, I have 
included both a Mac and a PC text version. I am thinking of using HTML, 
that would eliminate the problem with line endings and make the 
formatting much clearer. Any comments?

-- 
Chris Huff
e-mail: chr### [at] yahoocom
Web page: http://chrishuff.dhs.org/
Writing POV-Ray Patches(draft)

    Introduction
    Adding Reserved Words
    Adding Parsing Code
    Data Types
    Utility Functions and Macros
    Step-by-step tutorials:
        Adding a solid pattern.
        Adding a noise3d function.

Introduction
When I started writing patches, I found that there were no "getting started" documents
around. This is my attempt to rectify this problem. The finished document will consist
of a few reference sections, and some fully self-contained tutorials. Because the
tutorials are completely self-contained, there is a lot of duplication of things such
as reserving words and tokens, and parsing. I will try to keep things separated and
organized enough that you don't have to read the same thing over and over.




Adding Reserved Words
There are 2 places you need to modify to add a reserved word: an enum named TOKEN_IDS
in parse.h, and an array named Reserved_Words[] in tokenize.c.
The entry in TOKEN_IDS gives POV a way to refer to the token, and the entry in
Reserved_Words[] tells POV what string to associate with that token.

While it doesn't matter where in Reserved_Words[] the entry is placed, the position in
TOKEN_IDS does matter. Just follow the comments for now, I plan on making a "map" of
TOKEN_IDS sometime in the future.




Adding Parsing Code
Here I will explain how to add parsing code, and explain the various functions and
macros. For now, try finding a piece of code that does something close to what you
want, and copy off of that.




Data Types
Here I will document the various data types used in POV-Ray.




Utility Functions and Macros
Here I will document the various functions and macros available.




Tutorials:
Adding a solid pattern.
Here we will add a "solid" pattern to POV-Ray. This pattern is constant over space,
and is probably the simplest pattern possible, but it is quite useful when used with
animations and blend maps.
The first step is to add the reserved word. The syntax for this pattern will be as
follows:
pigment {
    solid PATT_VAL
    ...other pigment stuff...
}
As you can see, we need to reserve the word "solid" for use with the solid pattern.
To do this, you need to add a line in parse.h and tokenize.c.
In parse.h, add an entry to the enum TOKEN_IDS, at the end, but before LAST_TOKEN:
  MATERIAL_ID_TOKEN,
  UV_ID_TOKEN,
  VECTOR_4D_ID_TOKEN,
  SOLID_TOKEN,//Here
  LAST_TOKEN
};
This is what the token will be referred to as from within the program. However, you
need to tell POV-Ray what string to associate with that token. You do that by adding
an entry to the array Reserved_Words[] in tokenize.c:
  {X_TOKEN, "x"},
  {YES_TOKEN, "yes"},
  {Y_TOKEN, "y"},
  {Z_TOKEN, "z"},
  {SOLID_TOKEN, "solid"}
};
You may want to put the entry in alphabetical order with the other entries, I prefer
to leave my additions at the end in one place.

Next, you need to give POV-Ray what to refer to the pattern itself as. The token is
just for parsing, the pattern itself is referred to by a #define in pattern.h.
Place yours after the density file identifier in the list of #defines at the top of
this file, and number it accordingly:
#define BOXED_PATTERN           29
#define CYLINDRICAL_PATTERN     30
#define DENSITY_FILE_PATTERN    31
#define SOLID_PATTERN           32
The placement in this list does matter, depending on the type of pattern you are
adding. There are comments to show where to put the #define. Because this is just an
ordinary float pattern like bozo or gradient, we put it at the end with the others.

Next, you need to add a place to store the value of the pattern. In frame.h, find the
#define TPATTERN_FIELDS. Add in a variable for the pattern value:
#define TPATTERN_FIELDS                  \
  DBL solidVal;/*here*/                  \
  unsigned short Type, Wave_Type, Flags; \
  int References;                        \
  SNGL Frequency, Phase;                 \
Notice that we use DBL and SNGL, these are #defines commonly used in the POV source.
They are defined in frame.h, among other things. I recommend you look through frame.h
just to see what other things are defined there.

Now we have the tokens, the pattern identifier, and a place to store the pattern data,
the next step is the parser. You need to add a case in the function Parse_Pattern() in
the file parstxtr.c for the solid pattern.
This is pretty straightforward, there are macros for adding the cases. I usually just
copy an existing pattern that does something close to what I want. Add the case for
the solid pattern right after the one for density file:
     CASE (DENSITY_FILE_TOKEN)
       if (Old_Type==DENSITY_FILE_PATTERN) 
       {
         Destroy_Density_File(Old_Density_File);
       }   
       New->Type = DENSITY_FILE_PATTERN;
       New->Vals.Density_File = Create_Density_File();
       GET(DF3_TOKEN);
       New->Vals.Density_File->Data->Name = Parse_String();
       Read_Density_File(New->Vals.Density_File);
       EXIT
     END_CASE

     CASE (SOLID_TOKEN)
       New->Type = SOLID_PATTERN;
       New->solidVal = Parse_Float();
       EXIT
     END_CASE
Use the token ID for your pattern keyword as the parameter to the CASE macro, and set
the type of the pattern to your pattern identifier defined in pattern.h. Also add a
call to Parse_Float() to parse the parameter. This function returns the value of the
float parsed, so assign that value to the variable you added in frame.h.

Now all you have to do is add code for calculating the actual pattern. This time, open
pattern.c. Add a prototype for your function in the list at the top of this file:
static DBL density_file (VECTOR EPoint, TPATTERN *TPat);
static DBL solid(VECTOR EPoint, TPATTERN *TPat);
Then go to a convenient place in the file, I choose the end, and add the
implementation for the function:
static DBL solid(VECTOR EPoint, TPATTERN *TPat)
{
    return TPat->solidVal;
}
If you ever need to, you can add initializers for the variables in Init_TPat_Fields(),
and copy them in Copy_TPat_Fields(), although they aren't necessary for this pattern,
since the only time the solid() function will be called the variable will have been
assigned, and Copy_TPat_Fields() already takes care of copying it.

The only thing remaining is to tell POV-Ray to use this function for calculating the
solid pattern. Add a single line to the function Evaluate_TPat(), after density file:
    case DENSITY_FILE_PATTERN:value = density_file (TPoint, TPat);  break;
    case SOLID_PATTERN:value        = solid(TPoint, TPat);          break;


That should be it. You now have a working solid pattern.



Adding a noise3d() function.
Here we will add a noise3d() function to POV-Ray. This function returns a value in the
range 0-1 depending on the location in space, and is identical to the bozo pattern. If
you have used isosurfaces, you are probably already familiar with it.
The first step is to add the reserved word. The syntax for this function will be:
noise3d(VECTOR_POS)
I chose a vector for this instead of 3 float values, because if you are using float
values, it only means you have to use 2 extra characters(<>), but if I chose float
values, it would require 3 dot operators and 2 more copies of the vector identifier to
use it with a vector:
noise3d(<FloatX, FloatY, FloatZ>)//using 3 floats with vector syntax
noise3d(VectID.x, VectID.y, VectID.z)//using vector identifier with float syntax

For this patch, we need to reserve the word "noise3d".
To do this, you need to add a line in parse.h and tokenize.c.
In parse.h, add an entry to the enum TOKEN_IDS. Since this is a float function, it
needs to be entered in a specific place:
  DEFINED_TOKEN,
  NOISE3D_TOKEN,//Here
  FLOAT_FUNCT_TOKEN,

/*WARNING: All new functions returning a float value must be placed
  before FLOAT_FUNCT_TOKEN or the parser won't work right. */

This is what the token will be referred to as from within the program. However, you
need to tell POV-Ray what string to associate with that token. You do that by adding
an entry to the array Reserved_Words[] in tokenize.c:
  {X_TOKEN, "x"},
  {YES_TOKEN, "yes"},
  {Y_TOKEN, "y"},
  {Z_TOKEN, "z"},
  {NOISE3D_TOKEN, "noise3d"}
};
You may want to put the entry in alphabetical order with the other entries, I prefer
to leave my additions at the end in one place.

The next step is to tell POV-Ray what to do when it encounters this token. Open up
express.c, and go to the function Parse_Num_Factor(). Since noise3d() returns a float,
we will put it in the case for float functions. Lets add our function at the top:


    CASE (FLOAT_FUNCT_TOKEN)
       /* All of these functions return a DBL result */
       switch(Token.Function_Id)
         {
          case NOISE3D_TOKEN:
            Parse_Vector_Param(Vect);
            Val = Noise(Vect);
            break;

          case ABS_TOKEN:
            Val = Parse_Float_Param();
            Val = fabs(Val);
            break;

That is it, you now have a working noise3d() function!


Writing POV-Ray Patches(draft)

    Introduction
    Adding Reserved Words
    Adding Parsing Code
    Data Types
    Utility Functions and Macros
    Step-by-step tutorials:
        Adding a solid pattern.
        Adding a noise3d function.

Introduction
When I started writing patches, I found that there were no "getting started" documents
around. This is my attempt to rectify this problem. The finished document will consist
of a few reference sections, and some fully self-contained tutorials. Because the
tutorials are completely self-contained, there is a lot of duplication of things such
as reserving words and tokens, and parsing. I will try to keep things separated and
organized enough that you don't have to read the same thing over and over.




Adding Reserved Words
There are 2 places you need to modify to add a reserved word: an enum named TOKEN_IDS
in parse.h, and an array named Reserved_Words[] in tokenize.c.
The entry in TOKEN_IDS gives POV a way to refer to the token, and the entry in
Reserved_Words[] tells POV what string to associate with that token.

While it doesn't matter where in Reserved_Words[] the entry is placed, the position in
TOKEN_IDS does matter. Just follow the comments for now, I plan on making a "map" of
TOKEN_IDS sometime in the future.




Adding Parsing Code
Here I will explain how to add parsing code, and explain the various functions and
macros. For now, try finding a piece of code that does something close to what you
want, and copy off of that.




Data Types
Here I will document the various data types used in POV-Ray.




Utility Functions and Macros
Here I will document the various functions and macros available.




Tutorials:
Adding a solid pattern.
Here we will add a "solid" pattern to POV-Ray. This pattern is constant over space,
and is probably the simplest pattern possible, but it is quite useful when used with
animations and blend maps.
The first step is to add the reserved word. The syntax for this pattern will be as
follows:
pigment {
    solid PATT_VAL
    ...other pigment stuff...
}
As you can see, we need to reserve the word "solid" for use with the solid pattern.
To do this, you need to add a line in parse.h and tokenize.c.
In parse.h, add an entry to the enum TOKEN_IDS, at the end, but before LAST_TOKEN:
  MATERIAL_ID_TOKEN,
  UV_ID_TOKEN,
  VECTOR_4D_ID_TOKEN,
  SOLID_TOKEN,//Here
  LAST_TOKEN
};
This is what the token will be referred to as from within the program. However, you
need to tell POV-Ray what string to associate with that token. You do that by adding
an entry to the array Reserved_Words[] in tokenize.c:
  {X_TOKEN, "x"},
  {YES_TOKEN, "yes"},
  {Y_TOKEN, "y"},
  {Z_TOKEN, "z"},
  {SOLID_TOKEN, "solid"}
};
You may want to put the entry in alphabetical order with the other entries, I prefer
to leave my additions at the end in one place.

Next, you need to give POV-Ray what to refer to the pattern itself as. The token is
just for parsing, the pattern itself is referred to by a #define in pattern.h.
Place yours after the density file identifier in the list of #defines at the top of
this file, and number it accordingly:
#define BOXED_PATTERN           29
#define CYLINDRICAL_PATTERN     30
#define DENSITY_FILE_PATTERN    31
#define SOLID_PATTERN           32
The placement in this list does matter, depending on the type of pattern you are
adding. There are comments to show where to put the #define. Because this is just an
ordinary float pattern like bozo or gradient, we put it at the end with the others.

Next, you need to add a place to store the value of the pattern. In frame.h, find the
#define TPATTERN_FIELDS. Add in a variable for the pattern value:
#define TPATTERN_FIELDS                  \
  DBL solidVal;/*here*/                  \
  unsigned short Type, Wave_Type, Flags; \
  int References;                        \
  SNGL Frequency, Phase;                 \
Notice that we use DBL and SNGL, these are #defines commonly used in the POV source.
They are defined in frame.h, among other things. I recommend you look through frame.h
just to see what other things are defined there.

Now we have the tokens, the pattern identifier, and a place to store the pattern data,
the next step is the parser. You need to add a case in the function Parse_Pattern() in
the file parstxtr.c for the solid pattern.
This is pretty straightforward, there are macros for adding the cases. I usually just
copy an existing pattern that does something close to what I want. Add the case for
the solid pattern right after the one for density file:
     CASE (DENSITY_FILE_TOKEN)
       if (Old_Type==DENSITY_FILE_PATTERN) 
       {
         Destroy_Density_File(Old_Density_File);
       }   
       New->Type = DENSITY_FILE_PATTERN;
       New->Vals.Density_File = Create_Density_File();
       GET(DF3_TOKEN);
       New->Vals.Density_File->Data->Name = Parse_String();
       Read_Density_File(New->Vals.Density_File);
       EXIT
     END_CASE

     CASE (SOLID_TOKEN)
       New->Type = SOLID_PATTERN;
       New->solidVal = Parse_Float();
       EXIT
     END_CASE
Use the token ID for your pattern keyword as the parameter to the CASE macro, and set
the type of the pattern to your pattern identifier defined in pattern.h. Also add a
call to Parse_Float() to parse the parameter. This function returns the value of the
float parsed, so assign that value to the variable you added in frame.h.

Now all you have to do is add code for calculating the actual pattern. This time, open
pattern.c. Add a prototype for your function in the list at the top of this file:
static DBL density_file (VECTOR EPoint, TPATTERN *TPat);
static DBL solid(VECTOR EPoint, TPATTERN *TPat);
Then go to a convenient place in the file, I choose the end, and add the
implementation for the function:
static DBL solid(VECTOR EPoint, TPATTERN *TPat)
{
    return TPat->solidVal;
}
If you ever need to, you can add initializers for the variables in Init_TPat_Fields(),
and copy them in Copy_TPat_Fields(), although they aren't necessary for this pattern,
since the only time the solid() function will be called the variable will have been
assigned, and Copy_TPat_Fields() already takes care of copying it.

The only thing remaining is to tell POV-Ray to use this function for calculating the
solid pattern. Add a single line to the function Evaluate_TPat(), after density file:
    case DENSITY_FILE_PATTERN:value = density_file (TPoint, TPat);  break;
    case SOLID_PATTERN:value        = solid(TPoint, TPat);          break;


That should be it. You now have a working solid pattern.



Adding a noise3d() function.
Here we will add a noise3d() function to POV-Ray. This function returns a value in the
range 0-1 depending on the location in space, and is identical to the bozo pattern. If
you have used isosurfaces, you are probably already familiar with it.
The first step is to add the reserved word. The syntax for this function will be:
noise3d(VECTOR_POS)
I chose a vector for this instead of 3 float values, because if you are using float
values, it only means you have to use 2 extra characters(<>), but if I chose float
values, it would require 3 dot operators and 2 more copies of the vector identifier to
use it with a vector:
noise3d(<FloatX, FloatY, FloatZ>)//using 3 floats with vector syntax
noise3d(VectID.x, VectID.y, VectID.z)//using vector identifier with float syntax

For this patch, we need to reserve the word "noise3d".
To do this, you need to add a line in parse.h and tokenize.c.
In parse.h, add an entry to the enum TOKEN_IDS. Since this is a float function, it
needs to be entered in a specific place:
  DEFINED_TOKEN,
  NOISE3D_TOKEN,//Here
  FLOAT_FUNCT_TOKEN,

/*WARNING: All new functions returning a float value must be placed
  before FLOAT_FUNCT_TOKEN or the parser won't work right. */

This is what the token will be referred to as from within the program. However, you
need to tell POV-Ray what string to associate with that token. You do that by adding
an entry to the array Reserved_Words[] in tokenize.c:
  {X_TOKEN, "x"},
  {YES_TOKEN, "yes"},
  {Y_TOKEN, "y"},
  {Z_TOKEN, "z"},
  {NOISE3D_TOKEN, "noise3d"}
};
You may want to put the entry in alphabetical order with the other entries, I prefer
to leave my additions at the end in one place.

The next step is to tell POV-Ray what to do when it encounters this token. Open up
express.c, and go to the function Parse_Num_Factor(). Since noise3d() returns a float,
we will put it in the case for float functions. Lets add our function at the top:


    CASE (FLOAT_FUNCT_TOKEN)
       /* All of these functions return a DBL result */
       switch(Token.Function_Id)
         {
          case NOISE3D_TOKEN:
            Parse_Vector_Param(Vect);
            Val = Noise(Vect);
            break;

          case ABS_TOKEN:
            Val = Parse_Float_Param();
            Val = fabs(Val);
            break;

That is it, you now have a working noise3d() function!


Post a reply to this message

From: TonyB
Subject: Re: Making POV Patches(first draft) - attached files (1/1)
Date: 5 Dec 1999 14:30:43
Message: <384abd63@news.povray.org>
Thank you very much, that was very enlightening. I wouldn't have been able
to come up with all that on my own. God bless you. =)

Now, I'm off to learn C, and <gulp> C++...


Post a reply to this message

Copyright 2003-2021 Persistence of Vision Raytracer Pty. Ltd.