
- By Sonya
Roberts
The Second Lesson - Building in
Intelligence with #IF...#ELSE...#END, and #IFNDEF (Continuted)
Suppose we wanted our table to have more than just the two different styles of legs...what would be the best way to handle defining this? We could chain or nest together a whole bunch of #if...#else...#end statements, or we could use a statement set that is formulated specifically for dealing with multiple-value variables. We'll be looking at this command in more detail in future lessons, but at this point it makes sense to introduce you to the #switch...#case statement set in its most basic form.
What #switch....#case does is break code up into segements, with each segment being used only if the condition in the #case statement is true. So we can easily set up our file to have multiple leg style definitions, assign each definition a sequential number, and assemble the definitions into one #switch...#case test.
// ---- Coffee Table Definition ----
// Uses approx. scale of 1 unit = 1 foot
#declare True=1
#declare False=0
#declare Width=-1.5
#declare Height=1.25
#declare LegStyle=3
#declare Brace=True
#declare Surface=texture{T_Wood23}
object {
#include "coffee5b.inc"
}
#ifndef (Width)
#declare Width=1.5
#else
#declare Width=abs(Width)
#if (Width<1) #declare Width="1"
#end
#end
#ifndef (Length)
#declare Length=4
#else
#declare Length=abs(Length)
#if (Length<1) #declare
Length="1" #end
#end
#ifndef (Height)
#declare Height=1.25
#else
#declare Height=abs(Height)
#if (Height<.5) #declare
Height=".1" #end
#end
#ifndef (Surface)
#declare Surface=texture{T_Wood7}
#end
#ifndef (LegStyle)
#declare LegStyle=1
#end
#ifndef (Brace)
#declare Brace=False
#end
object {
union {
box
{<0,Height-.1,0>,<Length,Height,Width>} // Top
#switch (LegStyle)
#case (1)
box
{<.1,0,.1>,<.25,Height-.1,.25>} // Legs
box
{<Length-.25,0,.1>,<Length-.1,Height-.1,.25>}
box
{<.1,0,Width-.25>,<.25,Height-.1,Width-.1>}
box
{<Length-.25,0,Width-.25>,<Length-.1,Height-.1,Width-.1>}
#if (Brace)
cylinder
{<.175,Height/2,.175>,<.175,Height/2,Width-.175>.05}
cylinder{<Length-.175,Height/2,.175>,<Length-.175,Height/2,Width-.175>.05}
cylinder{<.175,Height/2,Width/2>,<Length-.175,Height/2,Width/2>.05}
#end
#break
#case (2)
box {<.1,0,.1>,<.3,.1,Width-.1>}
//Legs
box
{<Length-.3,0,.1>,<Length-.1,.1,Width-.1>}
box
{<.15,.1,.2>,<.25,Height-.1,Width-.2>}
box
{<Length-.25,.1,.2>,<Length-.15,Height-.1,Width-.2>}
#if (Brace)
cylinder
{<.175,Height/2,Width/2>,<Length-.175,Height/2,Width/2>,.1}
#end
#break
#case (3)
cone
{<.2,0,.2>.05,<.2,Height-.1,.2>.1}
cone
{<Length-.2,0,.2>.05,<Length-.2,Height-.1,.2>.1}
cone
{<.2,0,Width-.2>.05,<.2,Height-.1,Width-.2>.1}
cone
<Length-.22,0,Width-.2>.05,<Length-.2,Height-.1,Width-.2>.1}
#if (Brace)
cylinder
{<.2,Height/2,.2><.2,Height/2,Width-.2>.05}
cylinder
{<Length-.2,Height/2,.2><Length-.2,Height/2,Width-.2>.05}
cylinder
{<.2,Height/2,.2><Length-.2,Height/2,.2>.05}
cylinder
{<.2,Height/2,Width-.2><Length-.2,Height/2,Width-.2>.05}
#end
#break
#else
#error "LegStyle is not a valid
value\n"
#end
}
texture{Surface}
}
The line #switch (LegStyle) tells POV which variable is to be tested by the #case commands. The value following each #case command tells what condition must be true for POV to parse the section of code within the #case...#break statement. In this example, #case (1)...#break is the equivalent of saying #if (LegStyle=1)...#end.
In addition to using #if...#else...#end to add some basic intelligence to our objects, we can also use it to make complex scenes easier to work on. For example, suppose we're working on creating a scene with many different things in it. We might have a scene of a living room - it would require us to define the empty room, create windows, doors, trim, moldings, window frames, etc. We would fill it with furniture and knick-knacks, put in several different light sources, and so on. As we worked on the scene, it would become more and more complex, and test renders would start to take longer and longer periods of time.
What we can do is to break our scene up into different files, each #included into a master file, and in the master file use #declare and #if...#end to set it up so that we can "turn off" any combination of items (including entire files of items) we wish. Consider the following rough code outline as an example of this:
// ---- Set Up The Variables ----
#declare True=1
#declare False=0
#declare I_Room=True
#if (I_Room)
#declare I_Molding=True
#declare I_Carpet=True
#declare I_Door=True
#declare I_WindowFrame=True
#declare I_Trim=True
#include "room.pov"
#end
#if (I_Furnish)
#declare I_Couch=True
#declare I_CoffeeTab=True
#declare I_Entertain=True
#declare I_Stereo=True
#declare I_SideTab=True
#include "furnish.pov"
#end
#if (I_Stuff)
#declare I_FloorLamp=True
#declare I_Magazines=True
#declare I_TVRemote=True
#declare I_Snacks=True
#include "miscjunk.pov"
#end
// ---- Assemble the Scene ----
union {
#if (I_Room) object (Room) #end
#if (I_Furnish) object (Furnish) #end
#if (I_Stuff) object (Stuff) #end
}
// ---- Setup Selected Objects ----
#if (I_FloorLamp)
#declare Floorlamp=
object {
...
}
#end
#if (I_Magazines)
#declare Magazines=
object {
...
}
#end
#if (I_TVRemote)
#declare TVRemote=
object {
...
}
#end
#if (I_Snacks)
#declare Snacks=
object {
...
}
#end
// ---- Assemble Selected Objects ----
#declare Stuff=
union {
#if (I_FloorLamp) object (FloorLamp) #end
#if (I_Magazines) object (Magazines) #end
#if (I_TVRemote) object (TVRemote) #end
#if (I_Snacks) object (Snacks) #end
}
With your scene files defined in this way, it's very easy to turn on and off individual items. Or you can tell POV not to parse an entire selection of items at all, by setting just one variable (I_Room, I_Furnish, or I_Stuff, in this example) to false. This flexibility makes the development of scenes go much faster. You can also define multiple camera viewpoints in your master file, using #switch...#case to change from one to another by setting the value of a single variable. Or have both good lighting and "test render" lighting set up, switching between them by setting a specific variable to true or false. It's a good idea to have all of these "switching variables" declared within one section of your master file, so that you don't have to keep scrolling around in search of the necessary variables.
Used with a bit of planning, these functions can greatly increase your productivity. Not only does organizing your files and objects in this manner make your scenes easier to work with, but the resultant files are extremely modular, making it easy to re-use existing code and objects in new scenes.
| BONUS INFORMATION:
It's a good idea to follow a naming convention with your files that allows you to easily differentate between files that are just on/off collections of objects, and files that create specific objects that can be varied in appearance by setting variables to different values. I like to use the .pov extension for the former, and .inc for the latter. Other people name all #included files with the .inc extension. I've thought of switching to naming object collections with an .obj extension (POV itself does not appear to have any actual preferences in the naming of it's script files, though both .pov and .inc fall within it's existing naming conventions). Whatever your choice is for a good organized naming convention, you should select it early and stick to it religiously. |
In the next lesson, we will look at using the #while...#end statements and rand() functions to quickly assemble and arrange repetitive objects.
<Crystal
Clarity> <Topas Try Me's>
<Povabilities> <Soap Box> <Cranky's
Corner>
<Top> <Back>
<Home> <CG Web Board> <Contact>
The Rendering
Times: Design and Copyright © 1997 -- DCS
& WorkForce Graphics. All rights reserved.
Reproduction in whole or in part in any form or medium without
the express written permission of DCS and/or WorkForce Graphics
is prohibited.