sceneSimpleZone.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "platform/platform.h"
  23. #include "scene/zones/sceneSimpleZone.h"
  24. #include "scene/sceneRenderState.h"
  25. #include "scene/sceneManager.h"
  26. #include "scene/zones/sceneTraversalState.h"
  27. #include "scene/culling/sceneCullingVolume.h"
  28. #include "core/stream/bitStream.h"
  29. #include "console/engineAPI.h"
  30. #include "platform/profiler.h"
  31. extern bool gEditingMission;
  32. //-----------------------------------------------------------------------------
  33. SceneSimpleZone::SceneSimpleZone()
  34. : mUseAmbientLightColor( false ),
  35. mAmbientLightColor( 0.1f, 0.1f, 0.1f, 1.f ),
  36. mIsRotated( false )
  37. {
  38. // Box zones are unit cubes that are scaled to fit.
  39. mObjScale.set( 10, 10, 10 );
  40. mObjBox.set(
  41. Point3F( -0.5f, -0.5f, -0.5f ),
  42. Point3F( 0.5f, 0.5f, 0.5f )
  43. );
  44. }
  45. //-----------------------------------------------------------------------------
  46. void SceneSimpleZone::initPersistFields()
  47. {
  48. addGroup( "Lighting" );
  49. addProtectedField( "useAmbientLightColor", TypeBool, Offset( mUseAmbientLightColor, SceneSimpleZone ),
  50. &_setUseAmbientLightColor, &defaultProtectedGetFn,
  51. "Whether to use #ambientLightColor for ambient lighting in this zone or the global ambient color." );
  52. addProtectedField( "ambientLightColor", TypeColorF, Offset( mAmbientLightColor, SceneSimpleZone ),
  53. &_setAmbientLightColor, &defaultProtectedGetFn,
  54. "Color of ambient lighting in this zone.\n\n"
  55. "Only used if #useAmbientLightColor is true." );
  56. endGroup( "Lighting" );
  57. Parent::initPersistFields();
  58. }
  59. //-----------------------------------------------------------------------------
  60. String SceneSimpleZone::describeSelf() const
  61. {
  62. String str = Parent::describeSelf();
  63. str += String::ToString( "|zoneid: %i", getZoneRangeStart() );
  64. return str;
  65. }
  66. //-----------------------------------------------------------------------------
  67. bool SceneSimpleZone::onSceneAdd()
  68. {
  69. if( !Parent::onSceneAdd() )
  70. return false;
  71. // Register our zone.
  72. SceneZoneSpaceManager* manager = getSceneManager()->getZoneManager();
  73. if( manager )
  74. manager->registerZones( this, 1 );
  75. return true;
  76. }
  77. //-----------------------------------------------------------------------------
  78. U32 SceneSimpleZone::packUpdate( NetConnection* connection, U32 mask, BitStream* stream )
  79. {
  80. U32 retMask = Parent::packUpdate( connection, mask, stream );
  81. if( stream->writeFlag( mask & AmbientMask ) )
  82. {
  83. stream->writeFlag( mUseAmbientLightColor );
  84. stream->writeFloat( mAmbientLightColor.red, 7 );
  85. stream->writeFloat( mAmbientLightColor.green, 7 );
  86. stream->writeFloat( mAmbientLightColor.blue, 7 );
  87. stream->writeFloat( mAmbientLightColor.alpha, 7 );
  88. }
  89. return retMask;
  90. }
  91. //-----------------------------------------------------------------------------
  92. void SceneSimpleZone::unpackUpdate( NetConnection* connection, BitStream* stream )
  93. {
  94. Parent::unpackUpdate( connection, stream );
  95. if( stream->readFlag() ) // AmbientMask
  96. {
  97. mUseAmbientLightColor = stream->readFlag();
  98. mAmbientLightColor.red = stream->readFloat( 7 );
  99. mAmbientLightColor.green = stream->readFloat( 7 );
  100. mAmbientLightColor.blue = stream->readFloat( 7 );
  101. mAmbientLightColor.alpha = stream->readFloat( 7 );
  102. }
  103. }
  104. //-----------------------------------------------------------------------------
  105. void SceneSimpleZone::setUseAmbientLightColor( bool value )
  106. {
  107. if( mUseAmbientLightColor == value )
  108. return;
  109. mUseAmbientLightColor = value;
  110. if( isServerObject() )
  111. setMaskBits( AmbientMask );
  112. }
  113. //-----------------------------------------------------------------------------
  114. void SceneSimpleZone::setAmbientLightColor( const LinearColorF& color )
  115. {
  116. mAmbientLightColor = color;
  117. if( isServerObject() )
  118. setMaskBits( AmbientMask );
  119. }
  120. //-----------------------------------------------------------------------------
  121. bool SceneSimpleZone::getZoneAmbientLightColor( U32 zone, LinearColorF& outColor ) const
  122. {
  123. AssertFatal( zone == getZoneRangeStart(), "SceneSimpleZone::getZoneAmbientLightColor - Invalid zone ID!" );
  124. if( !mUseAmbientLightColor )
  125. return false;
  126. outColor = mAmbientLightColor;
  127. return true;
  128. }
  129. //-----------------------------------------------------------------------------
  130. U32 SceneSimpleZone::getPointZone( const Point3F& p )
  131. {
  132. if( !containsPoint( p ) )
  133. return SceneZoneSpaceManager::InvalidZoneId;
  134. return getZoneRangeStart();
  135. }
  136. //-----------------------------------------------------------------------------
  137. bool SceneSimpleZone::getOverlappingZones( const Box3F& aabb, U32* outZones, U32& outNumZones )
  138. {
  139. PROFILE_SCOPE( SceneBoxZone_getOverlappingZones );
  140. bool isOverlapped = false;
  141. bool isContained = false;
  142. // If the zone has not been rotated, we can simply use straightforward
  143. // AABB/AABB intersection based on the world boxes of the zone and the
  144. // object.
  145. //
  146. // If, however, the zone has been rotated, then we must use the zone's
  147. // OBB and test that against the object's AABB.
  148. if( !mIsRotated )
  149. {
  150. isOverlapped = mWorldBox.isOverlapped( aabb );
  151. isContained = isOverlapped && mWorldBox.isContained( aabb );
  152. }
  153. else
  154. {
  155. // Check if the zone's OBB intersects the object's AABB.
  156. isOverlapped = aabb.collideOrientedBox(
  157. getScale() / 2.f,
  158. getTransform()
  159. );
  160. // If so, check whether the object's AABB is fully contained
  161. // inside the zone's OBB.
  162. if( isOverlapped )
  163. {
  164. isContained = true;
  165. for( U32 i = 0; i < Box3F::NUM_POINTS; ++ i )
  166. {
  167. Point3F cornerPoint = aabb.computeVertex( i );
  168. if( !mOrientedWorldBox.isContained( cornerPoint ) )
  169. {
  170. isContained = false;
  171. break;
  172. }
  173. }
  174. }
  175. }
  176. if( isOverlapped )
  177. {
  178. outNumZones = 1;
  179. outZones[ 0 ] = getZoneRangeStart();
  180. }
  181. else
  182. outNumZones = 0;
  183. return !isContained;
  184. }
  185. //-----------------------------------------------------------------------------
  186. void SceneSimpleZone::setTransform( const MatrixF& mat )
  187. {
  188. Parent::setTransform( mat );
  189. // Find out whether the zone has been rotated.
  190. EulerF rotation = getTransform().toEuler();
  191. mIsRotated = !mIsZero( rotation.x ) ||
  192. !mIsZero( rotation.y ) ||
  193. !mIsZero( rotation.z );
  194. // Update the OBB.
  195. _updateOrientedWorldBox();
  196. }
  197. //-----------------------------------------------------------------------------
  198. void SceneSimpleZone::prepRenderImage( SceneRenderState* state )
  199. {
  200. if( isRootZone() )
  201. return;
  202. Parent::prepRenderImage( state );
  203. }
  204. //-----------------------------------------------------------------------------
  205. void SceneSimpleZone::traverseZones( SceneTraversalState* state )
  206. {
  207. traverseZones( state, getZoneRangeStart() );
  208. }
  209. //-----------------------------------------------------------------------------
  210. void SceneSimpleZone::traverseZones( SceneTraversalState* state, U32 startZoneId )
  211. {
  212. PROFILE_SCOPE( SceneSimpleZone_traverseZones );
  213. AssertFatal( startZoneId == getZoneRangeStart(), "SceneSimpleZone::traverseZones - Invalid start zone ID!" );
  214. // If we aren't the root of the traversal, do a number of checks
  215. // to see if we can early out of the traversal here. The primary reason
  216. // we don't do the checks if we are the root is because of the frustum
  217. // near plane. The start zone of a traversal is selected based on the
  218. // current viewpoint. However, that point still has some space in between
  219. // it and the near plane so if we end up with a case where that's all the
  220. // space that is needed to cull away our starting zone, we won't see any
  221. // traversal at all and get a blank scene back even if the starting zone
  222. // would actually hand the traversal over to other zones and eventually
  223. // discover visible space.
  224. if( state->getTraversalDepth() > 0 )
  225. {
  226. // If we have already visited this zone in this traversal chain,
  227. // exit out. This can happen when zones are grouped together.
  228. // Note that this can also happen with the outdoor zone since it isn't
  229. // convex. However, in that case, this acts as a nice side optimization
  230. // we get since if the outdoor zone is already on the stack, our culling
  231. // culling volume can only be smaller than the one we started with and thus
  232. // by earlying out here, we prevent adding a pointless culling volume
  233. // to the zone.
  234. //TODO: would be nice to catch this via "visibility changed?" checks but
  235. // that's non-trivial
  236. if( state->haveVisitedZone( getZoneRangeStart() ) )
  237. return;
  238. // First check whether we even intersect the given frustum at all.
  239. if( mIsRotated )
  240. {
  241. // Space has been rotated, so do a frustum/OBB check.
  242. if( !state->getCurrentCullingVolume().test( _getOrientedWorldBox() ) )
  243. return;
  244. }
  245. else
  246. {
  247. // Space has not been rotated, so we can do a faster frustum/ABB check.
  248. if( !state->getCurrentCullingVolume().test( getWorldBox() ) )
  249. return;
  250. }
  251. }
  252. // Add the current culling volume to the culling state for this zone.
  253. // If that doesn't result in new space becoming visible, we can terminate the traversal
  254. // here.
  255. if( !state->getCullingState()->addCullingVolumeToZone( startZoneId, state->getCurrentCullingVolume() ) )
  256. return;
  257. // Add our occluders to the rendering state.
  258. _addOccludersToCullingState( state->getCullingState() );
  259. // Merge the zone into the traversal area.
  260. state->addToTraversedArea( getWorldBox() );
  261. // Push our zone ID on the traversal stack and traverse into our
  262. // connected zone managers.
  263. state->pushZone( startZoneId );
  264. _traverseConnectedZoneSpaces( state );
  265. state->popZone();
  266. }
  267. //-----------------------------------------------------------------------------
  268. bool SceneSimpleZone::_setUseAmbientLightColor( void* object, const char* index, const char* data )
  269. {
  270. SceneSimpleZone* zone = reinterpret_cast< SceneSimpleZone* >( object );
  271. zone->setUseAmbientLightColor( EngineUnmarshallData< bool >()( data ) );
  272. return false;
  273. }
  274. //-----------------------------------------------------------------------------
  275. bool SceneSimpleZone::_setAmbientLightColor( void* object, const char* index, const char* data )
  276. {
  277. SceneSimpleZone* zone = reinterpret_cast< SceneSimpleZone* >( object );
  278. zone->setAmbientLightColor( EngineUnmarshallData< LinearColorF >()( data ) );
  279. return false;
  280. }