Well, it has been quite a great couple of days for the Trencher. Actor selection, holding objects and new SDK features! Eden has significantly changed in a short span of time. The car simulator super-class – Motor – is under development and several bugs in the Text2D interface are history.
Nothing beats a good day’s coding; when things actually work in the end. Automatic resolution detection is also on the way, coupled with OS identification. Small problem is that you can’t detect resolution in Linux with standard Python – which means we’ll just have to improvise. On Windows it’s a done deal – thanks Mr.Gates!
THE EDEN SDK MANUAL
The tutorials I’ll be posting are extracted from the Eden SDK manual so don’t mind any strange chapter numbers you might see. Just ignore them.
2.0 Introduction to the SDK
2.0.1 Class Demonstrators
Eden has a complete set of class demonstrators that is targeted at all levels of game development on the SDK, from novice to pro. The tutorial (section 2.1) is a code walkthrough for the class demonstrators (which we call feature tests here at FunTrench). We do not assume much in the tutorial but we would really like it if you had the following:
- A good grasp of the Python programming language.
- The Panda3D Manual
- A computer where you have set up the whole SDK. This will enable you to actually run the feature tests – a good idea.
2.0.2 Preliminaries
As has been stated before, the Eden framework is built with the Panda3D engine as its base. Panda3D in turn is built with DirectX and OpenGL as the foundation. Both rendering APIs are supported because DirectX is only applicable on Windows while OpenGL can work on both Windows and Linux. Thus Panda allows for one to take advantage of the benefits of DirectX on Windows while maintaining a more universal support for many platforms via OpenGL.
In order to use Eden you do not have to be conversant with OpenGL or DirectX, in fact we prefer it if you are blissfully unaware of them. Eden’s sworn purpose is to hide all the plumbing code from you. Moreover, you do not have to be a Panda3D guru to work with Eden – thanks to the super-classes. We only require that you have some knowledge of various Panda3D components that Eden makes use of, and essentially constitute the basic s of 3D game development in Eden.
2.0.3 Panda at a Glance
This section is extracted from the Panda3D manual, available at panda3d.etc.cmu.edu/wiki/index.php. We recommend that you refer to this resource regularly for clarifications.
Panda3D is a 3D engine: a library of subroutines for 3D rendering and game development. The library is C++ with a set of Python bindings. Game development with Panda3D usually consists of writing a Python or C++ program that controls the Panda3D library.
Panda was created for commercial game development, and its primary users are still commercial game developers. Because of this, Panda3D needs to emphasize four areas: power, speed, completeness, and error tolerance. Everyone knows what power and speed are. But completeness and error tolerance deserve some extra commentary.
Completeness means that Panda3D contains tons of unexciting but essential tools: scene graph browsing, performance monitoring, animation optimizers, and so forth. These things may not be sexy, and as a result, open-source engines often don’t have them. But when you’re serious about getting work done, and not just playing, these tools need to be there.
Error tolerance is about the fact that all game developers create bugs. When you do, you want your engine to give you a clear error message and help you find the mistake. Too many engines will just crash if you pass the wrong value to a function. Panda3D almost never crashes, and much code is dedicated to the problem of tracking and isolating errors.
To successfully use Panda3D, you must be a skilled programmer. If you do not know what an API is, or if you don’t know what a tree is, you will probably find Panda3D overwhelming. This is no point-and-click game-maker: this is a tool for professionals.
Some people have seen screenshots of children’s’ games written in Panda3D, and concluded that Panda3D is graphically limited. Not so. Developers of children’s’ games often choose not to use shaders or other advanced graphics, because children often own older hand-me-down computers. But Panda3D supports the full range of what modern engines should: it provides convenient support for normal mapping, gloss mapping, HDR, cartoon shading and inking, bloom, and a number of other things. It also allows you to write your own shaders, making it capable of anything.
People sometimes have the mistaken impression that Panda3D is written in python, which would make it very slow. But Panda3D is not written in python – it’s written in C++. The python is just for scripting, developers usually write the performance-intensive bits in C++.
2.0.4 How free is Panda?
Panda3D’s license was meant to be a typical free software license, very similar to the BSD and MIT licenses. However, when we created the license, we made one mistake which classifies the license as not free.
The mistake is this: we put in a clause that says you must give all modifications back to the community. That is, if you make a change, you can’t keep it to yourself – you have to email it back to us. Note that a game developed in Panda3D doesn’t count as a modification, only alterations to the Panda3D source code count as modifications.
That may seem like a not-so-bad rule, but Richard Stallman explained to us that this makes it not free software. We, the Panda3D developers, are now planning to change the Panda3D license to make it truly free. Unfortunately, ownership of Panda3D’s copyrights is being transferred right now, and until that’s done, the license is frozen. It could take quite a while for the transfer to be finished, so the bad clause is in there for the foreseeable future.
You have to decide for yourself whether or not the “give modifications back” rule is a deal-breaker. Aside from that clause, the license is a pretty typical free software license, not unlike the BSD license.
2.0.5 Python
Python is the lingua franca for Eden development and is an interpreted, interactive, object-oriented language comparable to Java or Perl. It is available on several platforms, including UNIX, Windows, OS/2, and Mac. Python also has a large number of modules outside of the standard Python installation, and additional modules can be created in C or C++. Because it is late-binding and requires minimal memory management, it is an ideal language for rapid prototyping.
Python is available for free download at www.python.org and has pretty good documentation within the installation. It is open-source. The documentation can be obtained at docs.python.org too. It is vital that you have a working knowledge of Python before you continue. If you can write Python code (you understand the syntax and the data types) then you are good to go.
We recommend the Komodo IDE for editing your Python code, chiefly because it has capabilities for making projects where the entire bunch of files and folders that make up your game is immediately accessible. We also like Komodo because it is a multi-purpose editor capable of handling XML, text and Python – the three varieties of input that you will deal with in Eden. Komodo is a commercially available editor, visit www.activestate.com. A free version, the Komodo Edit program, is also available.
2.0.6 The FunTrench Free Software License (F2S)
The Eden SDK feature set is distributed under the FunTrench Free Software (F2S) License. The Eden Framework itself is distributed under the FunTrench Open Software (FOS) License.
FUNTRENCH FREE SOFTWARE LICENSE
This software product is distributed (or otherwise transferred) to all recipients (or anyone who assumes possession of it, also included in the term recipient) under the following terms and conditions:
i. FunTrench PLC or its associates are not in any way obliged to the recipient(s) in any way whatsoever.
ii. FunTrench PLC hereby allows you explicitly and in writing, to do absolutely anything you want with this software without restriction or fear. This includes but is not limited to selling, modifying, destroying, cloning, copying and anything else within the bounds of possibility and even outside them.
iii. Upon undertaking to do activities allowed in condition (ii), FunTrench PLC is NOT responsible for the software or its action and all such responsibility immediately falls upon the recipient.
iv. FunTrench PLC only accepts credit for authoring the original, unmodified software. Any modification MUST NOT be represented as originating from FunTrench PLC in any forum or media up to the extent that it is at variance with the original, unmodified software.
v. FunTrench PLC is not liable to the recipient or anyone else for anything that this software does contrary to the recipient’s expectation or prediction. By extension see condition (vi).
vi. FunTrench does not offer any claim, guarantee or warranty as to the application or utility of this software in any human endeavor.
vii. FunTrench PLC would appreciate any comments the recipient may have on our software. Please feel free to contact info@funtrench.com. Please note that a reply is not guaranteed.
viii. All recipients of this software have the same permissions as the original recipient, permissions limited to the provisions of this license.
ix. The recipient may remove this license from the software if it bugs him/her, though FunTrench requests kindly that you retain it. However, this license MUST NOT be modified if you choose to retain it.
In conclusion, this software is FREE, and will always be available for free at www.funtrench.com
2.0.7 The FunTrench Open Software License (FOS)
FUNTRENCH OPEN SOFTWARE LICENSE
This software product is distributed (or otherwise transferred) to all recipients (or anyone who assumes possession of it, also included in the term recipient) under the following terms and conditions:
i. FunTrench PLC or its associates are not in any way obliged to the recipient(s) in any way whatsoever.
ii. FunTrench PLC hereby avails the source code into the public domain, for modification, adoption and/or any other use that the recipient may find it suitably fit for. In addition, FunTrench PLC welcomes the entire developer community worldwide to make contributions to the software and undertakes to make all useful modifications part future versions of the software.
iii. Upon undertaking to do activities allowed in condition (ii), FunTrench PLC is NOT responsible for the software or its action and all such responsibility immediately falls upon the recipient. However, FunTrench PLC will at its own discretion recognize the effort of the recipient in improving the software and make such recognition public on its website.
iv. FunTrench PLC only accepts credit for authoring the original, unmodified software. Any modification MUST NOT be represented as originating from FunTrench PLC in any forum or media up to the extent that it is at variance with the original, unmodified software.
v. FunTrench PLC is not liable to the recipient or anyone else for anything that this software does contrary to the recipient’s expectation or prediction. By extension see condition (vi).
vi. FunTrench does not offer any claim, guarantee or warranty as to the application or utility of this software in any human endeavor.
vii. FunTrench PLC would appreciate any comments the recipient may have on our software. We would also appreciate a report on any changes that the recipient makes to the software, though this is not mandatory. Please feel free to contact info@funtrench.com. Please note that a reply is not guaranteed.
viii. All recipients of this software have the same permissions as the original recipient, permissions limited to the provisions of this license.
ix. This license MUST NOT be removed from the software and any subsequent redistribution must include a copy of the license.
In conclusion, this software is OPEN SOURCE, and the source will always be available for free at www.funtrench.com
2.0.8 The Scene Graph…..and other stories
In this sub-section we begin the definition of some graphics engine components that are fundamental to 3D game development. Again we recommend that you at least skim through the Panda 3D manual’s introductory pages. However, this rather long section should give you a good grip of the Panda scene structure. Some of the concepts here are for understanding and for advanced developers because Eden provides easier and cleaner ways of doing those things.
Many simple 3D engines maintain a list of 3D models to render every frame. In these simple engines, one must allocate a 3D model (or load it from disk), and then insert it into the list of models to render. The model is not ‘visible’ to the renderer until it is inserted into the list.
Panda3D is slightly more sophisticated. Instead of maintaining a list of objects to render, it maintains a tree of objects to render. An object is not visible to the renderer until it is inserted into the tree.
The tree consists of objects of class PandaNode. This is actually a superclass for a number of other classes: ModelNode, GeomNode, LightNode, and so forth. Throughout this manual, it is common for us to refer to objects of these classes as simply nodes. The root of the tree (the parent from which all nodes branch out) is a node called render. There may be additional roots for specialized purposes, like render2d for 2D objects.
Panda3D’s ‘tree of things to render’ is named the scene graph. It is really a simple concept when you think of folders on your computer. The root is the drive, then folders form sub-trees that are all branches of the tree.
Here are the most important things you need to know about the hierarchical arrangement of the scene graph:
- You control where objects go in the tree. When you insert an object into the tree, you specify where to insert it. You can move branches of the tree around. You can make the tree as deep (many branches) or as shallow (few branches) as you like.
- Positions of objects are specified relative to their parent in the tree. For example, if you have a 3D model of a hat, you might want to specify that it always stays five units above a 3D model of a certain person’s head. Insert the hat as a child of the head, and set the position of the hat to (0, 0, 5).
- When models are arranged in a tree, any rendering attributes you assign to a node will propagate to its children. For example, if you specify that a given node should be rendered with depth fog, then its children will also be rendered with depth fog, unless you explicitly override at the child level.
Beginners usually choose to make their tree completely flat – everything is inserted immediately beneath the root. This is actually a very good initial design. Eventually, you will find a reason to want to add a little more depth to the hierarchy. But it is wise not to get complicated until you have a clear, specific reason to do so.
Panda3D has a very conventional representation of 3D co-ordinates, often called the Z-up convention. The X, Y, Z co-ordinates are represented as a triple with:
o Z representing the up-down axis of the monitor (where our 3D world will be shown).
o X representing the left-right axis of the monitor.
o Y representing the axis that makes things 3D on the screen: the near-far axis. This axis is what brings about the perception of depth that distinguishes 3D from 2D.
A NodePath is a very small object containing a pointer to a node, plus some administrative information. It is the intent of the Panda designers that you should think of a NodePath as a handle to a node. Any function that creates a node returns a NodePath that refers to the newly-created node.
A NodePath isn’t exactly a pointer to a node; it’s a ‘handle’ to a node. Conceptually, this is almost a distinction without a difference. However, there are certain API functions that expect you to pass in a NodePath, and there are other API functions that expect you to pass in a node pointer. Because of this, although there is little conceptual difference between them, you still need to know that both exist.
You can convert a NodePath into a ‘regular’ pointer at any time by calling nodePath.node(). However, there is no unambiguous way to convert back. That’s important: sometimes you need a NodePath, sometimes you need a node pointer. Because of this, it is recommended that you store NodePaths, not node pointers. When you pass parameters, you should probably pass NodePaths, not node pointers. The callee can always convert the NodePath to a node pointer if it needs to.
There are many methods that you can invoke on nodepaths, which are appropriate for nodes of any type. Specialized node types, like LODNodes and Cameras (for instance), provide additional methods that are available only for nodes of that type, which you must invoke on the node itself.
When you invoke a method of NodePath, you are actually performing an operation on the node to which it points.
By default, there are two different scene graphs created automatically when you start up Panda3D. These graphs are referred to by their top nodes: render and render2d.
You use render most often; this is the top of the ordinary 3D scene. In order to put an object in the world, you will need to parent it to render (or to some node that is in turn parented to render).
You will use render2d to render 2-D GUI elements, such as text or buttons, that you want to display onscreen; for instance, a heads-up display. Anything parented to render2d will be rendered on top of the 3D scene, as if it were painted on the screen glass. The Eden sub-package Eden2D takes care of all render2d operation, though you are free to try your hand at setting up menus, HUDs, text and images directly. While Eden exposes the Panda3D API for you, we strongly recommend that you use Eden’s interfaces to speed up your development, reduce your code, practice good software engineering and most importantly: avoid re-inventing the wheel.
The coordinate system of render2d is set up to match that of the mouse inputs: the lower-left corner of the screen is (-1, 0, -1), and the upper-right corner is (1, 0, 1). Since this is a square coordinate system, but the screen is usually non-square, objects parented directly to render2d may appear squashed. For this reason, Panda3D also defines a child of render2d, called aspect2d, which has a scale applied to it to correct the non-square aspect ratio of render2d. Most often, you will parent GUI elements to aspect2d rather than render2d. Eden uses aspect2d exclusively for rendering images and text in the Eden2D sub-package.
Specifically, the coordinate system of aspect2d is by default scaled such that x ranges over [-ratio,ratio], and y ranges over [-1,1] where ratio is screen_size_x/screen_size_y (in the normal case of a window wider than it is tall).
One of the most fundamental scene graph manipulations is changing a node’s parent. You need to do this at least once after you load a model, to put it under render for viewing:
myModel.reparentTo(render)
And to remove it again:
myModel.detachNode()
To completely remove a nodePath from the scene graph and memory call removeNode, this has the effect of emptying the node and releasing the memory taken up by the node. Use it only when you have no further use for the node:
myNodePath.removeNode()
When you load a model using the Eden interfaces, you may specify a parent otherwise a default parent for static geometry (models with no animation) will be used. Using the node path that Eden stores for you, you can freely reparent your model anywhere you like.
As you become more comfortable with scene graph operations, you may find yourself taking more and more advantage of a deeply nested scene graph, and you may start to parent your models to other nodes than just render. Sometimes it is convenient to create an empty node for this purpose, for instance, to group several models together:
dummyNode = render.attachNewNode(“Dummy Node Name”)
myModel.reparentTo(dummyNode)
myOtherModel.reparentTo(dummyNode)
Since a node inherits its position information from its parent node, when you reparent a node in the scene graph you might inadvertently change its position in the world. If you need to avoid this, you can use a special variant on reparentTo():
myModel.wrtReparentTo(newParent)
The ‘wrt’ prefix stands for ‘with respect to’. This special method works like reparentTo(), except that it automatically recomputes the local transform on myModel to compensate for the change in transform under the new parent, so that the node ends up in the same position relative to the world.
Note that the computation required to perform wrtReparentTo() is a floating-point matrix computation and is therefore inherently imprecise. This means that if you use wrtReparentTo() repeatedly, thousands of times on the same node, it may eventually accumulate enough numerical inaccuracies to introduce a slight scale on the object (for instance, a scale of 1, 1, 0.99999); if left unchecked, this scale could eventually become noticeable. You should not use wrtReparentTo() unless there is a real reason to use it.
2.0.9 More on the Scene Graph
If you’ve come this far you’re doing well. There’s very little you still need to know about Panda before we begin the tutorials. And that will be a lot of fun because you’ll not learn a single new thing – Eden adds utility to Panda, not a new scene graph.
Two of the most common changes are position and orientation.
myNodePath.setPos(X,Y,Z)
By default in Panda3D, the positive X axis points to the right, the positive Y axis is forward, and positive Z is up. An object’s rotation is usually described using Euler angles called Heading, Pitch, and Roll (sometimes called Yaw, Pitch, and Roll in other packages) – these specify angle rotations in degrees. If you are more comfortable using quaternions, the setQuat() method can be used to specify the rotation as a quaternion.
The HPR co-ordinates are borrowed from aviation and so it will be instructive to picture a small aircraft in flight. Heading is the axis you rotate about when you change direction in your aircraft. The range is of course 0° to 360° and corresponding negative values to express the opposite direction. Pitch is the axis you rotate about when you climb – the aircraft nose goes up – and dive – the aircraft nose goes down. At 90° pitch your craft points straight up and at -90° it points straight down. Roll is the axis you rotate about when you lift one wing (bank the aircraft). This is the same axis as that of the propeller on the nose. That’s it for Orientation axes 101, class dismissed.
You can change an object’s size, either uniformly, or with a different value of x, y, and z.
myNodePath.setScale(uniform)
Sometimes it is convenient to adjust a single component individually:
Positions:
myNodePath.setX(X)
myNodePath.setY(Y)
myNodePath.setZ(Z)
Orientation:
myNodePath.setH(H)
myNodePath.setP(P)
myNodePath.setR(R)
Scale:
myNodePath.setSx(SX)
myNodePath.setSy(SY)
myNodePath.setSz(SZ)
Or all at the same time:
myNodePath.setPosHprScale(X,Y,Z,H,P,R,SX,SY,SZ)
You can also query the current transform information for any of the above:
myNodePath.getPos()
myNodePath.getX()
myNodePath.getY()
myNodePath.getZ()
By using the functions setTag() and getTag() you can store your own information in key value pairs – like a Python dictionary. For example:
myNodePath.setTag(‘Key’, ‘Value’)
Please note that the key and value can only be of string type – no other data type is valid!
As a more advanced feature, you may also set or query the position (or any of the above transform properties) of a particular NodePath with respect to another one. To do this, specify the relative NodePath as the first parameter:
myNodePath.setPos(otherNodePath, X, Y, Z)
myNodePath.getPos(otherNodePath)
Whoa! A break. We’ll continue next time from right here.