[1. Overview] Ogre supports the bsp format of Quake3. The related code is in the Plugin_BSPSceneManager project. The main classes are as follows:Class BspSceneNode: BspSceneNode is a derived class of SceneNode, which is specifically provided to BSPSceneManager. It mainly provides visibility judgment for BSP trees. This class is not a BSP tree node. The node in the BSP tree uses BspNode. BspSceneNode is placed in the leaf node of the BSP tree. Because SceneNode uses the package box method, it is inseparable. Therefore, a BspSceneNode may be placed in the leaf nodes of multiple Bsp trees. From the definition of the class, BspSceneNode does not store any additional data. The number of virtual functions to be rewritten is mainly used to notify BspSceneMapager that BspSceneNode: _ update () will call BspSceneManager: _ policyobjectmoved (), and detach objcect will call destroy (). Class BspSceneManager: BspSceneManager is similar to OctreeSceneManager. First, a BspLevel pointer is saved, and then an aggregate tree () function is used to traverse the tree structure. Quake uses the BSP leaf tree, so it has an additional processVisibleLeaf () function. Another obvious difference is that there is a renderStaticGeometry () function, "Renders the static level geometry tagged in each tree ". This function renders all data in "mMatFaceGroupMap. One advantage of BSP is that non-transparent surfaces can be rendered in the order of front-back, while transparent back-front rendering. How does OGRE save this feature to MaterialFaceGroupMap? Class BspLevel: This is a core class. It stores all BSP data. The key data includes: <! -- [If! SupportLists] --> 1. <! -- [Endif] --> "BspNode * mRootNode;"-the root node of the BSP tree <! -- [If! SupportLists] --> 2. <! -- [Endif] --> "VertexData * mVertexData;"-all vertices of the entire level; <! -- [If! SupportLists] --> 3. <! -- [Endif] --> "StaticFaceGroup * mFaceGroups;"-faces <! -- [If! SupportLists] --> 4. <! -- [Endif] --> "BspNode: Brush * mBrushes;"-the Brush used for collision detection is another essence of QuakeBSP apart from rendering! The name of the Brush is a bit strange. It is actually a convex volume, which can reduce the workload of the CD. <! -- [If! SupportLists] --> 5. <! -- [Endif] --> "VisData mVisData;"-PVS data is the essence of another Quake! When designing Quake, Carmark also used software rendering. hiden surface removal and over draw reduction were another headache. BSP's idea should be viewed from the Internet, but PVS should be his idea. PVS greatly reduces over draw. (See Michael Abrash's Graphics Programming Black Book.) <! -- [If! SupportLists] --> 6. <! -- [Endif] --> "PatchMap mPatches;" -- Quake3 supports the besell surface. Key functions: <! -- [If! SupportLists] --> 1. <! -- [Endif] --> bool isLeafVisible (const BspNode * from, const BspNode * to) const; uses PVS to detect visibility. <! -- [If! SupportLists] --> 2. <! -- [Endif] --> void _ policyobjectmoved (const MovableObject * mov, const Vector3 & pos ); Void _ policyobjectdetached (const MovableObject * mov ); À void tagNodesWithMovable (BspNode * node, const MovableObject * mov, const Vector3 & pos ); Mount MovableObject (Note: It is not SceneNode) to leaves of BSP. Class BspNode: This is another important class in Bsp. Node and Leaf both use this class. Important data: <! -- [If! SupportLists] --> 1. <! -- [Endif] --> Plane mSplitPlane; BspNode * mFront; BspNode * mBack; Split the plane and the front and back nodes; <! -- [If! SupportLists] --> 2. <! -- [Endif] --> int mVisCluster; Each cluster occupies a bit of pvs to reduce the memory occupied by pvs. <! -- [If! SupportLists] --> 3. <! -- [Endif] --> int mNumFaceGroups; int mFaceGroupStart; It is used to find out which face groups belong to my leaf in BspLevel, so as to optimize storage; <! -- [If! SupportLists] --> 4. <! -- [Endif] --> IntersectingObjectSet mMovables; The movable object that intersection the current node. <! -- [If! SupportLists] --> 5. <! -- [Endif] --> NodeBrushList mSolidBrushes; This node contains a brush. In addition, the remaining OgreQuake3Level. h. OgreQuake3Shader. h. OgreQuake3ShaderManager. h. OgreQuake3Type. h is mainly used to read the bsp and shader information in Quake3 format, and convert it into the local bsp definition and Material of Ogre. Now the source code of quake3 has been published (thanks very much to id software and carmark). You can refer to the source code of quake3. [2. Loading of Quake3 bsp] Take Demo_BSP as an example. First, you need to modify "quake3settings. cfg". Two parameters: "Pak0Location" is the pk package path (a zip file), and "Map" is the Map to be loaded. OGRE uses BspLevel to store Bsp scenario information. This class is irrelevant to the file format. Therefore, another class is required to read the bsp file of Quake3. The Quake3Level reading disk is mainly completed by two functions: 1. "void Quake3Level: loadHeaderFromStream ()". The calling process is as follows: BspApplication: loadResources () À ResourceGroupManager: loadResourceGroup () [] À BspSceneManager: estimateWorldGeometry () À BspLevel: calculateLoadingStages () À Quake3Level: loadHeaderFromStream () The file format of Quake3 BSP is simple and clear. The first is a file header, followed by several data blocks. The file header stores several lump, including the start position and size of the data block. With lump, you can directly seek to the data block. After the file header is loaded, this function calls the Quake3Level: initialiseCounts () function to calculate the number of objects contained in each lump, such as face, vertex, and bursh. 2. The second function "void Quake3Level: loadFromStream ()". The call process is as follows: ResourceGroupManager: loadResourceGroup () [] À BspSceneManager: setWorldGeometry () À BspResourceManager: load () À ResourceManager: load () À Resource: load () À BspLevel: loadImpl () À Quake3Level: loadFromStream () Here, the OGRE style continues. BspSceneManager uses BspResourceManager to load scenarios. BspLevel is implemented as a Resource. BspResourceManager uses the standard ResourceManager-Resource to locate BspLevel, then, call the load function. This function first constructs a "MemoryDataStream" object. In the MemoryDataStream constructor, it reads all the file data into its buffer (Quake does the same), and then calls "void Quake3Level :: initialisePointers (void) "function to obtain the pointer of all objects indexed by lump. After Quake3Level reads the file and defines the pointer of all data, it calls "BspLevel: loadQuake3Level ()" In void BspLevel: loadQuake3Level () the function is used to copy data in Quake3level to your own data object. The following operations are performed: <! -- [If! SupportLists] --> 1. <! -- [Endif] --> "BspLevel: loadEntities ()", which stores a string to describe some game information, the Ogre function only reads the Player start information (location and angle ). <! -- [If! SupportLists] --> 2. <! -- [Endif] --> "Quake3Level: extractLightmaps ()". Each light map of Quake3 BSP is 128x128 large. This function calls the data in the Light map lump one by one "TextureManager: loadImage () creates a Texture object (class D3D9Texture for D3D9 RenderSystem ). <! -- [If! SupportLists] --> 3. <! -- [Endif] --> Create VertexData: [Create vertex declaration] the vertex formats used by the OGRE BspLevel are Position3, Normal3, Diffuse, uv0, and uv1; [Build initial patches] Call BspLevel: initQuake3Patches (). This function traverses all the faces in Quake3Level. For each face type of "BSP_FACETYPE_PATCH", create a PatchSurface object, call the PatchSurface: defineSurface () function, and save it to BspLevel:: In the mPatches array. This function also calculates BspLevel: mPatchVertexCount and BspLevel: mPatchIndexCount; [Hardware vertex buffer] Call HardwareBufferManager to create a HardwareVertexBuffer object. Use the "BspLevel: quakeVertexToBspVertex ()" function to convert the q3 bsp vertex format to the Ogre BSPLevel vertex format. Then bind to BspLevel: mVertexData; <! -- [If! SupportLists] --> 4. <! -- [Endif] --> Create Faces: Create a BspLevel: mLeafFaceGroups array; create a BspLevel: mFaceGroups array. The data in this array is filled in the next step; Create indexbuffer and set Quake3Level:: Copy mElements; <! -- [If! SupportLists] --> 5. <! -- [Endif] --> Create materials for shaders: for each bsp_face_t of Quake3Level: mFaces, locate the Quake3Shader it indexed and Create Material, if no Quake3Shader is found, use the shader name to find the texture file; In this cycle, the "Copy face data" operation is also performed to fill in the data in BspLevel: mFaceGroups; <! -- [If! SupportLists] --> 6. <! -- [Endif] --> Nodes: Create the BspLevel: mRootNode array and copy the data. <! -- [If! SupportLists] --> 7. <! -- [Endif] --> Brushes: Copy Data to BspLevel: mBrushes; <! -- [If! SupportLists] --> 8. <! -- [Endif] --> Leaves: Set the data of each leaf node, including the Package Box, mFaceGroupStart, mNumFaceGroups, mVisCluster, and mSolidBrushes. See the BspNode class; <! -- [If! SupportLists] --> 9. <! -- [Endif] --> Vis data: Copy data to BspLevel: mVisData. This is basically the load process of Quake3 BSP. [3. Bsp tree scene rendering] Demo_BSP is used as an example for analysis. The core rendering operation process starts with SceneManager: _ renderScene () (see "Ogre learning notes (3): Mesh rendering process"). The following is SceneManager :: _ updateSceneGraph (), SceneManager: prepareRenderQueue (), BspSceneManager does not override these functions. However, it is worth noting that SceneManager: _ updateSceneGraph () calls BspSceneNode: _ update () and callback. If necessary, BspSceneManager: _ policyobjectmoved () is called () -- "BspLevel: _ policyobjectmoved (), attach the MovableObject in SceneNode to the correct leaf node. Next, BspSceneManager: findVisibleObjects (), which is a function rewritten from SceneManager. Logically, this function calls BspSceneManager: Vertex Tree (). In this function, we first find the leaf node where camera is located (through the BspLevel: findleaf () function), and then traverse each leaf node in BspLevel, first, PVS is used to detect visibility (through the BspLevel: isLeafVisible () function). If it is visible, camera--bounding box is used for detection. If it is still visible, BspSceneManager is called for this leaf node :: processVisibleLeaf () function. This function mainly performs two operations. One is to add the World Geometry to the rendering data table (mFaceGroupSet and mMatFaceGroupMap ), the other is to add the MoveableObject that intersection the leaf node to the rendering Queue (mMovablesForRendering ). One of the more questionable things is that the histogram tree traverses all leaf nodes cyclically instead of recursively traversing according to the BSP tree, which greatly weakens the advantages of the early sorting of BSP. Then BspSceneManager overrides another important function _ renderVisibleObjects (). This function contains two operations: renderStaticGeometry () and SceneManager: _ renderVisibleObjects () that calls the parent class (). The former traverses mMatFaceGroupMap cyclically, and then calls RenderSystem: _ rendr (). The latter has analyzed it in detail in "Ogre learning notes (3): Mesh rendering process. |