Anim8or Community

Please login or register.

Login with username, password and session length
Advanced search  


Ian Ross has just released a book on Anim8or. It's perect for a beginner and a good reference for experienced users. It contains detailed chapters on every aspect, with many examples. Get your own copy here: "Anim8or Tutorial Book"

Author Topic: [ TUTORIAL ] [ INTERMEDIATE ] N-Gon Parametric Shape  (Read 15325 times)


  • Administrator
  • Hero Member
  • *****
  • Posts: 1482
    • View Profile
[ TUTORIAL ] [ INTERMEDIATE ] N-Gon Parametric Shape
« on: October 31, 2014, 10:28:48 pm »

N-Gon Parametric Shape

Good day budding ASL gurus. Today we'll learn how to make a parametric shape plugin! This tutorial assumes you've already completed the Hello World! tutorial, so if you haven't done it yet and have never scripted before, you should complete that tutorial first.

What is a Parametric Shape?
In Anim8or, a parametric shape is a 3D mesh whose form and structure changes based on the parameters set by the user. This could be as simple as scaling a pre-made mesh or as complex as a generating a planet! There are a lot of awesome things that someone with a little scripting know-how can accomplish.

In ASL, parametric shapes are made much like any other script. You have your header with any comments, the directive(s), the variable declarations, and then the code to create the shape.

Fleshing out the framework
We'll be creating an N-Gon, much like Anim8or's native N-Gon shape, except already filled in as a mesh with UV coordinates.

So let's create ourselves a quick framework for parametric shapes so that we can come in afterwards and 'fill in the blanks'.  Type this code into a new file and save the file as 'N-Gon.a8s'

Go ahead and change the header with information pertaining to you and the script. Change "Parametric Shape Plugin" to the name of the shape  ("N-Gon Shape Plugin", perhaps). The author is you, and the rest is optional, dependent on what type of script it is and how much you want to put into it.

#Plugin Directive
Like with any script, the first command must be the directive telling Anim8or what the script is. For plugins, this is the #plugin("<editor>", "<type>", "<name>") directive. Go ahead and change the name to something like "N-Gon".

ASL Snippet
  1. #plugin("object", "mesh", "N-Gon");

Parameter Directives
Following that, we have parameters! These are the input fields that show up when you double-click the shape. The #parameter directives allow you to define the parameters of the parameters (hah).

For example,

ASL Snippet
  1. #parameter("<name>", <type>, <starting value>, <floor value>, <ceiling value>, <optional params>);
  2. #parameter("Scale", float, 1, 0.01, 10000, scale);

The label to the left of the input field will be titled "Scale", it'll be a float value (decimal number), starting with the number 1, and can range from 0.001 to 10000. Additionally, the Uniform Scale tool increases/decreases this value when it's used on the shape.

#Return Directive
The #return directive contains the shape that we're working on. This must be globally declared (see below the #button directive). If you want to make it more context-specific, you can change the variable from $S to something like $nGonShape or something. Just remember that you have to use this variable everywhere instead, and to change the global variable declaration. I tend to use very short variable names for those variables that will be referenced *a lot* in the script, possibly hundreds of times.

#button Directive
This maps out what the button looks like.

ASL Snippet
  1. #button(<width>, <height>, <number of colors>, <data>);

If you're using Kubajzz's ASL Editor, you can flesh this out using its built-in button editor. It's a little complicated otherwise, and outside the scope of this tutorial.

Global Variables
Alright, variables! In a lot of programming languages, there's something called variable scope. When you declare variables outside of any functions (we cover functions after this) at the beginning, it becomes accessible to all the functions. Such variables are called global variables, since they can be accessed and/or changed everywhere in the script. However, if you declare the variable inside a function, it can only be accessed and/or changed from within that function. Such variables are called local variables.

Because the #return directive required it, we declared the $S shape data type here. And in case we needed to access $output and $obj from within any custom functions, they were declared globally as well.

In the "Hello World!" tutorial, the script didn't have fancy functions. ASL is (currently) a procedural programming language. This means all the commands are interpreted from top to bottom, and that's how you program it. Functions add a little modularity to it, allowing you to program a block of code that can be called at any point in the script, for as many times as needed, according to parameters that you define.

