Issues concerning Q3 map data and the jME scenegraph

While I was trying to improve the q3 map rendering, I found that the q3 map data are not very well suited for the jME scenegraph. I’ve written a small document about it, its a bit lengthy to post here.

http://cli-clan.de/~batman/q3problems.pdf



I would appreciate if those of you either interested in indoor maps or with a good knowledge of jME have a look at it and give me some aid.

Will look into this tonight and pitch my 2 cents. :slight_smile:

Batman, I can’t seem to view the pdf. It appears to load, but when finished there isn’t any text or anything else for that matter. Tried to view it in both IE and firefox.



EDIT: Updating Acrobat Reader did the trick.

Ok, took a look at your paper.



What would be involved in creating a BspNode and a BspGeometry. These both extend Spatial and are distinct from Node and Geometry. BspGeometry could have multiple parents and their own system of rendering. Basically, using Spatial as your starting point build a distinct and new graph system.



However, the difficult would lie in placing objects in your BSP map. Of Course you could attachChild(Spatial) to the nodes, but your objects would not be organized as a scene graph anymore. But is this a bad thing? I have heard it referenced before that BSP and Scenegraphs don’t mix. Maybe it’s time to bite the bullet and simply say:


  1. Using a BspTree (BspNode, BspGeometry) is distinct from the scene graph.
  2. You can add trimeshes, actually whole trees to the bsp graph, but they would have to be manually seperated into multiple trees for each "room" or what ever, because the PVS would be handling culling groups.



    This is the initial thoughts off the top of my head after reading your paper. So, take it with a grain of salt.

Here is a first draft proposal to solve the problems that I have found.



Suppose we have a toplevel Node class for the map, which is abstract:


+


+
| <<abstract class>>                                                           |
| MapNode: Node                                                                |
+
+
| -faces: TriMesh[]                                                            |
| -root: BSPNode                                                               |
| -regions: Region[]                                                           |
+
+
| +MapNode(String): <<create>>                                                 |
| +draw(r: Renderer): void                                                     |
| +updateWorldData(time: float): void                                          |
| +updateWorldBound(): void                                                    |
| +findCollisions(scene: Spatial, results: CollisionResults): void             |
| +setTexturedFaces(faces: TriMesh[]): void                                    |
| +setTree(rootNode: BSPNode): void                                            |
| - abstract findVisibleRegions(cam: Camera, currentRegion: Region): Regions[] |
+
+


As you can see, it maintains an independent list of (textured) TriMeshes plus a BSP root node. It also has an abstract method findVisibleRegions, which is called with a "region" (this will be the one the cam is currently in) and returns an array of regions visible from there. The idea is that if you want to display map information based on Q3, you write a Q3MapNode and implement that method using the PVS, for other map input data it may be something different like a GETICMapNode implementing its own method based on portals. It could even one day be the JMEMapNode using the jME map format. This way the different visibility algorithms (pure PVS, true portals etc) can be abstracted away into the concrete implementations and we do not lose the possibility for an own format. Additionally, this MapNode subclass may add the information required for collision detection like brushes for Q3.
You can also see that this node always maintains a BSP tree, which is used to find the region the camera is currently in. If your map data does not contain a BSP tree, extend the BSPNode class to provide your own findCameraRegion method.
The array of regions attribute is created by walking the tree when it is set.

The nonabstract implementation of the MapNode that is used in an application, like Q3MapNode, would be fed by a specific loader for it's type of data. The map data of the various systems are so different that I doubt there can be one common format.

Here is the BSPNode class:


+


+
| <<class>>                                |
| BSPNode                                  |
+
+
| -splittingPlane: Plane                   |
| -frontChild: BSPNode                     |
| -backChild: BSPNode                      |
| -attachedRegion: Region                  |
+
+
| +setFrontChild(BSPNode): void            |
| +setBackChild(BSPNode): void             |
| +setAttachedRegion(region: Region): void |
| +findCameraRegion(cam: Camera): Region   |
+
+


That is pretty straightforward. It could be directly fed from Q3 bsp data or GETIC data or even an own BSP tree node compiler during the map loading process.

Here's the region base class:


+


+
| <<class>>                            |
| Region                               |
+
+
| -boundingVolume: BoundingVolume      |
| -faceIndexes: int[]                  |
+
+
| +getBoundingVolume(): BoundingVolume |
| +getFaces(): int[]                   |
+
+


As you can see, a region maintains a list of face indices plus a bounding volume for quick culling against the view frustum. Note that there may be subclasses of Region which eg. maintain portals between them, so that for a complete map system XYZ you need three different specialized classes: the XYZMapNode, the XYZRegion and the XYZMapLoader (replace XYZ with the map format of your choice).

The main point is the draw method of MapNode. Here it is in pseudocode:


+draw(r: Renderer): void
   initialize list of trimeshes to be drawn
   find the region where the camera currently is in
   find the list of regions visible from this one by calling findVisibleRegions
   for each region
      if its bounding box is visible in the view frustum
         add it's trimeshes to the list of trimeshes to be drawn (in a sorted order?!?)
      endif
   endfor
   draw all selected trimeshes
end


This is just a first rough idea.

meh, couldn’t get to responding all that happened this weekend. Will try again tonight.