| 1 | /* |
|---|
| 2 | ================================================================================= |
|---|
| 3 | This file is part of Cafu, the open-source game engine and graphics engine |
|---|
| 4 | for multiplayer, cross-platform, real-time 3D action. |
|---|
| 5 | Copyright (C) 2002-2012 Carsten Fuchs Software. |
|---|
| 6 | |
|---|
| 7 | Cafu is free software: you can redistribute it and/or modify it under the terms |
|---|
| 8 | of the GNU General Public License as published by the Free Software Foundation, |
|---|
| 9 | either version 3 of the License, or (at your option) any later version. |
|---|
| 10 | |
|---|
| 11 | Cafu is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; |
|---|
| 12 | without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR |
|---|
| 13 | PURPOSE. See the GNU General Public License for more details. |
|---|
| 14 | |
|---|
| 15 | You should have received a copy of the GNU General Public License |
|---|
| 16 | along with Cafu. If not, see <http://www.gnu.org/licenses/>. |
|---|
| 17 | |
|---|
| 18 | For support and more information about Cafu, visit us at <http://www.cafu.de>. |
|---|
| 19 | ================================================================================= |
|---|
| 20 | */ |
|---|
| 21 | |
|---|
| 22 | #ifndef CAFU_ORTHO_BSP_TREE_HPP_INCLUDED |
|---|
| 23 | #define CAFU_ORTHO_BSP_TREE_HPP_INCLUDED |
|---|
| 24 | |
|---|
| 25 | #include "Math3D/BoundingBox.hpp" |
|---|
| 26 | |
|---|
| 27 | |
|---|
| 28 | class MapElementT; |
|---|
| 29 | |
|---|
| 30 | |
|---|
| 31 | /// This class represents an orthogonal BSP tree (axis-aligned split planes) that spatially organizes MapElementT instances. |
|---|
| 32 | /// |
|---|
| 33 | /// A map element is generally linked in each leaf that it intersects, or instead whenever possible in the node closest to the |
|---|
| 34 | /// root where the element intersects *all* children. |
|---|
| 35 | /// Note that the latter is an optional feature that yields a tree that is logically equivalent to the pure "store all contents |
|---|
| 36 | /// in leaves" strategy (where nodes generally remain empty and store no contents at all), but in comparison saves some storage. |
|---|
| 37 | /// Also note the subtle difference to storing some contents in nodes (the elements that intersect a split plane) and some (the |
|---|
| 38 | /// rest) in leaves, like ancient version of our CaBSP: Keeping some contents in nodes like this would make it impossible to |
|---|
| 39 | /// determine the contents (the set of all map elements) that are inside a given node, because the set would be grossly oversized. |
|---|
| 40 | /// Our "extended leaves-only" approach saves both storage and is able to produce for each node the exact set of map elements |
|---|
| 41 | /// (required e.g. for view frustum tests). |
|---|
| 42 | /// |
|---|
| 43 | /// Another feature of this implementation is that the bounding-box of a node is usually *not* the tight bounding-box over its |
|---|
| 44 | /// contents, but typically larger: |
|---|
| 45 | /// The bounding-box of the root node is the maximum bounds of the world, the bounding-box of its children is determined by |
|---|
| 46 | /// subdividing it along the split plane, etc. As such, the bounding-boxes in the tree only depend on the split planes, but not |
|---|
| 47 | /// directly on the contents of the nodes (and thus the BB member of the NodeT class is const). |
|---|
| 48 | /// |
|---|
| 49 | /// Finally, note how these larger bounding-boxes affect the tree structure and size. There are two important differences: |
|---|
| 50 | /// 1. Where we would not have node planes at the outmost brush faces with a "tight" starting box, our approach definitively |
|---|
| 51 | /// comes up with these outmost walls as split planes sooner or later. This enlarges the tree, but these extra nodes are |
|---|
| 52 | /// fortunately not completely useless: When the user extends the map spatially, the extra nodes come in just handy. |
|---|
| 53 | /// 2. The center of our larger boxes is often different from the center of the tight bounding-boxes, as the tight boxes are |
|---|
| 54 | /// usually not symmetric to the origin. This causes different split planes to be chosen when the tree is built. |
|---|
| 55 | /// As with issue 1. though, this difference is not a principal or serious problem. |
|---|
| 56 | class OrthoBspTreeT |
|---|
| 57 | { |
|---|
| 58 | public: |
|---|
| 59 | |
|---|
| 60 | class NodeT |
|---|
| 61 | { |
|---|
| 62 | public: |
|---|
| 63 | |
|---|
| 64 | /// As the nodes of the tree are not subdivided by arbitrary planes, but only by planes that are parallel |
|---|
| 65 | /// to the major axes, we do not store a full Plane3T<> member with the nodes, but only a "compacted" representation: |
|---|
| 66 | /// The type expresses which axes the plane is parallel to, the distance is the offset from the origin. |
|---|
| 67 | enum PlaneTypeE |
|---|
| 68 | { |
|---|
| 69 | NONE=-1, ///< No plane at all. Used for nodes that are actually leaves. |
|---|
| 70 | ALONG_X, ///< A plane with normal vector (1, 0, 0), parallel to the y- and z-axis. |
|---|
| 71 | ALONG_Y, ///< A plane with normal vector (0, 1, 0), parallel to the x- and z-axis. |
|---|
| 72 | ALONG_Z ///< A plane with normal vector (0, 0, 1), parallel to the x- and y-axis. |
|---|
| 73 | }; |
|---|
| 74 | |
|---|
| 75 | |
|---|
| 76 | /// The constructor. |
|---|
| 77 | /// @param BB The bounding box of this node (one "half" of the box of the parent). |
|---|
| 78 | NodeT(const BoundingBox3fT& BB); |
|---|
| 79 | |
|---|
| 80 | /// The destructor. |
|---|
| 81 | ~NodeT(); |
|---|
| 82 | |
|---|
| 83 | PlaneTypeE GetPlaneType() const { return m_PlaneType; } |
|---|
| 84 | float GetPlaneDist() const { return m_PlaneDist; } |
|---|
| 85 | const NodeT* GetChild(unsigned int ChildNr) const { return m_Children[ChildNr]; } |
|---|
| 86 | const ArrayT<MapElementT*>& GetElems() const { return m_Elems; } |
|---|
| 87 | const BoundingBox3fT& GetBB() const { return m_BB; } |
|---|
| 88 | |
|---|
| 89 | /// Returns the number of nodes in the (sub-)tree at and below this node. |
|---|
| 90 | unsigned long GetNumNodes() const; |
|---|
| 91 | |
|---|
| 92 | /// Determines an axis-aligned split plane for further BSP partitioning of the contents of this node. |
|---|
| 93 | /// For choosing the split plane, the method considers the bounding-box planes of all objects (polygons, brushes, terrains) |
|---|
| 94 | /// of this node and all its ancestors, provided that they are wholly or partially in BB. |
|---|
| 95 | /// When a split plane was found, the m_PlaneType and m_PlaneDist members are appropriately set and true is returned, |
|---|
| 96 | /// otherwise they are initialized with NONE and 0, respectively, and the return value is false. |
|---|
| 97 | /// @returns whether a split plane has successfully been determined. |
|---|
| 98 | bool DetermineSplitPlane(); |
|---|
| 99 | |
|---|
| 100 | /// Determines whether the given BB intersects (is partly inside) each child of this node. |
|---|
| 101 | /// @param BB The bounding box that is tested for intersection. |
|---|
| 102 | bool IntersectsAllChildren(const BoundingBox3fT& BB) const; |
|---|
| 103 | |
|---|
| 104 | /// Finds any map elements in the (sub-)tree whose spatial position disagrees with their position in the tree. |
|---|
| 105 | void FindMismatches(ArrayT<MapElementT*>& Mismatches) const; |
|---|
| 106 | |
|---|
| 107 | /// Recursively inserts the given element into the (sub-)tree at and below this node. |
|---|
| 108 | void Insert(MapElementT* Elem); |
|---|
| 109 | |
|---|
| 110 | /// Removes the given element from the (sub-)tree at and below this node (the structure of the tree remains unchanged). |
|---|
| 111 | void Remove(MapElementT* Elem); |
|---|
| 112 | |
|---|
| 113 | |
|---|
| 114 | private: |
|---|
| 115 | |
|---|
| 116 | friend class OrthoBspTreeT; |
|---|
| 117 | |
|---|
| 118 | NodeT(const NodeT&); ///< Use of the Copy Constructor is not allowed. |
|---|
| 119 | void operator = (const NodeT&); ///< Use of the Assignment Operator is not allowed. |
|---|
| 120 | |
|---|
| 121 | PlaneTypeE m_PlaneType; ///< The type of the plane that subdivides this node (no plane at all, normal vector along the x-, y- or z-axis). |
|---|
| 122 | float m_PlaneDist; ///< The distance of the plane to the origin. Corresponds to the Plane3fT::Dist member in a full plane. |
|---|
| 123 | NodeT* m_Parent; ///< The parent of this node, NULL if this is the root node. |
|---|
| 124 | NodeT* m_Children[2]; ///< The children of this node at each side of the plane (NULL if there is no plane / the node is a leaf). |
|---|
| 125 | ArrayT<MapElementT*> m_Elems; ///< The list of map elements in this node. |
|---|
| 126 | const BoundingBox3fT m_BB; ///< The bounding-box of this node. |
|---|
| 127 | }; |
|---|
| 128 | |
|---|
| 129 | |
|---|
| 130 | /// The constructor. |
|---|
| 131 | OrthoBspTreeT(const ArrayT<MapElementT*>& Elems, const BoundingBox3fT& BB); |
|---|
| 132 | |
|---|
| 133 | /// The destructor. |
|---|
| 134 | ~OrthoBspTreeT(); |
|---|
| 135 | |
|---|
| 136 | /// Returns the BSP trees root node. |
|---|
| 137 | const NodeT* GetRootNode() const { return m_RootNode; } |
|---|
| 138 | |
|---|
| 139 | /// Inserts the given element into the tree (the structure of the tree remains unchanged). |
|---|
| 140 | void Insert(MapElementT* Elem) { m_RootNode->Insert(Elem); } |
|---|
| 141 | |
|---|
| 142 | /// Removes the given element from the tree (the structure of the tree remains unchanged). |
|---|
| 143 | void Remove(MapElementT* Elem) { m_RootNode->Remove(Elem); } |
|---|
| 144 | |
|---|
| 145 | /// Removes and re-inserts map elements from and into the tree whose bounding-box changed so that it disagrees with the tree structure. |
|---|
| 146 | /// This method is called once per "document frame" in ChildFrameT::OnIdle(). |
|---|
| 147 | /// @returns the number of map elements that have been updated. |
|---|
| 148 | unsigned long Update(); |
|---|
| 149 | |
|---|
| 150 | |
|---|
| 151 | private: |
|---|
| 152 | |
|---|
| 153 | OrthoBspTreeT(const OrthoBspTreeT&); ///< Use of the Copy Constructor is not allowed. |
|---|
| 154 | void operator = (const OrthoBspTreeT&); ///< Use of the Assignment Operator is not allowed. |
|---|
| 155 | |
|---|
| 156 | void BuildTree(NodeT* node); |
|---|
| 157 | |
|---|
| 158 | NodeT* m_RootNode; |
|---|
| 159 | }; |
|---|
| 160 | |
|---|
| 161 | #endif |
|---|