In Anim8or, a function is structured like so:

ASL Snippet
  1. <type> $<name> (<parameters>){}
  3. int $RoundDown (float $a)
  4. {
  5. return $a;
  6. }

<type> is the type of data that'll be returned when you use the function in code. The type can be nearly anything, such as int, float, point2, point3, string, float4x4. For $RoundDown, int is the type. <name> needs to be structured the same as variables, with a dollar sign preceding it. It's conventional to make function names as uppercase CamelCase. That is, the beginning of every word, including the first one, is uppercase, whereas everything else is undercase. With variables, it's lowercase CamelCase, where the first word's first letter is lowercase and every subsequent word's first letter is uppercase.

The parameters are basically local variables that are created on the spot, as copies of whatever data that is input. For example , if $RoundDown(1.333); was used, $a is a variable that holds the value 1.333 within that function. So in the following example,

ASL Snippet
  1. float $plusOnePointOne (float $a)
  2. {
  3. $a = $a + 1.1;
  4. return $a;
  5. }

$a is a local variable that can be accessed anywhere within that function. So if $plusOnePointOne(1.0) was used, $a holds the value of 1.0 (a float), and in the function $a gets re-assigned as the old value of $a (that is, 1.0), plus 1.1. 1.1 + 1.0 = 2.1, which would be the result.

Notice the "return $a;" line. All functions (except those with a "void" type) require a return command that returns a value of the same type as that function. That's the point of declaring the type of the function in the first place! It can be a variable of the same type as the function, or it can be a value by itself that is of the same type. For example, "return 1.5;" would work instead of "return $a"...except that every time $plusOnePointOne() is called, it'll give the value of 1.5 regardless of the parameters used.

The $main() Function
Now, look further down in the template and find the $main() function. This is where the meat of the script needs to reside. Note the "void" data type. This means that this function doesn't return anything--it only performs actions on data that is already there (or locally created). You can create your own void-type functions to manipulate global data if need be, but the $main() function must be of the type void.

Scripts can run without a $main() function. As mentioned before, it'll just interpret the script procedurally from top to bottom. However, if you decide that you need functions, you must have a $main() function. Additionally, you can only have directives, custom functions, and globally declared variables outside of the $main() function. You can't have a command, (such as $output.print("Print Me!");) outside and roaming alone.

It should go without saying, but the $main function is what gets run when the script is executed. All other functions get compiled, but unless it's called from the $main function, it will never get used. The same goes for global variables.

Assigning Parameters to Variables
ASL has a specific function for pulling info from the parameters you specified in the #parameter directive. It's simply:

ASL Snippet
  1. parameter("<name>");

The name must be the exact same as the name in the #parameter directive. Also, the type of the variable must be exactly the same as the the type indicated in the directive, or else you'll come across errors. Note, however, that you can access the value without assigning it, just by plugging in parameter("<name>"), anywhere a normal variable or value would go. Generally it's easier to just assign it to a variable at the start and use that variable where needed.

Another note: You cannot directly change parameters via code, they can only be read from what the user input.

Opening and Closing Shapes
If you wish to edit and add onto or subtract from shapes, you have to open them first. And then when you're done, close them! So make sure you're using .Open() before you try editing a shape, and .Close() before the script ends. Simple right?

Creating the N-Gon Shape
Before you can code a parametric shape, you need to have some understanding of how to go about programming the logic of it.

What is an N-Gon?
An N-Gon is really just a single polygon with multiple points spread evenly in a circular fashion around its origin. Basically, a shape with N amount of sides. Got 4 sides? That's a square. Got 8 sides? An Octagon. Got 100 sides? Well, might as well call it a circle.

A bit of math is usually required to figure out where each point goes on the circle. If you have any experience with trigonometry, you'll know that sine and cosine are the keys to this riddle. Don't know trig? Well, here's a quick explanation (not really an explanation, just a way for you to remember what sin and cosine achieve).

