As the access to broadband grows and providers continually add more capacity to their systems you will see more and more broadband deals. Companies will be competing for your business and are even offering free laptops to obtain your connection. Sites like FaceBook, MySpace, and LinkedIn are helping to expand wireless broadband...
Posted by admin | Posted in Animation | Posted on 26-03-2010
1
3D Camera Movement
Click & drag to arc camera
Ctrl-click & vertical drag to dolly
Ctrl-click & horizontal drag to zoom (field-of-view) 3D Camera Movement – 1000×700 – source movieCtrl-dragging upper-left to lower-right gives that “depth compression” effect used occasionally in cinema (Jaws, LOTR Fellowship, etc).
Moving the Camera
The camera orientation is specified by a transform. The renderer gets the camera transform from the 3Dnode that its “camNode” property points to.
This 3Dnode may or may not be part of the 3D world hierarchy. If it is, the camera transform will be affected by movement in the 3D world. If it isn’t, it moves independent of the 3D world.
In this demo the camera is not part of the 3D world tree, but it does have a few ancestor nodes used for arcing the camera.
Rendering from the Camera’s Point of View
In order to use the perspective equation the 3D world is transformed in rendering so that the camera sits at the origin looking up the z axis:
The key to finding the right transform to use is that when it is multiplied by the camera transform it gives the identity transform (which positions the camera at the origin looking up the z axis).
Since multiplying a transform by its inverse gives the identity transform, the right transform to use is the inverse of the camera transform.
In this demo the 3D world is transformed in this way by passing the inverse of the camera’s transform as the initial parent world transform:
stepFrame() handler of script “3Drender”So the camera adjustment is applied once at the start instead of calculating world transforms for each object then applying the camera.
Field of View
The “field of view” is related to the distance from the focal point to the projection plane:
In this demo the view height is the height of the stage. For Shockwave 3D the view height is the height of the sw3D sprite.
As the focal point gets closer to the projection plane, the field of view increases and more of the 3D world is seen through the projection plane, giving a wide-angle lens effect. When the focal point is further away the field of view is narrow which gives a telephoto lens effect.
Orthographic View
In orthographic view the projections from the 3D scene are orthogonal to the projection plane rather than converging on a focal point:
The word “orthogonal” means perpendicular in n dimensions, hence the name. In orthographic view the 2D mapping comes straight from the x and y coordinates of the 3D model. Just remove the perspective equation from the algorithm, or set persp equal to 1.
If you remove perspective to get an orthographic view you’ll see that back-face culling causes some of the faces to disappear that should be visible, because it is programmed for the view from the focal point. How would you correct this for orthographic view? A hint: vCent is the vector from the focal point (at the origin) to the center of the face. Find a different vector to use when calculating dotCam.
Cube Bounce
The programming of the cube bounce is specialized for this demo so it’s much simpler than a generalized version. Here is a brief description of what is going on in the code.
The model behind it is eight masses, one for each cube resource vertex, connected by springs (physics model). An independent model rate is used since the springs are very stiff. The vertices that exert spring force on verts[i] are stored in spgVerts[i][], with initial (resting) spring lengths stored in spgLength[i][].
Each model frame the forces acting on each vertex combine to give total force on the vertex. The force spgVerts[i][j] exerts on verts[i] is given by a vector found like this:
Calculating total spring force on one vertex, modelFrame() method of script “cubeBounce”The force vector is found as a percentage of the original spring length and scaled according to the stiffness setting.
When a vertex moves out of bounds, it is repositioned at the boundary edge. This compresses the springs it is “attached” to which exerts force on other vertices and causes the cube to bounce and spin.
The cube bounce algorithm can be made more efficient by only calculating the force between each pair of vertices once, rather than twice. The forces between the vertices in each pair are opposites, so if vecF is the force on one then -vecF is the force on the other.
In this demo, resource geometry is separated from the 3D hierarchy so that the same resource can be used multiple times in the 3D world. One advantage to this is animating the resource will animate all appearances of it—the three discs in the demo below all use the same disc resource.
Drag off objects to rotate group 3D Resource Objects - source movie
The resources in this demo are programmed as trees, and consist of a root node with the faces as its children. To use a resource in the 3D world, the “resource” property of a 3Dnode is made a reference to the root of a resource tree:
With this arrangement rendering involves traversing the 3D world tree, and when a 3Dnode uses a resource, traversing the resource tree and rendering its nodes.
Use of Inheritance
The nodes in the two types of trees in this demo, 3D world tree and resource trees, both need tree-related properties and methods but also need properties and methods unique to each type of tree. For example, nodes in the 3D world tree need a resource property to point to a resource, while nodes in a resource tree need a list of vectors to store geometry. Programming this using inheritance is a good way to go.
To do that, a script called “treeNode” is programmed as a generic tree node, with parent and child properties and methods for managing a tree. This script serves as the ancestor for 3Dnode and resNode, the two types of nodes that make up the 3D world tree and resource trees, respectively. The 3Dnode and resNode scripts contain the properties and methods unique to their respective tree types.
Inheritance is used again for the scripts that automate creation of different resources geometries such as cylinders, planes, cubes, etc. These scripts inherit the resNode script and function as the root of the resource trees. (They also inherit treeNode via resNode).
Animating the Resource
This animation uses a “spring system” which is a bunch of objects that act on each other with spring forces (as if they were connected by springs). The objects in this case are the vectors that specify the outer edge of the disc resource. These vectors are stored in the edgeVerts list of the resDisc object.
Using forces means using the physics model (position, velocity, acceleration):
The disc resource lies in the x-y plane so the wave is created by animating the z coordinates of the edge vectors.
Three spring forces act on each vector, all given by the spring formula (restPosition – position). The first pulls the vector back to its initial position. The second two cause the vectors on either side to pull the vector in their direction, which creates the wave.
The velocity is dampened so the wave eventually dies out.
The wave is started by setting the velocity of a vector (zVelo[i]) to a non-zero number.
•What would happen if only the first spring force was used? What if only the last two were used?
This animation can be done in Shockwave 3D by animating a mesh vertexList.
This demo shows how a 3D hierarchy is built using a tree data structure. It shows how world transforms are calculated from relative transforms, some object-oriented ideas, and how to generate XML from a tree. It has one directional light and a fixed camera.
Drag on object to scale, drag off objects to rotate group 3D World Hierarchy -source movie
Trees are used to model 3D worlds because they reflect how real-world objects are related to each other—objects are composed of smaller objects, which are composed of still smaller objects, etc. In tree terms, an object is the parent of the objects it is made of, which are its children. Each object is programmed as a node in the tree data structure:
In addition, moving an object moves all the parts it is made of. This is accomplished in the 3D model by storing the orientation of each object as an offset of its parent’s orientation. Moving the parent will then move all its children. The absolute orientation of any object in the 3D world is thus determined by a combination of the relative orientations of all its ancestors up to the root of the 3D hierarchy.
In Lingo, the transform data type can be used to store the orientation of each node relative to its parent. It also supports operations that simplify the combining of orientations.
For example, multiplying the transforms of the nodes from the root node to a descendant node gives the world orientation of the descendant node. This 3Dnode method shows how it is done recursively, where trans is the node’s parent-relative transform:
on getWorldTransform() if parent = voidthen return trans –root return parent.getWorldTransform() * trans end
Calculating a node’s world transform recursively
For more on the transform data type, see Director’s Help.
Object Geometry
In Shockwave 3D, the geometry of a visible object is determined by what is called a “resource” which can be used at any number of nodes in the hierarchy.
In this demo there is one type of geometry, the plane, which is rendered using the quad property. Instead of using separate resource objects, resource information is included in each node. Each node is either rendered as a plane or is not visible.
Keeping resource information in the tree has limitations (ie can’t reuse), but simplifies the code for this introductory demo. 3D Resource Objects shows how to program separate resource geometry.
Overview of the code All the code necessary for creating and rendering the 3D world is contained in the two scripts “3Dnode” and “3Drender”. The 3D hierarchy is first built with nodes created from the “3Dnode” script. The root node of the tree is then passed to a new 3Drender object which starts rendering the tree.
This set up process is shown in the setup() handler of the movie script, and is similar to how a Shockwave 3D world is built:
–build 3D tree
root = script(“3dnode”).new(“root”)
cube1 = script(“3dcube”).new(“cube1″,“txtr”,vector(40,40,40))
cube2 = script(“3dcube”).new(“cube2″,“txtr”,vector(40,40,40))
cube3 = script(“3dcube”).new(“cube3″,“txtr”,vector(40,40,40))
The “3Dcube” script shows how a 3Dnode can be extended to make geometry creation easier. 3Dcube isn’t a resource, it is a 3Dnode with a few extra properties and methods added to automate cube creation (notice 3Dcube’s ancestor is 3Dnode).
3Dnode Most of the properties and methods of 3Dnode are tree-related methods for adding, finding, and removing nodes. In addition it has four vectors, a transform, and a sprite which store the node’s 3D-related properties.
If a cast member is passed when creating a new 3Dnode, the node will be rendered as a quad (sprite with quad property set). If no member is passed, the node won’t be rendered and only acts to group the nodes beneath it in the tree.
The getWorldTransform() method shows how world transforms are calculated. The world transform of each node is the product of the parent-relative transforms of the nodes in the path from the root to the node.
The toXml() method recursively generates an XML representation of the tree. This is a simple version that lists the names of the nodes, useful for testing and debugging. The method can be modified to write out enough of the 3D world information that the world can be saved as XML and recreated later.
on toXML(me, _tab) if _tab = voidthen _tab = “”
str = return & _tab & “<” & name if child.count = 0then
str = str & “/>” else
str = str & “>” repeat with c = 1 to child.count
str = str & child[c].toXML(_tab & ” “) end repeat
str = str & return & _tab & “</” & name & “>” end if
return str end
Encoding the tree as XML
3Dcube
3Dcube extends 3Dnode to make creation and manipulation of cubes easier. Since 3Dcube’s ancestor is 3Dnode, it inherits the properties and methods of 3Dnode. It can be considered a 3Dnode with some extra properties and methods for making cubes.
When a new 3Dcube is created, it automatically creates the 3Dnodes for the cube faces and adds them as its children. The dimensions of the cube are determined by the vector passed to the new() method.
The way it is programmed in the demo shows how object-oriented techniques can be used to make cube manipulation easier. Notice that changing a cube vertex (cuVert) will alter all three faces that share that vertex, because those faces all point to the same vector object for that cube vertex.
One note about altering a cube vertex. The statement
cuNode.cuVert.LTF = cuNode.cuVert.LTF * 2
looks like it should stretch the left-top-front corner of the cube, but doesn’t. What happens is it creates a new vector object (vector() * 2) and stores it in cuNode.cuVert.LTF. This doesn’t affect the vector object that the faces point to. To do that, operate on the x, y, and z values of the vector object separately:
cuNode.cuVert.LTF.x = cuNode.cuVert.LTF.x * 2
etc.
After the cube is created, calling setCorners() will resize the cube to the dimensions of the passed vector.
3Drender
This object provides the render method, and stores the camera and light information. The render method traverses the tree and sets the properties of each node’s sprite. The sprite rendering is taken from The Quad Property, with lighting, z-axis blocking, and clipping added.
Instead of using each node’s getWorldTransform() method, the world transforms are calculated as the tree is traversed. If getWorldTransform() were used each node’s world transform would be recalculated for each node in its sub-tree, which is less efficient.
Things To Try New geometries
Extend 3Dnode (a la 3Dcube) to make different geometries.
Separate resources from 3Dnodes
To reuse resources, separate the resource information from the 3D hierarchy. Each resource would be a separate tree, and each 3Dnode would have a resource property that would point to the root of a resource tree (or void).
This would allow the same resource to be used more than once in the 3D hierarchy, and modifying the resource would modify all appearances of it in the 3D world. 3D Resource Objects shows how to program separate resource objects.
Mobile camera
This can be done by performing one more transform in rendering, based on the camera’s transform. For an example see 3D Camera Movement.
The Space Viewport shows how model/display separation makes panning/zooming of a 2D ‘camera’ possible, and also an object-oriented design for implementing it. This design can be built upon to make a variety of games or ‘edutainment.’
Object-Oriented Design for the Model
The model for this simple universe consists of variables that store all aspects of the universe as well as the algorithms that act on those variables. For example, the position of each star and sun as well as the algorithm that makes the ship move.
Organizing these variables and algorithms into a number of scripts that work together is an exercise in object-oriented design. The variables are the properties of objects, and the algorithms are written as methods of objects. (Make sure you’re familiar with the material in Object Oriented Fundamentals.)
The Objects
Each type of element in the universe is programmed as a script (class). So there is one for suns, one for stars, and one for the ship. In addition, there is a script for the universe itself which holds all the elements.
Script “universe”
property width – width of universe (in universe units) property height – height of universe (in universe units) property elements – contains all objects in universe property ship – ship object
on new(me)
width = 250.0
height = 250.0
elements = [] repeat with i = 1to100
elements.add(script(“star”).new(width, height)) end repeat
repeat with i = 1to7
elements.add(script(“sun”).new(width, height)) end repeat
ship = script(“ship”).new(me)
elements.add(ship) return me
end
Use of Inheritance
When you take a look at the properties that the stars, suns, and ship need you notice that they all have some in common. For one, they all need to store their position within the universe, because each is a universe element. These common properties can be inherited from one ancestor script. Inheritance indicates an ‘is a’ relationship.
So in this demo the star, sun, and ship scripts all have as an ancestor the universeElement script. This saves having to duplicate common properties and methods in each of these scripts, which becomes more significant as more scripts and functionality are added to the model.
Public and Private Methods
The methods of the objects are divided into ‘public’ and ‘private’ sections. Public methods are meant to be called from outside the object, while private methods are only called from within. In Lingo this makes it easier to tell how an object is to be used. In formal OO languages like Java these declarations are an integral part of the language.
The Model
The model for this simple universe consists of:
•Dimensions of the 2D space
•Position and dimensions of each element in the universe
•Algorithm to animate ship
•Algorithm to animate suns
Not really much to it. The ship movement algorithm comes from Direction using Sine & Cosine: of Force and Acceleration. In this demo, however, the ship’s position, velocity, and acceleration values are relative to the model coordinate system rather than the screen.
The model coordinate system is not explicitly programmed; there isn’t a block of code that you can point to and say ‘this is the model coordinate system.’ But it is implied in that the properties of the elements in the universe are relative to the same coordinate system and handled as such. These include position, size, velocity, acceleration, etc.
It is also implied in the functions that map model coordinates to screen coordinates. These functions are part of the rendering process, so here I’ll just note that these functions are written so that the model coordinate system is similar to the stage coordinate system. That is, all the universe elements lie in the lower-right quadrant with the positive-y axis downward. This simplifies rendering functions.
In this demo, positions within the model coordinate system use the 3D vector data type even though only the x and y coordinates are used. This makes it easier to differentiate between model versus screen coordinates (which uses point data type) and also makes it easier to later add depth to the model if desired.
Rendering
The bulk of the code in the demo deals with rendering, not the model. The scripts for the universe elements contain code for both model and rendering. The rendering code consists of anything relating to the sprite that represents the model element.
The render script renders the model by setting the location and other properties of the sprites that represent the elements in the model.
The Camera
The ‘camera’ in this demo consists of a location in the model (camVec) and a zoom level (zoomLevel). These values are used in mapping universe coordinates to screen coordinates, with the camera location centered in the view area.
The camera is constrained in a few ways. If ‘follow’ is turned on, the camera follows the ship by simply setting the camera location equal to the ship location. Imagine how much more complicated this would be to do if a model/display technique was not used!
A minimum zoom level keeps the camera from zooming out to where the universe is smaller than the viewport. And the camera’s location is kept a certain margin from the edge of the universe so that space outside the universe is not seen within the viewport. The size of this margin changes with the zoomLevel.
Mapping between coordinate systems
The universe coordinate system is mapped to both the viewport and the radar. In addition, the viewport is mapped to the universe in order to get the universe dimensions for the ‘view area’ box in the radar. And the radar is mapped to the universe in order to position the camera based on a click on the radar.
The mapping functions consist of ’shifting and scaling’. The scaling is done by multiplying by the ratio of the dimensions of one area to another. The shifting is done by adding the difference between the positions of the upper left corner of each area. For the universe that is (0,0), for the viewport and radar it is the stage coordinates of the upper left corner of each.
The methods that contain the mapping functions [uniToView() uniToRadar() viewToUni() radarToUni()] are only called from within the render script and so may be considered ‘private’ methods. However, as complexity is added it is conceivable that other scripts may need access to these methods and so I made them public.
Rendering to the Viewport
Rendering is done on two levels. First, the render script sets properties common to all universe elements. Then it calls each element’s own render() method:
on renderToView() repeat with element in universe.elements
element.sp.loc = uniToView(element.posVec)
element.sp.height = element.height * zoomLevel
element.sp.width = element.width * zoomLevel
element.render() end repeat
end
renderToView() method of “render” script
This way each type of element can make some custom modifications to its sprite, such as the ship and suns setting rotation.
Notice that the sp property and render() method of each element in universe.elements is accessed even though the element variable can be a variety of object types. The element variable points to ship, star, and sun objects in turn. It works because these objects have common properties and methods by inheriting them from the universeElement script (class). This is known in OO jargon as ‘polymorphism’.
This is the reason there is an empty render() method defined in the universeElement script. Scripts that inherit from universeElement can override this method, as do sun and ship scripts. But they don’t need to, as shown by the star script. In either case the call to the render() method of the object is valid.
Rendering to the Radar
This consists of setting the location of the radar ship dot, by making use of the uniToRadar() mapping method.
Also on the radar is the ‘view area’ box, but placing and sizing this box is not technically a part of rendering since the box doesn’t represent anything in the universe model. Instead, the box shows the relationship between the Viewport and the universe as specified by the camera.
To set the box, first the Viewport rectangle is mapped to universe coordinates and then those universe coordinates are mapped to the radar:
on renderToRadar()
spRadarShip.loc = uniToRadar(universe.ship.posVec)
Miscellaneous Single Starfield Image
Another way to program the starfield, which is less processor intensive, is to use one image for the whole starfield rather than using a sprite for each star. Add a “starfield” element to the universe model with a script like this:
propertyancestor
on new(me, uniW, uniH)
me.ancestor = script(“universeElement”).new()
me.posVec = vector(uniW/2, uniH/2, 0)
me.height = uniH
me.width = uniW
me.sp.member = “starFieldMember” return me
end
Tree Data Structure for the Model
For simplicity in this demo, the universe object uses a list to store the elements in the universe. But what if, for example, you wanted to have moons circling planets which circle suns? Or you have several elements you want to animate as a group? Using a tree to store the universe elements makes this type of animation much easier.
This is the technique used for 3D worlds, and it can be used very similarly for 2D. See 3D World Hierarchy for more on using trees in this way. A few of the primary differences between using a list and a tree are:
•instead of iterating through the list, you’ll traverse the tree recursively
•each element’s position is relative to its parent, so its absolute position is found
algorithmically
I’d stick with using the transform data type as in the 3D quad demos. In 2D there will be just a transform to specify position, dropping the vectors used for the quad corners. A bonus is you get rotation and scale built in to the transform type. Take sprite rotation from the transform rotation vector z value (assuming you are working in 2D xy plane), and scale width/height from transform scale vector xy values.
A Rendering Alternative
In this demo, each universe element has a dedicated sprite. When the element is first created it puppets the sprite, sets a few of its properties, and uses it throughout the program.
A different way is to puppet the sprites and set the sprite properties for all the elements each time the universe is rendered, first releasing the sprites used for the last render. This takes more computation but is a cleaner render technique and is a better solution in some cases.
For example, say you had 10 universe models to view alternately with each using 150 sprites to render. This would be easy to do, just instantiate 10 universe objects and set the renderObj.universe property to the one you wanted to view. However, if the sprites are dedicated as in this demo you’d need 1500 sprites puppeted simultaneously. So this alternate way of rendering would be the better solution in this case.