sceneSimpleZone.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  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. docsURL;
  49. addGroup( "Lighting" );
  50. addProtectedField( "useAmbientLightColor", TypeBool, Offset( mUseAmbientLightColor, SceneSimpleZone ),
  51. &_setUseAmbientLightColor, &defaultProtectedGetFn,
  52. "Whether to use #ambientLightColor for ambient lighting in this zone or the global ambient color." );
  53. addProtectedField( "ambientLightColor", TypeColorF, Offset( mAmbientLightColor, SceneSimpleZone ),
  54. &_setAmbientLightColor, &defaultProtectedGetFn,
  55. "Color of ambient lighting in this zone.\n\n"
  56. "Only used if #useAmbientLightColor is true." );
  57. endGroup( "Lighting" );
  58. Parent::initPersistFields();
  59. }
  60. //-----------------------------------------------------------------------------
  61. String SceneSimpleZone::describeSelf() const
  62. {
  63. String str = Parent::describeSelf();
  64. str += String::ToString( "|zoneid: %i", getZoneRangeStart() );
  65. return str;
  66. }
  67. //-----------------------------------------------------------------------------
  68. bool SceneSimpleZone::onSceneAdd()
  69. {
  70. if( !Parent::onSceneAdd() )
  71. return false;
  72. // Register our zone.
  73. SceneZoneSpaceManager* manager = getSceneManager()->getZoneManager();
  74. if( manager )
  75. manager->registerZones( this, 1 );
  76. return true;
  77. }
  78. //-----------------------------------------------------------------------------
  79. U32 SceneSimpleZone::packUpdate( NetConnection* connection, U32 mask, BitStream* stream )
  80. {
  81. U32 retMask = Parent::packUpdate( connection, mask, stream );
  82. if( stream->writeFlag( mask & AmbientMask ) )
  83. {
  84. stream->writeFlag( mUseAmbientLightColor );
  85. stream->writeFloat( mAmbientLightColor.red, 7 );
  86. stream->writeFloat( mAmbientLightColor.green, 7 );
  87. stream->writeFloat( mAmbientLightColor.blue, 7 );
  88. stream->writeFloat( mAmbientLightColor.alpha, 7 );
  89. }
  90. return retMask;
  91. }
  92. //-----------------------------------------------------------------------------
  93. void SceneSimpleZone::unpackUpdate( NetConnection* connection, BitStream* stream )
  94. {
  95. Parent::unpackUpdate( connection, stream );
  96. if( stream->readFlag() ) // AmbientMask
  97. {
  98. mUseAmbientLightColor = stream->readFlag();
  99. mAmbientLightColor.red = stream->readFloat( 7 );
  100. mAmbientLightColor.green = stream->readFloat( 7 );
  101. mAmbientLightColor.blue = stream->readFloat( 7 );
  102. mAmbientLightColor.alpha = stream->readFloat( 7 );
  103. }
  104. }
  105. //-----------------------------------------------------------------------------
  106. void SceneSimpleZone::setUseAmbientLightColor( bool value )
  107. {
  108. if( mUseAmbientLightColor == value )
  109. return;
  110. mUseAmbientLightColor = value;
  111. if( isServerObject() )
  112. setMaskBits( AmbientMask );
  113. }
  114. //-----------------------------------------------------------------------------
  115. void SceneSimpleZone::setAmbientLightColor( const LinearColorF& color )
  116. {
  117. mAmbientLightColor = color;
  118. if( isServerObject() )
  119. setMaskBits( AmbientMask );
  120. }
  121. //-----------------------------------------------------------------------------
  122. bool SceneSimpleZone::getZoneAmbientLightColor( U32 zone, LinearColorF& outColor ) const
  123. {
  124. AssertFatal( zone == getZoneRangeStart(), "SceneSimpleZone::getZoneAmbientLightColor - Invalid zone ID!" );
  125. if( !mUseAmbientLightColor )
  126. return false;
  127. outColor = mAmbientLightColor;
  128. return true;
  129. }
  130. //-----------------------------------------------------------------------------
  131. U32 SceneSimpleZone::getPointZone( const Point3F& p )
  132. {
  133. if( !containsPoint( p ) )
  134. return SceneZoneSpaceManager::InvalidZoneId;
  135. return getZoneRangeStart();
  136. }
  137. //-----------------------------------------------------------------------------
  138. bool SceneSimpleZone::getOverlappingZones( const Box3F& aabb, U32* outZones, U32& outNumZones )
  139. {
  140. PROFILE_SCOPE( SceneBoxZone_getOverlappingZones );
  141. bool isOverlapped = false;
  142. bool isContained = false;
  143. // If the zone has not been rotated, we can simply use straightforward
  144. // AABB/AABB intersection based on the world boxes of the zone and the
  145. // object.
  146. //
  147. // If, however, the zone has been rotated, then we must use the zone's
  148. // OBB and test that against the object's AABB.
  149. if( !mIsRotated )
  150. {
  151. isOverlapped = mWorldBox.isOverlapped( aabb );
  152. isContained = isOverlapped && mWorldBox.isContained( aabb );
  153. }
  154. else
  155. {
  156. // Check if the zone's OBB intersects the object's AABB.
  157. isOverlapped = aabb.collideOrientedBox(
  158. getScale() / 2.f,
  159. getTransform()
  160. );
  161. // If so, check whether the object's AABB is fully contained
  162. // inside the zone's OBB.
  163. if( isOverlapped )
  164. {
  165. isContained = true;
  166. for( U32 i = 0; i < Box3F::NUM_POINTS; ++ i )
  167. {
  168. Point3F cornerPoint = aabb.computeVertex( i );
  169. if( !mOrientedWorldBox.isContained( cornerPoint ) )
  170. {
  171. isContained = false;
  172. break;
  173. }
  174. }
  175. }
  176. }
  177. if( isOverlapped )
  178. {
  179. outNumZones = 1;
  180. outZones[ 0 ] = getZoneRangeStart();
  181. }
  182. else
  183. outNumZones = 0;
  184. return !isContained;
  185. }
  186. //-----------------------------------------------------------------------------
  187. void SceneSimpleZone::setTransform( const MatrixF& mat )
  188. {
  189. Parent::setTransform( mat );
  190. // Find out whether the zone has been rotated.
  191. EulerF rotation = getTransform().toEuler();
  192. mIsRotated = !mIsZero( rotation.x ) ||
  193. !mIsZero( rotation.y ) ||
  194. !mIsZero( rotation.z );
  195. // Update the OBB.
  196. _updateOrientedWorldBox();
  197. }
  198. //-----------------------------------------------------------------------------
  199. void SceneSimpleZone::prepRenderImage( SceneRenderState* state )
  200. {
  201. if( isRootZone() )
  202. return;
  203. Parent::prepRenderImage( state );
  204. }
  205. //-----------------------------------------------------------------------------
  206. void SceneSimpleZone::traverseZones( SceneTraversalState* state )
  207. {
  208. traverseZones( state, getZoneRangeStart() );
  209. }
  210. //-----------------------------------------------------------------------------
  211. void SceneSimpleZone::traverseZones( SceneTraversalState* state, U32 startZoneId )
  212. {
  213. PROFILE_SCOPE( SceneSimpleZone_traverseZones );
  214. AssertFatal( startZoneId == getZoneRangeStart(), "SceneSimpleZone::traverseZones - Invalid start zone ID!" );
  215. // If we aren't the root of the traversal, do a number of checks
  216. // to see if we can early out of the traversal here. The primary reason
  217. // we don't do the checks if we are the root is because of the frustum
  218. // near plane. The start zone of a traversal is selected based on the
  219. // current viewpoint. However, that point still has some space in between
  220. // it and the near plane so if we end up with a case where that's all the
  221. // space that is needed to cull away our starting zone, we won't see any
  222. // traversal at all and get a blank scene back even if the starting zone
  223. // would actually hand the traversal over to other zones and eventually
  224. // discover visible space.
  225. if( state->getTraversalDepth() > 0 )
  226. {
  227. // If we have already visited this zone in this traversal chain,
  228. // exit out. This can happen when zones are grouped together.
  229. // Note that this can also happen with the outdoor zone since it isn't
  230. // convex. However, in that case, this acts as a nice side optimization
  231. // we get since if the outdoor zone is already on the stack, our culling
  232. // culling volume can only be smaller than the one we started with and thus
  233. // by earlying out here, we prevent adding a pointless culling volume
  234. // to the zone.
  235. //TODO: would be nice to catch this via "visibility changed?" checks but
  236. // that's non-trivial
  237. if( state->haveVisitedZone( getZoneRangeStart() ) )
  238. return;
  239. // First check whether we even intersect the given frustum at all.
  240. if( mIsRotated )
  241. {
  242. // Space has been rotated, so do a frustum/OBB check.
  243. if( !state->getCurrentCullingVolume().test( _getOrientedWorldBox() ) )
  244. return;
  245. }
  246. else
  247. {
  248. // Space has not been rotated, so we can do a faster frustum/ABB check.
  249. if( !state->getCurrentCullingVolume().test( getWorldBox() ) )
  250. return;
  251. }
  252. }
  253. // Add the current culling volume to the culling state for this zone.
  254. // If that doesn't result in new space becoming visible, we can terminate the traversal
  255. // here.
  256. if( !state->getCullingState()->addCullingVolumeToZone( startZoneId, state->getCurrentCullingVolume() ) )
  257. return;
  258. // Add our occluders to the rendering state.
  259. _addOccludersToCullingState( state->getCullingState() );
  260. // Merge the zone into the traversal area.
  261. state->addToTraversedArea( getWorldBox() );
  262. // Push our zone ID on the traversal stack and traverse into our
  263. // connected zone managers.
  264. state->pushZone( startZoneId );
  265. _traverseConnectedZoneSpaces( state );
  266. state->popZone();
  267. }
  268. //-----------------------------------------------------------------------------
  269. bool SceneSimpleZone::_setUseAmbientLightColor( void* object, const char* index, const char* data )
  270. {
  271. SceneSimpleZone* zone = reinterpret_cast< SceneSimpleZone* >( object );
  272. zone->setUseAmbientLightColor( EngineUnmarshallData< bool >()( data ) );
  273. return false;
  274. }
  275. //-----------------------------------------------------------------------------
  276. bool SceneSimpleZone::_setAmbientLightColor( void* object, const char* index, const char* data )
  277. {
  278. SceneSimpleZone* zone = reinterpret_cast< SceneSimpleZone* >( object );
  279. zone->setAmbientLightColor( EngineUnmarshallData< LinearColorF >()( data ) );
  280. return false;
  281. }