What cosine and sine do is give the X and Y coordinates of a circle when you specify the angle (assume the circle starts on the positive X-axis, and the radius is 1.0). You just have to remember that cosine = X-Coordinates, sine = Y-Coordinates. You can practice with a calculator and the circle image above. Use sine and cosine on any degrees you want, and plot it on the grid above. You'll find it'll always rest on the circle!

Anim8or's sin() and cos() functions deal in radians. There are PI (3.14) radians in 180 degrees. So 2*PI radians in 360 degrees. In our code, we're going to be manipulating points based on fractions of a circle (for example, if we want 5 sides, a point will be placed every 1/5 of the circle). So when we figure out what fraction of the circle we need, we just multiply that fraction by 2*PI.

How are shapes made?
When you think about creating a parametric shape via code, you should be thinking about these things:
  • What math/formulas/algorithms/patterns do I need to plot out the points in 3D space?
  • How do I connect these points together to make faces?
  • How do I manage the UV coordinates?
  • How do I tie in the parameters?
  • Materials, textures, etc?

We'll be doing the first four of those items in this tutorial.

Time To Code!
Plotting points with math
Time to start coding! Generally, when creating a parametric shape, you plot out the points using whatever formulas or patterns you figured out, into 3D space. You do this using the point3 data type. A "point3" variable holds an x, y, and z float value (it's also known as a vector).

So in the $main() function, we need to declare a local point3 variable. In a new line under the float variable declarations, add this:

ASL Snippet
  1. point3 $p[0];

Woah! It's not just a variable, it's an array! If a variable is declared with a [number] after it, such as in the line above, that means it's an array. An array is basically a variable that holds many variables of the same type inside it, accessed by the number specified. Kind of like if you went to a library and needed a book off the shelf, you'd find it by using the catalog number it's sorted by. We call this number the index, and indexes in Anim8or start on 0 instead of 1.

When we declare an array, we must also set the array size. In some programming languages, it's required to allocate the entire size of an array first. Fortunately for us, Anim8or allows us to add or subtract from an array whenever we want. In the line above, we set the size to zero (empty), since we'll be adding to it later. If we wanted to start it with 25 null elements, we'd write "point3 $p[25]".

Let's go ahead and create the number-of-sides variable, since we'll be playing with that as well. I'm calling it "$numSides". It's an int(eger) type, since we can only deal with whole numbers when describing the number of sides of a shape. We'll also need an index variable for code later, so add $i to the int declarations. Go ahead and add $math to the float variables as well. Your local variables should look something like:

ASL Snippet
  1. /* Local Variables */
  2. float $s, $xs, $ys, $zs, $math;
  3. int $numSides, $i;
  4. point3 $p[0];

Since we want the user to be able to input the number of sides, let's start being creative. The N-Gon will exist on the X and Y plane, meaning that the Z-axis is going to be totally ignored. This means we can swap the "Z Scale" parameter for the number of sides instead.

Go back to the top of the script, and change this line:

ASL Snippet
  1. #parameter("Z Scale", float, 1, 0.01, 10000, scale_z);

to this:

ASL Snippet
  1. #parameter("Number of Sides", float, 4, 3, 250, scale_z);

What we just did replace the "Z Scale" parameter with one for the number of sides. It's a float value (scale_z only works with float types), starting with four sides, with a minimum of three sides and a max of 250 (250 is an internal limit in Anim8or for the number of sides a face can have -- See this post for a method to bypass this limit). It's going to seem a little weird since this means you can set a decimal number of sides, such as 4.37, but the decimal part will be ignored in the code. You don't have to make this into a scalable parameter and instead make it an int-type parameter, but it's fun to be able to right-click and drag to increase/decrease the number of sides ;)

Now go back in the $main() function and replace this line:

ASL Snippet
  1. $zs = parameter("Z Scale");


ASL Snippet
  1. $numSides = parameter("Number of Sides");

What this does is read the float value of the "Number of Sides" parameter, and save it as an integer to the $numSides integer variable (it rounds it down, lopping off the decimal portion).

You can delete the ", $zs" out of the local variable declarations since we no longer need it.

