2
0

BsLight.cpp 11 KB

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