PART 4 - TUTORIAL 6.54 - Figure T6-62: Parallel Waves (PICTURE: T6-62) Play the animation and the method generates waves in "x" direction. 6.23.4 Waves and Ripples Of course, it is possible to create more than one WAVE method at the same level, in which case the generated waves will interfere with each other. In other words, the result wave is the sum of the component waves. Because the size of the parameters for wave methods defines the wave-length and -height, this feature can be used for example for generating "ripples" on waves. So, let's create two wave methods generating parallel waves perpendicular to each other. 1. Create parallel waves as described in the previous example. 2. Duplicate the method object, rotate it and make it much smaller. Figure T6-63: Interfering Waves (PICTURE: T6-63) Play the animation and there will be small waves in the big waves. 6.23.5 Waving Particles This example demonstrates how the wave method can be used for waving a set of primitives instead of a mesh. If the target object is not a freeform object, then it is treated as a "point" of a freeform object. Instead of using one freeform target for the method, we can use hundreds of primitives. So, let' s create an example where the wave method is applied to a number of spheres: 1. Create a line consisting of small spheres. 2. Create a WAVE method to the same level with the spheres. 3. Create a coordsys and a circle under the method. 4. Play the animation. - TUTORIAL 6.55 - Figure T6-64: Waving Particles (PICTURE: T6-64) - TUTORIAL 6.56 - Chapter 7 RPL ------------- 7.1 INTRODUCTION RPL stands for REAL 3D Programming Language. It is a FORTH-like language integrated into the REAL 3D environment. It is a full featured programming language which provides you with power, flexibility and total control over all the features of REAL 3D. The language has three main uses: as a method for storing macros, as a file format for importing and exporting all the information needed to describe the scenes and animations you create, and as a programming interface to allow you to expand the functions of REAL 3D to fulfill any requirements you might have. Because RPL is meant to be used for expanding the REAL 3D program, it has been designed to be as fast in its execution as possible. It is also capable of making direct access to the Operating System. For these reasons, it is quite a low level language. However, considerable effort has been devoted to make it easy to learn with the intention of all users being able to make use of it. This section is intended to introduce you to RPL programming as easily as possible; but some understanding of basic computer programming principles has been assumed. If after reading through the introduction and working through the tutorial section you do not feel you have a full understanding of how to use RPL then there are many good books on introductory programming. Any that directly relate to FORTH will provide the best assistance, and most if not all the examples will work in RPL. 7.1.1 Basic Concepts First some RPL terminology to get started: Word A word is a collection of constants and references to previously defined words that are to be executed when the word is run; like a procedure or function in the languages "C" or Pascal. Vocabulary This is the collection of all defined words, variables and named constants. Stack This is an area of memory used by the RPL system for storing the information it needs to process the words you want to execute and the operands those words need for their execution. RPL, like FORTH, is a threaded language. Threaded languages grow incrementally; you define new words using existing words that are either predefined (standard RPL words) or words that you have defined earlier. RPL is interactive, it executes or compiles words the instant they are entered. New words become a part of the RPL vocabulary as soon as their definitions are completed. They can then be executed just by typing in their names and pressing , or they can be included in the definitions of new words. Words must be entered exactly as they appear in the vocabulary, upper-case letters must be upper-case and lower-case must be entered as lower-case. They must also be entered in full, they cannot be abbreviated in any way. - TUTORIAL 7.1 - 7.2 TUTORIAL 7.2.1 Getting Started To start your introduction to RPL the first step you must take is to open an RPL Window. Select PROJECT/Windows/RPL and a window very similar to an OS shell window will open. Through this window you will pass information to the RPL system and it can pass information back to you. To examine the interactive nature of RPL and for you to get started try the following simple examples. Remember to enter the words EXACTLY as they appear in the examples! Example 1: Print a number. Type the following text exactly as it is shown: 2. When you press RPL will respond: 2 Note: You just asked RPL to print out the number "2". The character "." is a predefined command or word in RPL's vocabulary which prints out an integer value. Example 2: Print some text. Now enter the following line: "hello world" PUTS RPL responds: hello world Note: The word "PUTS" (Put String) is another predefined word which printed the text "hello world". Before you test any further examples some explanation of how RPL operates is necessary. 7.2.2 Stacks RPL is a stack oriented language. This means that RPL uses stacks to store operands, intermediate results and return addresses. RPL uses four stacks. The Parameter Stack holds arguments for RPL words as well as results after executing those words. The Return Stack holds addresses of locations from where other RPL words were called, so that program execution can return to the location from where the calls were made. The other two stacks are only used internally. The Control Stack is used to check that the structure of the word is correct and all flow control structures are balanced while compiling a new word. The Vocabulary Stack stores the references to the compiled words that form the RPL vocabulary. These stacks work in Last In First Out (LIFO) manner, which is explained below in "Parameter Stack". 7.2.3 Reverse Polish Notation RPL uses postfix-notation, also known as Reverse Polish Notation (RPN). In RPN the arguments, or operands, are pushed onto stack first and then an operation is applied to them. The result is left on top of the stack, and can be used in consecutive operations. The conversion from algebraic notation (the way you write mathematical expressions on paper) to RPN is quite natural. You start with the operands you would start with if you were doing the calculation manually, and then append an operator. Then you take other operands and apply other operations until all the operands and operations are used. That may sound difficult but a couple of examples should clarify the point. - TUTORIAL 7.2 - Example 3: Convert 12 + 5 + 2 to RPN. 1. Manually you would first add 5 to 12. Therefore you write: 12 5 + Remember, the operands come first and then the operator. 2. Then you would add 2 to the result of the previous addition. 2 + Note: Both "+" operators require two operands. The first addition uses 12 and 5 but the second "+" seem to have only one operand, the number 2. The second addition takes the result of the first addition as its second operand. How the result of the first operation is preserved will be explained in the section about the Parameter Stack. So the whole expression in RPN is: 12 5 + 2 + 3. Finally you will use the "." word to print the result; so that the RPL text becomes: 12 5 + 2 + . And RPL responds by printing the result: 19 Example 4: Convert 2 + 5 * 7 to RPN. 1. First you multiply 5 with 7 and then add 2 to the result. So in RPL you enter: 5 7 * 2 + . 7.2.4 Parameter Stack When RPL scans input, whether it comes from the keyboard or from a file, it uses the Parameter Stack to store all operands it detects. This will be just called "the stack" for brevity, when there's no risk of confusing it with the Return stack. There are three kinds of operands in the RPL system: integer , floating- point, and address. The address operands can be addresses of RPL words, or addresses of variables (integer or floating-point) or generic addresses, addresses to any kind of value you want. All the addresses are handled in the same way. That is to say that RPL makes no distinction between addresses of different types of items. The stack operates on the basis of the last operand in will be the first out. This is the LIFO principle as mentioned. If you picture the stack as a pile of bricks (or any other neatly stackable material), you put new bricks on the top of the pile and also take them off from the top. So how are operands put onto the stack? Just enter them, and they are "pushed" onto the stack so that the last entered number will be the top- most one. In Example 1, you pushed one operand onto the stack (number 2). Then the command "." pulled it off and printed it in your RPL window. Now push several operands onto the stack, Try the following: 1 2 3 You pushed three numbers onto the stack. Print them all out by entering three "." commands and RPL responds: 3 2 1 - TUTORIAL 7.3 - Try to enter "." command once more time and RPL will respond: STACK EMPTY There were no more operands left on the stack for RPL to print. You get the same error message if you, for whatever reason, try to pull a value from the stack when none are available. This would occur if you had two integers on the stack and entered the "+" word twice. The second "+" has only one stack item and the error message is given. Now examine what happened to the stack for Example 3. Diagrams will be used to illustrate what is happening with the stack as each step is interpreted. 1. Suppose you start with an empty stack. 2. The RPL statement "12 5 + 2 + ." was scanned by the RPL interpreter. 3. First, 12 was pushed to stack. +----+ <- top of stack | 12 | +----+ 4. Then 5 was pushed on stack +----+<- top of stack | 5 | +----+ | 12 | +----+ 5. The "+" was scanned, which, because it is a executable word not an operand, is executed. The word "+" pops (takes off) the first two operands from the stack, then adds them and pushes the result back onto the stack. +----+ | 17 | +----+ 6. Then 2 was pushed on stack +----+ | 2 | +----+ | 17 | +----+ 7. The last "+" was scanned, the top two stack items are added, resulting in the following stack situation: +----+ | 19 | +----+ 8. Finally the "." was scanned and interpreted and the value is printed leaving the stack in its initial empty state. Note: RPL words must be separated from each other and their operands by either a or , but using also passes the current line to the RPL system and it will be interpreted immediately. So the example above could also have been entered as: 12 5 + 2 + . 7.2.5 DataTypes There are three kinds of data types in the RPL system: integer, floating- point and string. These are often refered to as literals. In order to enter values on the operand stack, you type in literals separated by or just as you have done in the examples above. Integers can contain only integral values, such as -3, 0 and 12, while floating-point numbers can be used for representing decimal numbers like -1.2, 0.12 and 1243.1. - TUTORIAL 7.4 - 7.2.5.1 integers Integer literals begin with a optional sign (+ or -) and contain only decimal digits. So far you have only used integer arithmetic, with integer literals and their corresponding integer operator words. 7.2.5.2 Floating-points Floating-point literals are similar to integer literals but they contain a decimal point or a exponent separator or both. The exponent separator can be either "e" or "E". The exponant can then contain an optional sign. As you would expect, arithmetic operations can also be carried out with floating-point values. There are separate words to carry out these operations: F+ - add two floating-point values F- - subtract F* - multiply F/ - divide F. - print Fxx - etc. Example 5: Enter the following text: 100.0 0.5 F* F. RPL replies: 50.0000 It did that because you pushed two floating-point values onto the stack then multiplied them as floating-points and finally printed out the return value from the "F*" word, as a floating-point. Example 6: Now try the following: 123 F. and RPL responds with: 123.0000 This demonstrates an important principle of RPL. RPL uses Object- Orientation Theory to handle the operands on the stack. This means that it knows the data type of each stack value and the data type it requires for each operand of a word and handles them accordingly. In Example 6 above, when the command "F." pulled the integer value 123 off the stack, it converted the value to the required type before it tried to use it. This also works if words requiring integer operands receive floating-point values. Example 7: When you enter: 6.8 . RPL will respond with: 7 The RPL word "." works with integers, it had to convert the floating- point value it pulled from the stack to an integer before it printed it; because integers cannot contain any fractional part the value was rounded to the nearest integer value. 7.2.5.3 STRINGS String literals begin and end with a double quote character. If a double-quote is wanted in a string literal, it should be preceded by a back-slash. - TUTORIAL 7.5 - For example: "This string contains a \"." Will be interpreted as: This string contains a ". If a back-slash is required then two must be entered consecutively. For example: "This string contains a \\." Will be interpreted as: This string contains a \. RPL handles strings slightly differently from the way the integers and floating-points are treated. With integer and floating-point literals the numerical value is pushed on stack, and with string literals the address of the first character of the string is placed on the stack. You have already encountered the string printing word "PUTS". Let's look at how it actually operates. Consider the RPL text: "Hello, this is a string" PUTS 1. When the string "Hello, this is a string" was typed, RPL did NOT push the entire string onto the stack. Instead the address of the string was pushed. The memory for the string itself was allocated from elsewhere. 2. The word "PUTS" then pulls the address out off the stack and copies the string from that address to the window. Every character has a corresponding value, which is used for representing the character, this is called the ASCII code. For example, the ASCII code for the character "!" is 33. So, when RPL stores the character "!", it actually stores the value 33. The RPL word "EMIT" can be used for printing out characters by supplying ASCII code for them as operands. Example 8: Enter the following: 33 EMIT and RPL replies: ! So, you can store a simple string onto the stack and print it out using the EMIT function. Example 9: Enter: 76 80 82 EMIT EMIT EMIT and RPL replies: RPL Finally , you can investigate one property of string literals that might not be immediately obvious. As you just learned, entering a string caused RPL to allocate the memory for the string and push the address of it onto the stack. To be precise, the address of the first character is pushed onto the stack. This means that it is possible to use arithmetic operations to select part of a string. Example 10: 1. Type the following string: "Hello again!" - TUTORIAL 7.6 - Note: The first character of "again!" is the 6th in the whole string 2. Enter: 6 + Note: You have now added 6 to the address of the first character ("H") of the string. 3.Finally, type: PUTS and RPL replies again! Some care is needed when using this "address arithmetic"; if you make an error and change the address so that it is outside of the original string data structure, then the results are quite unpredictable. In the worst case you could crash the system if you read from certain hardware registers by mistake. 7.2.6 Stack Manipulation Words There are a number of words that change the order of the stack items. The "SWAP" word, as it's name implies, takes two values off the stack top and puts them back in reverse order. Example 11: 1 5 SWAP after 1 after 5 after SWAP +-------+-------+----------+ | 1 | 5 | 1 | +-------+-------+----------+ | 1 | 5 | +-------+----------+ "SWAP" is only needed if the order of the operands affects the result. It is unnecessary when using words such as "+" and "*" among others. Quite often there is more than one way to enter an algebraic expression in RPL: Example 12: Calculate the value of 28 / (2 + 4 * 3) 1. You start from within the parentheses as you would when calculating manually: 4 3 * 2 + 28 SWAP / after 4 after 3 after * after 2 +-------+-------+-------+-------+ | 4 | 3 | 12 | 2 | +-------+-------+-------+-------+ | 4 | | 12 | +-------+ +-------+ after + after 28 after SWAP after / +-------+--------+----------+-------+ | 14 | 28 | 14 | 2 | +-------+--------+----------+-------+ | 14 | 28 | +--------+----------+ 2. Or, start from the left: 28 2 4 3 * + / after 28 2 4 3 after * after + after / +--------------+-------+-------+-------+ | 3 | 12 | 14 | 2 | +--------------+-------+-------+-------+ | 4 | 2 | 28 | +--------------+-------+-------+ | 2 | 28 | +--------------+-------+ | 28 | +--------------+ This illustrates that although you can usually manage without "SWAP", it can quite often make - TUTORIAL 7.7 - it easier to enter your expressions in an understandable way. The next useful word for manipulating the stack, is "DUP"; which, as its name suggests, duplicates the top stack value. This is useful when you need an entered value or intermediate result more than once. Example 13: Calculate (3 + 7 * 2) / 3 + (3 + 7 * 2) * 4. You can convert it to RPL and get: 3 7 2 * + 3/ 3 7 2 * + 4 * + . or you can use DUP and get 3 7 2 * + DUP 3 / SWAP 4 * + . You can try these out and see for yourself that they give the same result. To get a copy of the second stack item you use the word OVER. Example 14: To calculate 3 + 3 * 7 you can enter: 3 7 OVER * + . If you need get a copy of a value deeper in the stack you use the PICK word. Example 15: To get a copy of the fourth value on the stack you enter: 4 PICK Note: The count of stack items (i.e. 4) does not include the count itself although it will be on the stack top when "PICK" is applied. In the previous example 4 PICK will copy to the stack top the value which was the fourth stack item before 4 was entered. "ROT" rotates the three topmost stack values so that the third item will become the top item, and the first and the second item (counting from the stack top) will be the second and the third stack item respectively. Example 16: Push 3, 5 and 7 onto the stack and then apply ROT and see how it changes the stack. 3 5 7 ROT after 3 after 5 after 7 after ROT +-------+-------+-------+---------+ | 3 | 5 | 7 | 3 | +-------+-------+-------+---------+ | 3 | 5 | 7 | +-------+-------+---------+ | 3 | 5 | +-------+---------+ Note: You can use the .S" word toprint the contents of the whole stack without removing any values to see how this example progresses. The word ROLL is used to rotate a given number (n) of stack items so that the n'th stack item becomes the stack top item and all the items between the first and the n'th item are moved one position deeper in the stack. Finally there is also a word for popping a value off the stack without printing it. The word is "DROP". You will need it later when you examine conditional execution. - TUTORIAL 7.8 - 7.2.7 Compiling New Words The examples so far have only demonstrated that RPL can be used as a slightly peculiar calculator. If it is to be used as a programming language, RPL needs to be able to store a sequences of operations for repeated use or which need to be executed by another part of the program. Many programming languages call these sequences functions or subroutines. The term "word" from the FORTH programming language is adopted for RPL. A word is a collection of RPL code which can be recognised by its name. This code can consist of literals, references to variables and constants, and references to other words. A definition of a word begins with a colon ":" continues with the name of the new word, and ends with a semicolon ";". When the RPL interpreter scans the colon word, a new entry in the vocabulary is started. The name of the word and the type of entry (in this case a "word definition") are stored. Everything between the name and the ending semicolon is compiled as the word's definition. When a previously defined word is given as part of the definition, a reference to it is stored. When a literal is given, its type (integer, float or string) and its value are stored in the entry. When a named constant or a variable (See: Constants and Variables) is given, a reference to its own entry is stored. In summary, the vocabulary entry for a word is made of a name, the word type and a set of literal values and references to previously defined words, The following examples should help to explain: Example 17: Define a word that prints out a specific string. Enter: :Prompt "PROMPT TEXT>" PUTS ; Remember, there has to be at least one space between ":" and the name for the new word, because the ":" is itself a word. User-defined words are used just like any pre-defined word. To use them you just need to enter their name. Example 17b: Use the word "Prompt" defined above. Enter: Prompt RPL will print: PROMPT_TEXT> User defined words would be quite useless if you couldn't pass information to them to control their operation. However, as mentioned above, user- defined words are indistinguishable from the built-in words. This means that passing operands to them is carried out using the Parameter Stack in exactly the same way as you have passed operands to words since Example 1! Now test this by defining your own interger printing word. Example 18: New integer printing word. : MyPrint . ; - TUTORIAL 7.9 - Call it: 10 MyPrint and RPL responds: 10 Or call it as follows: 10 20 100 MyPrint MyPrint MyPrint and the reply will be: 100 20 10 Now create a function which prints numbers out twice. To do this, you have to use the "DUP" word mentioned previously. Example 19: Print number twice as an integer. : My2Print DUP . . ; Now call it: 10 My2Print and it will reply: 10 10 or if you call it as follows: 10 My2Print 20 My2Print 100 My2Print RPL responds with: 10 10 20 20 100 100 Before the final example in this section you should learn how to use "comments" so you can add notes to your program to inform others how it works (or remind yourself at a later date). The word "(" tells RPL to skip all following text up to the next ")" command or until the end of the current line. Because "(" is a word, you have to separate it from the text that follows with a space. Comment Examples: 1. ( this is a comment ) 2. ( these are also ( legal comments 3. (this produces syntax error) 4. ( this is an illegal comment too ) Finally create a function, which takes three numbers as an input and returns the integer average of them, Example 20: Calculate the average of three numbers. : MyAver ( add 3 numbers and divide result by 3 ) + + 3/ ; If you call it as follows: 10 20 30 MyAver . RPL returns: 20 or try: 10 20 120 MyAver . and the response will be: 50 - TUTORIAL 7.10 - 7.2.8 Constants and Variables RPL has a mechanism to store and fetch values not only from the stack but also in "constants" and "variables". A "constant" is a named entity whose value is set when it is defined, and the value can be referenced but not changed. A "variable" is a named entity whose value can be referenced and used for either input or output. Constants and variables must be defined outside word definitions. When a constant is defined, its value is taken from the stack top. When you later want to reference a constant you enter its name. 7.2.8.1 Constants Constants are used for storing values that won't change. Although this might seem to be the same as using literals, it is good programming practice to use constants instead of literal values. Constants should be descriptive, and this makes your code much easier to read by others as well as yourself. When a constant is referrenced by its name, the value is placed on the stack. Example 21 : Define integer constant "DaysInAWeek" with the value 7. Enter: 7 CONSTANT DayslnAWeek Then entering: DaysInAWeek . prints out: 7 Example 22: Define the value for the floating-point constant "PI". 3.14159 FCONSTANT PI 7.2.8.2 Variables When a numeric variable is defined, its initial value is set to zero. For each numeric type (integer and floating-point) there are two words that are used to access this value. For integer variables these words are: @ (fetch) - takes the address of an integer variable and returns the value of that variable on the stack. ! (store) - takes two operands: the address of the integer variable and the new value for the variable, and changes the value of the variable. The address must be the top item on the stack and the new value the second. The address of a variable is pushed onto the stack when it is referred to, not the value as with constants. Example 23: Define integer variable COUNTER and store 17 into it, then check its value. VARIABLE counter 17 counter ! counter @ . Examine what happened during this example step by step: "VARIABLE counter" 1. Space was reserved for one integer value, to be accessed by the name "counter". "17 counter !" 2. The number 17 was pushed onto the stack. - TUTORIAL 7.11 - 3. The address of the variable was fetched and pushed onto the stack. 4. The word "!" was executed, which takes the address of a variable off the stack and assigns the second operand to the value at that address. In a way, the name of a variable is the address of its value. "counter @ ." 5. The address of the "counter" variable was again pushed onto the stack. 6. The word "@" was executed, which moves the contents from the given address onto the stack. 7. The "." word was executed, which printed the value from the stack to your window. There are corresponding words for floating-point variables; which are "FVARIABLE", "F!" and "F@". Variables and constants can be used inside or outside of a word definition. When used inside a word definition, only a reference to the constant or variable is compiled in the word's dictionary entry. The value of the constant or the address of the variable is fetched only when the word is executed, not when it is defined. Although the stack is object oriented and can automatically convert between numeric types, the internal storage of integer and floating- point variables is different. This means that words that operate on floating-point variables, i.e. F! and F@ should not be used to access integer variables. The same applies to floating-point variables and the words for integer variable access (! and @). 7.2.9 Flow Control Every programming language has to be able to alter the program flow when certain conditions are met. It is also necessary to be able to repeatably execute a section of code. RPL has addressed these needs by including a versatile set of words into its basic vocabulary for controlling program flow. The flow control words can only be used from within word definitions. 7.2.10 Conditional Execution It is often necessary to decide whether a section of code should be executed or not. This is called conditional execution . The most basic conditional execution structure is "IF..ENDIF". The "IF" word takes the stack top value and uses it to make a decision about how the execution is to proceed; this decision value is called a "flag". In RPL a flag value is said to be "FALSE" if it is equal to zero and "TRUE" if it has any other value. If the flag is TRUE, the words between IF and the corresponding ENDIF are executed, otherwise execution skips to the word that is immediately after the ENDIF. 7.2.10.1 Comparisons To put a flag on the stack ready for IF, it is useful to use words that compare stack items and as a result push a flag on the stack. Built into RPL are the following comparison words: Integer Float ---------------------------- = F= - is equal < F< - is less > F< - is greater >= F>= - greater or equal <= F<= - less or equal <> F<> - not equal Each of these takes two stack values, compares them, and places a flag onto the stack whose value depends upon the result of the comparison. - TUTORIAL 7.12 - Example 24: Compare 2 and 3 to see which is less. 2 3 < The second stack item is compared to the first stack item; the flag pushed on the stack indicates whether 2 is less than 3. A value of 1 means "TRUE", and a 0 means "FALSE". after 2 after 3 after < +-------+-------+-------+ | 2 | 3 | 1 | +-------+-------+-------+ | 2 | +-------+ The flag states that two is less than three (as you may have guessed). 7.2.10.2 IF..ENDIF Structure This is the simplest form of conditional execution structure. Example 25: Unpredictable program execution : Maybe RANDOM 0.5 F< IF "Yes," PUTS ENDIF "Thats it" PUTS ; Try executing "Maybe" several times and examine its response. Note: This introduces the word "RANDOM" which returns a random floating- point number between 0.0 and 1.0 when executed. The previous example tests whether the return value from "RANDOM" is less than 0.5. If it is, it prints out the text "Yes, Thats it" otherwise it just prints "Thats it". This is the explanation of the previous example. When the "Maybe" is executed: 1. Executing "RANDOM" pushes a floating-point value onto the stack 2. 0.5 is pushed onto the stack 3. The "F<" word takes two (floating-point) operands, compares "random<0.5" and pushes the flag back onto the stack. 4. The "IF" function takes the flag off the stack and if it is non-zero, then the ""Yes," PUTS"" text is executed; otherwise execution skips to ""Thats it" PUTS"" after the "ENDIF". Note: That "IF" and "ENDIF" words must be contained within a word definition; also that every "IF" needs one corresponding "ENDIF". 7.2.10.3 IF..ELSE..ENDIF Structure Now you can define a word that prints only positive values, negative values are just disposed of. Example 26: Word to print only positive values. : .Pos DUP 0> ( compare operand to zero ) IF . ( print it if greater ) ENDIF ; If you enter: 4 .Pos RPL returns: 4 So that seems to work. But what happens if you enter: - TUTORIAL 7.13 - -2 4 3 -1 .Pos .Pos .Pos .Pos It doesn't produce: 3 4 but nothing at all. The problem is that the word does not get rid of the negative values, so you called .Pos four times with the same value of -1 on the stack. You need to take some action if the value on the stack is not greater than zero. This is handled using an "ELSE" branch where the negative value will be dropped off the stack. Example 26b: Correct way to print only positive values. : .Pos DUP 0 > IF . ELSE DROP ENDIF ; This demonstrates another form of the IF structure, the IF. .ELSE..ENDIF structure. If you now enter -2 4 3 -1 .Pos .Pos .Pos .Pos you get 3 4 as originally intended. 7.2.10.4 Inverting Flags As well as the integer and floating-point comparison words, there is one other useful word for use in conditional structures.The "NOT" word can be used for reversing the result of a comparison. For example: 1 0 20 <> . and: 10 20 = NOT . Both print out the same value of "1" because the condition is TRUE. 7.2.10.5 Nesting Conditionals Conditional structures can also be nested. This means that they can be stacked inside each other providing that for each "IF" of "IF..ELSE" there is a corresponding "ENDIF". Example 27: Use of nested IF..ELSE..ENDIF structure. : CheckIt DUP ( duplicate the operand so that you don't lose it 0 = ( compare it against to zero ) IF "Zero" PUTS DROP ( dispose of duplicate because it won't be needed ELSE 0 > ( make comparison "is greater than" with duplicate IF "Greater" PUTS ELSE "Less " PUTS ENDIF ENDIF ; - TUTORIAL 7.14 - Now call it as follows: -2 CheckIt 3.7 CheckIt 0 CheckIt you should get the following response: Less Greater Zero 7.2.11 Loops In this section, the RPL words for branching back to repeat sections of code several times will be explained. This type of control structure is called a "loop". There are two basic kinds of loop: definite loops, which execute a specific number of times unless interupted, and indefinite loops, whose repeated execution is controlled conditionally in much the same way as the IF..ENDIF structure. 7.2.11.1 Definite Loops The basic RPL control structure for executing definite loops is "DO..LOOP". You specify beginning and ending values, called the limits, for a "loop variable". The ending value is first pushed onto the stack, then the beginning value, before the word "DO". Then you put the words to be repeated, and the structure ends with the word "LOOP". Example 28: Printing a string 1 00 times. : DoLoop 100 0 DO "RPL loops in style!" PUTS LOOP ; DoLoop The "loop variable", or index, starts with the starting value, and is incremented by 1 at the end of each iteration of the loop. When it reaches the ending value the loop is exited. There is a word for retrieving the value of the index. Its name is "I", and it should only be used between "DO" and "LOOP". It puts the current value of the loop variables on the stack. Example 29: Count up from 1 to 9 and print the index. : OneToNine 10 1 DO I. LOOP ; OneToNine Prints: 1 2 3 4 5 6 7 8 9 The "DO..LOOP" always starts at a value and counts up by one until it reaches the ending value. If you want to increment the index by a value other than 1 , you can use another form of the definite loop: the "DO..+LOOP". The "+LOOP" word takes an operand from the stack and adds it to the index. If the ending value is greater than the beginning value, then the loop is exited if the loop variable becomes greater than or equal to the ending value. Example 30: Count from 0 to 1 0 in twos, printing the index. : UpInTwos 10 0 DO I. 2+LOOP ; UpInTwos Prints: 0 2 4 6 8 - TUTORIAL 7.15 - If the ending value is less than the beginning value, then the "DO..+LOOP" is exited when the loop variable becomes less than or equal to the ending value. Example 31: Count down by one and print the index. : DownByOnes 10 0 DO I. -1 +LOOP ; 0 5 DownByOnes Prints: 5 4 3 2 1 Note: In this example the word "DownByOnes" takes its operands from the stack, so you must supply them when you call the word. Both forms of the "DO" loop execute at least once since the conditional check is made when "LOOP" or "+LOOP" is executed. It is possible to terminate a definite loop prematurely. The execution of word "LEAVE" causes the loop to terminate at the next "LOOP" or "+LOOP". Example 32: Print the squares of numbers until the square > 50. : SquareLoop 10 0 DO I DUP * ( square index ) DUP 50 > IF ( compare the square to 50 ) DROP LEAVE ELSE . ENDIF LOOP ; When execute "SquareLoop" will print the squares of I until they exceed 50. 7.2.11.2 Indefinite Loops Unlike definite loops, an indefinite loop does not terminate after a specified number of iterations. It terminates when a condition is met, if ever. All indefinite loops, just like definite loops, must lie entirely within a single definition. The structure of the indefinite loops are: BEGIN..UNTIL BEGIN..WHILE..REPEAT BEGIN..AGAIN In each of these loops, "BEGIN" marks the beginning of the loop body, which extends to the terminating word "UNTIL" , "REPEAT", or "AGAIN". In the "BEGIN. .UNTIL" loop a flag is tested and removed from the stack at the end of each repetition of the loop. If the flag is TRUE, the loop terminates. Otherwise the loop repeats from the first word following "BEGIN". Since the test is made at the end of the loop, the loop will always be executed at least once. Example 33: : CountDown 1 BEGIN DUP . ( print it out ) 1 - ( decrement by one ) DUP 0 <= ( if TRUE, terminate loop ) UNTIL DROP "PANG" PUTS ; 10 CountDown1 The "BEGIN..WHILE..REPEAT" form of indefinite loop first executes the code between "BEGIN" and "WHILE", and then a flag is tested, If the flag is TRUE, the words between "WHILE" and "REPEAT" are executed; then execution returns to "WHILE". If the flag is FALSE, then execution skips to after the "REPEAT". - TUTORIAL 7.16 - Example 34: : CountDown2 BEGIN 1 - ( decrement by one ) DUP ( if 0, terminates the loop ) WHILE DUP . REPEAT "Bang" PUTS DROP ; 10 CountDown2 The third form of indefinite loop has the form "BEGIN..AGAIN". Which executes forever unless either the word "QUIT" or "EXIT" is executed. "QUIT" terminates execution, empties all stacks and returns control to the interpreter, while "EXIT" exits the current word. Example 35: : CountDown3 BEGIN DUP . 1 - DUP NOT IF EXIT ENDIF AGAIN ; 10 CountDown3 7.2.11.3 Nested Loops As with conditional structures, all the loop forms can be nested, In Examples 29 to 32 you used the RPL word "I" to obtain the value of the loop variable; when you nest "DO. .LOOP" structures the words "J" and "K" can be used to copy the indices of the second and third outer loops, respectively. Example 36: Print a multiplication table. ( print carriage return and line feed: ) : CR 13 EMIT 10 EMIT . : multTable 6 1 DO 11 1 DO I J * ( multiply indices . ( and print the product LOOP CR LOOP ; multTable Will print: 1 2 3 4 5 6 7 8 9 10 2 4 6 8 10 12 14 16 18 20 3 6 9 12 15 18 21 24 27 30 4 8 12 16 20 24 28 32 36 40 5 10 15 20 25 30 35 40 45 50 This looks a little untidy so you can tidy it up by adding a conditional to print a space. Example 36b: Print a neat multiplication table. : multTable 6 1 DO 11 1 DO I J * ( multiply indices DUP ( duplicate product for testing 10 < IF ( if<10 then print a space first "" PUTS . ( now print product ELSE ( two digits in product . ( so just print it ENDIF LOOP CR LOOP ; multTable - TUTORIAL 7.17 - Note: It is a convention to indent flow control structures as shown in the previous two examples. This does not make any difference to the RPL system but, like comments, makes your programs easier to read and understand. 7.2.12 Words and the Vocabulary The last example, as well as being your most comprehensive RPL program to date also demonstrates an important feature of the RPL language. If you define a word with the same name as a previously defined word then the latest definition is used. You can list the whole vocabulary of your current RPL window by entering the word "VLIST". Before you do this use the size gadget of the window to make it a decent size, the vocabulary is quite long. The whole vocabulary, including the built-in words, will be printed to the RPL window in chronological order with the latest words printed first. You should see that the last two words are both "mult_table". It is possible to remove word definitions from the vocabulary using the word "FORGET". Example 37 : Remove neat "multTable" from vocabulary. FORGET mult_table If you now execute "multTable" you will get the original untidy version. "FORGET" actually removes ALL the words after the specified word. So, assuming you have worked through all the examples, if you enter: FORGET Prompt Then all the words you have defined during this tutorial will be deleted. It is important to note that the RPL system makes NO DISTINCTION between the built-in words and words defined interactively or by loading a file (see next section). This means that if you create a word definition using the name of one of the built-in words then your new definition will replace the original. This can be quite significant if you replace an important or frequently used word. You can of course recover the situation using "FORGET"; which raises another important point: you can delete built-in words just as easily as your own. This makes it possible to create a situation where RPL does not have the vocabulary to do anything! Closing the window and opening a new one enables you to start with a fresh RPL environment. 7.2.13 Loading a File Entering RPL definitions directly to the window is acceptable for the short examples so far in this tutorial, but it is hard to recover from mistakes without having to completely re-enter a definition. Using a text editor you can write any RPL code to a file and then load that file into your RPL window. Example 38: Write the following RPL program using your favourite text editor and save it as "ram:test.rpl" "loading..." PUTS : CR 13 EMIT 10 EMIT ; : Hello "Hello world" PUTS CR ; "Done" CR - TUTORIAL 7.18 - Now type the following program: "ram:test.rpl" LOAD RPL responds: loading ... Done Now the words CR and Hello are defined and you can call them. Type: Hello and RPL responds Hello world 7.2.14 Using RPL windows You open a RPL window by selecting PROJECT/Windows/RPL. You can have many RPL windows if you want. RPL windows are similar to OS "Shell" windows. You can use C and V to cut and paste text to and from the "clipboard" and the cursor keys can be used to edit your text and even fetch previously entered text from the window "history" buffer. Each window is separate so that whatever words are defined in one window, cannot be used in other windows. Of course you can define identical words in different windows. The stacks are also specific to each window. It is possible though to use words defined in another window by establishing a parent-child link between two windows. This is done using the word INHERIT . The syntax for this word is: "name_of_RPL_window" INHERIT Example 39: 1. Define a word "HiThere" in your current RPL window : HiThere "Hi There" PUTS ; 2. Open a new RPL window and try to execute the word "HiThere": HiThere RPL responds HiThere ? because it didn't recognize the word. 3. Now enter the following line: "RPL" INHERIT 4. Try HiThere again and RPL responds "Hi There" From now on interactive commands, as well as new definitions entered in the "RPL.1" window search the vocabulary of "RPL.1", and then, if the word is not found, the vocabulary of "RPL". So if you have defined identical words in both windows the definition in "RPL.1" will take priority. Note: The INHERIT word actually takes an address as its operand, and this address points to the string containing the name of the RPL window whose vocabulary you wish to INHERIT Remember by entering the string between the double-quote marks its address was pushed onto the stack as an operand. - TUTORIAL 7.19 - 7.3 USING RPL FOR CUSTOMIZING EDITOR In this section we will show you how to use RPL to customize and expand the user interface and functionality of REAL 3D. The real power behind the customization is based on the fact that customization allows persons to develop working environments which suit as well as possible their different modelling needs, styles, and tools. Other people can take advantage of these additional tools etc developed by somebody else. 7.3.1 Communicating with RPL Programs Before we can go any further, let's take a look how RPL programs can interact with the user and vice versa. RPL contains sets of words which can be used for requesting information from the user. Open one View window and write the following program: : AreYouSure "YeslNo" "Are You Sure?" GET_ KEY IF "You are sure" PUTS ELSE "You are not sure" PUTS ENDIF ; AreYouSure When you call the word by typing its name, REAL 3D opens a requester with the header text "Are You Sure". Depending on which gadget you click RPL prints out either the text "You are sure" or "You are not sure". So, the word GET KEY takes two parameters: a string defining gadgets to be created and the header text for the requester. It returns a value corresponding to the selected gadget. Gadgets are separated with the character "l" and it is possible to create any number of them (well, there should be at least one). Write the following program: : GetKeyTest "FirstlSecondlThirdlCancel" "Select One of These" GET_KEY . ; Now call the word GetKeyTest several times and see what values it returns. The value corresponding to the rightmost gadget is always 0 (right selection is used for negative choice, hence the value 0). The value corresponding to the leftmost gadget corresponds to the value of 1 and is incremented gadget by gadget from left to right. The word GET STR can be used for requesting strings from the user, such as object names. It takes three parameters: the address of the buffer to hold the string, the maximum length for the string and the header text for the requester. Write the following program: 16 STRING ObjName : ObjCreate ObjName 1 5 "Create Object" GET _STR IF wOT_OR ObjName "CEND" C_LEVEL DROP ENDIF ; ObjCreate /* Get string from the user */ - TUTORIAL 7.20 - This word opens a requester which allows you to enter the name for the object. If you select the gadget OK, the word creates a level object with the name you defined. The word GET_FLT allows you to request numerical data from the user. It takes two parameters: the address of the buffer (variable) to hold the defined value and a title for the requester. For example: FVARIABLE Length : GetLen Length "Define Length" GET_FLT IF Length F@ F. ENDIF ; GetLen This program asks user to define "Length" and prints it out. Its time to leave these "user interface" words now. There are also words available for requesting file names and vectors etc. These words can be used in a pretty similar way to those we have already discussed. Consult the "Reference Manual" whenever you need to use them. 7.3.2 "Master" ENVIRONMENT When R3D2 is first started one RPL environment is created. Unlike RPL windows this has no window associated with it. "Master" environment is responsible for taking care of the following things: 1. Processing of AREXX commands. All ARexx commands arriving to ARexx port of REAL 3D are directly passed to RPL interpreter. 2. Executing MACROS. For example, whenever you select the menu Project/ Macros/Execute_Current, the following program is passed to Masters RPL environment: "t:macro.rpl" LOAD 3. Processing key bindings. When you press any key, that key with the prefix "KEY_" is passed to Masters RPL interpreter. For example, when you press the key "k", then the RPL word "KEY_k" attempts to execute. If such a word cannot be found, the display is flashed as an error indicator. 4. Processing RPL programs bound to icons in the Tool windows. 7.3.3 Binding Macros to Keys Any RPL program can be bound to any key by defining a word which has the same name as the key in question with the prefix KEY_. The word must be defined in Master's RPL Environment and it must return information with which windows should be refreshed. This return value consists of flags corresponding to each window type. Constants for these are defined in the file "editor.rpl". IWR_SELECT IWR_VIEW IWR_MAT IWR_ANIM IWR_COL IWR_ALL How to define words to "Masters" environment? There are three possible ways to do it: 1. Write a RPL program using any text editor, save it to a file and execute that file as a Macro. 2. Insert your RPL program to the file "s:rpl-startup". - TUTORIAL 7.21 - 3. Send your RPL program to ARexx port of REAL 3D. Let's write a simple RPL program and bind it to a key. 1. Add the following RPL program to the end of the file "s:rpl-startup". : KEY + 1 O_LOCK ( lock object data exclusively ( fetch selected objects on the stack: O_GETSEL 0.1 0 0 0 M_MOVE 0 O_LOCK (free lock IWR_VIEW ( only View must be refreshed ; 2. Start REAL 3D and create some objects. 3. Select one of the created objects and press the key "+". This causes "Master" to call your KEY + word which moves the selected objects to the positive direction of "x" axis. Of course this is very stupid thing to do. So, let's improve it by asking the user how much the selected objects should be moved. 1. Exit the Real and load s:rpl-startup file to your text editor. 2. Modify the KEY + word so that it looks like the following one: "vectors. rpl" LOAD VVARIABLE vMovement : KEY + vMovement "Move Selected Object" GET_VECT IF 1 O_LOCK O_GETSEL V@ vMovement 0 M_MOVE 0 O_LOCK ENDIF IWR_VIEW ; Now, start Real, create some objects, and press the key "+". REAL 3D opens a requester which allows you to enter three values defining how much to move selected objects. If you exit the requester with OK, all selected objects will be moved. Note: As a matter of fact, this is very handy macro because the requester allows you to define the formulas which are then automaticaly evaluated. 1. Press the key "+". 2. Type the following formula to the first gadget: 10*3.14/100 and press the enter key. Your formula is evaluated and replaced with the result: 0.314 3. Click OK and the macro moves all selected objects 0.314 units along the "x" axis. One very powerful feature provided by REAL 3D is ability to open several screens. For example, you can use one HAM screen for rendering images and another four color hires screen for modelling. The following macro tries to bring the "Palette" window to the front, and if there are no Palette windows, it opens one. This kind of macro can be very useful when you have a lot of screens open. ( Bring window to front or ) ( open it if it doesn't exist ) : KEY_8 0 "Palette" iWM_ACTIVATE WND_SENDMSG IF iWT_PALETTE "Palette" 200 0 60 640 WND_OPEN ENDIF 0 ; - TUTORIAL 7.22 - When you press the key "8" the WND_SENDMSG word sends iWM_ACTIVATE message to all "Palette" windows. If none was found, it uses the word WND_OPEN to open one, This prevents you from accidentally opening more than one "Palette" window. The RPL "MENU" allows you to execute whatever menu through RPL. The word takes three parameters which describes the menu to be executed. For example, in order to execute the menu Create/Visible/Polygon enter the following string in any RPL window: 1 0 0 MENU then click the mouse on any View window and you can create a polygon. The reason for this is that the "Create" menu is the second menu. Because numbering starts from 0, the second menu corresponds the number 1. The second parameter (0) defines the first menu item of "Create", which is "Visibles". The last parameter (0) defines the first sub-menu, which is "Polygon". Naturally, you can create a word in to the rpl-startup file and call the MENU word to activate any menu. For example, by adding the following word to your rpl-startup file, you can activate the Lathe tool by pressing the key "l". : KEY_l 1 7 0 MENU ( Create/Compound_Tools/Lathe 0 ; Note: If the menu is "Ghosted" you cannot execute it using the mouse or a word. Because the MENU word does the same as if you selected the actual menu, you don't have to worry about the return value. For example, the following key binding works only if the active window is a View window. : KEY_q 3 0 1 MENU ( View/Type/Perspective ) 0 ; The standard "rpl-startup" file contains a lot of examples of how to bind RPL programs to keys. Take a look at it for more examples and our attempt at a form of standardization. 7.3.4 Binding Macros to Icons REAL 3D allows you to create your own icons on Tool windows and bind RPL programs to them, When the icon is clicked, the corresponding RPL program is executed. There are two kind of bindings: RPL word bindings and RPL file bindings. In other words, the name of the icon can correspond either to the name of the RPL word or the name of the RPL file. In order to create an icon which is bound to a RPL word called HI THERE: 1. Write the program containing the word HI_THERE and save it as "Ram:test". : HI THERE "YeslNo" "Are You Sure" GET_KEY IF "You are Sure" PUTS ELSE "You are not sure" PUTS ENDIF 0 ; 2. Select the function Project/Macros/Execute Named and execute the macro "Ram:test" (or insert it to your rpl-startup file and restart Real). 3. Open one Tool window. 4. Select the menu Tools/Create Icons which contains one string gadget and two radio-buttons. Enter the string HI THERE to the string gadget and make sure that the radio-button "Word" is selected. Click OK and one icon is created on the Tool window with the text HI_THERE. - TUTORIAL 7.23 - 5. Click the icon and your HI_THERE word is called causing Real to open a requester with the title "Are You Sure". In order to bind an icon to a file, just select the "File" radio-button for the icon to be created and then REAL 3D attempts to load the file whose name is the name of the gadget. REAL 3D tries to find that file from the directory defined by Settings/Paths. One very fast way to write a RPL program is to let the REAL 3D do it for you, in other words, to record a macro: 1. Create one sphere 2. Activate macro recording by selecting a menu Project/Macro/Record and move the sphere a bit to the right. 3. End macro. 4. Select the menu Project/Macro/Current_To_Named. REAL 3D opens a file requester and allows you to define a file name for the named macro. Type "ram:test". 5. Now create one icon on the Tool window with the name "test". 6. With the sphere selected, click the icon and your sphere is moved to the right. Note: REAL 3D standard configuration contains the directory named "r3d2:macros". If you put your macros there, the RPL interpreter can always find them. Now we have gone through some basic concepts how to customize REAL 3D by writing RPL procedures and binding them to keys and icons. This chapter only scratches the surface of possibilities for this customization ability which can offer users a good basis for learning new things and get new ideas about how to increase your productivity. The fact is that usually you have to spent a little time to build up a new function, but once you get it done, it can speed up your modelling in incredible ways. 7.4 CREATING ANIMATIONS USING RPL 7.4.1 Modifying Objects Directly The animation system of REAL 3D is very sophisticated. However, there might be some animations which cannot be created with it, simply because they are absolutely crazy. In this section, we will learn more about RPL by showing basic concepts about how to create these absolutely crazy animations which cannot be created in any other way. The principle is that we don't use the animation system of REAL 3D at all. Instead we will create animations by following the procedure described below: 1. Create your scene 2. Render it 3. Save the rendered image (if not rendered to a file) 4. Test if all frames are rendered, if they are, exit 5. Do something 6. Go back to the line 2. - TUTORIAL 7.24 - The line 5 (Do something) really means what it says. Because the RPL is a full featured programming language, there is not much you can't do with it. As a matter of fact, this is the way professional animators have created many of their really impressive animations. So, lets create an animation where the object just moves with a constant speed and direction. In order to do this, you need one View, one Select and one RPL window. Create one rectangle, select it and enter the following lines on your RPL window: O_GETSEL 0.1 0.2 0.0 0 M_MOVE REFRESH This program first fetches the addresses of selected objects on the stack, then moves the objects, and finally refreshes all windows. By the way, you can enter as many commands and operands in a single line as you wish (as long as their order is correct). For example, it is possible to enter the previous program as follows: O_GETSEL 0.1 0.2 0.0 0 M_MOVE REFRESH or as follows: O_GETSEL 0.1 0.2 0.0 0 M_MOVE REFRESH As you can see, moving objects are not very difficult to create. However, they can be made even easier: Write the following function: : Up O_GETSEL 0 0.1 0 0 M_MOVE REFRESH ; Now, make sure that the rectangle is selected and enter the following command: Up RPL moves selected primitive up and refreshes your windows. Lets create another function which moves object down. It will look much like the first one. : Down 0 -0.1 0 0 M_MOVE REFRESH ; In order to move objects up and down several times enter: Up Down Up Down Up Down Up Down And all objects moved up and down several times. To make up and down stuff easier to carry out, enter the following function: : UpDown Up Down Up Down Up Down Up Down ; Type UpDown and you rectangle will move up and down several times. This way you can define useful functions and group them together. - TUTORIAL 7.25 - Now you know enough to create a simple animation. Create the following program: : AnimLeft 10 0 DO O_GETSEL -0.1 0 0 0 M_MOVE REFRESH LOOP ; Play it and you will see that it animates all selected objects by moving them to the left. : AnimRight 10 0 DO O_GETSEL 0.1 0 0 0 M_MOVE REFRESH LOOP ; Call this function and it moves your objects to the right. Lets create a function which moves objects first to the right, and then back to the left: : AnimRL AnimRight AnimLeft ; Call this function and you will see your objects moving as you expected. Then let's create an animation where objects this several times: : AnimRL10 10 0 DO AnimRL LOOP ; Call the "Anim10" and your objects goes back and forth ten times. Lets create a function, which allows you to define how many times to move forwards and backwards: : AnimN 0 DO AnimRL LOOP ; You didn't enter the upper limit for the DO function inside the "AnimM" function. So you have to pass it to the function when you call it. Call the "AnimN" as follows: 10 AnimN and it goes 10 times. Call it as follows: 20 AnimN and your objects goes 20 times. You can use the function "I" to get the current value of the "loop index" each time around. Let's consider the following: : LoopTest 5 -5 DO I. LOOP ; When you call this function, the result will be: -5 -4 -3 -2 -1 0 1 2 3 4 You can use this index as a operand for something. For example, it can be used as a "frame counter" or parameter to trigonometric functions for defining nice motions. - TUTORIAL 7.26 - So, lets create an animation where all selected objects move along a sin curve: : SinMotion 100 0 DO O_GETSEL I 50 F/ ( x = I / 5 ) I 10 F/ SIN F* ( y = sin 1/10 ) 0 ( z=0 ) 0 M_MOVE REFRESH LOOP ; Call the word "SinMotion" and all selected objects will move along a sin curve to the positive direction of the "x" axis. Loops can be nested. Write the following program: : LoopLoop 10 0 DO O_GETSEL -0.1 0 0 0 M_MOVE REFRESH 10 0 DO O_GETSEL 0 0.2 0 0 M_MOVE REFRESH LOOP LOOP ; and call it. Selected objects move ten frames to the left, then one frame downwards and this cycle is repeated ten times. Sometimes a programmer wants to create a loop which terminates when some condition or event occurs. BEGIN UNTIL loop can be used for this purpose. It repeats until a condition is TRUE. : MyLoopTest BEGIN O_GETSEL 0.1 0.1 0 0 M_MOVE REFRESH RANDOM 0.9 F> UNTIL ; Previous function loops until the RANDOM function returns the value which is greater than 0.9. So the function UNTIL pushes one operand off the stack and if it is non zero, the loop is terminated. There are several other loops in RPL, but let's leave them for now. In the next section you will see how to use variables in RPL. A programming language is not a full featured programming language if it does not support variables. So, here you go! FVARIABLE t : Rotate 0 t F! ( t = 0 ) BEGIN O_GETSEL t F@ SIN t F@ COS 0.0 0 M_MOVETO REFRESH t F@ 0.1 F+t F! ( t=t+0.1 ) t F@ 3.14 F> UNTIL ( exit if t> 3.14 ) ; Now call this function and it moves all the selected objects along a circular route with a radius of 1. Let's improve this function a bit so that it moves the selected objects along a circular path where the radius is passed to the function as a operand. FVARIABLE rad : Rot2 ( radius ) rad F! ( store user defined radius to variable "rad" ) 0 t F! BEGIN t F@ SIN rad F@ F* ( rad * sin t ) t F@ COS rad F@ F* ( rad * cos t ) 0.0 ( z = 0 ) 0 M_MOVETO REFRESH t F@ 0.1 F+ t F! (t=t+0.1 ) t F@ 3.14 F> UNTIL ( exit if t > 3.14 ) ; - TUTORIAL 7.27 - Call this function as follows: 0.1 Rot2 and it will rotate all selected objects along the circular path with radius 0.1. Lets go through some lines of the previous function. The line t F@ SIN rad F@ F* does the following: 1. The address of the variable "t" is pushed onto the stack. 2. The function "F@" pulls the address of floating-point variable from the stack, fetches the value from that address and pushes the value onto the stack. 3. The function "SIN" takes one number off the stack and pushes the corresponding sin-value onto the stack. 4. The address of "rad" variable is fetched onto the stack 5. The function "F@" pushes the value from that address onto the stack 6. The function "F*" takes two numbers off the stack, multiplies them and pushes the result back onto the stack. Later this value is passed to function M_MOVE, which moves selected objects in x direction accordingly. Thats all for variables now. Just remember, when you type the name of any variable the pointer to the variable is returned onto the stack. If you type the string, the pointer to the string is returned onto the stack. So far we have created just wire frame animations. Lets create a complete ray traced animation: Write the following word: : RayTrace 10 0 DO ( ten frames ) O_GETSEL 0.1 0 0 0 M_MOVE RENDER LOOP ; Now we use the word RENDER instead of REFRESH causing REAL 3D to use ray tracing instead of wire frame rendering. However, this animation does not yet save the rendered images. The word SCR_SAVE can be used for saving the screen containing rendered image. The following shows the complete program for creating an animation. Note that this example assumes that your View window is opened on REAL 3D custom screen whose name is "Real.1". 100 STRING FileName : RayTraceSave 10 0 DO RENDER I "Ram:test%d" FileName SPRINTF "Real.1" FileName SCR_SAVE O_GETSEL 0.1 0 0 0 M_MOVE LOOP ; The previous program uses the word SPRINT for building up the file names for rendered images so that the name of the first image is ram:test0 and so on. The SCR_SAVE word takes two parameters: the name of the screen to be saved and the file name. - TUTORIAL 7.28 - Right, now we have created one complete animation. Although, it is very simple (objects just moved with constant speed along an "x" axis), it demonstrates the basic concepts needed for creating animations using this technique. However , there is one strange thing in this example: the result of the animation depends on the selected objects because we used the word O GETSEL to fetch the target objects for the M MOVE word. Although this might give your some good ideas, it is propably not what you wanted. So, lets create such an example where all cubes move to the right, and all rectangles move to the left. Create some cubes and rectangles and write the following program: 100 STRING FileName : RayTraceSave 10 0 DO RENDER I "Ram:test%d" FileName SPRINTF "Real.1" FileName SCR_SAVE "Root/cube*" O_FINDWILD 0.1 0 0 0 M_MOVE "Root/rect*" O_FINDWILD -0.1 0 0 0 M_MOVE LOOP ; Right, lets leave this section now and take a look at how to create animations using the actual Animation system of REAL 3D. 7.4.2 Creating New Methods In this section, we will demonstrate how to expand the animation system of the REAL 3D by writing new animation methods. As you already know, the animation system of REAL 3D is based on so called methods. A method is a procedure which can be associated with objects and which makes the object "intelligent enough to know what to do when the user plays with the time controlling gadgets. So, when do you have to create a new method? Only if there are no suitable methods available. It is so easy to create new animation methods, it is possible that somebody else has already created a method that solves your problem (Check the REAL 3D Support BBS for files 519-436-0140 Canada 1200 to 38400 Baud 24 hrs). Sometimes it is also possible to get the desired result by "customizing" existing methods. So, if there is no suitable method available and you cannot create such by customizing existing ones, then you probably have to write your own. Let's try it. 7.4.2.1 Do Nothing Method In this example we will learn how to create a new a method which actually does just nothing. The purpose of this kind of silly example is just to demonstrate the entire concept of how to attach procedures to objects in order to make them intelligent. So, start your favourite text editor program, type in the following RPL program and save it as RAM:test.rpl. : DoNothing 1 ; & DoNothing "DO NOTHING" MTH_CREATE DROP Then select the menu Project/Macros/Execute Named and execute the file RAM:test.rpl. This installs the new method to the method list of Real. Select the menu Create/Structure/Method. Real opens a requester which allows you to define the type of the method to be created. The last method in the list should be DO NOTHING method. So, select it, click OK and you have just created one intelligent level object. Now create one target object for the method and play the animation. As you can see, it really does nothing. - TUTORIAL 7.29 - There is only one requirement for the object procedure: it must return either 1 or 0 (TRUE or FALSE). The return value 1 indicates that the method in question succeeded in its mission. The return value 0 indicates that something went wrong and the animation should be cancelled. Our procedure "DoNothing" always returns 1 because nothing really can go wrong. The last line of the program & DoNothing "DO_NOTHING" MTH_CREATE DROP then installs our method to the method list of Real. The MTH_CREATE takes two parameters: the address of the RPL word and the address of the name for the method. So, the first command "&" just pushes the address of the "DoNothing" word onto the stack. If the creation succeeded, the MTH_CREATE word returns the address of the created method. We are not interested in this adress so it is discarded with the word DROP. Because the DoNothing procedure is really stupid, it is easy to make it smarter. Add the following line to your "s: rpl-startup" "ram:test.rpl" LOAD This installs your method automatically when you start Real. Now modify the contents of the ram:test.rpl file so that the word DoNothing actually does something: : DoNothing "YeslNo" "Continue" GET_KEY ; Restart Real, create one DO_NOTHING method and play the animation. Requester is opened with the header text "Continue" and two gadgets "Yes" and "No". If you select "No" gadget, the GET_KEY returns 0 and the animation is terminated. If you select "Yes" gadget, the GET_KEY returns 1 and Real proceeds to the next frame asking you the same question again. Before we leave this DO_NOTHING example, let's do something useful. Let's create one real animation method which moves the target objects randomly. So, modify the "ram:test.rpl" file so that the DoNothing word looks like the following. : DoNothing 0 o2 @ RANDOM RANDOM RANDOM 0 M_MOVETO 1 ; Now, restart Real, create one DO_NOTHING method with some targets and play the animation. Now we have finally managed to get something which looks like an animation. The reason why we first showed these silly "do nothing" examples was that it is important to realize that it is totally up to the user to decide what his/her object procedures (methods) should do. They can be simple or complex and tailored to address specific needs of the user. 7.4.2.2 Move Absolutely Along a Path In this example, we will demonstrate how to create a method which moves target objects along a path so that if the target is dislocated from the path, it is immediately pulled back to it. This is very good example, because it demonstrates how powerful a tool RPL is and how user created methods expand the functionality of the program. The first thing to do is to decide what kind of properties our custom methods should have and what kind of parameteres it requires. In this case our method strongly resembles the PATH methods. So, the syntax diagram of the method looks like the following: +--------+ | Parent | +--------+ / \ +--------+ +----------+ | target | | Level(M) | +--------+ +----------+ \ +------+ | Path | +------+ - TUTORIAL 7.30 - Next we have to work out what the procedure to be created should do. In this case, it should do the following things: - Read the current time - Evaluate one point from the parameter object corresponding to the current time. - Move target object(s) to the evaluated point. So, when the time runs from 0 up to 1, the evaluated points move along the parameter object from its beginning point towards its end point accordingly pulling all target objects with it. The following program defines AbsPath method procedure and adds a new animation method to the Animation System. ( Load definitions for object data structure "objects. rpl" LOAD ( One general usage support word : GetTargets 0 ( 0 terminates the list o2 @ O_GETSUB ( first subobject BEGIN ( test if the object address is not zero DUP IF ( fetch the contents of wMETHOD field: DUP O.wMETHOD W@ NOT IF ( if zero, valid target DUP ENDIF ELSE ( object address was zero, so EXIT ( no more targets, exit the word ENDIF ( fetch the address of the next object: O_GETNEXT AGAIN ; ( Actual object procedure : AbsPath GetTargets ( fetch target objects on the stack o1 @ O_GETSUB ( find the parameter object DUP NOT IF ( if no parameter, syntax error "Parameter Object Missing" PUTS 0 ( cancel animation EXIT ( exit word ENDIF t F@ u F@ v F@ O_EVAL 0 M_MOVECOG 1 ( return 1, everything is okay ; ( Install new method to the animation system & AbsPath "ABS_PATH" MTH_CREATE DROP As you can see, it takes only some lines of RPL code to define a completely new animation method. After executing the program, one new method called ABS_PATH can be seen in the Create/Structure/Method requester. The word GetTargets is a general usage word which can be used by all your custom methods. It just fetches the addresses of all target objects on the stack and terminates the list with 0 as required by M_XXXX words. The variables "o1", "o2" and "t", "u", and "v" are automatically defined by the animation system. "o1" contains the address of the method object, "o2" contains the address of the parent object of method and t, u and v variables reflects the new time for the method. So, let's take a look at what the actual method procedure does. Whenever the time is changed, the animation system calls our procedure AbsPath. As already mentioned, the word: GetTargets just pushes the addresses of all target objects on the stack. Then the line o1 @ O_GETSUB - TUTORIAL 7.31 - fetches the address of the parameter object on the stack (parameter object is sub object of the method object). The code: DUP NOT IF "ERROR: Parameter Object Missing" PUTS 0 EXIT ENDIF then tests the return value of O_GETSUB and if 0, proper error message is printed out and 0 returned indicating that the method failed. The return value of 0 causes animation system to stop. Otherwise the code after ENDIF is executed: t F@ u F@ v F@ O_EVAL 0 M_MOVECOG 1 which evaluates a point from the parameter curve and moves all targets to that point. Finally the value 1 is pushed on the stack, which indicates that method succeeded in its task. 7.4.2.3 Chain In this example, we will create a method which attempts to keep the distance between subsequent target objects constant. The distance between targets can be defined by associating a tag with the method object. If the tag does not exists, then our method uses default value, say 0.5. This method can be very powerful with some particle system oriented methods like radial force etc. Our methods should do the following things: 1. Find the tag value of "FDIS" tag associated with the method object. 2. Loop through all targets and move them so that the distance between COGs of subsequent targets equals the fetched tag value. Note: That we use vector operations here. Note: That this requires some basic knowledges of vector operations like how to add and subtract vectors. ( Useful support function Distribute FVARIABLE fLen VVARIABLE vTmp : Distribute ( 0 aObjects .... fDist ) fLen F! iOP_COG O_PROP vTmp V! BEGIN DUP WHILE DUP iOP_COG O_PROP vTmp V@ VSUB VNORM fLen F@ VMUL vTmp V@ VSUB vTmp V! 0 SWAP vTmp V@ 0 M_MOVECOG REPEAT DROP ; ( Actual Object Procedure : OP_Chain GetTargets o1 @ "FDIS" O_FINDTAG DUP ( find tag FDIS IF ( if found, fetch its value on the stack: 4 + @ F@ ELSE DROP 0.5 ( else push 0.5 on the stack ENDIF ( call distribute with objects and distance: Distribute 1 ; & OP_Chain "CHAIN" MTH_CREATE DROP - TUTORIAL 7.32 - Again, we divided our method into two different words: Distribute and the actual object procedure OP_Chain. The reason for this is that "Distribute" is in general usage a useful word and it is a good programming practice to write modular programs by subdividing bigger tasks to smaller ones. For example, your can apply the "Distribute" now interactively to selected objects by defining just a core word which uses the GET_FLT and O_GETSEL words for fetching the required parameters for it instead of using O_FINTAG and GetTargets as we did here. The Distribute words take the following parameters: - object list terminated with 0 - distance The first line fLen F! Just assigns the passed distance parameter to the variable "fLen". This makes it easy to use all over because we don't have to remember where it is in the stack. The next line starts the actual job: iOP_COG O_PROP vTmp V! O_PROP word is first used for fetching the COG of the first object on the stack and assigns it to the vector variable vTmp. Then in the beginning of the loop: DUP iOP_COG O_PROP fetches the COG of the second object on the stack. The line vTmp V@ VSUB VNORM fetches the COG of the first object on the stack, subtracts the COG of the second object from the COG of the first object and finally normalizes the result vector so that the lenght of it will be 1. The line fLen F@ VMUL then multiplies the unit vector by given distance. Now the length of the vector is given distance and it points from the second object towards the first one. Then we have to subtract it from the COG of the first object and we get the position for the second object. The following line subtracts the vector from the COG of the first object and saves the result back to the vTmp. vTmp V@ VSUB vTmp V! Then we just move the second target object to the defined point. 0 SWAP vTmp V@ 0 M_MOVECOG There is not much to tell about the actual object procedure OP Chain. It uses the word "GetTargets" to fetch the addresses of target objects on the stack, the distance to be used from the tag FDIS (or uses the value 0.5 if the tag does not exists) and then calls the word Distribute which does the actual job. Because the method cannot fail, it always returns 1. Before we leave this method example, let's take a look at how the Distribute word can be used interactively. 1. Open one RPL window and define the word Distribute. 2. Create 10 spheres resembling pearl necklace. 3. Write the following program: FVARIABLE fDistance : Distr fDistance "Define Distance" GET_FLT IF O_GETSEL fDistance f@ Distribute ENDIF ; 4. Select all spheres and call the word "Distr". - TUTORIAL 7.33 - 7. 4.2.4 Strange Force In this example we will create a method object which applies a very strange force field over targets causing the velocity and the spin of target objects to be changed . This is an absolutely crazy method, because the way we modify the velocity is absolutely random and it might be quite a difficult job to find such a force field in the real world. However, it demonstrates well the concept of designing new physical based methods and how to create and update object tags. First we need a word which defines a direction and the strength of the force field. : GetStrangeForce RANDOM RANDOM RANDOM ; Because we separate the word which defines the strength and direction for the force, it is very easy to change this method just by modifying this word. Then we need a word which attemps to read the value of velocity tag. If the VVEL tag does not exist, then it creates it. If the velocity tag exists, then the method modifies its value according to Newtons Laws of Motion. So, how much should the velocity of the object be changed if the force affects the object for "dt" seconds? According to Mr Newton, the strength of the force equals the mass of the object multiplied by the acceleration. Mathematically the problem can be solved as follows: F=ma => F=m*dv/dt => dv = F*dt/m ----------- Because our force is not sensitive to the mass of the target object, we can forget the mass (m). Hence, the new velocity can be solved from the equation: dv = F*dt --------- The following word does all this: ( Word modifying the velocity ) ( of a given object ) : DoStrangeVEL ( aObj ) "VVEL" O_FINDTAG DUP ( try to find VVEL tag IF ( if found: 4 + @ ( fetch the address of tag value DUP V@ ( fetch the velocity on the stack ( fetch the force vector on the stack: GetStrangeForce dt F@ VMUL ( dv = F* dt ( add "dv" vector to original velocity: VADD ( assign result back to the velocity tag: 4 ROLL V! ELSE DROP DUP "CEND" 0 0 0 "VVEL" O_CREATAG DROP ENDIF ; In order to get the new particle method perfect, also the spin must be affected somehow. The only difference between this and the DoStrangeSPI words is that the result vector for the VSPI tag is multiplied by 2*PI. This is because the spin is represented in radians and 2*PI radians corresponds to a full circle (360 deg). ( A word modifying the spin of the object ) : DoStrangeSPI ( aObj ) "VSPI" O_FINDTAG DUP IF 4 + @ DUP V@ ( original spin GetStrangeForce dt F@ 2 PI F* F* VMUL ( delta VADD ( new spin 4 ROLL V ( assign result to VSPI ELSE DROP DUP "CEND" 0 0 0 "VSPI" O_CREATAG DROP ENDIF ; - TUTORIAL 7.34 - The actual object procedure then just scans through all targets and calls DoStrangeVEL and DoStrangeSPI words for each object. : OP StrangeForce GetTargets BEGIN DUP WHILE DUP DoStrangeVEL DoStrangeSPI REPEAT DROP 1 ; & OP StrangeForce "STRANGE_FORCE" MTH_CREATE DROP 7.5 USING EVAL The EVAL word provides the user with an easy way to access data structures of Real, such as materials and objects. In this chapter we will show you some basic ways to use EVAL. Create one material with all properties set to default. How to set the brilliancy of the created material "mymat to 100? Type the following line: "data(mymat->spec)=100" EVAL DROP You just set the specularity of "mymat" to 100. To make sure that this really happened, open the material requester with the material and see. If you don't like Reverse Polish Notation, you can use eval to evaluate your formulas like this: "2*3+5" EVAL . which is the same as the phrase "2 3 * 5 + ." and RPL will reply: 11 or like this: "2*3*sin(3*cos(123)+3)" EVAL F. or like this: "data(mymat->spec)=(data(mymat->bril) +data(mymat->tran))/2" EVAL. and RPL calculates an average of the brilliancy and transparency and assigns these results into a specularity field. So, let's create a material morphing animation. Create one sphere and add material "mymat" to it by creating one texture at the same level. Ensure that the rendering settings for the current View window are correct (don't use the Fast-mode because it ignores all material properties). Then write the following program: VARIABLE rg : MyAnim 10 0 DO ( ten frames ) I 10 * rg! ( rg=I*10 ) "data(mymat->roug)=rg" EVAL DROP RENDER LOOP ; and everything is ready for the show. Call the previous function and you'll see how the roughness of your rectangle is morphed during the animation. Right, how about objects. Assume that the full name (including the path) for you rectangle is "/Root/rectangle". Then you could print out the color of you rectangle as follows: "data(/Root/rectangle->R)" EVAL. "data(/Root/rectangle->G)" EVAL. "data(/Root/rectangle->B)" EVAL. - TUTORIAL 7.35 - or you can change the red signal of the color as follows: "data(/Root/rectangle->R)=120" EVAL. or you can print out the name of your object as follows: "data(/Root/rectangle->name)" EVAL PUTS or you can change the line pattern of your rectangle as follows: "data(/Root/rectangle->ptrn)=255" EVAL. REFRESH or you can change the register color for wire frame rendering: data(/Root/rectangle-> reg)= 3" EVAL. REFRESH For more about EVAL, see the reference manual. 7.6 OBJECTS In this section, we will demonstrate how RPL can be used for creating and manipulating objects. 7.6.1 Object Creation So, let's get started by creating a simple RPL program which just creates one primitive and renders it using default rendering settings. ( A minimal program for rendering a sphere 0.0 0.0 0.0 ( center ) 0.5 0.0 0.0 ( a ) 0.0 0.5 0.0 ( b ) 0.0 0.0 0.5 ( c ) 255 0 0 0 ( RGBA ) "ellipsoid" ( name ) 0 ( flags ) "CEND" ( tags ) C_ELLIPSOID DROP RENDER When passed to REAL 3D's stand-alone renderer, it should produce an image representing one red sphere. Lets go through what this program actually does. The first word C_ELLIPSOID creates an ellipsoid. The general form of all creation words is as follows: Geometry Color Attributes C_nnnn Address Geometric data is described first (in the example it consists of four vectors: center, a, b, and c). The form of geom. data depends on the type of the object to be created. Not all objects have geometric description. In this example, the geom. data defines a sphere, because the length of all three axes are the same. ( Geometric data for ellipsoid ) 0.0 0.0 0.0 ( center ) 0.5 0.0 0.0 ( a ) 0.0 0.5 0.0 ( b ) 0.0 0.0 0.5 ( c ) The second data section is a color for the object. Color is defined by 32 bits (RGB+A). Like geom. description, not all objects have color section. ( Color is defined by 32 bits R,G,B+A ) 255 0 0 0 ( RGBA ) The third data section defines so called "Attributes" for objects. Attributes can be divided into three different parts: name, flags and tag list. The name field defines a symbolic name for the object. This name can be used in many ways. For example, it can be used for finding the address of the object in order to create new objects, more on this later. The flags field is a 32 bit wide integer value. It is used for defining on/off kinds of attributes like whether or not this object should be "motion blurred" or "invisible". In the REAL 3D Editor, these flags can be defined using Modify/Properties/Attributes function. For more information about possible attribute flags, see the reference manual. - TUTORIAL 7.36 - The tag list can be used for defining custom attributes for objects needed by procedural textures, animation methods etc. The tag list consists of a number of tag identifier-value pairs. The tag value is a string whose length is four characters which identifies the value. The type of the tag value then depends on the tag identifier. The tag list is always terminated by "CEND" string. ( Attributes ) "ellipsoid" ( name ) 0 ( flags ) "CEND" ( tags ) C_ELLIPSOID creates an ellipsoid and returns the address. This address can be used in many ways as we will later see. In this example, it is not needed, so it is discarded by using the word DROP. The last word "RENDER" then asks REAL 3D to render the defined scene which in this example consists of only one sphere. One basic idea of RPL is that at the lowest level all data needed for defining objects must be passed to the RPL interface! There are no graphical environments used for defining parts of the data needed as in Renderman. However , this does not mean that in the case where a scene consists of a large number of almost identical objects the program would have to redefine the same data again for each object. Because RPL is a full featured programming language, there are many ways to solve this problem. For instance, let us imagine a scene consisting of 10 spheres. Would it take ten times more space to create such a scene as a scene which consists of only one sphere? Only if all the objects are totally different. Usually they are not, in which case identical data could be defined using, for example, a separate RPL word instead of redefining it for all primitives. Lets demonstrate this by creating an RPL program. The program would create two identical spheres to different position. ( A simple program implementing graphics ) ( environment using different RPL words ) : MY ELLIPSOID ( center addr ) 0.5 0.0 0.0 ( a ) 0.0 0.5 0.0 ( b ) 0.0 0.0 0.5 ( c ) 255 0 0 0 ( RGBA ) "ellipsoid" ( name ) 0 ( flags ) "CEND" ( tags ) C_ELLIPSOID ; 0 0 0 ( center ) MY_ELLIPSOID DROP 1 0 0 ( center ) MY_ELLIPSOID DROP RENDER To make this clear, let's create another program. The program creates a small "Renderman" style graphical environment used for defining "Attribute" data section for objects to be created. ( data for simple graphics environment ) VARIABLE R VARIABLE G VARIABLE B VARIABLE FLAGS 16 STRING NAME ( words for manipulating ) ( graphics environment ) : RiName ( name - ) NAME CPY ; : RiColor ( R G B - ) B!G!R! ; : RiFlags ( Flags - ) FLAGS ! ; - TUTORIAL 7.37 - ( Ri style interface for creating ) ( an ellipsoid ) : RiEllipsoid ( center, a, b, c - ) ( fetch color from graphics environment ) R @ G @ B @ 0 ( A=0 ) NAME ( fetch the address of the name ) FLAGS @ ( fetch flags ) "CEND" ( no tags ) C_ELLIPSOID DROP ; ( create 3 blue spheres ) 0 0 255 0 RiColor 0 0 0 1 0 0 0 1 0 0 0 1 RiEllipsoid 1 0 0 1 0 0 0 1 0 0 0 1 RiEllipsoid 2 0 0 1 0 0 0 1 0 0 0 1 RiEllipsoid ( create 2 red ellipsoids ) 255 0 0 RiColor 0 1 0 0.5 0 0 0 1 0 0 0 1 RiEllipsoid 0 2 0 0.5 0 0 0 1 0 0 0 1 RiEllipsoid ( create one green ellipsoid, "blue" ) 0 255 0 RiColor "blue" RiName 0 -1 0 0.5 0 0 0.8 0 0 0 1.2 RiEllipsoid This listing defines a custom interface where geometric data is defined by the caller and the attributes are fetched from the graphics environment. 7.6.2 Object Instances RPL allows creation of "object instances". This is a useful feature whenever it takes less space to define geometric transformations and modify new objects out of existing ones than defining object data itself. The following example creates a number of spheres using this technique. FVARIABLE Rad VARIABLE Obj ( create first object ) : MY SPHERE ( center rad - addr ) Rad F! ( assign radius to variable "rad" ) Rad F@ 0 0 ( a ) 0 Rad F@ 0 ( b ) 0 0 Rad F@ ( c ) 255 0 0 0 ( RGBA ) "ellipsoid" ( name ) 0 ( flags ) "CEND" ( tags ) C_ELLIPSOID ; 0 0 0 0.5 MY_SPHERE Obj ! ( create two object instances ) ( from the first one ) Obj @ M DUPLICATE 0 SWAP 1.5 0.2 0.2 0 M_MOVE ( the second one: ) Obj @ M_DUPLICATE 0 SWAP 255 255 0 0 1 0 M_COLOR The word MY SPHERE takes two parameters which are center and radius and returns an address of the created sphere. The line 0 0 0 0.5 MY_SPHERE Obj ! creates one sphere whose center is 0,0,0 and radius 0.5. The return value from the C_ELLIPSOID is not discarded using DROP but returned so that it can be stored into the variable "Obj". The last two lines contain the big idea so lets go through them line by line: Obj @ ( fetch the address of the sphere M_DUPLICATE ( duplicate it and leave ( its address on to the stack - TUTORIAL 7.38 - For the sake of efficiency, more than one object can be modified by RPL modify words at one call. The address 0 means that there are no more object addresses in the stack. So the following expression: 0 SWAP just terminates the object pointer list by 0. 1.5 0.2 0.2 0 M_MOVE then moves object instance 1 .5 along x axis and 0.2 along y and z axes. The second instance is created by changing the color of the sphere from red to yellow. 7.6.3 Grouping Objects Together Objects can be grouped together to create hierarchical object trees. This hierarchical construction of object conforms the basis for a large number of other features of RPL. The word C_LEVEL can be used for creating new levels into the object hierarchy and its syntax is as follows: Type Attributes C_LEVEL Addr The following example creates one simple object tree using the word MY_SPHERE created in the previous example. The structure of the tree to be represented is as follows: +------+ | Root | +------+ / \ +--------+ +-----+ | Sphere | | Sub | +--------+ +-----+ / \ +--------+ +--------+ | Sphere | | Sphere | +--------+ +--------+ The following program builds up the hierarchy. ( Creating a simple hierarchical object ) wOT_OR "Root" 0 "CEND" C_LEVEL O_CURRENT DROP 0 0 0 0.5 MY_SPHERE DROP wOT OR "Sub" 0 "CEND" C_LEVEL O_CURRENT 0 0 0 0.5 MY_SPHERE DROP 0 0 0 0.5 MY_SPHERE DROP O_GETCURR O_GETPAR O_CURRENT DROP The first two lines wOT_OR "Root" 0 "CEND" C_LEVEL O_CURRENT DROP create one object of the type wOT_OR. The constant named "wOT_OR" is defined "objects.rpl" in file and the actual value of it is 2. By using constants makes the type of the level more easy to understand for human beings. It is strongly recommended to use these kind of definitions instead of direct values. The first parameter "wOT_OR" defines how volumes of all sub objects are interpreted. In this example, they are "OR" read with each other. The following fields "Root" 0 "CEND" defines attributes for the object and are identical with those we have already seen in previous examples. The word O_CURRENT takes one parameter, which is an address of an object and makes it the current level so that all latter objects will be created inside it. The word returns the address of the previous current level and we get rid of it using the DROP word. - TUTORIAL 7.39 - The creation of the second level is similar to the first one except for two things: the name of the level to be created is now "Sub" and we don't delete the return value from the O_CURRENT. We leave it on the stack so that we can use it later to pop back to the previous level using O_GETPAR and O_CURRENT words. O_GETPAR takes the address of an object and returns the address of its parent object. Then O_CURRENT makes it the new current level. Althought all previous words are pretty simple and clear, our example itself is not perhaps as clear as it could be. The reason for this is that it shows how to built up a hierarchy at the lowest level. Again, it is not necessary to define all parameters for C_LEVEL every time a new level is created. We can create our own core word, which takes only the name of the level to be created. In the following example we will create some useful words which reduce the size of the program and make it much more understandable. ( create level ) : ObjBegin ( name ) wOT_OR SWAP 0 "CEND" C_LEVEL O_CURRENT DROP ; ( close level ) : ObjEnd O_GETCURR O_GETPAR O_CURRENT DROP ; "Root" ObjBegin 0 0 0 0.5 MY_SPHERE DROP "Sub" ObjBegin 0 0 0 0.5 MY_SPHERE DROP 0 0 0 0.5 MY_SPHERE DROP ObjEnd ( Now words ObjBegin and ObjEnd ) ( can be used for defining hierarchy ) It is very easy to close the hierarchy levels by leaving the return address of each created level on the stack and pull it out when it is needed. To get the parent object of it in order to pop up one hierarchy level. However, sometimes it is easier to build up the hierarchy first and then create the actual primitives. For example, let's build up a hierarchical tree which contains no primitives. The structure of the tree should be as follows: +------+ | Root | +------+ / | \ / | \ +------+ +------+ +------+ | Sub1 | | Sub2 | | Sub3 | +------+ +------+ +------+ / \ / \ +-------+ +-------+ | Sub21 | | Sub22 | +-------+ +-------+ / \ / \ +--------+ +--------+ | Sub221 | | Sub222 | +--------+ +--------+ Using the words ObjBegin/ObjEnd defined in previous example a program would look as follows: ( Building up an empty hierarchy ) "Root" ObjBegin "Sub1" ObjBegin ObjEnd "Sub2" ObjBegin "Sub21" ObjBegin ObjEnd "Sub22" ObjBegin "Sub221 ObjBegin ObjEnd "Sub222 ObjBegin ObjEnd ObjEnd ObjEnd Because we discarded all addresses of created objects using the DROP word in the ObjBegin word, we don't remember any addresses of any objects. So, how do we create something inside the desired hierarchy level? The address of any object can be found by its name. For example, we could create a sphere inside the object named "sub222" as follows: - TUTORIAL 7.40 - ( find the address of object ) "/Root/Sub2/Sub22/Sub221" O_FIND O_CURRENT DROP ( make it current ) ( create one sphere ) 0 0 0 0.5 MY_SPHERE DROP Another possibility is to use variables to store object addresses instead of fetching addresses using the O_FIND word. 7.6.4 SphereMan As a final example for this RPL tutorial, we will go through how to create "freeform objects" consisting of thousands of particles. For example, a body of the human being can be created using thousands of spheres distributed evenly over the body instead of using freeform meshes. This kind of task where we have to deal with thousands of objects would be almost impossible task to do without programming language. In this example we will use a SIMPLE SKELETON method to pull the sphere mass on the freeform mesh representing a head. RPL is used for creating all spheres as well as defining positions for each sphere so that they will be scattered all over the head. Clear the Settings/Creation/Auto_Index menu. This dramatically speeds up the creation process. It might be a good idea to switch the Undo feature off. Write the following program using your favourite text editor and save it: : CrSpheres ( iHowmany ) 1 O_LOCK 0 DO 0 0 0 ( center ) 0.05 0 0 ( a ) 0 0.05 0 ( b ) 0 0 0.05 ( c ) 255 255 255 0 ( RGBA ) "ell" ( name ) 0 ( flags ) "CEND" C_ELLIPSOID NOT IF "Not Enough Memory" PUTS 0 O_LOCK EXIT ENDIF I. LOOP 0 O_LOCK "Done" PUTS ; Now open one RPL window and load the program in. This just defines the word CrSpheres. As you can see the word CrSpheres takes one parameter which is the number of spheres to be created. Depending on the memory available, your can call this word with a different amount of spheres. If you have six megabytes of RAM memory installed, you should be able to create about 1000 spheres. So call the CrSphere as follows: 1000 CrSphere This creates 1000 spheres. Then we have to define the positions of the spheres on the skeleton mesh. If you are familiar with the skeleton methods, you know that the tag VPHS is used for defining at which position of the skeleton the target object in question is pulled. Because we are going to use a freeform mesh (a head) as a skeleton object, we have to define the first two components of the VPHS tag. For example, if the value 0,0 corresponds to the left top edge of the mesh, then the value 1,1 corresponds to the right bottom edge. The value 0.5 0.5 corresponds to the middle point of the mesh etc. - TUTORIAL 7.41 - So, write the following program (or just load it in from the rpl directory): ( A program scattering spheres evenly ) ( over a skeleton mesh ) VARIABLE iWidth VARIABLE iHeight : DefPhs ( aParent iWidth iHeight ) iHeight! iWidth ! O_GETSUB DUP NOT IF "No subobjects" PUTS EXIT ENDIF iHeight @ 0 DO iWidth @ 0 DO DUP "CEND" I iWidth @ F/J iHeight @ F/ 0.0 "VPHS" O_CREATAG NOT IF "Cannot create tag" PUTS EXIT ENDIF "." PUTS O_GETNEXT DUP NOT IF "AII Done" PUTS EXIT ENDIF LOOP LOOP ; The word DefPhs takes three parameters: the address of the object containing all spheres, the number of spheres in "u" direction and the number of spheres in "v" direction. For example, if the Width parameter is 100, there will be 100 spheres in the "u" direction of the mesh. Now, make the object containing all created spheres the current level and enter the following command: O_GETCUR 35 35 DefPhs This defines the VPHS tag for each sphere in the current level so that each cross-section curve of the head will consist of 35 spheres. Create one SIMPLE SKELETON method beside all the spheres and add the following tag to it: ISKE 3 This tells the skeleton method that the VPHS tags are already created and are up to date. If you forget to add this tag, the method will automatically define tags according to the current positions of the target objects. Load your freeform mesh representing a head inside the method object. Now the hierarchical structure of your object should be as follows: +------+ | Root | +------+ / \ +--------+ +-------------+ | o1..oN | | Skeleton(M) | +--------+ +-------------+ \ +---------------+ | B-Spline_Head | +---------------+ So, spheres (o1 ... oN) are pulled on the B-Spline_Head object by the skeleton method. Your "SphereMan" is now ready. Select the menu Animate/Control/Refresh and all the spheres are pulled on the head. Make the skeleton object invisible if you don't want to see the mesh itself. We will stop here, but you can carry on by animating the skeleton mesh. This also causes the spheres to be animated (because whatever you do to the head object, the spheres are always pulled on its surface). For example, you can add one WAVE method beside the B-Spline_Head and the sphere head will wave. +------+ | Root | +------+ / \ +--------+ +-------------+ | o1..oN | | Skeleton(M) | +--------+ +-------------+ / \ +---------------+ +---------+ | B-Spline_Head | | Wave(M) | +---------------+ +---------+ / \ +------+ +-------+ | axis | | curve | +------+ +-------+ - TUTORIAL 7.42 - Now the spheres (o1 ... oN) are pulled on the B-Spline_Head which is morphed by the Wave method. Or you can use this technique just as a creation tool and delete the skeleton after all spheres are pulled on it. Then you can use the sphere as a beginning situation in particle animations. The possibilities are infinite and the only existing limit is your imagination. - TUTORIAL 7.43 - REFERENCE --------- Chapter 1 MENU FUNCTIONS ------------------------ The following syntax is used to describe all the functions available by menu selection: / - SUB-MENU ITEMS FOLLOW * - TOGGLE GADGET 1.1 PROJECT The REAL 3D binary file format follows the IFF standard. The various data-structures necessary to define a complete REAL 3D project are each saved in a different data-section. Each data-structure can be saved and loaded independently. For each major data-structure type, there is a specific function. In addition, under the Project/ sub-menu are functions for loading and saving each individual section. There are two variants of loading for each data-section: "Insert" which loads the new data and adds it to the existing data-structure; and "Replace" which clears the existing data-structure if or when it loads the new data. The loader is intelligent, and can cope with loading files which contain some or all data-sections, but each loading function will only try to load those sections it is specified to load. The "Replace" variant of the loading functions do NOT clear the existing data-structure if the specified file does not contain the appropriate data-section. The different "Save" functions only save those sections they are specified to save. All the functions open a file-requester to allow the user to select the REAL 3D IFF file to load from. Changing the DOS path updates path- settings, so it is remembered by the requester the next time it is used. If "Confirm Save" gadget is set by using Settings/General requester, the user is asked to confirm the operation before the old file is overwritten. When working on a Workbench(TM) screen, Real-IFF files can also be loaded by moving their icons onto a View window. Objects/ Insert Load new objects from specified file into the current level of hierarchy. Save Save selected objects to the specified file, Replace Clears the hierarchy if/when it loads the object section of the specified file. Project/ The user is presented with a requester to define the new name for the root object. New This does the following: 1. Hierarchy cleared 2. Material Library deleted 3. Named Colors Library deleted 4. Grid Definitions removed 5. Vector stack is cleared 6. Undo Buffer cleared 7. Time and Frame Count are reset to zero - REFERENCE 1.1 - Insert Load all the available sections of a project definition from the specified file. Save Saves all sections of the current project definition to specified file. Replace Load all available sections from the IFF file and delete any corresponding data structures in memory. Insert Sections A requester is opened to allow only those sections of a REAL 3D file the user selects to be loaded. After the selection has been confirmed, the file requester will be opened. Save Sections Opens requester to select data sections for saving, then the file requester. Replace Sections Opens data sections requester for selecting the sections to replace from a file. Materials/ Material definitions are stored in the material library. They are created and modified using a material window. Window Open a Material editor window. This allows materials to be created and modified while other actions, in particular rendering, are taking place. The material definitions are stored in a material data structure, called the material library, complete with their material name, The material definitions are created and modified using a material editor window. Figure R1-1: Material Editor Window (PICTURE: R1-1) - REFERENCE 1.2 - Local Menu: DEFINE/ Texture D Select name of image file using file requester. Show Texture S Display image file. If the image is 24 bit and the External screen is open, then a requester will be produced asking for confirmation, as displaying the image may overwrite the contents of the External screen. Trans. Color C Select Transparent color from current color. TagsT Open the Tag requester to modify or define the tags associated with the current material. Gadgets of the Material window: Name Name of material in the Material library. Texture The path and name of an image file to use for defining various material properties like color or transparency. Each pixel in the bit-map relates to a point on the surface of the object, depending upon the type of mapping control object associated with the material and its position relative to the physical object, An index format string can be included as part of the name. This means that the value of the index variable "i" will be formatted to this specification and included as part of the file name. Spline When this gadget is set, and the target object for the material is a B-spline mesh, then the object itself is used for the mapping definition. Any coordinate transformations defined by mappings associated with the material are ignored. This means that the mapping of the texture file follows any modifications to the shape of the mesh. S-map uvwh These four numeric gadgets control the position and size of the image file when it is mapped onto a spline. The u and v values determine the position of the top left corner of the image and w and h control what proportion of the spline is covered. Each of these can be between 0.0 and 1,0, Color map Texture is to be used for material color definition. Bump map Red component of texture is used for bump map evaluation. The brighter the red component, the higher the bump. Transparency map Green component is used for transparency evaluation. The brighter the green component, the more transparent the material. Brilliance map Blue component is used for evaluating brilliance. The brighter the blue component, the more mirrorlike the material. Shadow map The RGB values of each pixel of the texture file are used to modify the current color values for the corresponding point of the objects surface, The modification formula is: R*r/255, G*g/255 & B*b/255 Where: r, g, b are the color values of the pixel and R, G, B are the current color values of the surface point It is intended for this function to be used with grayscale texture files to artificially produce shadow effects as an alternative to using Render Settings/Normal mode or , when it is necessary, to create artificial shadows in addition to rendered ones. Note: If shadow mapping is used with a pure white object, it produces the same results as color mapping. - REFERENCE 1.3 - Clip map The surface of the object is clipped by the texture file. The object surface is removed wherever it is not covered by the texture file. This includes any areas not covered by the mapping or tiling, or any areas selected as transparent. Scope mask If this gadget is set, the material effect is modified by using the texture as a mask, which defines where the material is applied. Only the points which are affected by the texture, included in the material, get non-zero scope. The application test is equivalent to the one used for clip mapping. Transparent Color (Transp. col) The color to use as the transparent color is taken from the numeric gadgets: Trans.R, Trans.G, and Trans.B. This affects the application of a texture for clip mapping and scope mask. Transparent color is defined in 24-bit color space, so all values vary between 0 and 255. You can translate a color in original Amiga color system, values ranging from 0 to 15, to 24-bit system by multiplying each color component value by 16. For example, pure white R=15,G=15,B=15 corresponds R=240, G=240, B=240. Unshaded Light sources and shadows do not affect the shading of this material. Smooth Removes specular reflections on the boundaries of transparent materials. Exclusive When the rendering engine evaluates the material properties for each point on a surface, it mixes all the properties for each material defined by the mappings at the same hierarchical level. If only one mapping is present, then the material properties are mixed with the default material properties. Setting the Exclusive property means that other material definitions are ignored for any points on the surfaces covered by that specific material. See Also: "Effect" and "Scope" in this sub-chapter. Tiling Gadgets Tile Selects whether texture file is to be tiled in X, Y or both directions. Flip Selects whether tiled texture file is to be mirrored in X, Y or both directions. If set, then every second tile is flipped, making texture map edges match better. X-Freq. & Y-Freq. These numeric gadgets specify the number of tiling repetitions over the surface of the texture. If the frequency is set to 0, the texture is tiled infinitely for all mapping types, except spherical mapping. For parallel, both X & Y can be infinitely tiled; and for cylindrical and disk mapping, only Y = 0 is treated as infinite tiling. When infinite tiling is used, each tile is the size of the texture along that axis. Grade Selects whether color gradients are calculated for X, Y or both directions of the texture file. Color gradients produce smooth transitions from the color of one pixel to the color of the adjacent pixels. Specularity Controls how sharply defined are the high-lights reflected from the surface of the material by light sources. The higher the specularity, the smaller the high-light and the harder its edges. Specular brightness (Spec. bright.) This affects how intense the specular high-lights are. Brilliance The degree to which light is reflected directly from the material surface ("mirror-like" property), Transparency The degree to which light passes through the material surface. - REFERENCE 1.4 - Turbidity Controls the degree to which light is affected as it passes through the material. The higher the setting the denser the "fog". Turbidity saturation (Turbid. sat.) Defines how the distance which light travels in a turbid material affects the light. The default value 25 gives a linear result; so if the distance is doubled, then the effect is doubled. If this value is zero, then the distance has no effect at all. The actual equation is: "turbidity effect" = Turbidity * (distance^Turbid. sat.), ^ = to the power of Refraction This determines the degree to which light is bent as it passes through the material. It represents the speed of light in the material as a percentage of its maximum speed through empty space. The higher this value the less the light is bent. Roughness This controls the degree of "molecular texture" applied by the material. This "molecular" texture is a random bump-map which is independent of the magnification of the material. Dither This enables dithering of material color to be applied to individual objects and the precise amount of dithering to be selected. The actual amount of color deviation applied is given by: R = R + rnd, G = G + rnd, B = B + rnd where -di/2 < rnd < di/2 where: di is the dither setting. Bump height Relative scale of bumps produced by bump-mapping texture file and procedural bump handler. Effect All materials at the same hierarchical level are mixed together. This controls how strongly the properties of the current material will affect the objects to which it is applied. Effect level is combined with the Scope material variable "s" to determine the final strength of the material properties at any point on the surface of the targets. APPLY Apply the current settings to the library under the name specified in the "Name" text-gadget. If a material under the name already exists, the old definition is overwritten. Otherwise a new material is created and appended to the material library. LOAD Open a requester to allow one of the materials in the current material library to be selected and loaded into the editor window. RESET Reset the material editor to display default material properties. Procedural Handlers In addition to being defined by a slider value or an IFF file, certain material properties can be defined using mathematical equations. These handlers are modifiers to the properties defined by the other settings. The equations to control these properties can come from several sources: Default No procedural handler. Formula An equation with an assignment to a system variable is entered in the expression box. This expression is evaluated using the RPL word EVAL. - REFERENCE 1.5 - RPL RPL text is used to define the handler. This can either take the form of direct RPL words and operands, or an external RPL file to be loaded. The RPL text is interpreted, so the fastest way to implement any handler is to define an RPL word which accesses the material variables directly. The name of this word is then entered into the expression box as the RPL text. The text is interpreted by the RPL "Master" environment, so if the word was defined in an RPL window, then it is necessary for "Master" to INHERIT the other environment. For more details of this see RPL documentation. Built-in Handlers Each procedural handler has several built-in formulas to produce particular effects more rapidly than using the equivalent as a Formula or RPL handler. These handlers use the a and b variables as control parameters for various aspects of their formulas. The default value for both a and b is usually 1.0, but Scope and Color/Waves are exceptions. For the Scope handlers, the default for a is Material Variable sz, the texture size, which is used if a = 0.0. In the Color/Waves handler, the default for b is 128. Descriptions The equivalent formula for each of these handlers are given under the heading for the handler type to which they apply. The formulas use w for the texture map width and h for texture map height, but these are not material variables. Material Variables a,b - User definable variables, initially assigned the value of the numeric gadgets to the right of the expression gadget. x - Horizontal texture coordinate. Type: FLOAT y - Vertical texture coordinate. Type: FLOAT z - Depth texture coordinate. Type: FLOAT sz - Size of texture geometry. Type: FLOAT r - Distance from the origin of the texture. Type: FLOAT s - Scope output variable. Type: FLOAT sp - specularity Type: FLOAT sb - specular brightness Type: FLOAT br - brilliance Type: FLOAT tr - transparency Type: FLOAT tu - turbidity Type: FLOAT ts - turbid saturation Type: FLOAT ri - refraction index Type: FLOAT ro - roughness Type: FLOAT di - dithering scale Type: FLOAT bh - bump height Type: FLOAT bx - Bump-map horizontal coefficient Type: FLOAT by - Bump-map vertiCal coefficient Type: FLOAT R - Red color component Type: INTEGER G - Green color component Type: INTEGER B - Blue color component Type: INTEGER t - Local animation time Type: FLOAT i - Material texture index Type: INTEGER - REFERENCE 1.6 - The following variables are available as a part of EVAL's function: Frm - Current Frame index Type: INTEGER Res - Frame Resolution Type: INTEGER T - Global animation time Type: FLOAT The ranges of x & y are either between 0,0 and 1,0 if no texture mapping type is being used, or between zero and the number of pixels along the x or y dimension of the texture file. z, sz & r are distances expressed in spatial coordinates and can have any positive value, If the mapping used is type Default, then x, y & z are the absolute spatial coordinates, and any mathematical handlers will effectively use Parallel mapping along z-axis. The value sz depends on the texture geometry in the following way: parallel - length of the shorter texture rectangle edge cylinder - radius of the cylinder (average if elliptic) sphere - radius of the sphere (average if elliptic) disk - radius of the disk (average if elliptic) The values for R,G & B are from 0 to 255. If a user defined formula assigns a value greater than 255, then it will be limited to 255. Negative values become zero. The range for T & t is between 0.0 and 1.0. The value of i is assigned by the user and can have any integer value. Frm & Res are a positive integer values between 0 and MAX_INT All the others should be between 0,0 and 100.0. If a formula takes a variable outside this range, then the effects are unpredictable. The order of the handlers indicates the order in which they are evaluated; Mapping first and Index last. Although the material variables can be assigned values at any time, assigning them values before the handler in which they are properly assessed has no practical effect, e.g. The color components are assigned their values from the texture file after the evaluation of the mapping and scope. The material variables x,y & z can be modified in any of the expressions, Handler Types: Mapping Material Variables: x, y & z. The Mapping handler is evaluated after the initial coordinate transformation calculations that use the mapping geometry have evaluated the values of x,y & z. Using an equation to modify one of these variables changes the way in which a texture file is mapped onto the surface. The following diagram shows how the mapping axes relate to the geometry of each mapping type. The x, y, z coordinates in the diagram relate directly to the material variables. - REFERENCE 1.7 - Figure R1-2: Mappings (PICTURE: R1-2) Built-in Handlers Tilt y = y + x*a*h/w Waves y = y + sin(x*a*PI/w)*b*h SwapXY tmp = x, x = y, y = tmp Scope Material Variables: s, sp, sb, br, tr, tu, ts, ro, ri The primary variable for the scope handler is "s" which, along with Effect level, determines how much of the material properties are mixed with the material properties already applied to the objects. If no other materials are applied, then the material is mixed with the default properties. Built-in Handlers Sphere if r < a s=100, else s=0 InvLin s = 100/(1 + r^2/a) InvExp s = 100*exp(-r/a) Local s = max(100*a/(a-r),0) Temporal s = s*(a*(1 - T) + b*T) Where a = sz by default, except in Temporal, where a = 1 by default. Bump Material Variables: bh, bx & by. Bump-mapping is a process whereby the surface normal produced by the ray hit routines of the rendering engine are modified to make the surface appear to have "ripples" or "bumps". This modification is carried out by using vector addition between the true normal and two other vectors. The variables bx & by define the vectors used to deviate the normal and produce the bump-mapping effect. If a texture file is being used as a bump map, then bx and by are first evaluated from the red component of the texture. - REFERENCE 1.8 - The bump-mapping vectors and their relationship to the normal (denoted by n) are illustrated in the following diagram: Figure R1-3: Bump Variables in Mappings (PICTURE: R1-3) Built-in Handlers Waves bx = bx + sin(x*a*PI/w)*b Bumps bx = bx + sin(x*a*PI/w)*b, by = by + sin(y*a*PI/h)*b Color Material Variables: R, G & B. The color components of the material can also be mathematically modified. The initial values for R,G & B are evaluated from the texture file if used. Mathematical formulas can then be used to modify or replace these initial values, as with the other Material Variables. The size variable (sz) can be used for to bind a formula to the size of a texture. Built-in Handlers Bright R = R*b/(r*a + 1.0) ( G,B similar ) Waves R = R + sin(x*a*w/h)*b ( G,B similar ) Index Material Variable: i The material variable i is evaluated by any index format string used in the texture file name. Using a mathematical formula based upon either T, t or Frm makes it possible to control the indexes of texture files in very flexible ways to create moving material textures. Built-in Handlers Default i = a, a+1, ..., b-1, 0, 1, ..., b-1, ... PingPong i = a, a+1, ..., b-1, b-1, b-2, ..., 0, 1, ... Index handler default values are a = 0.0, b = 0.0 (zero offset and no modulo cycle). - REFERENCE 1.9 - Non-homogeneous Material Properties By using the procedural handlers, or mixing multiple materials, certain material properties can be evaluated non-uniformly, so the properties vary with their depth in the material. The number of times the material properties are sampled is dependent upon Render Settings/Mat. samples. The following properties, and their corresponding Material Variables are evaluated in this way: specularity specular brightness turbidity turbid saturation These non-homogeneous materials enable atmospheric gasses and sun-glow effects to be created. Delete Open a requester allowing the user to select a material from the library for deletion. If mappings still refer to deleted materials, then default material is used, If a new material with the same name is created or loaded, then the mappings will use the new definition. Delete All Delete all material definitions from the material library, Insert Load previously saved material definitions into material library. Save Save all material definitions in material library to the specified file. Replace Material library is cleared if the specified file contains a material section and new materials are loaded. Macros/ Macros are files containing the ASCII text of the operands and RPL words which recreate the actions of the user. Hierarchical selection is not recorded, so the macro executes using the selected objects as operands. The current macro is saved in the file t:macro.rpl. Only those actions which modify objects or the hierarchy structure are recorded. As well as being executed by the functions below, macros can be executed from an RPL window using the "LOAD" word. The RPL system saves and executes macro files on a line by line basis. This means the only limit to the size of a macro is the OS maximum file size. * Record Macro Record all user selected functions and input data as the current macro. When macro recording is started, the previous current macro is deleted. Execute Current Execute current macro upon the selected objects. This is equivalent to executing the RPL command t:macro.rpl LOAD. Execute Named Produces file requester for user to select name of stored RPL text file. This is usually a macro, but it could be any executable file. The selected file is then executed in the "Master" RPL environment. - REFERENCE 1.10 - Repeat Current Execute current macro specified number of times. This is implemented internally using following RPL code: : Repeat_Current n 0 DO t:macro.rpl LOAD LOOP ; where n is the number of repetitions requested by the user. Spread Current Executes the current macro upon selected objects increasing execution count by one for each consecutive object. The execution count starts from zero, so it is not executed at all for the first object, then once for the second and twice for the third. Current to Named Opens a File Requester to the Macro Directory allowing user to define a new name for the current macro. This does not affect the operation of the current macro. This is the equivalent to copying the macro file using the OS. Named to Current Opens file requester allowing the user to select the macro file to be copied to the current macro file. This can also be accomplished using OS file copying. Named Colors/ Named colors are a way of storing special colors which need to be used repeatedly and on different projects. The color values of these colors are saved in a library which can be saved to a file, then re-loaded at a later date. Select A color is selected from the named colors library using a requester. The value of the current color will assume the value of the named color selected. Create The RGB values of the current color are saved in the named colors library under the name specified. Modify A named-color is selected from the library and its RGB values are changed to those of the current color. Delete The selected color is deleted from the library. Insert Load the file specified into the Named colors library. The existing named colors are not replaced. Save Save the Named colors library as the file specified, - REFERENCE 1.11 - Replace Replace the Named colors library if the file specified contains appropriate data section. Windows/ Most windows are opened on the default public screen unless they are too large, when they will be opened on another public screen which is large enough. The animation window and palette window each open on their own private screens, which take their Interlace/Non-Interlace properties from the default public screen. Select Open window for selecting objects and moving through the hierarchy. Clicking on individual names selects a single object. Using over the Select window multi-selects objects. To de-select objects use or . The Select window uses the following text styles to indicate which types of objects are in the hierarchy: - Normal style used for primitives. - Bold style used for levels. - Italic used for controls and mappings. The current level is shown at the top left of the select window. All the objects in that level are indented and arranged in their order in the hierarchy from first to last. - Methods are shown with "(M)" after their name, unless they are the current level, when their method type will be shown in full. - A level with the boolean operator "AND" is shown with "(A)" after its name. - In addition to being printed in italics, mappings have "(T)" as a postfix to their name. The purpose of the select window is to show the logical structure of the object and to make it possible to point objects through their names. View Open a general purpose View which is the window into the REAL 3D universe. Individual Views can be configured using the functions in the View menu. Each View stores all its own settings, including both the sizes and positions selectable with the "zoom-gadget" to the right of the "depth- gadget". When the Select window shows the logical structure of objects, the View window is used for showing the physical structure of objects. The operation of View windows is part of the asynchronous design of REAL 3D. This means that they take full advantage of the Amiga multi-tasking environment by executing time consuming functions, like rendering, as a separate "task". This task will continue to run in the background, leaving the View free to respond to all user actions promptly. View Superbitmap Open superbitmap View, This means the window can be moved over a much larger "virtual" View using the scroll-bars at the bottom and right side. Objects can be created and modified, even rendered on any part of the superbitmap. View Borderless Open View covering the full screen. This has no visible borders and obscures the menu-strip. This window type is usually needed for rendering final images or animations where window borders are not desirable. View DBuffered Opens a double-buffered borderless View on a new screen. This enables wire-frame motion to be observed without seeing the intermediate drawing steps, so the image does not flicker. The new screen is always 4 color, 640 wide, maximum height and interlaced. This screen is - REFERENCE 1.12 - a special private screen and no other windows can be opened on it. By default the View projection type is perspective. RPL Open RPL Shell window for programming RPL words. This is an OS Shell window with support for all the editing, text history and clipboard hot-keys (c and v). It has no menu strip associated with it. RPL window is designed for interactive use. Henceforth each RPL window has its own environment where all RPL definitions are private to the window in question and cannot be accessed by other windows unless parent/child relationship is established using inheritance. All defined words, variables etc. also disappear when the window is closed. When more permanent definitions are desired, the Master environment can be used for that purpose and, when necessary, RPL windows can inherit Master's RPL context by executing the command Master INHERIT RPL words, variables etc. can be defined to the master's RPL environment by loading them as macros, sending them through the ARexx port or defining them in the "rpl-startup" file. The Master RPL environment is allocated when REAL 3D is started and it is used for executing macros, ARexx commands and requester formula evaluation. Tool Open Tool window for loading with tool icons. There are a number of pre- defined tool icons in the REAL 3D system, and these provide single-click short-cuts to selecting the most frequently used creation and modification functions. The user can also create his own icons using Tool/Create Icon. These icons can invoke any executable RPL file or RPL word. The purpose of Tool window is to provide the user with ability to expand and customize the user interface of the program to suit different kinds of projects. Tool windows have no separate RPL environment, so in order to bind icons to RPL words, the word must be defined in Master's RPL environment. If the icon is bind to a file, the RPL file must be found from the macros directory specified in Settings/Paths. Animation Open Animation window on its own private Hires screen. The animation window is used for controlling the animation system, for defining animation settings such as the number of frames to be used for rendering an animation and the name of the result file. Figure R1-4: Animation Window (PICTURE: R1-4) - REFERENCE 1.11 - Gadgets of Animation Window: Time The slider gadget and its associated numeric gadget provide the user with an interactive way of altering the global time. The affect of changing the time, with either the slider or by entering it numerically, depends on the state of the Play To/Jump To gadget. Play Controls These "video recorder" style controls have the following functions: |<... - Play backwards from current time to time 0.0 ..>| - Play forwards to time 1.0 |<- - Go to time 0.0 ->| - Go to time 1.0 <. - Single step one interval backwards .> - Single step one interval forwards CANCEL - Cancel all animation processing including rendering Play To/Jump To This gadget alters the way the Animation System reacts to changes to the time gadgets. If "Play To" is selected, then changing the time causes the animation to play from the current time to the new time. If "Jump To" is selected, then the display is just updated for the new time. Screens & Saved The list selector enables the one of the open REAL 3D screens to be selected for saving to an IFF file after each animation frame. The name can also be typed directly into the Saved text gadget. If an External screen has been opened, then it can be selected and will be saved using the file settings selected with Project/External_Screen/Settings. Resolution & Smpl (Samples) Resolution controls how many intervals time is divided into when it is evaluated from 0.0 to 1.0. Each step is evaluated internally with additional intervals set by Samples. Increasing Samples improves the quality of particle method evaluation. Samples is also used to determine the number of intervals over which to evaluate Motion Blur. Frame The current frame number. This value increase by one for every interval into which Time is divided by Resolution as the animation is saved. You can also use the gadget to move in time by entering the corresponding time value. Seconds The value in this field is only used by the Particle animation system to determine the amount of real time to evaluate particle movement properties over. This means that a particle with a velocity of 1.0 will travel 1.0 ASCs in 1.0 real time seconds; the evaluation of spin is treated in a similar way. Increasing "Seconds" increases the amount of motion for each particle during the whole animation. Format This is an index format string which is used to format the current Frame number before appending it to the end of Screen file text, Individual Views, which are also handles to files, use the formatted index for their file saving. Save If set, then the selected screen is saved to file and Frame is incremented, after rendering of the current frame is completed. Screen File The name and path for the screen image files saved by the Animation system. - REFERENCE 1.14 - Frame Cmd This can contain RPL text which is interpreted by the "Master" RPL environment every time Frame is incremented. By using the RPL word "SYSTEM", commands can be passed to the CLI to control, for example, a single-frame video recorder system. Or alternatively, the frame command can for example convert IFF images the renderer produces to the JPEG format. Palette Open Palette window. This is opened on its own private HAM screen. The Palette is used to control the R,G,B values of the current color and the 16 register color values. The current color is used whenever REAL 3D requires a color value for a function e.g. Modify/Properties/Color. Figure R1-5: Palette Window (PICTURE: R1-15) The register colors give the user fast access to 16 different color definitions. Selecting one of the register color buttons makes the current color the same as the current value of the register color. The value of the register color is then changed using either the sliders or selecting from the color spectrum with the left mouse button. Holding the left button down while moving over the color spectrum changes the current color and the slider values continuously. There is no need to confirm these changes. The effects take place when the "OK" gadget is pressed. It is also necessary to select the settings required BEFORE using a function which uses the current color. If a Palette window is not currently open, then the last setting for the current color will be used. Measuring This opens the Measuring system window. The Measuring system can be used for replacing the mouse when accurate input is needed. It also allows the user to define coordinates using natural units, such as meters and feet, instead of just plain absolute space coordinates. Figure R1-6: Measuring Window (PICTURE) - REFERENCE 1.15 - Measuring window gadgets: X, Y, Z These gadgets show the current position of the mouse in current units. The user can also enter coordinates through these gadgets, in which case the effect is the same as if the user moved the mouse to the corresponding point in View window. Formula evaluation is also supported, making it possible to enter formulas instead of coordinate values. The purpose of these gadgets depends on the measuring type used. If "Normal" method is used, these fields define 3D coordinate values relative to input-plane/absolute space and origin/hotpoint depending on the state of other gadgets on the window. If "Polar" is used, X and Y fields define the direction (horizontal and vertical angle) and Z field is used for distance. Polar coordinates can also be relative to hot-point/origin and input-plane/absolute space. The direction is always defined in degrees. N The purpose of this gadget varies from one function to another. It can be used for defining angles for sector primitives, depths for primitives etc. Absolute/I-plane (Input-plane) This radio button defines whether the coordinates are relative to the Input Plane or Absolute Space. Normal/Polar This radio button defines whether to use normal 3D coordinate system or polar (angle&radius) coordinates. Origin/Hot-point This radio button defines whether to use coordinates relative to origin of absolute space or to hot-point. Unit The cycle gadget defines the unit to be used. The absolute space units corresponds to meters. Possible choices are: Symbol Description Abs. Space Units m meters 1.0 mm millimeters 0.0001 cm centimeters 0.01 in inches 0.02548 ft feet 0.3048 Screen This opens a small window which enables control of the public screens used by REAL 3D. It is opened automatically when a new screen is created to provide a "handle" to the REAL 3D menus, until other windows have been opened. Gadgets of the screen window: Default Make active screen the default public screen. Jump Jump to next public screen. The control window will follow. Close Close all REAL 3D windows on the current screen then attempt to close the screen. If other windows prevent the closure, then a warning requester will be opened Hijack When enabled, windows and requesters opened for public screens will be hijacked and opened on the default public screen. This allows other applications which use public screens to be integrated into the REAL 3D system. One possibility for this is to integrate a text editor for creating and editing the text for RPL. - REFERENCE 1.16 - Pop-to-front When windows and requesters are opened on the default public screen, it will be brought to the front. Close Close the currenCy selected window. Some windows, for example a borderless-View, do not have a close gadget. This function enables all windows to be closed. * No Gadgets This function removes all the gadgets for controlling the View window to leave a window with a narrow surrounding border. This window cannot be moved or re-sized until this function is selected again. Environment/ "Environment consists of the following data sections: - screens - windows (including Measuring system) - global settings: - frame buffer library name - save format for frame buffer (tga, iff, ...) - RPL initial configuration - current tool name for modify system - current register and RGB color - operation level - settings for modify subsystem - default directory paths for projects, objects, textures etc. - aspect ratio - hot-point - undo settings - load/save sections - sections - vector/float format strings - file name for current macro - minimum dragging time - default settings for fractal generators Open Screen Open new REAL 3D screen using Screen-settings requester. Make Def.Pub. Make currenCy selected screen the default public screen. Close Screen Close selected REAL 3D screen. Close Current Close any windows currently open on active screen and then attempt to close the screen. If other programs have opened windows on the REAL 3D screen, then the screen cannot be closed and the user will be warned. Insert Load a new environment definition from the file specified. This will create a set of windows and screens in addition to the existing ones. Save Save the complete environment definition to the file specified. Replace Delete existing environment definition before loading the specified environment file. Save Screen Open a File Requester and then save the active-screen as the specified IFF file. - REFERENCE 1.17 - Screen Palette Figure R1-7: Screen Palette Requester (PICTURE: R1-7) Open a requester enabling the active screen colors to be changed. The screen register color to modify is selected from the color spectrum, then it can be adjusted using the RGB sliders. Two pre-defined palette definitions can be loaded by selecting either of the two button-gadgets. The "COLOR SCALE" definition is used with Render Settings/"Color Shading". Screen palettes are saved as part of the screens data section. External Screen/ These functions enable a library for controlling an External screen, like a frame buffer, to be used. Open Open an External screen with its specified External screen library. Close Close External screen and its library. Set Modes This function allows settings of an External screen to be modified. Not all libraries need this function, and so will not produce a requester. Settings Opens External screen requester allowing the user to select External screen library and file format. Figure R1-8: External Screen Settings Requester (PICTURE: R1-8) Gadgets of the External screen requester: Library Name of External screen library. IFF24 IFF 24 file format used. TARGA Targa file format used. TARGA+A- Targa file format with alpha-information used for saving. - REFERENCE 1.18 - CUSTOM External screen's own format. Save Save image from External screen using format specified by settings. Exit Real Close all REAL 3D windows and screens and return to WB. If other programs have opened windows or requesters on any of REAL 3D's public screens, then the program will warn the user and leave the screens open. 1.2 CREATE Creating the various primitive and compound-objects is carried out by selecting the function and then supplying the appropriate data coordinates. This is usually carried out with the mouse. The defines the View in which the operation will be carried out, selects the first coordinate and starts the operation. It also defines consecutive coordinates. The completes a phase of the function. This may either move onto the next phase, complete the creation, or cancel the whole function. The first component of each primitive is ALWAYS created on the input plane. Those components with depth are then extruded perpendicular to the input plane. The direction of extrusion is determined using the Right Hand Rule. All 2D primitives, such as rectangle, circle and polygon, contain a small stick called as "dvect", which describes the volume defined by the primitive. This information is needed when object is used in Boolean operations. 2D primitives divide the space to two different parts and the dvect describes which side is the inside of the primitive. This means that REAL 3D can handle infinite solids and they don't have to be closed (some other solid modelling packages require that the solid must be closed, so it is not possible to get inside it without passing through surfaces used for defining it). Visibles/ This sub-menu contains all the creation functions for the basic visible primitives. Polygon PRIMITIVE: polygon Create polygonal plane. DEFINE: three or more points on a polygon. Terminating the function before creating 3rd point cancels the creation. Polyhedron PRIMITIVE: polyhedron Create polygonal prism. DEFINE: polygonal cross-section. The cross-section is extruded to defined depth. Polymid PRIMITIVE: polymid Create polygonal based pyramid. DEFINE: polygonal base and apex. Cut polymid PRIMITIVE: cut-polymid Create polygonal based pyramid with truncated apex. Figure R1-9: Polygon-based Visibles (PICTURE: R1-9) - REFERENCE 1.19 - DEFINE: polygonal base, center of truncating surface and radial size of truncating surface. Figure R1-10: Rectangle-based Visibles (PICTURE: R1-10) Rectangle PRIMITIVE: rectangle Create rectangular plane. DEFINE: two coordinates of diagonally opposite corners of rectangle. Cube PRIMITIVE: cube Create rectangular prism. DEFINE: rectangular cross-section. The cross-section is defined as for rectangle. On completion, the rectangle is extruded to depth. Pyramid PRIMITIVE: pyramid Create rectangular based prism. DEFINE: rectangular base and apex. Cut pyramid PRIMITIVE: cut-pyramid Create rectangular based prism with truncated apex. DEFINE: two separate rectangles. The two rectangles form the two parallel surfaces with additional planes created automatically for the sides. END OF PART 4