In recorded animation, model variables are set from stored values. A few uses are recording human motion (mouse movement) and recording incremental animation. Recording incremental animation would then let you play it back with the kind of control parametric animation gives. You could...
For programmers who are looking to make a name for themselves it is important to establish your presence online. Having your own domain name that you use as a starting point for your customers can help create a loyal fan base and allow you to connect your other social network interactions all in one place. Most programmers are good at developing code and applications that allow sites and blogs to operate but are usually less inclined to market their services and create their own space on the internet. Using services from Discount Domains UK you can register your own domain and use their Search engine services to help grow your site. Since they are an accredited ICANN registrar site you can be assured that you are dealing directly with the registrar and not a third party solution provider. They also offer web hosting packages that can grow and expand as your online needs grow.Create your space online with simple and effective web management solutions from Discount Domains UK.
Posted by admin | Posted in Marketing | Posted on 07-07-2010
0
Today many families are using self storage units in order to gain more space at home. It is amazing how much stuff you accumulate after moving into a home. We have three school aged children and with all the various toys, bikes, and stuff that we have purchased over the last few years we are now in the need of additional storage. Self storage units is a great option for families like us because they are relatively inexpensive and most are secure and offer multiple options depending on what you need to store. There are national self storage unit companies like Uncle Bobs where they offer Houston storage space and are one of the leading storage companies in America. Because they are a national self storage unit facility you will find locations across the country which is great for when you move you know that you will great the same great service and features that you have become use to. From Atlanta storage units to self storage units in Ohio Uncle Bobs where they will offer the use of a free moving truck to help you get your stuff to the self storage unit. They offer some great rates where you are allowed to move in for free and then name your price for the next month and then pay the regular prices the remaining time. Use self storage to free up some space in your home and garage and you will thank yourself later for just how clean and spacious your house looks.
When I first got married, I had absolutely no idea how to cook anything. Even trying to cook the most simple things such as macaroni and cheese turned out to be a complete disaster. I have burned macaroni, started stove fires cooking Hamburger Helper, and even had major grease fires from attempting to cook a meatloaf in the oven. I am sure glad that my husband did not marry me for my cooking skills, or we would clearly be divorced by now.
After I got married, I was sitting home one afternoon alone. I was trying to figure out how to cook my husband the perfect dinner by surprise. With my current cooking abilities, this was really not possible. I decided to get my shoes on and get ready to head to town to the local grocery store to buy a frozen lasagna dinner for the two of us. I thought to myself to not forget to turn off the television, as it was still on in the other room. With my keys and purse at hand, I went to the living room to turn off the TV, and noticed a popular cooking show was on. They were in the process of showing step by step directions on how to make the perfect lasagna. This was very odd considering I was off to the store to pick up the same exact dinner, but frozen. I decided to grab a pen and paper and take some notes; how hard could this possibly be?
45 minutes went by and the cook on TV has the most amazing lasagna already prepared. I decided to run to the grocery store and grab the listed ingredients. I carefully followed every step as the professional on television had instructed. I decided to go watch more of the cooking shows while my first homemade dinner bakes. Forty-five minutes later, I pulled the most beautiful lasagna I have ever seen, well ever cooked, out of my oven. To my surprise, it tasted just as good as it looked. My husband was impressed, needless to say.
After that, I continued to watch daily food shows on the food network and some of my satellite tv indian channels. I now am a chef at a popular restaurant in town. I owe all credit to my television set, and good timing. Watching cooking shows on TV can be very exciting!
Posted by admin | Posted in Blogging | Posted on 04-06-2010
0
I am currently employed in the Health Care field and with the latest passage of the Health Care reform it creates some uncertainty as to what the future holds for our industry. It has gotten me thinking about what possibilities are available out in the job place where my sales and leadership skills can be used. I have started to look at going back to school to obtain my MBA in order to advance my education and make myself more marketable since the job pool is so large with many talented people now looking for work because of the recent down turn in the economy. I was able to have a chance to look at the University of Redlands located in Southern California that offers various business degrees for the busy professional as well as the full time student. They have some impressive credentials of which some are: They are the only California business school given an A+ School and a Best Value by U.S. News & World Report.
For business professionals like myself having a smaller campus where the teacher to student ratio is smaller presents a better and more robust learning environment instead of trying to learn online or through a large auditorium of people where you have limited access to the professors. This Inland Empire college offers a holistic approach to your overall learning experience and pride themselves on their students community service which counted up over 100K hours last year and earned them the Presidents Award for community service. You know that the students have to be invested in their school and education to work outside the campus to make their community a better place. So if you are like me and deciding on what the future holds in this dynamic work place and have a desire to learn and grow then learning institutions like the University of Redlands may be just what you are looking for.
Posted by admin | Posted in Marketing | Posted on 25-05-2010
0
On our site we talk about some of the design and development techniques for various java based applications. We have been growing our various content and slowly developed a steady flow of readers and visitors that help to contribute to the conversation. For many bloggers out there it can be difficult to cut through the clutter and grow your reach and customer by simply posting or developing content. You can do manually submit your content to various RSS feeder sites which is a great way to help grow organically but can take a long time. In order to help multiply your efforts you can also Submit Articles to places like Article Alley which is a free site where you can grab content for free and submit your stories and writings as well.
As people browse through the site and find your articles they will then place your content with links back to your site that will in turn create additional traffic. As you grow your content on your blog by submitting your articles they can then be shared by others. This is very similar to a news story being reported by the Associated Press and then being picked up by thousands of newspapers across the country or world. One simple story with mass appeal can help generate a lot of new readers that may have taken years to develop.
Posted by admin | Posted in Uncategorized | Posted on 23-05-2010
0
Collision Using Spring Force
When two objects rebound after colliding with each other, it is due to a spring force, even though it may not look like it. When two billiard balls collide, the compression of the ball is limited to a miniscule fraction of its width but it still happens.
Using a spring force requires higher model rates to realistically collide faster, harder objects. The fps display in the demo shows the actual frame rate. The movie itself is set to 999 fps. On a 1.3 GHz P3 it averages about 980 actual fps.
repeat with s = 1to4 if s <> me.spritenum then
spOther = sprite(s)
–distance components
distX = (spOther.x) – x
distY = (spOther.y) – y
–pythagorean theorem to get distance
dist = sqrt(power(distX,2) + power(distY,2))
–spring force
xSpring = 0
ySpring = 0
minDist = (sp.width + spOther.width) / 2.0 - 10 if dist < minDist then
springF = (minDist – dist) * .02
xSpring = springF * -(distX/dist)
ySpring = springF * -(distY/dist) end if
xTotalForce = xTotalForce + xSpring
yTotalForce = yTotalForce + ySpring end if
end repeat
The algorithm is almost identical to general gravity, so only the repeat loop is shown. Instead of calculating gravity, it calculates spring force. Spring force only acts when the objects are within a certain distance of each other, expressed as dist < minDist.
The magnitude of the spring force is given by minDist – dist. This is the restPosition – position equation from the Spring Forces section. Scaling by .02 puts the force into proportion with other values in the animation. It can be considered the stiffness of the object, the higher it is the stiffer the object.
This demo gives each object a mass, and uses the mass in the accel = force / mass equation. The behavior of the large object shows how mass affects acceleration. When two objects collide, each experiences the same amount of force, acting in opposite directions. This force translates into a smaller acceleration for more massive objects.
Mass can be set however you’d like. In this demo it is set to the cube of the sprite width, which would roughly correspond to its mass if it was a
3-dimensional sphere.
The type of collision modelled in this demo is for round objects with no friction between them. Friction or non-round shape would cause part of the energy in the collision to go into rotational velocity, making the objects spin.
For comments on the rest of the script, see General Gravity.
Posted by admin | Posted in Blogging, Marketing | Posted on 11-05-2010
0
There are multiple ways to get auto insurance quotes by simply filling out one simple form and then having companies earn your business. It’s a law in my state to have insurance on your car, so we don’t have a choice here. We have to pay the insurance rates, so we certainly want to find the places with the best deals. I’m not really sure where we can save money when it comes to our cars, because most of the costs are something you need. I guess fewer trips to the car wash or washing it at home is one way to save car costs. The best thing to do is drive less and it’s the best way car owners can lower the cost they pay each year for their automobiles. Please be sure to leave any tips or suggestions, if you know of a way to save money with our cars and trucks, without risking our safety, the engine, or being unlawful.Companies like NetQuote.com provide a quick and easy solution to those that are looking to save money on their current insurance plans and want one simple way to do it.
Posted by admin | Posted in Animation | Posted on 10-04-2010
0
In recorded animation, model variables are set from stored values. A few uses are recording human motion (mouse movement) and recording incremental animation. Recording incremental animation would then let you play it back with the kind of control parametric animation gives. You could also get ahold of some motion-capture data and massage it into a form that can be used for playback within Director.
The recording of the animation may be done as part of the program, or done beforehand. If it is done beforehand, the data needs to be saved, most conveniently in a field or text cast member, and then retrieved at run-time. For saving lists in text form, see the Lingo functions string() and value().
Playback of recorded motion is essentially a parametric animation, with the parameter driving an index into the list of recorded values.
This demo allows the user to record a piece of incremental animation, and then play it back with a slider driver. The animation is a combination of collision and general gravity.
on startRecording()
recordList = [:]
repeat with sp in instanceList
recordList[sp] = []
end repeat
state = #recording
end
on record()
repeat with sp in instanceList
recordList[sp].add([#x: sp.x, #y: sp.y])
end repeat
end
on playBack(p) — p:0->1
totalPos = recordList[1].count
index = integer(p * (totalPos – 1) + 1) –index:1->totalPos
repeat with sp in instanceList
pos = recordList[sp][index]
sp.x = pos.x
sp.y = pos.y
end repeat
end
Methods from script “modelManager”
In this demo the recording is done by a script adapted from the modelRate script in Independent Model Rate, and renamed modelManager. The three methods above show how it does recording and playback.
Notice that the variables recorded are the animation model variables, not the sprite properties. This is generally the best way to go, for several reasons:
less information to record. For example, in 3D the x, y, and z coordinates are used to set more than three sprite properties. Of course, this means that during playback a rendering algorithm will still be needed.
more importantly, at playback you still have control over rendering.
The variables are recorded once each movie frame in this demo, rather than once each model frame. In this demo the model rate is about 20 times the movie rate. If you wanted to do “super slo-mo” playback, you could record the variables based on model frames, and at slow playback you’ll get a smoother motion.
To record the movement of the mouse, use a script that keeps its x and y animation model variables at the mouse location, and pass the script instance to modelManager’s addInstance() method.
Miscellaneous Points recordList
This is the property list used by modelManager to record the x and y variables of each recorded object. The unusual thing about it is that the keys in the list are object references. recordList associates each object reference with the linear list of recorded position settings for that object.
Usually in sample code the keys are symbols, but the property list can be used to associate values of any data type with each other. This is generally called a hash table, and most likely property lists are implemented in Director using a hash table of some sort.
If recordList is saved in a text cast member, retrieving the list using value() will not recreate object references. To save recordList, use something like sprite numbers for the keys instead of references.
Animation Anomalies
If you watch the animation long enough you’ll notice some interesting anomalies in the behavior of the objects. When I have time I’ll try to pinpoint what is happening. There may or may not be a perfect fix.
Things To Try
Make the playback “instant replay” style by using a time driver instead of a slider to control the playback. You might use a slider to control the speed of the replay.
Posted by admin | Posted in Animation, Lingo | Posted on 08-04-2010
0
Collision Using Spring Force
When two objects rebound after colliding with each other, it is due to a spring force, even though it may not look like it. When two billiard balls collide, the compression of the ball is limited to a miniscule fraction of its width but it still happens.
Using a spring force requires higher model rates to realistically collide faster, harder objects. The fps display in the demo shows the actual frame rate. The movie itself is set to 999 fps. On a 1.3 GHz P3 it averages about 980 actual fps.
repeat with s = 1to4 if s <> me.spritenum then
spOther = sprite(s)
–distance components
distX = (spOther.x) – x
distY = (spOther.y) – y
–pythagorean theorem to get distance
dist = sqrt(power(distX,2) + power(distY,2))
–spring force
xSpring = 0
ySpring = 0
minDist = (sp.width + spOther.width) / 2.0 - 10 if dist < minDist then
springF = (minDist – dist) * .02
xSpring = springF * -(distX/dist)
ySpring = springF * -(distY/dist) end if
xTotalForce = xTotalForce + xSpring
yTotalForce = yTotalForce + ySpring end if
end repeat
The algorithm is almost identical to general gravity, so only the repeat loop is shown. Instead of calculating gravity, it calculates spring force. Spring force only acts when the objects are within a certain distance of each other, expressed as dist < minDist.
The magnitude of the spring force is given by minDist – dist. This is the restPosition – position equation from the Spring Forces section. Scaling by .02 puts the force into proportion with other values in the animation. It can be considered the stiffness of the object, the higher it is the stiffer the object.
This demo gives each object a mass, and uses the mass in the accel = force / mass equation. The behavior of the large object shows how mass affects acceleration. When two objects collide, each experiences the same amount of force, acting in opposite directions. This force translates into a smaller acceleration for more massive objects.
Mass can be set however you’d like. In this demo it is set to the cube of the sprite width, which would roughly correspond to its mass if it was a
3-dimensional sphere.
The type of collision modelled in this demo is for round objects with no friction between them. Friction or non-round shape would cause part of the energy in the collision to go into rotational velocity, making the objects spin.
For comments on the rest of the script, see General Gravity.
Posted by admin | Posted in Blogging, Marketing | Posted on 07-04-2010
0
For those that are seeking ways to stay alert and mentally tough in today’s demanding world you need to understand how to control your emotions and stay focused on the important tasks that will help you achieve your goals. Through specially created sounds your brainwaves can be influenced to help increase energy, improve memory, and many other great benefits that will improve your overall performance.Through the brainwave meditation system which consists of 6 CD’s it will provide you with the solutions to better focus, higher energy, and help reduce bad habits.
You can find more information and all the science behind the Brain Evolution System can be found at http://www.gobrainev.com. The secret of how this works is based on the frequency of sounds. If you think about the feeling and state that you are in when hearing the crashing sound of waves, the slow patter of rain drops and how calming these various sounds can have on the human spirit and mind. The principles are put into practice and then fine tuned in order to create a unique training system for your brain. You can check out their free trial and see for yourself what this program may be able to offer you or a loved one looking to gain command over their hectic schedule and bring a sense of piece and energy to their lives.
Intro
Data structures store data in a way that reflects some relationship between the data, and is easily useable in a program. Lingo provides some built-in data structures such as lists. This section describes how data structures are created using objects, and some of their common uses in animation. Make sure you’re familiar with the material in Object-Oriented Fundamentals.
The two types of structures covered here are lists and trees, which are both very common and useful. Lingo provides an implementation of lists already; this section shows how to make lists using objects as an intro to making trees.
Uses for lists are fairly obvious. Some uses for trees:
working with XML, HTML, etc, which are hierarchical document formats
hierarchical displays (such as a file system display)
decision trees
and many others
Terminology
An object data structure is made of objects that are created from the same script (class). When these objects are assembled into a data structure they are often called nodes of the data structure.
Each node will have properties that are references to other nodes. These object references are what bind the data structure together and are called pointers. How the nodes are arranged using these pointers determines the type of data structure created.
In the diagrams of this section, a square represents a node and an arrow represents a pointer.
Lists
A list is a sequence of nodes. Each node has a pointer to the next node in the list. The nodes in this diagram are created from a script called “listNode”.
To store data in a list, each node has a property or properties that store the data.
The script shown below can be used for the nodes of a list data structure. It has methods for adding a node to the end of the list, and for getting a node at a given position. The property key stores some type of data.
property nextNode
property key
on new(me, _key)
key = _key
return me
end
on addNode(me, _key)
if nextNode = void then
nextNode = script(“listNode”).new(_key)
else
nextNode.addNode(_key)
end if
end
on getNodeAt(me, index)
if index = 1 then return me
if nextNode = void then return void –index out of range
return nextNode.getNodeAt(index – 1)
end if
script “listNode”
Recursion
It might appear strange that the addNode() and getNodeAt() methods in the listNode script actually call themselves. This programming technique is known as recursion, and is essential for making efficient use of lists and trees.
To understand what is happening, trace the execution from when the method is called on the first node in the list. Each time the method calls itself it moves one node down the list, which is called “traversing” the list. Eventually, the execution reaches what is called a base case at which point there are no further recursive method calls.
In the addNode() method the base case is nextNode = void, which means the end of the list has been reached and the new node can be added.
In the getNodeAt() method there are two base cases. Either the desired node is reached (index = 1) or the end of the list is reached before finding the desired node (nextNode = void). When the getNodeAt() method reaches its base case, the result is returned back through the stack of recursive method calls.
Recursion is useful for working with lists because a list is a recursive data structure. That is, a list is defined in terms of itself. The recursive definition of a list is: “a list is a node followed by a list, or void.”
Try writing recursive methods for the listNode script that:
delete the last node in the list
delete a node at a given position
add a node at a given position
return the node whose key matches a given value
return the number of nodes in the list
return a string that contains the keys of the nodes
return the sum of the keys of the nodes (if key is numeric)
Some of these methods are easier to write if each node also has a pointer to the previous node in the list. Such a list is called a “double-linked” list. A listNode script that contains the methods above can be downloaded here. It doesn’t support deleting the first node in the list for the sake of simplicity (how would you do it? Hint: it requires a list implementation using two scripts).
Trees
A tree is also a recursive data structure. It’s similar to a list, except that it can have more than one “next node”. Usually it is drawn vertically to emphasize its heirarchical nature, with next nodes called ‘children’ and the previous node called the ‘parent’.
The top node of the tree is usually called the ‘root’, and is the only node that has no parent. The bottom nodes, which have no children, are called ‘leaves’. Following successive parent pointers from a node will eventually reach the root, and the nodes on this path are the ancestors of the node (not to be confused with the script ancestor property used for inheritance).
Each node in the tree is the root of a sub-tree. A sub-tree can be described as “the sub-tree rooted at this node”.
property parent
property child
property a –numerical
on new(me, _a)
child = []
a = _a
return me
end
on addChild(me, node)
child.add(node)
node.parent = me
end
on addTo(me, b)
a = a + b
repeat with ch in child
ch.addTo(b)
end repeat
end
on sumTree()
sum = a
repeat with ch in child
sum = sum + ch.sumTree()
end repeat
return sum
end
Sample tree node script that stores a single number
The script above can be used for the nodes of a tree that stores a single number at each node. Like the methods that traverse the list data structure, a recursive method can be written to traverse and process a tree data structure. The example methods addTo() and sumTree() both traverse the tree.
n.addTo(num) increments by num the value in each node in the sub-tree rooted at n. If n is the root of the tree, all nodes in the tree are incremented.
n.sumTree() returns the sum of the values stored in the nodes in the sub-tree rooted at n. If n is the root of the tree, the sum of all nodes in the tree is returned.
These recursive methods visit the nodes in the tree in this order:
This is called a ‘depth-first’ traversal since it travels to the bottom of the tree first, rather than processing one level before going to the next.
Here is another tree node script that shows some more recursive tree methods
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.