Alright, we're ready to start plotting those points. Go down to the area where the main shape creation code will go, after "$S.Open();". Add the following lines:
ASL Snippet
  1. /* Main shape creation code goes here */
  2. for $i = 0 to $numSides - 1 do
  3. {
  5. }

This is called a "for" loop. It's a way to repeat the same code over and over until whenever it's told to stop. It increments the initial variable (in our case, $i) every time the loop happens. So the above statement is saying "$i starts at 0. Execute the following loop until $i equals $numSides minus 1".

In any shape, the number of sides is equal to its number of points. Hence we're plotting the same number of points as the number of sides. We're starting at 0 instead of 1 to make it simpler for later code, which is why we have to do the number of sides minus 1.

Now, add this line of code inside the for loop:

ASL Snippet
  1. $p.push((0, 0, 0));

"$p" is the array that holds the points. ".push()" is a function that adds a new element to the array, containing whatever data is within the parenthesis. In our case, it's (0, 0, 0), which is the x, y, and z coordinates, in that order.

Well, (0, 0, 0) is the origin...we want each point at their rightful place on the circle! So it's time for that cosine and sine math. As a bit of an optimization, instead of doing all the math inside the for loop, let's do as much of it before the for loop as possible. Replace that line with:

ASL Snippet
  1. $p.push((cos($i*$math), sin($i*$math), 0));

Before the for loop, after the $S.Open(); line, add this line:

ASL Snippet
  1. $math = 2.0*PI/$numSides;

What we're doing is pre-computing as much of it as possible so that it doesn't have to compute it over and over every time the for loop iterates. It's not necessarily important considering how fast computers these days compute things, but it's good practice especially if you have code that needs to iterate thousands upon thousands of times.

