• Advertisement
Sign in to follow this  
  • entries
    155
  • comments
    100
  • views
    94126

Nuts I say

Sign in to follow this  

86 views

I'd like to comment about comments first. Thanks for the interest, keeps me much more motivated about writing this journal. :)

For the AP who wrote about camera implementation details: yes, I know how to calculate view transforms and lookat rotations, I was just trying to drive across a point about inheritance. Granted, it might not been the best example ever. Making the transform a callable is not a good option IMO, instead I'm planning to calculate lookat rotation each frame using the logic property (which is a callable, called once per world "step").

As for the AP who suggested creating an OBJ -> Collada converter instead of an OBJ loader: this might be a good future project, but an OBJ loader is simpler to write, so we'll do that for now (there are actually two or three people helping me with Spineless at the moment, depending on how you count).

So, you'd like to hear about nuts and bolts. I will first write about how I started with the scene graph and how I arrived at the current solution. I will detail the current implementation later, so sorry for the teaser post. :)

In the last post I mentioned how nodes are just containers for arbitrary attributes and don't know anything about what specific attributes are for, or how they are used. In fact, the only thing a node really cares about is its parent and children. The methods of Node are only concerned about searching and manipulating nodes and attributes in the node tree. There are methods like getRoot() for finding the root of the tree and cloneTree() for copying an entire subtree of nodes while recycling the attributes (this is useful eg. for instancing the same model in different parts of the scene graph; you can't simply use the same node since this is a tree and nodes can only have one parent). There are no methods like rotate() and setTexture().

As I said, all the intelligence is in the World class and the various scene workers, such as Physics and Renderer. When you call World.update(), it will in turn call the update functions of workers, which will process all the nodes. The Logic worker will see if a node has the "logic" attribute and call it with some parameters, while the Audio worker will look for "audioListener" and "audioSource" and update their position and orientation (for surround sound). View.render() will call the render() method of the Renderer, which will go through the nodes, apply attributes relevant for rendering (such as "projection" and "lighting") and call the "render" attribute of nodes which have one. Note that the attributes don't have to be objects of a specific class, as long as they implement the proper interface.

A trivial implementation would just walk through the whole tree starting from the root while looking for interesting attributes, and actually the initial implementation did so. Of course, transparent nodes can't be handled quite this way since they need to be sorted, so they were stored instead of rendered. After rendering opaque nodes and sorting transparents, they were rendered by walking the path of nodes from the root of the tree to the transparent node and applying attributes on the way. This worked, but was obviously too slow, since all the workers were processing all the nodes each step, while forgetting all about the attributes they processed last time.

Next try was adding addNode() and removeNode() methods to the World class, the point being that only "content nodes" would be added, such as nodes with "audioListener" and "render" attributes. Nodes with only transforms or attributes not directly affecting rendering would not be added (like "projection" and "fog"). Fairly quickly I realized this attempt was doomed for various reasons. The API was unnecessarily complex; the developer is already adding and removing nodes by connecting them together in a tree, they shouldn't have to notify the World about this themselves. Requiring the developer to know which attributes are content attributes and which are not is kinda dumb. Some optimizations were still not possible; while workers were only processing "content nodes", all of them were processing all the content nodes even if there were no attributes interesting to them. Also, not walking the tree actually made rendering less optimal; full state had to be set for each node, and the inherent grouping by state in the tree was disregarded.

Current implementation works by connecting the nodes together in a tree and setting the root node of the World. Still, there are various caching schemes and optimizations present, and there's a lot of optimization potential left there, to be taken advantage of later. Workers are storing nodes in the most optimal data structure for them, and it's easy to change the implementation and data structure of a worker without changing the interface. More on that later.
Sign in to follow this  


0 Comments


Recommended Comments

There are no comments to display.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Advertisement