Pre-transforms and batches up meshes for efficient use as static geometry in a scene.
Modern graphics cards (GPUs) prefer to receive geometry in large batches. It is orders of magnitude faster to render 10 batches of 10,000 triangles than it is to render 10,000 batches of 10 triangles, even though both result in the same number of on-screen triangles.
Therefore it is important when you are rendering a lot of geometry to batch things up into as few rendering calls as possible. This class allows you to build a batched object from a series of entities in order to benefit from this behaviour. Batching has implications of it's own though:
  • Batched geometry cannot be subdivided; that means that the whole group will be displayed, or none of it will. This obivously has culling issues.
  • A single world transform must apply to the entire batch. Therefore once you have batched things, you can't move them around relative to each other. That's why this class is most useful when dealing with static geometry (hence the name). In addition, geometry is effectively duplicated, so if you add 3 entities based on the same mesh in different positions, they will use 3 times the geometry space than the movable version (which re-uses the same geometry). So you trade memory and flexibility of movement for pure speed when using this class.
  • A single material must apply for each batch. In fact this class allows you to use multiple materials, but you should be aware that internally this means that there is one batch per material. Therefore you won't gain as much benefit from the batching if you use many different materials; try to keep the number down.

In order to retain some sort of culling, this class will batch up meshes in localised regions. The size and shape of these blocks is controlled by the SceneManager which contructs this object, since it makes sense to batch things up in the most appropriate way given the existing partitioning of the scene.
The LOD settings of both the Mesh and the Materials used in constructing this static geometry will be respected. This means that if you use meshes/materials which have LOD, batches in the distance will have a lower polygon count or material detail to those in the foreground. Since each mesh might have different LOD distances, during build the furthest distance at each LOD level from all meshes in that region is used. This means all the LOD levels change at the same time, but at the furthest distance of any of them (so quality is not degraded). Be aware that using Mesh LOD in this class will further increase the memory required. Only generated LOD is supported for meshes.
There are 2 ways you can add geometry to this class; you can add Entity objects directly with predetermined positions, scales and orientations, or you can add an entire SceneNode and it's subtree, including all the objects attached to it. Once you've added everthing you need to, you have to call build() the fix the geometry in place.
This class is not a replacement for world geometry (see SceneManager.WorldGeometry). The single most efficient way to render large amounts of static geometry is to use a SceneManager which is specialised for dealing with that particular world structure. However, this class does provide you with a good 'halfway house' between generalised movable geometry (Entity) which works with all SceneManagers but isn't efficient when using very large numbers, and highly specialised world geometry which is extremely fast but not generic and typically requires custom world editors.
You should not construct instances of this class directly; instead, call SceneManager.CreateStaticGeometry, which gives the SceneManager the option of providing you with a specialised version of this class if it wishes, and also handles the memory management for you like other classes.
Exemple #1
0
 public Region(StaticGeometry parent, string name, SceneManager mgr, UInt32 regionID, Vector3 center)
 {
     this.parent         = parent;
     this.name           = name;
     this.sceneMgr       = mgr;
     this.regionID       = regionID;
     this.center         = center;
     queuedSubMeshes     = new List <QueuedSubMesh>();
     lodSquaredDistances = new List <float>();
     aabb              = new AxisAlignedBox();
     lodBucketList     = new List <LODBucket>();
     shadowRenderables = new ShadowRenderableList();
 }
 public Region(StaticGeometry parent, string name, SceneManager mgr, UInt32 regionID, Vector3 center)
 {
     this.parent = parent;
     this.name = name;
     this.sceneMgr = mgr;
     this.regionID = regionID;
     this.center = center;
     queuedSubMeshes = new List<QueuedSubMesh>();
     lodSquaredDistances = new List<float>();
     aabb = new AxisAlignedBox();
     lodBucketList = new List<LODBucket>();
     shadowRenderables = new ShadowRenderableList();
 }
Exemple #3
0
 public Region(StaticGeometry parent, string name, SceneManager mgr, UInt32 regionID, Vector3 center)
     : base(name)
 {
     MovableType            = "StaticGeometry";
     this.parent            = parent;
     this.sceneMgr          = mgr;
     this.regionID          = regionID;
     this.center            = center;
     this.queuedSubMeshes   = new List <QueuedSubMesh>();
     this.lodValues         = new LodValueList();
     this.aabb              = new AxisAlignedBox();
     this.lodBucketList     = new List <LODBucket>();
     this.shadowRenderables = new ShadowRenderableList();
 }
