Brush.cpp 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. /* Copyright (c) 2002-2012 Croteam Ltd.
  2. This program is free software; you can redistribute it and/or modify
  3. it under the terms of version 2 of the GNU General Public License as published by
  4. the Free Software Foundation
  5. This program is distributed in the hope that it will be useful,
  6. but WITHOUT ANY WARRANTY; without even the implied warranty of
  7. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  8. GNU General Public License for more details.
  9. You should have received a copy of the GNU General Public License along
  10. with this program; if not, write to the Free Software Foundation, Inc.,
  11. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
  12. #include "stdh.h"
  13. #include <Engine/Brushes/Brush.h>
  14. #include <Engine/World/World.h>
  15. #include <Engine/World/WorldEditingProfile.h>
  16. #include <Engine/Math/Object3D.h>
  17. #include <Engine/Base/ListIterator.inl>
  18. #include <Engine/Math/Projection_DOUBLE.h>
  19. #include <Engine/Templates/StaticArray.cpp>
  20. #include <Engine/Templates/DynamicArray.cpp>
  21. #include <Engine/Math/Float.h>
  22. #include <Engine/Entities/Entity.h>
  23. // constructor
  24. CBrush3D::CBrush3D(void)
  25. {
  26. br_penEntity = NULL;
  27. br_pfsFieldSettings = NULL;
  28. br_ulFlags = 0;
  29. }
  30. // destructor
  31. CBrush3D::~CBrush3D(void)
  32. {
  33. }
  34. /* Delete a brush mip with given factor. */
  35. void CBrush3D::DeleteBrushMip(CBrushMip *pbmToDelete)
  36. {
  37. ASSERT(pbmToDelete!=NULL);
  38. ASSERT(pbmToDelete->bm_pbrBrush == this);
  39. // if there is only one brush mip
  40. if (br_lhBrushMips.Count()<=1) {
  41. // do nothing;
  42. return;
  43. }
  44. // remove it from list
  45. pbmToDelete->bm_lnInBrush.Remove();
  46. // destroy it
  47. delete pbmToDelete;
  48. }
  49. /* Create a new brush mip. */
  50. CBrushMip *CBrush3D::NewBrushMipAfter(CBrushMip *pbmOld, BOOL bCopy)
  51. {
  52. ASSERT(pbmOld!=NULL);
  53. ASSERT(pbmOld->bm_pbrBrush == this);
  54. // create one brush mip
  55. CBrushMip *pbmNew = new CBrushMip;
  56. pbmNew->bm_pbrBrush = this;
  57. // add it to the brush
  58. pbmOld->bm_lnInBrush.AddAfter(pbmNew->bm_lnInBrush);
  59. // copy the original to it
  60. if (bCopy) {
  61. pbmNew->Copy(*pbmOld, 1.0f, FALSE);
  62. }
  63. // respread the mips after old one
  64. pbmOld->SpreadFurtherMips();
  65. return pbmNew;
  66. }
  67. CBrushMip *CBrush3D::NewBrushMipBefore(CBrushMip *pbmOld, BOOL bCopy)
  68. {
  69. ASSERT(pbmOld!=NULL);
  70. ASSERT(pbmOld->bm_pbrBrush == this);
  71. // create one brush mip
  72. CBrushMip *pbmNew = new CBrushMip;
  73. pbmNew->bm_pbrBrush = this;
  74. // add it to the brush
  75. pbmOld->bm_lnInBrush.AddBefore(pbmNew->bm_lnInBrush);
  76. // copy the original to it
  77. if (bCopy) {
  78. // copy the original to it
  79. pbmNew->Copy(*pbmOld, 1.0f, FALSE);
  80. }
  81. // get factor of mip before the new one
  82. FLOAT fFactorBefore = 0.0f;
  83. CBrushMip *pbmBefore = pbmNew->GetPrev();
  84. if (pbmBefore!=NULL) {
  85. fFactorBefore = pbmBefore->bm_fMaxDistance;
  86. }
  87. // calculate factor for new one to be between those two
  88. pbmNew->bm_fMaxDistance = (fFactorBefore+pbmOld->bm_fMaxDistance)/2.0f;
  89. return pbmNew;
  90. }
  91. // make 'for' construct for walking a list reversely
  92. #define FOREACHINLIST_R(baseclass, member, head, iter) \
  93. for ( LISTITER(baseclass, member) iter(head.IterationTail()); \
  94. !iter->member.IsHeadMarker(); iter.MoveToPrev() )
  95. /*
  96. * Get a brush mip for given mip-factor.
  97. */
  98. CBrushMip *CBrush3D::GetBrushMipByDistance(FLOAT fMipDistance)
  99. {
  100. // initially there is no brush mip
  101. CBrushMip *pbmLastGood=NULL;
  102. // for all brush mips in brush reversely
  103. FOREACHINLIST_R(CBrushMip, bm_lnInBrush, br_lhBrushMips, itbm) {
  104. CBrushMip &bm = *itbm;
  105. // if this mip cannot be of given factor
  106. if (bm.bm_fMaxDistance<fMipDistance) {
  107. // return last mip found
  108. return pbmLastGood;
  109. }
  110. // remember this mip
  111. pbmLastGood = itbm;
  112. }
  113. // return last mip found
  114. return pbmLastGood;
  115. }
  116. /* Get a brush mip by its given index. */
  117. CBrushMip *CBrush3D::GetBrushMipByIndex(INDEX iMip)
  118. {
  119. INDEX iCurrentMip = 0;
  120. // for all brush mips in brush
  121. FOREACHINLIST(CBrushMip, bm_lnInBrush, br_lhBrushMips, itbm) {
  122. iCurrentMip++;
  123. // if this is the mip
  124. if (iCurrentMip == iMip) {
  125. // return the mip found
  126. return &*itbm;
  127. }
  128. }
  129. return NULL;
  130. }
  131. // get first brush mip
  132. CBrushMip *CBrush3D::GetFirstMip(void)
  133. {
  134. return LIST_HEAD(br_lhBrushMips, CBrushMip, bm_lnInBrush);
  135. }
  136. // get last brush mip
  137. CBrushMip *CBrush3D::GetLastMip(void)
  138. {
  139. return LIST_TAIL(br_lhBrushMips, CBrushMip, bm_lnInBrush);
  140. }
  141. /*
  142. * Wrapper for CObject3D::Optimize(), updates profiling information.
  143. */
  144. void CBrush3D::OptimizeObject3D(CObject3D &ob)
  145. {
  146. _pfWorldEditingProfile.StartTimer(CWorldEditingProfile::PTI_OBJECTOPTIMIZE);
  147. _pfWorldEditingProfile.IncrementCounter(CWorldEditingProfile::PCI_SECTORSOPTIMIZED,
  148. ob.ob_aoscSectors.Count());
  149. ob.Optimize();
  150. _pfWorldEditingProfile.StopTimer(CWorldEditingProfile::PTI_OBJECTOPTIMIZE);
  151. }
  152. /*
  153. * Free all memory and leave empty brush.
  154. */
  155. void CBrush3D::Clear(void) {
  156. // delete all brush mips
  157. FORDELETELIST(CBrushMip, bm_lnInBrush, br_lhBrushMips, itbm) {
  158. delete &*itbm;
  159. }
  160. }
  161. /* Copy brush from another brush with possible mirror and stretch. */
  162. void CBrush3D::Copy(CBrush3D &brOther, FLOAT fStretch, BOOL bMirrorX)
  163. {
  164. // clear this brush in case there is something in it
  165. Clear();
  166. // for all brush mips in other brush
  167. FOREACHINLIST(CBrushMip, bm_lnInBrush, brOther.br_lhBrushMips, itbmOther) {
  168. // create one brush mip
  169. CBrushMip *pbmBrushMip = new CBrushMip;
  170. // add it to the brush
  171. br_lhBrushMips.AddTail(pbmBrushMip->bm_lnInBrush);
  172. pbmBrushMip->bm_pbrBrush = this;
  173. // copy the brush mip from original
  174. pbmBrushMip->Copy(*itbmOther, fStretch, bMirrorX);
  175. }
  176. }
  177. /*
  178. * Prepare a projection from brush space to absolute space.
  179. */
  180. void CBrush3D::PrepareRelativeToAbsoluteProjection(
  181. CSimpleProjection3D_DOUBLE &prRelativeToAbsolute)
  182. {
  183. // brush that does not have an entity is initialized at origin
  184. if(br_penEntity==NULL) {
  185. prRelativeToAbsolute.ObjectPlacementL().pl_PositionVector = FLOAT3D(0.0f, 0.0f, 0.0f);
  186. prRelativeToAbsolute.ObjectPlacementL().pl_OrientationAngle = ANGLE3D(0, 0, 0);
  187. } else {
  188. prRelativeToAbsolute.ObjectPlacementL() = br_penEntity->en_plPlacement;
  189. }
  190. prRelativeToAbsolute.ViewerPlacementL().pl_PositionVector = FLOAT3D(0.0f, 0.0f, 0.0f);
  191. prRelativeToAbsolute.ViewerPlacementL().pl_OrientationAngle = ANGLE3D(0, 0, 0);
  192. prRelativeToAbsolute.Prepare();
  193. }
  194. /*
  195. * Calculate bounding boxes in all brush mips.
  196. */
  197. void CBrush3D::CalculateBoundingBoxes(void)
  198. {
  199. CalculateBoundingBoxesForOneMip(NULL);
  200. }
  201. void CBrush3D::CalculateBoundingBoxesForOneMip(CBrushMip *pbmOnly) // for only one mip
  202. {
  203. // set FPU to double precision
  204. CSetFPUPrecision FPUPrecision(FPT_53BIT);
  205. // prepare a projection from brush space to absolute space
  206. CSimpleProjection3D_DOUBLE prBrushToAbsolute;
  207. PrepareRelativeToAbsoluteProjection(prBrushToAbsolute);
  208. // for all brush mips
  209. FOREACHINLIST(CBrushMip, bm_lnInBrush, br_lhBrushMips, itbm) {
  210. CBrushMip *pbm = itbm;
  211. if (pbmOnly==NULL || pbm==pbmOnly) {
  212. // calculate its boxes
  213. pbm->CalculateBoundingBoxes(prBrushToAbsolute);
  214. }
  215. }
  216. // if this brush is zoning
  217. if (br_penEntity!=NULL && (br_penEntity->en_ulFlags&ENF_ZONING)) {
  218. // portal links must be updated also
  219. extern BOOL _bPortalSectorLinksPreLoaded;
  220. extern BOOL _bDontDiscardLinks;
  221. br_penEntity->en_pwoWorld->wo_bPortalLinksUpToDate = _bPortalSectorLinksPreLoaded||_bDontDiscardLinks;
  222. }
  223. br_penEntity->UpdateSpatialRange();
  224. }
  225. // switch from zoning to non-zoning
  226. void CBrush3D::SwitchToNonZoning(void)
  227. {
  228. CalculateBoundingBoxes();
  229. // for all brush mips
  230. FOREACHINLIST(CBrushMip, bm_lnInBrush, br_lhBrushMips, itbm) {
  231. // for all sectors in the mip
  232. {FOREACHINDYNAMICARRAY(itbm->bm_abscSectors, CBrushSector, itbsc) {
  233. // unset spatial clasification
  234. itbsc->bsc_rsEntities.Clear();
  235. }}
  236. }
  237. }
  238. // switch from non-zoning to zoning
  239. void CBrush3D::SwitchToZoning(void)
  240. {
  241. CalculateBoundingBoxes();
  242. // for all brush mips
  243. FOREACHINLIST(CBrushMip, bm_lnInBrush, br_lhBrushMips, itbm) {
  244. // for all sectors in the mip
  245. {FOREACHINDYNAMICARRAY(itbm->bm_abscSectors, CBrushSector, itbsc) {
  246. // find entities in sector
  247. itbsc->FindEntitiesInSector();
  248. }}
  249. }
  250. }