Light.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801
  1. //
  2. // Urho3D Engine
  3. // Copyright (c) 2008-2011 Lasse Öörni
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a copy
  6. // of this software and associated documentation files (the "Software"), to deal
  7. // in the Software without restriction, including without limitation the rights
  8. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the Software is
  10. // furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. // THE SOFTWARE.
  22. //
  23. #include "Precompiled.h"
  24. #include "DebugRenderer.h"
  25. #include "Light.h"
  26. #include "ReplicationUtils.h"
  27. #include "ResourceCache.h"
  28. #include "StringUtils.h"
  29. #include "Texture2D.h"
  30. #include "TextureCube.h"
  31. #include "XMLElement.h"
  32. #include "DebugNew.h"
  33. static LightType DEFAULT_LIGHTTYPE = LIGHT_POINT;
  34. static const Vector3 DEFAULT_DIRECTION = Vector3::sDown;
  35. static const float DEFAULT_FOV = 30.0f;
  36. static const float DEFAULT_CONSTANTBIAS = 0.0001f;
  37. static const float DEFAULT_SLOPESCALEDBIAS = 0.0001f;
  38. static const float DEFAULT_LAMBDA = 0.5f;
  39. static const float DEFAULT_SHADOWFADERANGE = 0.2f;
  40. static const float DEFAULT_SHADOWQUANTIZE = 0.5f;
  41. static const float DEFAULT_SHADOWMINVIEW = 3.0f;
  42. static const float DEFAULT_SHADOWNEARFARRATIO = 0.002f;
  43. static const std::string typeNames[] =
  44. {
  45. "directional",
  46. "point",
  47. "spot",
  48. "splitpoint"
  49. };
  50. void BiasParameters::validate()
  51. {
  52. mConstantBias = clamp(mConstantBias, 0.0f, 1.0f);
  53. mSlopeScaledBias = clamp(mSlopeScaledBias, 0.0f, 1.0f);
  54. }
  55. void CascadeParameters::validate()
  56. {
  57. mSplits = max(mSplits, 1);
  58. mLambda = clamp(mLambda, 0.0f, 1.0f);
  59. mSplitFadeRange = clamp(mSplitFadeRange, M_EPSILON, 0.5f);
  60. mShadowRange = max(mShadowRange, M_EPSILON);
  61. }
  62. void FocusParameters::validate()
  63. {
  64. mQuantize = max(mQuantize, SHADOW_MIN_QUANTIZE);
  65. mMinView = max(mMinView, SHADOW_MIN_VIEW);
  66. }
  67. Light::Light(Octant* octant, const std::string& name) :
  68. VolumeNode(NODE_LIGHT, octant, name),
  69. mLightType(DEFAULT_LIGHTTYPE),
  70. mDirection(DEFAULT_DIRECTION),
  71. mSpecularIntensity(0.0f),
  72. mRange(0.0f),
  73. mFov(DEFAULT_FOV),
  74. mAspectRatio(1.0f),
  75. mFadeDistance(0.0f),
  76. mDetailLevel(0),
  77. mShadowDetailLevel(0),
  78. mShadowBias(BiasParameters(DEFAULT_CONSTANTBIAS, DEFAULT_SLOPESCALEDBIAS)),
  79. mShadowCascade(CascadeParameters(1, DEFAULT_LAMBDA, DEFAULT_SHADOWFADERANGE, M_LARGE_VALUE)),
  80. mShadowFocus(FocusParameters(true, true, true, DEFAULT_SHADOWQUANTIZE, DEFAULT_SHADOWMINVIEW)),
  81. mShadowFadeDistance(0.0f),
  82. mShadowIntensity(0.0f),
  83. mShadowResolution(1.0f),
  84. mShadowNearFarRatio(DEFAULT_SHADOWNEARFARRATIO),
  85. mNearSplit(0.0f),
  86. mFarSplit(M_LARGE_VALUE),
  87. mNearFadeRange(M_EPSILON),
  88. mFarFadeRange(M_EPSILON),
  89. mFrustumDirty(true),
  90. mShadowMap(0)
  91. {
  92. }
  93. Light::~Light()
  94. {
  95. }
  96. void Light::save(Serializer& dest)
  97. {
  98. // Write VolumeNode properties
  99. VolumeNode::save(dest);
  100. // Write Light properties
  101. dest.writeUByte((unsigned char)mLightType);
  102. dest.writeVector3(mDirection);
  103. dest.writeColor(mColor);
  104. dest.writeFloat(mSpecularIntensity);
  105. dest.writeFloat(mRange);
  106. dest.writeFloat(mFov);
  107. dest.writeFloat(mAspectRatio);
  108. dest.writeFloat(mFadeDistance);
  109. dest.writeUByte(mDetailLevel);
  110. dest.writeUByte(mShadowDetailLevel);
  111. dest.writeFloat(mShadowBias.mConstantBias);
  112. dest.writeFloat(mShadowBias.mSlopeScaledBias);
  113. dest.writeFloat(mShadowCascade.mLambda);
  114. dest.writeFloat(mShadowCascade.mShadowRange);
  115. dest.writeFloat(mShadowCascade.mSplitFadeRange);
  116. dest.writeVLE(mShadowCascade.mSplits);
  117. dest.writeBool(mShadowFocus.mFocus);
  118. dest.writeBool(mShadowFocus.mNonUniform);
  119. dest.writeBool(mShadowFocus.mZoomOut);
  120. dest.writeFloat(mShadowFocus.mQuantize);
  121. dest.writeFloat(mShadowFocus.mMinView);
  122. dest.writeFloat(mShadowFadeDistance);
  123. dest.writeFloat(mShadowIntensity);
  124. dest.writeFloat(mShadowResolution);
  125. dest.writeFloat(mShadowNearFarRatio);
  126. dest.writeStringHash(getResourceHash(mRampTexture));
  127. if (mRampTexture)
  128. dest.writeBool(mRampTexture->getType() == TextureCube::getTypeStatic());
  129. dest.writeStringHash(getResourceHash(mSpotTexture));
  130. if (mSpotTexture)
  131. dest.writeBool(mSpotTexture->getType() == TextureCube::getTypeStatic());
  132. }
  133. void Light::load(Deserializer& source, ResourceCache* cache)
  134. {
  135. // Read VolumeNode properties
  136. VolumeNode::load(source, cache);
  137. // Read Light properties
  138. mLightType = (LightType)source.readUByte();
  139. mDirection = source.readVector3();
  140. mColor = source.readColor();
  141. mSpecularIntensity = source.readFloat();
  142. mRange = source.readFloat();
  143. mFov = source.readFloat();
  144. mAspectRatio = source.readFloat();
  145. mFadeDistance = source.readFloat();
  146. mDetailLevel = source.readUByte();
  147. mShadowDetailLevel = source.readUByte();
  148. mShadowBias.mConstantBias = source.readFloat();
  149. mShadowBias.mSlopeScaledBias = source.readFloat();
  150. mShadowCascade.mLambda = source.readFloat();
  151. mShadowCascade.mShadowRange = source.readFloat();
  152. mShadowCascade.mSplitFadeRange = source.readFloat();
  153. mShadowCascade.mSplits = source.readVLE();
  154. mShadowFocus.mFocus = source.readBool();
  155. mShadowFocus.mNonUniform = source.readBool();
  156. mShadowFocus.mZoomOut = source.readBool();
  157. mShadowFocus.mQuantize = source.readFloat();
  158. mShadowFocus.mMinView = source.readFloat();
  159. mShadowFadeDistance = source.readFloat();
  160. mShadowIntensity = source.readFloat();
  161. mShadowResolution = source.readFloat();
  162. mShadowNearFarRatio = source.readFloat();
  163. StringHash rampTexture = source.readStringHash();
  164. if (rampTexture)
  165. {
  166. if (!source.readBool())
  167. mRampTexture = cache->getResource<Texture2D>(rampTexture);
  168. else
  169. mRampTexture = cache->getResource<TextureCube>(rampTexture);
  170. }
  171. StringHash spotTexture = source.readStringHash();
  172. if (spotTexture)
  173. {
  174. if (!source.readBool())
  175. mSpotTexture = cache->getResource<Texture2D>(spotTexture);
  176. else
  177. mSpotTexture = cache->getResource<TextureCube>(spotTexture);
  178. }
  179. mShadowBias.validate();
  180. mShadowCascade.validate();
  181. mShadowFocus.validate();
  182. }
  183. void Light::saveXML(XMLElement& dest)
  184. {
  185. // Write VolumeNode properties
  186. VolumeNode::saveXML(dest);
  187. // Write Light properties
  188. XMLElement lightElem = dest.createChildElement("light");
  189. lightElem.setString("type", typeNames[mLightType]);
  190. lightElem.setVector3("direction", mDirection);
  191. lightElem.setColor("color", mColor);
  192. lightElem.setFloat("specular", mSpecularIntensity);
  193. lightElem.setFloat("range", mRange);
  194. lightElem.setFloat("fov", mFov);
  195. lightElem.setFloat("aspectratio", mAspectRatio);
  196. XMLElement lodElem = dest.getChildElement("lod");
  197. lodElem.setFloat("fadedistance", mFadeDistance);
  198. lodElem.setInt("detail", mDetailLevel);
  199. lodElem.setInt("shadowdetail", mShadowDetailLevel);
  200. XMLElement shadowElem = dest.createChildElement("shadows");
  201. shadowElem.setFloat("fadedistance", mShadowFadeDistance);
  202. shadowElem.setFloat("intensity", mShadowIntensity);
  203. shadowElem.setFloat("resolution", mShadowResolution);
  204. shadowElem.setFloat("nearfarratio", mShadowNearFarRatio);
  205. XMLElement biasElem = dest.createChildElement("shadowbias");
  206. biasElem.setFloat("constant", mShadowBias.mConstantBias);
  207. biasElem.setFloat("slopescaled", mShadowBias.mSlopeScaledBias);
  208. XMLElement cascadeElem = dest.createChildElement("shadowcascade");
  209. cascadeElem.setFloat("lambda", mShadowCascade.mLambda);
  210. cascadeElem.setFloat("maxrange", mShadowCascade.mShadowRange);
  211. cascadeElem.setFloat("faderange", mShadowCascade.mSplitFadeRange);
  212. cascadeElem.setInt("splits", mShadowCascade.mSplits);
  213. XMLElement focusElem = dest.createChildElement("shadowfocus");
  214. focusElem.setBool("enable", mShadowFocus.mFocus);
  215. focusElem.setBool("nonuniform", mShadowFocus.mNonUniform);
  216. focusElem.setBool("zoomout", mShadowFocus.mZoomOut);
  217. focusElem.setFloat("quantize", mShadowFocus.mQuantize);
  218. focusElem.setFloat("minview", mShadowFocus.mMinView);
  219. XMLElement rampElem = dest.createChildElement("ramptexture");
  220. rampElem.setString("name", getResourceName(mRampTexture));
  221. XMLElement spotElem = dest.createChildElement("spottexture");
  222. spotElem.setString("name", getResourceName(mSpotTexture));
  223. }
  224. void Light::loadXML(const XMLElement& source, ResourceCache* cache)
  225. {
  226. // Read VolumeNode properties
  227. VolumeNode::loadXML(source, cache);
  228. // Read Light properties
  229. XMLElement lightElem = source.getChildElement("light");
  230. mLightType = (LightType)getIndexFromStringList(lightElem.getStringLower("type"), typeNames, 3, 0);
  231. mDirection = lightElem.getVector3("direction");
  232. mColor = lightElem.getColor("color");
  233. mSpecularIntensity = lightElem.getFloat("specular");
  234. mRange = lightElem.getFloat("range");
  235. mFov = lightElem.getFloat("fov");
  236. mAspectRatio = lightElem.getFloat("aspectratio");
  237. XMLElement lodElem = source.getChildElement("lod");
  238. mFadeDistance = lodElem.getFloat("fadedistance");
  239. mDetailLevel = lodElem.getInt("detail");
  240. mShadowDetailLevel = lodElem.getInt("shadowdetail");
  241. XMLElement shadowElem = source.getChildElement("shadows");
  242. mShadowFadeDistance = shadowElem.getFloat("fadedistance");
  243. mShadowIntensity = shadowElem.getFloat("intensity");
  244. mShadowResolution = shadowElem.getFloat("resolution");
  245. mShadowNearFarRatio = shadowElem.getFloat("nearfarratio");
  246. XMLElement biasElem = source.getChildElement("shadowbias");
  247. mShadowBias.mConstantBias = biasElem.getFloat("constant");
  248. mShadowBias.mSlopeScaledBias = biasElem.getFloat("slopescaled");
  249. XMLElement cascadeElem = source.getChildElement("shadowcascade");
  250. mShadowCascade.mLambda = cascadeElem.getFloat("lambda");
  251. mShadowCascade.mShadowRange = cascadeElem.getFloat("maxrange");
  252. mShadowCascade.mSplitFadeRange = cascadeElem.getFloat("faderange");
  253. mShadowCascade.mSplits = cascadeElem.getInt("splits");
  254. XMLElement focusElem = source.getChildElement("shadowfocus");
  255. mShadowFocus.mFocus = focusElem.getBool("enable");
  256. mShadowFocus.mNonUniform = focusElem.getBool("nonuniform");
  257. mShadowFocus.mZoomOut = focusElem.getBool("zoomout");
  258. mShadowFocus.mQuantize = focusElem.getFloat("quantize");
  259. mShadowFocus.mMinView = focusElem.getFloat("minview");
  260. XMLElement rampElem = source.getChildElement("ramptexture");
  261. std::string name = rampElem.getString("name");
  262. if (getExtension(name) == ".xml")
  263. mRampTexture = cache->getResource<TextureCube>(name);
  264. else
  265. mRampTexture = cache->getResource<Texture2D>(name);
  266. XMLElement spotElem = source.getChildElement("spottexture");
  267. name = spotElem.getString("name");
  268. if (getExtension(name) == ".xml")
  269. mSpotTexture = cache->getResource<TextureCube>(name);
  270. else
  271. mSpotTexture = cache->getResource<Texture2D>(name);
  272. mShadowBias.validate();
  273. mShadowCascade.validate();
  274. mShadowFocus.validate();
  275. }
  276. bool Light::writeNetUpdate(Serializer& dest, Serializer& destRevision, Deserializer& baseRevision, const NetUpdateInfo& info)
  277. {
  278. // Write VolumeNode properties and see if there were any changes
  279. bool prevBits = VolumeNode::writeNetUpdate(dest, destRevision, baseRevision, info);
  280. // Build bitmask of changed properties
  281. unsigned char bits = 0;
  282. unsigned char bits2 = 0;
  283. unsigned char detailLevels = mDetailLevel | (mShadowDetailLevel << 4);
  284. checkUByte((unsigned char)mLightType, DEFAULT_LIGHTTYPE, baseRevision, bits, 1);
  285. checkUByte(detailLevels, 0, baseRevision, bits, 1);
  286. checkVector3(mDirection, DEFAULT_DIRECTION, baseRevision, bits, 2);
  287. checkColor(mColor, Color(), baseRevision, bits, 4);
  288. checkFloat(mSpecularIntensity, 0.0f, baseRevision, bits, 4);
  289. checkFloat(mRange, 0.0f, baseRevision, bits, 8);
  290. checkFloat(mFov, DEFAULT_FOV, baseRevision, bits, 16);
  291. checkFloat(mAspectRatio, 1.0f, baseRevision, bits, 16);
  292. checkFloat(mFadeDistance, 0.0f, baseRevision, bits, 32);
  293. checkStringHash(getResourceHash(mRampTexture), StringHash(), baseRevision, bits, 64);
  294. checkStringHash(getResourceHash(mSpotTexture), StringHash(), baseRevision, bits, 128);
  295. checkFloat(mShadowBias.mConstantBias, DEFAULT_CONSTANTBIAS, baseRevision, bits2, 1);
  296. checkFloat(mShadowBias.mSlopeScaledBias, DEFAULT_SLOPESCALEDBIAS, baseRevision, bits2, 1);
  297. checkFloat(mShadowCascade.mLambda, DEFAULT_LAMBDA, baseRevision, bits2, 2);
  298. checkFloat(mShadowCascade.mShadowRange, M_LARGE_VALUE, baseRevision, bits2, 2);
  299. checkFloat(mShadowCascade.mSplitFadeRange, DEFAULT_SHADOWFADERANGE, baseRevision, bits2, 2);
  300. checkVLE(mShadowCascade.mSplits, 1, baseRevision, bits2, 2);
  301. checkBool(mShadowFocus.mFocus, true, baseRevision, bits2, 4);
  302. checkBool(mShadowFocus.mNonUniform, true, baseRevision, bits2, 4);
  303. checkBool(mShadowFocus.mZoomOut, true, baseRevision, bits2, 4);
  304. checkFloat(mShadowFocus.mQuantize, DEFAULT_SHADOWQUANTIZE, baseRevision, bits2, 4);
  305. checkFloat(mShadowFocus.mMinView, DEFAULT_SHADOWMINVIEW, baseRevision, bits2, 4);
  306. checkFloat(mShadowFadeDistance, 0.0f, baseRevision, bits2, 8);
  307. checkUByte((unsigned char)(mShadowIntensity * 255), 0, baseRevision, bits2, 16);
  308. checkUByte((unsigned char)(mShadowResolution * 100), 100, baseRevision, bits2, 32);
  309. checkFloat(mShadowNearFarRatio, DEFAULT_SHADOWNEARFARRATIO, baseRevision, bits2, 64);
  310. // Send only information necessary for the light type, and shadow data only for shadowed lights
  311. if (mLightType == LIGHT_POINT)
  312. bits &= ~2;
  313. if (mLightType == LIGHT_DIRECTIONAL)
  314. bits &= ~(8 | 32);
  315. if (mLightType != LIGHT_SPOT)
  316. bits &= ~16;
  317. if (!mCastShadows)
  318. bits2 = 0;
  319. dest.writeUByte(bits);
  320. dest.writeUByte(bits2);
  321. writeUByteDelta((unsigned char)mLightType, dest, destRevision, bits & 1);
  322. writeUByteDelta(detailLevels, dest, destRevision, bits & 1);
  323. writeVector3Delta(mDirection, dest, destRevision, bits & 2);
  324. writeColorDelta(mColor, dest, destRevision, bits & 4);
  325. writeFloatDelta(mSpecularIntensity, dest, destRevision, bits & 4);
  326. writeFloatDelta(mRange, dest, destRevision, bits & 8);
  327. writeFloatDelta(mFov, dest, destRevision, bits & 16);
  328. writeFloatDelta(mAspectRatio, dest, destRevision, bits & 16);
  329. writeFloatDelta(mFadeDistance, dest, destRevision, bits & 32);
  330. writeStringHashDelta(getResourceHash(mRampTexture), dest, destRevision, bits & 64);
  331. if ((bits & 64) && (mRampTexture))
  332. dest.writeBool(mRampTexture->getType() == TextureCube::getTypeStatic());
  333. writeStringHashDelta(getResourceHash(mSpotTexture), dest, destRevision, bits & 128);
  334. if ((bits & 128) && (mSpotTexture))
  335. dest.writeBool(mSpotTexture->getType() == TextureCube::getTypeStatic());
  336. writeFloatDelta(mShadowBias.mConstantBias, dest, destRevision, bits2 & 1);
  337. writeFloatDelta(mShadowBias.mSlopeScaledBias, dest, destRevision, bits2 & 1);
  338. writeFloatDelta(mShadowCascade.mLambda, dest, destRevision, bits2 & 2);
  339. writeFloatDelta(mShadowCascade.mShadowRange, dest, destRevision, bits2 & 2);
  340. writeFloatDelta(mShadowCascade.mSplitFadeRange, dest, destRevision, bits2 & 2);
  341. writeVLEDelta(mShadowCascade.mSplits, dest, destRevision, bits2 & 2);
  342. writeBoolDelta(mShadowFocus.mFocus, dest, destRevision, bits2 & 4);
  343. writeBoolDelta(mShadowFocus.mNonUniform, dest, destRevision, bits2 & 4);
  344. writeBoolDelta(mShadowFocus.mZoomOut, dest, destRevision, bits2 & 4);
  345. writeFloatDelta(mShadowFocus.mQuantize, dest, destRevision, bits2 & 4);
  346. writeFloatDelta(mShadowFocus.mMinView, dest, destRevision, bits2 & 4);
  347. writeFloatDelta(mShadowFadeDistance, dest, destRevision, bits2 & 8);
  348. writeUByteDelta((unsigned char)(mShadowIntensity * 255), dest, destRevision, bits2 & 16);
  349. writeUByteDelta((unsigned char)(mShadowResolution * 100), dest, destRevision, bits2 & 32);
  350. writeFloatDelta(mShadowNearFarRatio, dest, destRevision, bits2 & 64);
  351. return prevBits || (bits != 0) || (bits2 != 0);
  352. }
  353. void Light::readNetUpdate(Deserializer& source, ResourceCache* cache, const NetUpdateInfo& info)
  354. {
  355. // Read VolumeNode properties
  356. VolumeNode::readNetUpdate(source, cache, info);
  357. unsigned char bits = source.readUByte();
  358. unsigned char bits2 = source.readUByte();
  359. if (bits & 1)
  360. {
  361. mLightType = (LightType)source.readUByte();
  362. unsigned char detailLevels = source.readUByte();
  363. mDetailLevel = detailLevels & 15;
  364. mShadowDetailLevel = detailLevels >> 4;
  365. }
  366. readVector3Delta(mDirection, source, bits & 2);
  367. readColorDelta(mColor, source, bits & 4);
  368. readFloatDelta(mSpecularIntensity, source, bits & 4);
  369. readFloatDelta(mRange, source, bits & 8);
  370. readFloatDelta(mFov, source, bits & 16);
  371. readFloatDelta(mAspectRatio, source, bits & 16);
  372. readFloatDelta(mFadeDistance, source, bits & 32);
  373. if (bits & 64)
  374. {
  375. StringHash rampTexture = source.readStringHash();
  376. if (rampTexture)
  377. {
  378. if (!source.readBool())
  379. mRampTexture = cache->getResource<Texture2D>(rampTexture);
  380. else
  381. mRampTexture = cache->getResource<TextureCube>(rampTexture);
  382. }
  383. }
  384. if (bits & 128)
  385. {
  386. StringHash spotTexture = source.readStringHash();
  387. if (spotTexture)
  388. {
  389. if (!source.readBool())
  390. mSpotTexture = cache->getResource<Texture2D>(spotTexture);
  391. else
  392. mSpotTexture = cache->getResource<TextureCube>(spotTexture);
  393. }
  394. }
  395. readFloatDelta(mShadowBias.mConstantBias, source, bits2 & 1);
  396. readFloatDelta(mShadowBias.mSlopeScaledBias, source, bits2 & 1);
  397. readFloatDelta(mShadowCascade.mLambda, source, bits2 & 2);
  398. readFloatDelta(mShadowCascade.mShadowRange, source, bits2 & 2);
  399. readFloatDelta(mShadowCascade.mSplitFadeRange, source, bits2 & 2);
  400. readVLEDelta(mShadowCascade.mSplits, source, bits2 & 2);
  401. readBoolDelta(mShadowFocus.mFocus, source, bits2 & 4);
  402. readBoolDelta(mShadowFocus.mNonUniform, source, bits2 & 4);
  403. readBoolDelta(mShadowFocus.mZoomOut, source, bits2 & 4);
  404. readFloatDelta(mShadowFocus.mQuantize, source, bits2 & 4);
  405. readFloatDelta(mShadowFocus.mMinView, source, bits2 & 4);
  406. readFloatDelta(mShadowFadeDistance, source, bits2 & 8);
  407. if (bits2 & 16)
  408. {
  409. int shadowIntensity = source.readUByte();
  410. mShadowIntensity = shadowIntensity / 255.0f;
  411. }
  412. if (bits2 & 32)
  413. {
  414. int shadowResolution = source.readUByte();
  415. mShadowResolution = shadowResolution / 100.0f;
  416. }
  417. readFloatDelta(mShadowNearFarRatio, source, bits2 & 64);
  418. if (bits)
  419. markDirty();
  420. }
  421. void Light::getResourceRefs(std::vector<Resource*>& dest)
  422. {
  423. if (mRampTexture)
  424. dest.push_back(mRampTexture);
  425. if (mSpotTexture)
  426. dest.push_back(mSpotTexture);
  427. }
  428. void Light::updateDistance(const FrameInfo& frame)
  429. {
  430. switch (mLightType)
  431. {
  432. case LIGHT_DIRECTIONAL:
  433. // Directional light affects the whole scene, so it is always "closest"
  434. mDistance = 0.0f;
  435. break;
  436. default:
  437. mDistance = frame.mCamera->getDistance(getWorldPosition());
  438. break;
  439. }
  440. }
  441. void Light::overrideTransforms(unsigned batchIndex, Camera& camera, const Matrix4x3** model, const Matrix4x3** view)
  442. {
  443. // Can use a static matrix because these are asked for one node at a time
  444. static Matrix4x3 lightModel;
  445. *model = &lightModel;
  446. switch (mLightType)
  447. {
  448. case LIGHT_DIRECTIONAL:
  449. lightModel = getDirLightTransform(camera, false);
  450. // Because of precision issues, set view to identity instead of adjusting model transform to match
  451. *view = &Matrix4x3::sIdentity;
  452. break;
  453. case LIGHT_POINT:
  454. lightModel.define(getWorldPosition(), Quaternion::sIdentity, mRange);
  455. break;
  456. case LIGHT_SPOT:
  457. {
  458. float yScale = tan(mFov * M_DEGTORAD * 0.5f) * mRange;
  459. float xScale = mAspectRatio * yScale;
  460. Quaternion rotation(Vector3(0.0f, 0.0f, 1.0f), mDirection);
  461. lightModel.define(getWorldPosition(), getWorldRotation() * rotation, Vector3(xScale, yScale, mRange));
  462. }
  463. break;
  464. case LIGHT_SPLITPOINT:
  465. {
  466. Quaternion rotation(Vector3(0.0f, 0.0f, 1.0f), mDirection);
  467. lightModel.define(getWorldPosition(), getWorldRotation() * rotation, mRange);
  468. }
  469. break;
  470. }
  471. }
  472. void Light::drawDebugGeometry(DebugRenderer* debug)
  473. {
  474. switch (mLightType)
  475. {
  476. case LIGHT_SPOT:
  477. debug->addFrustum(getFrustum(), mColor, false);
  478. break;
  479. case LIGHT_POINT:
  480. debug->addBoundingBox(getWorldBoundingBox(), getColor(), false);
  481. break;
  482. }
  483. }
  484. void Light::setLightType(LightType type)
  485. {
  486. mLightType = type;
  487. if (!isDirty())
  488. markDirty();
  489. }
  490. void Light::setDirection(const Vector3& direction)
  491. {
  492. mDirection = direction.getNormalized();
  493. if (!isDirty())
  494. markDirty();
  495. }
  496. void Light::setColor(const Color& color)
  497. {
  498. mColor = color;
  499. }
  500. void Light::setRange(float range)
  501. {
  502. mRange = max(range, 0.0f);
  503. if (!isDirty())
  504. markDirty();
  505. }
  506. void Light::setFov(float fov)
  507. {
  508. mFov = clamp(fov, 0.0f, M_MAX_FOV);
  509. if (!isDirty())
  510. markDirty();
  511. }
  512. void Light::setAspectRatio(float aspectRatio)
  513. {
  514. mAspectRatio = max(aspectRatio, M_EPSILON);
  515. if (!isDirty())
  516. markDirty();
  517. }
  518. void Light::setShadowNearFarRatio(float nearFarRatio)
  519. {
  520. mShadowNearFarRatio = clamp(nearFarRatio, 0.0f, 0.5f);
  521. }
  522. void Light::setSpecularIntensity(float intensity)
  523. {
  524. mSpecularIntensity = max(intensity, 0.0f);
  525. }
  526. void Light::setFadeDistance(float distance)
  527. {
  528. mFadeDistance = max(distance, 0.0f);
  529. }
  530. void Light::setDetailLevel(int detailLevel, int shadowDetailLevel)
  531. {
  532. mDetailLevel = clamp(detailLevel, 0, QUALITY_MAX);
  533. mShadowDetailLevel = clamp(shadowDetailLevel, 0, QUALITY_MAX);
  534. }
  535. void Light::setShadowBias(const BiasParameters& parameters)
  536. {
  537. mShadowBias = parameters;
  538. mShadowBias.validate();
  539. }
  540. void Light::setShadowCascade(const CascadeParameters& parameters)
  541. {
  542. mShadowCascade = parameters;
  543. mShadowCascade.validate();
  544. }
  545. void Light::setShadowFocus(const FocusParameters& parameters)
  546. {
  547. mShadowFocus = parameters;
  548. mShadowFocus.validate();
  549. }
  550. void Light::setShadowFadeDistance(float distance)
  551. {
  552. mShadowFadeDistance = max(distance, 0.0f);
  553. }
  554. void Light::setShadowIntensity(float intensity)
  555. {
  556. mShadowIntensity = clamp(intensity, 0.0f, 1.0f);
  557. }
  558. void Light::setShadowResolution(float resolution)
  559. {
  560. mShadowResolution = clamp(resolution, 0.25f, 1.0f);
  561. }
  562. void Light::setRampTexture(Texture* texture)
  563. {
  564. mRampTexture = texture;
  565. }
  566. void Light::setSpotTexture(Texture* texture)
  567. {
  568. mSpotTexture = texture;
  569. }
  570. void Light::copyFrom(Light* original)
  571. {
  572. setPosition(original->getWorldPosition());
  573. setRotation(original->getWorldRotation());
  574. setScale(original->getWorldScale());
  575. mCastShadows = original->mCastShadows;
  576. mDrawDistance = original->mDrawDistance;
  577. mShadowDistance = original->mShadowDistance;
  578. mViewMask = original->mViewMask;
  579. mLightMask = original->mLightMask;
  580. mDistance = original->mDistance;
  581. mDirection = original->mDirection;
  582. mLightType = original->mLightType;
  583. mRange = original->mRange;
  584. mFov = original->mFov;
  585. mAspectRatio = original->mAspectRatio;
  586. mColor = original->mColor;
  587. mSpecularIntensity = original->mSpecularIntensity;
  588. mFadeDistance = original->mFadeDistance;
  589. mShadowBias = original->mShadowBias;
  590. mShadowCascade = original->mShadowCascade;
  591. mShadowFocus = original->mShadowFocus;
  592. mShadowFadeDistance = original->mShadowFadeDistance;
  593. mShadowIntensity = original->mShadowIntensity;
  594. mShadowResolution = original->mShadowResolution;
  595. mRampTexture = original->mRampTexture;
  596. mSpotTexture = original->mSpotTexture;
  597. }
  598. bool Light::isNegative() const
  599. {
  600. return (mColor.mR < 0.0f) || (mColor.mG < 0.0f) || (mColor.mB < 0.0f);
  601. }
  602. const Frustum& Light::getFrustum()
  603. {
  604. // Note: frustum is also used for the point light when it is split for shadow rendering
  605. if (mFrustumDirty)
  606. {
  607. Matrix4x3 transform;
  608. Quaternion rotation(Vector3(0.0f, 0.0f, 1.0f), mDirection);
  609. transform.define(getWorldPosition(), getWorldRotation() * rotation, 1.0f);
  610. // Set a small near clip distance, so that the near plane can be calculated
  611. // Note: this is not necessarily the same near clip as on the actual shadow camera
  612. mFrustum.define(mFov, mAspectRatio, 1.0f, M_MIN_NEARCLIP, mRange, transform);
  613. }
  614. return mFrustum;
  615. }
  616. float Light::getVolumeExtent() const
  617. {
  618. switch (mLightType)
  619. {
  620. case LIGHT_POINT:
  621. return mRange * 1.36f;
  622. case LIGHT_SPOT:
  623. {
  624. float safeRange = mRange * 1.001f;
  625. float yScale = tan(mFov * M_DEGTORAD * 0.5f) * safeRange;
  626. float xScale = mAspectRatio * yScale;
  627. return sqrtf(xScale * xScale + yScale * yScale + safeRange * safeRange);
  628. }
  629. case LIGHT_SPLITPOINT:
  630. {
  631. float safeRange = mRange * 1.001f;
  632. return sqrtf(3.0f * safeRange * safeRange);
  633. }
  634. default:
  635. return M_LARGE_VALUE;
  636. }
  637. }
  638. Matrix4x3 Light::getDirLightTransform(Camera& camera, bool getNearQuad) const
  639. {
  640. Vector3 farVector = camera.getFrustumFarSize();
  641. float nearClip = camera.getNearClip();
  642. float farClip = camera.getFarClip();
  643. float distance;
  644. if (getNearQuad)
  645. distance = max(mNearSplit - mNearFadeRange, nearClip);
  646. else
  647. distance = min(mFarSplit, farClip);
  648. if (!camera.isOrthographic())
  649. farVector *= (distance / farClip);
  650. else
  651. farVector.mZ *= (distance / farClip);
  652. Matrix4x3 transform;
  653. transform.define(Vector3(0.0f, 0.0f, farVector.mZ), Quaternion::sIdentity, Vector3(farVector.mX, farVector.mY, 1.0f));
  654. return transform;
  655. }
  656. void Light::onMarkedDirty()
  657. {
  658. VolumeNode::onMarkedDirty();
  659. mFrustumDirty = true;
  660. }
  661. void Light::onWorldBoundingBoxUpdate(BoundingBox& worldBoundingBox)
  662. {
  663. switch (mLightType)
  664. {
  665. case LIGHT_DIRECTIONAL:
  666. // Directional light always sets humongous bounding box not affected by transform
  667. worldBoundingBox.define(-M_LARGE_VALUE, M_LARGE_VALUE);
  668. break;
  669. case LIGHT_POINT:
  670. {
  671. const Vector3& center = getWorldPosition();
  672. Vector3 edge(mRange, mRange, mRange);
  673. worldBoundingBox.define(center - edge, center + edge);
  674. }
  675. break;
  676. case LIGHT_SPOT:
  677. case LIGHT_SPLITPOINT:
  678. // Frustum is already transformed into world space
  679. worldBoundingBox.define(getFrustum());
  680. break;
  681. }
  682. }
  683. void Light::setNearSplit(float nearSplit)
  684. {
  685. mNearSplit = max(nearSplit, 0.0f);
  686. }
  687. void Light::setFarSplit(float farSplit)
  688. {
  689. mFarSplit = max(farSplit, 0.0f);
  690. }
  691. void Light::setNearFadeRange(float range)
  692. {
  693. mNearFadeRange = max(range, M_EPSILON);
  694. }
  695. void Light::setFarFadeRange(float range)
  696. {
  697. mFarFadeRange = max(range, M_EPSILON);
  698. }
  699. void Light::setShadowMap(Texture2D* shadowMap)
  700. {
  701. mShadowMap = shadowMap;
  702. }