Exemple #4
0
		/// <summary>
		///     Remove &amp; destroy a StaticGeometry instance.
		/// </summary>
		public void DestroyStaticGeometry( StaticGeometry geom )
		{
			DestroyStaticGeometry( geom.Name );
		}
Exemple #5
0
	    /// <summary>
	    ///     Creates a StaticGeometry instance suitable for use with this
	    ///     SceneManager.
	    /// </summary>
	    /// <remarks>
	    ///     StaticGeometry is a way of batching up geometry into a more
	    ///     efficient form at the expense of being able to move it. Please
	    ///     read the StaticGeometry class documentation for full information.
	    /// </remarks>
	    /// <param name="name">The name to give the new object</param>
	    /// <param name="logLevel"></param>
	    /// <returns>The new StaticGeometry instance</returns>
	    public StaticGeometry CreateStaticGeometry( string name, int logLevel )
		{
			// Check not existing
			if ( this.staticGeometryList.ContainsKey( name ) )
			{
				throw new AxiomException( "StaticGeometry with name '" + name + "' already exists!" );
			}
			StaticGeometry geometry = new StaticGeometry( this, name, logLevel );
			this.staticGeometryList[ name ] = geometry;
			return geometry;
		}
        // Lock the node dictionary, and rebuild the static
        // geometry for objects of this kind
        protected void Rebuild(SceneManager mgr, Dictionary<long, WorldEntity> nodeDictionary)
        {
            log.DebugFormat("Entering StaticGeometryHelper.Rebuild for geometry '{0}'", name);
            long tickStart = TimeTool.CurrentTime;
            try {
                nodesAddedSinceLastRebuild = 0;
                nodesRemovedSinceLastRebuild = 0;
                force = false;
                Monitor.Enter(mgr);
                if (objectGeometry != null)
                    objectGeometry.Reset();
                else
                    objectGeometry = new StaticGeometry(mgr, name);
                // Dictionary mapping Material into a list of
                // ObjectNodes in which some submesh uses the material
                Dictionary<Material, MaterialAndNodeCounts> materialsUsedMap = new Dictionary<Material, MaterialAndNodeCounts>();
                lock(nodeDictionary) {
                    foreach (WorldEntity entity in nodeDictionary.Values) {
                        if (entity is ObjectNode) {
                            ObjectNode node = (ObjectNode)entity;
                            // For now, we only consider "props" that have an associated SceneNode
                            // and are direct descendants of the root scene node, and are of the right
                            // kind, i.e., don't have a perception radius if this static geometry is for
                            // little nodes, and vice versa.
            //                             log.DebugFormat("StaticGeometry.Rebuild: Examining node {0}, oid {1}, type {2}, sceneNode {3}, InStaticGeometry {4}, top-level {5}",
            //                                 node.Name, node.Oid, node.ObjectType, node.SceneNode, node.InStaticGeometry, node.SceneNode.Parent == mgr.RootSceneNode);
                            if (node.ObjectType == ObjectNodeType.Prop &&
                                (node.InStaticGeometry || (node.SceneNode != null && node.SceneNode.Parent == mgr.RootSceneNode)) &&
                                RightKind(node)) {
                                foreach (Material m in node.Entity.SubEntityMaterials) {
                                    MaterialAndNodeCounts nodesUsingMaterial;
                                    if (!materialsUsedMap.TryGetValue(m, out nodesUsingMaterial)) {
                                        nodesUsingMaterial = new MaterialAndNodeCounts();
                                        materialsUsedMap[m] = nodesUsingMaterial;
                                    }
                                    nodesUsingMaterial.materialUseCount++;
                                    int subMeshUseCount;
                                    Dictionary<ObjectNode, int> submeshUseCounts = nodesUsingMaterial.submeshUseCounts;
                                    if (!submeshUseCounts.TryGetValue(node, out subMeshUseCount))
                                        submeshUseCounts[node] = 1;
                                    else
                                        submeshUseCounts[node] = subMeshUseCount + 1;
                                }
                            }
                        }
                    }
                }

                // Now we have a count of uses of each material, and
                // for each node, the number of subentities that use the
                // material.  Now we need to calculate the number of
                // instance of sharings for each object node
                Dictionary<ObjectNode, bool> candidateNodes = new Dictionary<ObjectNode, bool>();
                foreach (MaterialAndNodeCounts counts in materialsUsedMap.Values) {
                    if (counts.materialUseCount > 1) {
                        foreach (KeyValuePair<ObjectNode, int> pair in counts.submeshUseCounts)
                            candidateNodes[pair.Key] = true;
                    }
                }
                Dictionary<ObjectNode, int> staticNodes = new Dictionary<ObjectNode, int>();
                foreach (KeyValuePair<ObjectNode, bool> pair in candidateNodes) {
                    ObjectNode candidate = pair.Key;
                    bool useIt = pair.Value;
                    if (useIt)
                        staticNodes[candidate] = 0;
                }
                if (staticNodes.Count == 0)
                    log.InfoFormat("StaticGeometryHelper.Rebuild: Didn't build static geometry {0} because object count was zero", name);
                else {
                    log.InfoFormat("StaticGeometryHelper.Rebuild: {0} ObjectNodes", staticNodes.Count);
                    foreach(ObjectNode staticNode in staticNodes.Keys) {
                        SceneNode sc = staticNode.SceneNode;
                        if (!staticNode.InStaticGeometry) {
                            sc.RemoveFromParent();
                            staticNode.InStaticGeometry = true;
                        }
                        log.DebugFormat("StaticGeometryHelper.Rebuild: Add node {0} with name {1} to static geometry",
                            staticNode.Oid, staticNode.Name);
                        objectGeometry.AddSceneNode(sc);
                    }
                }
                if (lastStaticNodes != null) {
                    foreach(ObjectNode node in lastStaticNodes.Keys) {
                        if (!staticNodes.ContainsKey(node)) {
                            // Only 1 instance of the mesh, so make sure that if in a former build it was in
                            // static geometry, that we add it back to the scene graph.
                            if (node.InStaticGeometry) {
                                SceneNode sn = node.SceneNode;
                                if (sn != null)
                                    mgr.RootSceneNode.AddChild(sn);
                                node.InStaticGeometry = false;
                            }
                        }
                    }
                }
                if (staticNodes.Count > 0)
                    objectGeometry.Build();
                lastStaticNodes = staticNodes;
                timeOfLastRebuild = TimeTool.CurrentTime;
            }
            finally {
                Monitor.Exit(mgr);
            }
            log.InfoFormat("StaticGeometryHelper.Rebuild: Rebuild of geometry '{0}' took {1} ms",
                name, TimeTool.CurrentTime - tickStart);
        }
 public StaticGeometryHelper(WorldManager worldMgr, StaticGeometryKind kind, int rebuildTimeThreshold, 
                             int nodesAddedThreshold, int nodesRemovedThreshold, 
                             int nodesAddedInLastSecondThreshold)
 {
     this.worldMgr = worldMgr;
     this.kind = kind;
     this.name = (kind == StaticGeometryKind.BigOrLittleNode ? "StaticGeom" :
         (kind == StaticGeometryKind.BigNode ? "BigNodes" : "LittleNodes"));
     this.objectGeometry = null;
     this.rebuildTimeThreshold = rebuildTimeThreshold;
     this.nodesAddedThreshold = nodesAddedThreshold;
     this.nodesRemovedThreshold = nodesRemovedThreshold;
     this.nodesAddedInLastSecondThreshold = nodesAddedInLastSecondThreshold;
     this.nodesAddedSinceLastRebuild = 0;
     this.nodesRemovedSinceLastRebuild = 0;
     this.lastNodesAdded = 0;
     this.timeOfLastRebuild = 0;
     this.lastRebuildCheckTime = 0;
     this.enabled = false;
     this.force = false;
 }
