BsLight.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "Renderer/BsLight.h"
  4. #include "RTTI/BsLightRTTI.h"
  5. #include "Renderer/BsRenderer.h"
  6. #include "Scene/BsSceneObject.h"
  7. #include "Mesh/BsMesh.h"
  8. namespace bs
  9. {
  10. LightBase::LightBase()
  11. : mType(LightType::Radial), mCastsShadows(false), mColor(Color::White), mAttRadius(10.0f), mSourceRadius(0.0f)
  12. , mIntensity(100.0f), mSpotAngle(45), mSpotFalloffAngle(35.0f), mAutoAttenuation(false), mShadowBias(0.5f)
  13. {
  14. updateAttenuationRange();
  15. }
  16. LightBase::LightBase(LightType type, Color color, float intensity, float attRadius, float srcRadius, bool castsShadows,
  17. Degree spotAngle, Degree spotFalloffAngle)
  18. : mType(type), mCastsShadows(castsShadows), mColor(color), mAttRadius(attRadius), mSourceRadius(srcRadius)
  19. , mIntensity(intensity), mSpotAngle(spotAngle), mSpotFalloffAngle(spotFalloffAngle), mAutoAttenuation(false)
  20. , mShadowBias(0.5f)
  21. {
  22. updateAttenuationRange();
  23. }
  24. void LightBase::setUseAutoAttenuation(bool enabled)
  25. {
  26. mAutoAttenuation = enabled;
  27. if(enabled)
  28. updateAttenuationRange();
  29. _markCoreDirty();
  30. }
  31. void LightBase::setAttenuationRadius(float radius)
  32. {
  33. if (mAutoAttenuation)
  34. return;
  35. mAttRadius = radius;
  36. _markCoreDirty();
  37. updateBounds();
  38. }
  39. void LightBase::setSourceRadius(float radius)
  40. {
  41. mSourceRadius = radius;
  42. if (mAutoAttenuation)
  43. updateAttenuationRange();
  44. _markCoreDirty();
  45. }
  46. void LightBase::setIntensity(float intensity)
  47. {
  48. mIntensity = intensity;
  49. if (mAutoAttenuation)
  50. updateAttenuationRange();
  51. _markCoreDirty();
  52. }
  53. float LightBase::getLuminance() const
  54. {
  55. float radius2 = mSourceRadius * mSourceRadius;
  56. switch (mType)
  57. {
  58. case LightType::Radial:
  59. if (mSourceRadius > 0.0f)
  60. return mIntensity / (4 * radius2 * Math::PI); // Luminous flux -> luminance
  61. else
  62. return mIntensity / (4 * Math::PI); // Luminous flux -> luminous intensity
  63. case LightType::Spot:
  64. {
  65. if (mSourceRadius > 0.0f)
  66. return mIntensity / (radius2 * Math::PI); // Luminous flux -> luminance
  67. else
  68. {
  69. // Note: Consider using the simpler conversion I / PI to match with the area-light conversion
  70. float cosTotalAngle = Math::cos(mSpotAngle);
  71. float cosFalloffAngle = Math::cos(mSpotFalloffAngle);
  72. // Luminous flux -> luminous intensity
  73. return mIntensity / (Math::TWO_PI * (1.0f - (cosFalloffAngle + cosTotalAngle) * 0.5f));
  74. }
  75. }
  76. case LightType::Directional:
  77. if (mSourceRadius > 0.0f)
  78. {
  79. // Use cone solid angle formulae to calculate disc solid angle
  80. float solidAngle = Math::TWO_PI * (1 - cos(mSourceRadius * Math::DEG2RAD));
  81. return mIntensity / solidAngle; // Illuminance -> luminance
  82. }
  83. else
  84. return mIntensity; // In luminance units by default
  85. default:
  86. return 0.0f;
  87. }
  88. }
  89. void LightBase::updateAttenuationRange()
  90. {
  91. // Value to which intensity needs to drop in order for the light contribution to fade out to zero
  92. const float minAttenuation = 0.2f;
  93. if(mSourceRadius > 0.0f)
  94. {
  95. // Inverse of the attenuation formula for area lights:
  96. // a = I / (1 + (2/r) * d + (1/r^2) * d^2
  97. // Where r is the source radius, and d is the distance from the light. As derived here:
  98. // https://imdoingitwrong.wordpress.com/2011/01/31/light-attenuation/
  99. float luminousFlux = getIntensity();
  100. float a = sqrt(minAttenuation);
  101. mAttRadius = (mSourceRadius * (sqrt(luminousFlux - a))) / a;
  102. }
  103. else // Based on the basic inverse square distance formula
  104. {
  105. float luminousIntensity = getIntensity();
  106. float a = minAttenuation;
  107. mAttRadius = sqrt(std::max(0.0f, luminousIntensity / a));
  108. }
  109. updateBounds();
  110. }
  111. void LightBase::updateBounds()
  112. {
  113. const Transform& tfrm = getTransform();
  114. switch (mType)
  115. {
  116. case LightType::Directional:
  117. mBounds = Sphere(tfrm.getPosition(), std::numeric_limits<float>::infinity());
  118. break;
  119. case LightType::Radial:
  120. mBounds = Sphere(tfrm.getPosition(), mAttRadius);
  121. break;
  122. case LightType::Spot:
  123. {
  124. // Note: We could use the formula for calculating the circumcircle of a triangle (after projecting the cone),
  125. // but the radius of the sphere is the same as in the formula we use here, yet it is much simpler with no need
  126. // to calculate multiple determinants. Neither are good approximations when cone angle is wide.
  127. Vector3 offset(0, 0, mAttRadius * 0.5f);
  128. // Direction along the edge of the cone, on the YZ plane (doesn't matter if we used XZ instead)
  129. Degree angle = Math::clamp(mSpotAngle, Degree(-89), Degree(89));
  130. Vector3 coneDir(0, Math::tan(angle)*mAttRadius, mAttRadius);
  131. // Distance between the "corner" of the cone and our center, must be the radius (provided the center is at
  132. // the middle of the range)
  133. float radius = (offset - coneDir).length();
  134. Vector3 center = tfrm.getPosition() + tfrm.getRotation().rotate(offset);
  135. mBounds = Sphere(center, radius);
  136. }
  137. break;
  138. default:
  139. break;
  140. }
  141. }
  142. Light::Light()
  143. {
  144. }
  145. Light::Light(LightType type, Color color, float intensity, float attRadius, float srcRadius, bool castsShadows,
  146. Degree spotAngle, Degree spotFalloffAngle)
  147. : LightBase(type, color, intensity, attRadius, srcRadius, castsShadows, spotAngle, spotFalloffAngle)
  148. {
  149. // Calling virtual method is okay here because this is the most derived type
  150. updateBounds();
  151. }
  152. SPtr<ct::Light> Light::getCore() const
  153. {
  154. return std::static_pointer_cast<ct::Light>(mCoreSpecific);
  155. }
  156. SPtr<Light> Light::create(LightType type, Color color,
  157. float intensity, float attRadius, bool castsShadows, Degree spotAngle, Degree spotFalloffAngle)
  158. {
  159. Light* handler = new (bs_alloc<Light>())
  160. Light(type, color, intensity, attRadius, 0.0f, castsShadows, spotAngle, spotFalloffAngle);
  161. SPtr<Light> handlerPtr = bs_core_ptr<Light>(handler);
  162. handlerPtr->_setThisPtr(handlerPtr);
  163. handlerPtr->initialize();
  164. return handlerPtr;
  165. }
  166. SPtr<Light> Light::createEmpty()
  167. {
  168. Light* handler = new (bs_alloc<Light>()) Light();
  169. SPtr<Light> handlerPtr = bs_core_ptr<Light>(handler);
  170. handlerPtr->_setThisPtr(handlerPtr);
  171. return handlerPtr;
  172. }
  173. SPtr<ct::CoreObject> Light::createCore() const
  174. {
  175. ct::Light* handler = new (bs_alloc<ct::Light>())
  176. ct::Light(mType, mColor, mIntensity, mAttRadius, mSourceRadius, mCastsShadows, mSpotAngle, mSpotFalloffAngle);
  177. SPtr<ct::Light> handlerPtr = bs_shared_ptr<ct::Light>(handler);
  178. handlerPtr->_setThisPtr(handlerPtr);
  179. return handlerPtr;
  180. }
  181. CoreSyncData Light::syncToCore(FrameAlloc* allocator)
  182. {
  183. UINT32 size = getActorSyncDataSize();
  184. size += rttiGetElemSize(mType);
  185. size += rttiGetElemSize(mCastsShadows);
  186. size += rttiGetElemSize(mColor);
  187. size += rttiGetElemSize(mAttRadius);
  188. size += rttiGetElemSize(mSourceRadius);
  189. size += rttiGetElemSize(mIntensity);
  190. size += rttiGetElemSize(mSpotAngle);
  191. size += rttiGetElemSize(mSpotFalloffAngle);
  192. size += rttiGetElemSize(mAutoAttenuation);
  193. size += rttiGetElemSize(getCoreDirtyFlags());
  194. size += rttiGetElemSize(mBounds);
  195. size += rttiGetElemSize(mShadowBias);
  196. UINT8* buffer = allocator->alloc(size);
  197. char* dataPtr = (char*)buffer;
  198. dataPtr = syncActorTo(dataPtr);
  199. dataPtr = rttiWriteElem(mType, dataPtr);
  200. dataPtr = rttiWriteElem(mCastsShadows, dataPtr);
  201. dataPtr = rttiWriteElem(mColor, dataPtr);
  202. dataPtr = rttiWriteElem(mAttRadius, dataPtr);
  203. dataPtr = rttiWriteElem(mSourceRadius, dataPtr);
  204. dataPtr = rttiWriteElem(mIntensity, dataPtr);
  205. dataPtr = rttiWriteElem(mSpotAngle, dataPtr);
  206. dataPtr = rttiWriteElem(mSpotFalloffAngle, dataPtr);
  207. dataPtr = rttiWriteElem(mAutoAttenuation, dataPtr);
  208. dataPtr = rttiWriteElem(getCoreDirtyFlags(), dataPtr);
  209. dataPtr = rttiWriteElem(mBounds, dataPtr);
  210. dataPtr = rttiWriteElem(mShadowBias, dataPtr);
  211. return CoreSyncData(buffer, size);
  212. }
  213. void Light::_markCoreDirty(ActorDirtyFlag flag)
  214. {
  215. markCoreDirty((UINT32)flag);
  216. }
  217. RTTITypeBase* Light::getRTTIStatic()
  218. {
  219. return LightRTTI::instance();
  220. }
  221. RTTITypeBase* Light::getRTTI() const
  222. {
  223. return Light::getRTTIStatic();
  224. }
  225. namespace ct
  226. {
  227. const UINT32 Light::LIGHT_CONE_NUM_SIDES = 20;
  228. const UINT32 Light::LIGHT_CONE_NUM_SLICES = 10;
  229. Light::Light(LightType type, Color color,
  230. float intensity, float attRadius, float srcRadius, bool castsShadows, Degree spotAngle, Degree spotFalloffAngle)
  231. :LightBase(type, color, intensity, attRadius, srcRadius, castsShadows, spotAngle, spotFalloffAngle), mRendererId(0)
  232. {
  233. }
  234. Light::~Light()
  235. {
  236. gRenderer()->notifyLightRemoved(this);
  237. }
  238. void Light::initialize()
  239. {
  240. updateBounds();
  241. gRenderer()->notifyLightAdded(this);
  242. CoreObject::initialize();
  243. }
  244. void Light::syncToCore(const CoreSyncData& data)
  245. {
  246. char* dataPtr = (char*)data.getBuffer();
  247. UINT32 dirtyFlags = 0;
  248. bool oldIsActive = mActive;
  249. LightType oldType = mType;
  250. dataPtr = syncActorFrom(dataPtr);
  251. dataPtr = rttiReadElem(mType, dataPtr);
  252. dataPtr = rttiReadElem(mCastsShadows, dataPtr);
  253. dataPtr = rttiReadElem(mColor, dataPtr);
  254. dataPtr = rttiReadElem(mAttRadius, dataPtr);
  255. dataPtr = rttiReadElem(mSourceRadius, dataPtr);
  256. dataPtr = rttiReadElem(mIntensity, dataPtr);
  257. dataPtr = rttiReadElem(mSpotAngle, dataPtr);
  258. dataPtr = rttiReadElem(mSpotFalloffAngle, dataPtr);
  259. dataPtr = rttiReadElem(mAutoAttenuation, dataPtr);
  260. dataPtr = rttiReadElem(dirtyFlags, dataPtr);
  261. dataPtr = rttiReadElem(mBounds, dataPtr);
  262. dataPtr = rttiReadElem(mShadowBias, dataPtr);
  263. updateBounds();
  264. if((dirtyFlags & ((UINT32)ActorDirtyFlag::Everything | (UINT32)ActorDirtyFlag::Active)) != 0)
  265. {
  266. if (oldIsActive != mActive)
  267. {
  268. if (mActive)
  269. gRenderer()->notifyLightAdded(this);
  270. else
  271. {
  272. LightType newType = mType;
  273. mType = oldType;
  274. gRenderer()->notifyLightRemoved(this);
  275. mType = newType;
  276. }
  277. }
  278. else
  279. {
  280. LightType newType = mType;
  281. mType = oldType;
  282. gRenderer()->notifyLightRemoved(this);
  283. mType = newType;
  284. gRenderer()->notifyLightAdded(this);
  285. }
  286. }
  287. else if((dirtyFlags & (UINT32)ActorDirtyFlag::Mobility) != 0)
  288. {
  289. gRenderer()->notifyLightRemoved(this);
  290. gRenderer()->notifyLightAdded(this);
  291. }
  292. else if ((dirtyFlags & (UINT32)ActorDirtyFlag::Transform) != 0)
  293. {
  294. if (mActive)
  295. gRenderer()->notifyLightUpdated(this);
  296. }
  297. }
  298. }}