Remember the whole fractions of circles bit that I mentioned in the trig part of this tutorial? This is it. It's not very obvious, right? Well, for the purpose of explanation, if you substituted "$math" in the for loop with "2.0*PI/$numSides", and moved the operands around, you'd have something like this (don't change to this in the code):

ASL Snippet
  1. $p.push((cos(($i/$numSides)*2*PI), sin(($i/$numSides)*2*PI), 0));

For X, we have cos(($i/$numSides)*2*PI). ($i/$numSides) is the fraction, and 2*PI is the circle. Multiplied together, you get the angle in radians. Cosine finds the X coordinates based on this angle. Sine finds the Y coordinates. It's all coming together, right?

Let's think about it just to make sure. Let's say we have four sides that we want to generate, so $numSides would have the value of 4. In the first loop, $i equals 0. So $i/$numSides = 0/4 = 0. In the second loop, $i equals 1, so $i/$numSides = 1/4 of the circle. The third loop $i equals 2 => 2/4 = 1/2, and the fourth loop $i equals 3 => 3/4. The for loop ends at $numsides = 3, since $numSides - 1 = 4 - 1 = 3. We just mapped out four points at each quarter of the circle.
« Last Edit: November 05, 2014, 10:42:37 am by Raxx »


  • Administrator
  • Hero Member
  • *****
  • Posts: 1482
    • View Profile
[ TUTORIAL ] [ INTERMEDIATE ] N-Gon Parametric Shape
« Reply #1 on: November 03, 2014, 05:34:56 pm »

Adding the points to the $S shape
The next step is to tell Anim8or to add those points to the mesh. But before that, let's change "Scale" to "Diameter", so that the user can set this manually with ease. So first, change the first parameter:

ASL Snippet
  1. #parameter("Scale", float, 1, 0.01, 10000, scale);


ASL Snippet
  1. #parameter("Diameter", float, 1, 0.01, 10000, scale);

Next, in the $main() function, change:

ASL Snippet
  1. float $s, $xs, $ys, $math;


ASL Snippet
  1. float $r, $xs, $ys, $math;

Then change:

ASL Snippet
  1. $s = parameter("Scale");


ASL Snippet
  1. $r = parameter("Diameter") * 0.5;

What we did was set the parameter to Diameter so that the user can set the width of the shape easily, and then we derived the radius by dividing that value in half.

Continuing on, it's time for another for loop! Add the following lines after the previous for loop:

ASL Snippet
  1. for $i = 0 to $p.size - 1 do
  2. $S.AddPoint($p[$i]);

What this does is loop through each point3 value we just created and assigned to $p in the previous for loop, and add it to shape $S. Two new functions are introduced. ".size" is used on arrays to return the size of the array. ".AddPoint([point3 value])" is the function that adds a point to the mesh. Note that I didn't use the {} curly brackets this time. If you have one-liners in these types of statements, you can just add it to the next line (or on the same line even), and it'll run it like normal. If you have more than one statement, however, you have to have it in the curly brackets! If you want, you can go back to the first for loop and make it like this:

ASL Snippet
  1. for $i = 0 to $numSides - 1 do
  2. $p.push((cos($i*$math), sin($i*$math), 0));

In the end it doesn't affect the performance if you choose to do it one way or the other for one-liners. Speaking of performance, you may be wondering why we didn't just use one for loop that combines the two inner statements, like this:

ASL Snippet
  1. for $i = 0 to $numSides - 1 do
  2. $S.AddPoint((cos($i*$math), sin($i*$math), 0));

It's true that this would work, but next we'll be generating the UV coordinates and we'll have saved a little bit of computing power by referencing the saved data in the $p array rather than computing it all over again.

Before we start on the UV coordinates, let's go ahead and tie in our Scale, X Scale, and Y Scale parameters! Since this is making a circle with a radius of only 1, all we have to do is multiply those parameters against the X and Y coordinates. Let's change the $S.AddPoint() statement to this:

ASL Snippet
  1. $S.AddPoint(($p[$i].x*$xs, $p[$i].y*$ys, 0)*$r);

What this is doing is scaling the X and Y coordinates according to if the non-uniform scale tool is used, and otherwise scaling the entire coordinates by the radius. $xs, being the x-scale of the non-uniform tool, is scaling the X coordinates by multiplying against the math we did in cos($i*$math). Likewise $ys times sin($i*$math) for the Y-coords.

Your script should now look something like this:

ASL Snippet
  1. /*
  2.  * N-Gon Shape Plugin
  3.  * Author: Randall Bezant (aka Raxx)
  4.  * Version: 0.0001a
  5.  * Date: 11/01/2014
  6.  * Summary:
  7.  * This is an N-Gon shape, much like Anim8or&#039;s built-in
  8.  * N-Gon spline shape, except already filled in and with UV
  9.  *  coordinates attached.
  10.  */
  12. /* Parametric Shape Directive */
  13. #plugin("object", "mesh", "N-Gon");
  15. /* Parameters */
  16. #parameter("Diameter", float, 1, 0.01, 10000, scale);
  17. #parameter("X Scale", float, 1, 0.01, 10000, scale_x);
  18. #parameter("Y Scale", float, 1, 0.01, 10000, scale_y);
  19. #parameter("Number of Sides", float, 4, 3, 250, scale_z);
  21. /* #return directive - the shape that&#039;s being worked on */
  22. #return($S);
  24. /* The Button */
  25. #button(26, 26, 2, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000);
  27. /* Global Variables */
  28. shape $S;
  29. file $output;
  30. object $obj;
  32. /* Main Function */
  33. void $main()
  34. {
  35. /* Local Variables */
  36. float $r, $xs, $ys, $math;
  37. int $numSides, $i;
  38. point3 $p[0];
  40. /* Fill variables with the parameter data */
  41. $r = parameter("Diameter")*0.5;
  42. $xs = parameter("X Scale");
  43. $ys = parameter("Y Scale");
  44. $numSides = parameter("Number of Sides");
  46. /* Open up the console */
  47. $"$console", "w");
  49. /* Current Object */
  50. $obj = project.curObject;
  52. /* Open the shape */
  53. $S.Open();
  55. /* Main shape creation code goes here */
  57. /* Some pre-math for the following for loop */
  58. $math = 2.0*PI/$numSides;
  60. /* For each side of the face, create its point and assign it to $p */
  61. for $i = 0 to $numSides - 1 do
  62. $p.push((cos($i*$math), sin($i*$math), 0));
  64. /* For each point we just created, add it to the shape*/
  65. for $i = 0 to $p.size - 1 do
  66. $S.AddPoint(($p[$i].x*$xs, $p[$i].y*$ys, 0)*$r); /* Scale it using the parameters */
  68. /* Close the shape */
  69. $S.Close();
  71. /* Close the console */
  72. $output.close();
  73. }

