BsLight.cpp 11 KB

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