Exemple #8
0
			public Region( StaticGeometry parent, string name, SceneManager mgr, UInt32 regionID, Vector3 center )
				: base( name )
			{
				MovableType = "StaticGeometry";
				this.parent = parent;
				this.sceneMgr = mgr;
				this.regionID = regionID;
				this.center = center;
				this.queuedSubMeshes = new List<QueuedSubMesh>();
				this.lodValues = new LodValueList();
				this.aabb = new AxisAlignedBox();
				this.lodBucketList = new List<LODBucket>();
				this.shadowRenderables = new ShadowRenderableList();
			}
 /// <summary>
 ///     Creates a StaticGeometry instance suitable for use with this
 ///     SceneManager.
 /// </summary>
 /// <remarks>
 ///     StaticGeometry is a way of batching up geometry into a more 
 ///     efficient form at the expense of being able to move it. Please 
 ///     read the StaticGeometry class documentation for full information.
 /// </remarks>
 ///<param name="name">The name to give the new object</param>
 ///<returns>The new StaticGeometry instance</returns>
 public StaticGeometry CreateStaticGeometry(string name)
 {
     // Check not existing
     if (staticGeometryList.ContainsKey(name))
         throw new Exception("StaticGeometry with name '" + name + "' already exists!  In " +
             "SceneManager.CreateStaticGeometry");
     StaticGeometry geometry = new StaticGeometry(this, name);
     staticGeometryList[name] = geometry;
     return geometry;
 }