Reload Anim8or, and assuming there are no errors, create the shape (it'll be in the shapes toolbar). When you click and drag, it'll create a blank shape with the yellow bounding box, but if you convert it to mesh and look at it in point edit mode, you'll see all the points! It'll look something like this:

It might look something like this if you right-clicked and dragged it with the non-uniform scale tool first:

If you left-clicked and dragged around with the non-uniform scale tool, it'll also scale it along the X and Y axis properly. Now we know it works!

Creating the Face and UVs[/i]
Next we'll add all the points to a single face, and at the same time generate the UVs for the face. This is easily done since we saved the coordinates in $p beforehand!

Let's create one more for loop! After the $S.AddPoint for loop, add the following lines:

ASL Snippet
  1. $S.OpenFace(0,FACE_HAS_TEXCOORDS);
  2. for $i = $p.size - 1 to 0 step - 1 do
  3. {
  4. $S.TexCoordN($S.AddTexCoord(($p[$i].x + 1,$p[$i].y + 1)*0.5));
  5. $S.VertexN($i);
  6. }
  7. $S.CloseFace();

A lot of stuff happenin' here! Let's examine it line-by-line.

ASL Snippet
  1. $S.OpenFace(0,FACE_HAS_TEXCOORDS);

To create a face, we have to use the ".OpenFace(<material ID>, <flags>)" function. The default material is 0, and FACE_HAS_TEXCOORDS is a constant variable that contains the value we want to indicate it has UVs.

ASL Snippet
  1. for $i = $p.size - 1 to 0 step - 1 do {}

We have to create the face by indicating the points of the face in a counter-clockwise fashion. So this for loop is starting from the last point of the circle and going counter-clockwise around the circle until the first point. "step - 1" tells Anim8or to decrement $i by 1 at each loop, rather than by default incrementing by 1. It'll stop at $i = 0.

ASL Snippet
  1. $S.TexCoordN($S.AddTexCoord(($p[$i].x + 1,$p[$i].y + 1)*0.5));

We're combining two functions together here. The inner ".AddTexCoord(<2D coordinate>)" contains a point2 value (x, y) that represents the UV coordinate for a point in the mesh. "($p[$i].x + 1,$p[$i].y + 1)*0.5)" is a point2 value. The x-coordinate is "$p[$i].x + 1", which is the X coordinate of the points we generated earlier plus 1. The same goes for the Y coordinate. We also divide these two coordinates in half by multiplying by 0.5.

We're adding 1 to the coordinates and then dividing it in half because UVs range from 0 to 1 on the X and Y coordinates. Our circle that we generated ranges from -1 to 1. We're basically offsetting the X and Y values so that they are on the positive X and Y axis. Remember that the circle has a diameter of 2, so we scale it in half so that it has a diameter of 1 and thus fits within the required 0 to 1 range that UVs have.

Transforming from this....

[ Download Image ]

To this

[ Download Image ]

In the images above, pretend that the UV range is the checkered square. In the first image, if we used just the original coordinates, only the top-right quadrant would be used and it'd repeat that portion across the entire circle. The bottom image shows it moved and scaled so that it fits inside the UVs in a way that the user would expect.

The outer ".TexCoordN(<Texture Coord ID>)" function simply assigns the texture coordinate to the current point. ".CloseFace()" tells Anim8or that we're done with that face for that shape.

".VertexN(<Point Index>)" simply adds the point to that face. <Point Index> is an index of the points that were added using .AddPoint earlier. This happens to be the same index of points as in our point array ($p), so we're safe to use $i.

And that's it! The final script should look like the following:

Reload Anim8or and play around with it, and you'll find that it functions as it should.

Congratulations, you made a parametric shape!
« Last Edit: November 05, 2014, 10:44:50 am by Raxx »


  • Sr. Member
  • ****
  • Posts: 540
    • View Profile
Re: [ TUTORIAL ] [ INTERMEDIATE ] N-Gon Parametric Shape
« Reply #2 on: November 04, 2014, 11:28:15 am »

Nice tutorial, Randall!

It explains a lot of valuable ASL specific syntax. The explanations are very clear and easy to follow for a novice like me.

One note: when the amount of sides gets over 250 the created face gets clipped. Maybe there is a max amount of sides a face can have? In that case maybe lowering the limit would prevent this.

I'm sure I can use this tutorial to build on for future scripts!
« Last Edit: November 04, 2014, 11:29:54 am by $imon »


  • Administrator
  • Hero Member
  • *****
  • Posts: 1482
    • View Profile
Re: [ TUTORIAL ] [ INTERMEDIATE ] N-Gon Parametric Shape
« Reply #3 on: November 04, 2014, 04:42:59 pm »

Hey $imon, thanks for reminding me about that! Yeah it's easy to come across that limit when dragging, haha. I adjusted the tutorial to match it to that limit, but also I'm attaching a fix for that limit here.

Basically I changed the loop that creates the face to this:

ASL Snippet
  1. /* Step through each point counterclockwise */
  2. for $i = $p.size - 1 to 0 step - 1 do
  3. {
  4.   /* This detects every instance the face reaches 250 points */
  5.   if ((($p.size - $i - 1) % 249 == 0) && $i > 0 && $i < $p.size - 1)
  6. {
  7. $S.CloseFace(); /* Close the current face */
  8. $S.OpenFace(0, FACE_HAS_TEXCOORDS); /* Create a new face */
  10. /* Add the texture coordinate for the first point */
  11. $S.TexCoordN($S.AddTexCoord(($p[$p.size - 1].x + 1,$p[$p.size - 1].y + 1)*0.5));
  13. /* Add the first point */
  14. $S.VertexN($p.size - 1);
  16. /* Add the previous point to start off the face */
  17. $S.TexCoordN($S.AddTexCoord(($p[$i + 1].x + 1,$p[$i + 1].y + 1)*0.5));
  18. $S.VertexN($i + 1);
  19. }
  21. /* Add the texture coordinate for the current point */
  22. $S.TexCoordN($S.AddTexCoord(($p[$i].x + 1,$p[$i].y + 1)*0.5));
  24. /* Add the current point */
  25. $S.VertexN($i);
  26. }

Basically it creates a new face whenever it detects the limit. This lets you create an N-Gon with a virtually unlimited number of sides (I set the limit to 100,000 in the attached script for the sake of sanity).
« Last Edit: November 04, 2014, 04:43:09 pm by Raxx »


  • Sr. Member
  • ****
  • Posts: 299
  • Anim8or, does a body good.
    • View Profile
    • Truespace Anim8or Casual Modeling Forum.
Re: [ TUTORIAL ] [ INTERMEDIATE ] N-Gon Parametric Shape
« Reply #4 on: November 05, 2014, 10:07:39 am »

For some some reason the images in the posts are not displaying? Nice tutorial though. I just wish my math skills were better though. Thanks for posting this Randy. Leroy.


  • Administrator
  • Hero Member
  • *****
  • Posts: 1482
    • View Profile
Re: [ TUTORIAL ] [ INTERMEDIATE ] N-Gon Parametric Shape
« Reply #5 on: November 05, 2014, 10:38:27 am »

Hi Leroy, if you're using Internet Explorer, I apologize, I haven't figured out how to make attachments show up properly for IE. Chrome, Opera, and Firefox should all work fine. I'll add captions that you can click on to download the image.

Math is a pretty important part of programming, but the upside is that you can still accomplish what you want by looking up the math and algorithms online and wrangling them into your code, without having to have a complete understanding of what it's doing---you might learn something along the way without it being a total chore ;)