Batch.cpp 42 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019
  1. //
  2. // Copyright (c) 2008-2013 the Urho3D project.
  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 deal
  6. // in the Software without restriction, including without limitation the rights
  7. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. // 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 FROM,
  19. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. // THE SOFTWARE.
  21. //
  22. #include "Precompiled.h"
  23. #include "Camera.h"
  24. #include "Geometry.h"
  25. #include "Graphics.h"
  26. #include "GraphicsImpl.h"
  27. #include "Material.h"
  28. #include "Node.h"
  29. #include "Renderer.h"
  30. #include "Profiler.h"
  31. #include "Scene.h"
  32. #include "ShaderVariation.h"
  33. #include "Sort.h"
  34. #include "Technique.h"
  35. #include "Texture2D.h"
  36. #include "VertexBuffer.h"
  37. #include "View.h"
  38. #include "Zone.h"
  39. #include "DebugNew.h"
  40. namespace Urho3D
  41. {
  42. inline bool CompareBatchesState(Batch* lhs, Batch* rhs)
  43. {
  44. if (lhs->sortKey_ != rhs->sortKey_)
  45. return lhs->sortKey_ < rhs->sortKey_;
  46. else
  47. return lhs->distance_ < rhs->distance_;
  48. }
  49. inline bool CompareBatchesFrontToBack(Batch* lhs, Batch* rhs)
  50. {
  51. if (lhs->distance_ != rhs->distance_)
  52. return lhs->distance_ < rhs->distance_;
  53. else
  54. return lhs->sortKey_ < rhs->sortKey_;
  55. }
  56. inline bool CompareBatchesBackToFront(Batch* lhs, Batch* rhs)
  57. {
  58. if (lhs->distance_ != rhs->distance_)
  59. return lhs->distance_ > rhs->distance_;
  60. else
  61. return lhs->sortKey_ < rhs->sortKey_;
  62. }
  63. inline bool CompareInstancesFrontToBack(const InstanceData& lhs, const InstanceData& rhs)
  64. {
  65. return lhs.distance_ < rhs.distance_;
  66. }
  67. void CalculateShadowMatrix(Matrix4& dest, LightBatchQueue* queue, unsigned split, Renderer* renderer, const Vector3& translation)
  68. {
  69. Camera* shadowCamera = queue->shadowSplits_[split].shadowCamera_;
  70. const IntRect& viewport = queue->shadowSplits_[split].shadowViewport_;
  71. Matrix3x4 posAdjust(translation, Quaternion::IDENTITY, 1.0f);
  72. Matrix3x4 shadowView(shadowCamera->GetInverseWorldTransform());
  73. Matrix4 shadowProj(shadowCamera->GetProjection());
  74. Matrix4 texAdjust(Matrix4::IDENTITY);
  75. Texture2D* shadowMap = queue->shadowMap_;
  76. if (!shadowMap)
  77. return;
  78. float width = (float)shadowMap->GetWidth();
  79. float height = (float)shadowMap->GetHeight();
  80. Vector2 offset(
  81. (float)viewport.left_ / width,
  82. (float)viewport.top_ / height
  83. );
  84. Vector2 scale(
  85. 0.5f * (float)viewport.Width() / width,
  86. 0.5f * (float)viewport.Height() / height
  87. );
  88. #ifdef USE_OPENGL
  89. offset.x_ += scale.x_;
  90. offset.y_ += scale.y_;
  91. offset.y_ = 1.0f - offset.y_;
  92. // If using 4 shadow samples, offset the position diagonally by half pixel
  93. if (renderer->GetShadowQuality() & SHADOWQUALITY_HIGH_16BIT)
  94. {
  95. offset.x_ -= 0.5f / width;
  96. offset.y_ -= 0.5f / height;
  97. }
  98. texAdjust.SetTranslation(Vector3(offset.x_, offset.y_, 0.5f));
  99. texAdjust.SetScale(Vector3(scale.x_, scale.y_, 0.5f));
  100. #else
  101. offset.x_ += scale.x_ + 0.5f / width;
  102. offset.y_ += scale.y_ + 0.5f / height;
  103. if (renderer->GetShadowQuality() & SHADOWQUALITY_HIGH_16BIT)
  104. {
  105. offset.x_ -= 0.5f / width;
  106. offset.y_ -= 0.5f / height;
  107. }
  108. scale.y_ = -scale.y_;
  109. texAdjust.SetTranslation(Vector3(offset.x_, offset.y_, 0.0f));
  110. texAdjust.SetScale(Vector3(scale.x_, scale.y_, 1.0f));
  111. #endif
  112. dest = texAdjust * shadowProj * shadowView * posAdjust;
  113. }
  114. void CalculateSpotMatrix(Matrix4& dest, Light* light, const Vector3& translation)
  115. {
  116. Node* lightNode = light->GetNode();
  117. Matrix3x4 posAdjust(translation, Quaternion::IDENTITY, 1.0f);
  118. Matrix3x4 spotView = lightNode->GetWorldTransform();
  119. // Remove any scaling
  120. spotView.SetRotation(spotView.RotationMatrix());
  121. Matrix4 spotProj(Matrix4::ZERO);
  122. Matrix4 texAdjust(Matrix4::IDENTITY);
  123. // Make the projected light slightly smaller than the shadow map to prevent light spill
  124. float h = 1.005f / tanf(light->GetFov() * M_DEGTORAD * 0.5f);
  125. float w = h / light->GetAspectRatio();
  126. spotProj.m00_ = w;
  127. spotProj.m11_ = h;
  128. spotProj.m22_ = 1.0f / Max(light->GetRange(), M_EPSILON);
  129. spotProj.m32_ = 1.0f;
  130. #ifdef USE_OPENGL
  131. texAdjust.SetTranslation(Vector3(0.5f, 0.5f, 0.5f));
  132. texAdjust.SetScale(Vector3(0.5f, -0.5f, 0.5f));
  133. #else
  134. texAdjust.SetTranslation(Vector3(0.5f, 0.5f, 0.0f));
  135. texAdjust.SetScale(Vector3(0.5f, -0.5f, 1.0f));
  136. #endif
  137. dest = texAdjust * spotProj * spotView.Inverse() * posAdjust;
  138. }
  139. void Batch::CalculateSortKey()
  140. {
  141. unsigned shaderID = ((*((unsigned*)&vertexShader_) / sizeof(ShaderVariation)) + (*((unsigned*)&pixelShader_) / sizeof(ShaderVariation))) & 0x3fff;
  142. if (!isBase_)
  143. shaderID |= 0x8000;
  144. if (pass_ && pass_->GetAlphaMask())
  145. shaderID |= 0x4000;
  146. unsigned lightQueueID = (*((unsigned*)&lightQueue_) / sizeof(LightBatchQueue)) & 0xffff;
  147. unsigned materialID = (*((unsigned*)&material_) / sizeof(Material)) & 0xffff;
  148. unsigned geometryID = (*((unsigned*)&geometry_) / sizeof(Geometry)) & 0xffff;
  149. sortKey_ = (((unsigned long long)shaderID) << 48) | (((unsigned long long)lightQueueID) << 32) |
  150. (((unsigned long long)materialID) << 16) | geometryID;
  151. }
  152. void Batch::Prepare(View* view, bool setModelTransform) const
  153. {
  154. if (!vertexShader_ || !pixelShader_)
  155. return;
  156. Graphics* graphics = view->GetGraphics();
  157. Renderer* renderer = view->GetRenderer();
  158. Node* cameraNode = camera_ ? camera_->GetNode() : 0;
  159. // Set pass / material-specific renderstates
  160. if (pass_ && material_)
  161. {
  162. bool isShadowPass = pass_->GetType() == PASS_SHADOW;
  163. graphics->SetBlendMode(pass_->GetBlendMode());
  164. renderer->SetCullMode(isShadowPass ? material_->GetShadowCullMode() : material_->GetCullMode(), camera_);
  165. if (!isShadowPass)
  166. {
  167. const BiasParameters& depthBias = material_->GetDepthBias();
  168. graphics->SetDepthBias(depthBias.constantBias_, depthBias.slopeScaledBias_);
  169. }
  170. graphics->SetDepthTest(pass_->GetDepthTestMode());
  171. graphics->SetDepthWrite(pass_->GetDepthWrite());
  172. }
  173. // Set shaders
  174. graphics->SetShaders(vertexShader_, pixelShader_);
  175. // Set global frame parameters
  176. if (graphics->NeedParameterUpdate(SP_FRAME, (void*)0))
  177. {
  178. Scene* scene = view->GetScene();
  179. if (scene)
  180. {
  181. float elapsedTime = scene->GetElapsedTime();
  182. graphics->SetShaderParameter(VSP_ELAPSEDTIME, elapsedTime);
  183. graphics->SetShaderParameter(PSP_ELAPSEDTIME, elapsedTime);
  184. }
  185. }
  186. // Set camera shader parameters
  187. unsigned cameraHash = overrideView_ ? (unsigned)camera_ + 4 : (unsigned)camera_;
  188. if (graphics->NeedParameterUpdate(SP_CAMERA, (void*)cameraHash))
  189. {
  190. // Calculate camera rotation just once
  191. Matrix3 cameraWorldRotation = cameraNode->GetWorldTransform().RotationMatrix();
  192. graphics->SetShaderParameter(VSP_CAMERAPOS, cameraNode->GetWorldPosition());
  193. graphics->SetShaderParameter(VSP_CAMERAROT, cameraWorldRotation);
  194. Vector4 depthMode = Vector4::ZERO;
  195. if (camera_->IsOrthographic())
  196. {
  197. depthMode.x_ = 1.0f;
  198. #ifdef USE_OPENGL
  199. depthMode.z_ = 0.5f;
  200. depthMode.w_ = 0.5f;
  201. #else
  202. depthMode.z_ = 1.0f;
  203. #endif
  204. }
  205. else
  206. depthMode.w_ = 1.0f / camera_->GetFarClip();
  207. graphics->SetShaderParameter(VSP_DEPTHMODE, depthMode);
  208. Vector3 nearVector, farVector;
  209. camera_->GetFrustumSize(nearVector, farVector);
  210. Vector4 viewportParams(farVector.x_, farVector.y_, farVector.z_, 0.0f);
  211. graphics->SetShaderParameter(VSP_FRUSTUMSIZE, viewportParams);
  212. Matrix4 projection = camera_->GetProjection();
  213. #ifdef USE_OPENGL
  214. // Add constant depth bias manually to the projection matrix due to glPolygonOffset() inconsistency
  215. float constantBias = 2.0f * graphics->GetDepthConstantBias();
  216. // On OpenGL ES slope-scaled bias can not be guaranteed to be available, and the shadow filtering is more coarse,
  217. // so use a higher constant bias
  218. #ifdef GL_ES_VERSION_2_0
  219. constantBias *= 1.5f;
  220. #endif
  221. projection.m22_ += projection.m32_ * constantBias;
  222. projection.m23_ += projection.m33_ * constantBias;
  223. #endif
  224. if (overrideView_)
  225. graphics->SetShaderParameter(VSP_VIEWPROJ, projection);
  226. else
  227. graphics->SetShaderParameter(VSP_VIEWPROJ, projection * camera_->GetInverseWorldTransform());
  228. graphics->SetShaderParameter(VSP_VIEWRIGHTVECTOR, cameraWorldRotation * Vector3::RIGHT);
  229. graphics->SetShaderParameter(VSP_VIEWUPVECTOR, cameraWorldRotation * Vector3::UP);
  230. }
  231. // Set viewport shader parameters
  232. IntVector2 rtSize = graphics->GetRenderTargetDimensions();
  233. IntRect viewport = graphics->GetViewport();
  234. unsigned viewportHash = (viewport.left_) | (viewport.top_ << 8) | (viewport.right_ << 16) | (viewport.bottom_ << 24);
  235. if (graphics->NeedParameterUpdate(SP_VIEWPORT, (void*)viewportHash))
  236. {
  237. float rtWidth = (float)rtSize.x_;
  238. float rtHeight = (float)rtSize.y_;
  239. float widthRange = 0.5f * viewport.Width() / rtWidth;
  240. float heightRange = 0.5f * viewport.Height() / rtHeight;
  241. #ifdef USE_OPENGL
  242. Vector4 bufferUVOffset(((float)viewport.left_) / rtWidth + widthRange,
  243. 1.0f - (((float)viewport.top_) / rtHeight + heightRange), widthRange, heightRange);
  244. #else
  245. Vector4 bufferUVOffset((0.5f + (float)viewport.left_) / rtWidth + widthRange,
  246. (0.5f + (float)viewport.top_) / rtHeight + heightRange, widthRange, heightRange);
  247. #endif
  248. graphics->SetShaderParameter(VSP_GBUFFEROFFSETS, bufferUVOffset);
  249. float sizeX = 1.0f / rtWidth;
  250. float sizeY = 1.0f / rtHeight;
  251. graphics->SetShaderParameter(PSP_GBUFFERINVSIZE, Vector4(sizeX, sizeY, 0.0f, 0.0f));
  252. }
  253. // Set model transform
  254. if (setModelTransform && graphics->NeedParameterUpdate(SP_OBJECTTRANSFORM, worldTransform_))
  255. graphics->SetShaderParameter(VSP_MODEL, *worldTransform_);
  256. // Set skinning transforms
  257. if (shaderData_ && shaderDataSize_ && graphics->NeedParameterUpdate(SP_OBJECTDATA, shaderData_))
  258. graphics->SetShaderParameter(VSP_SKINMATRICES, shaderData_, shaderDataSize_);
  259. // Set zone-related shader parameters
  260. BlendMode blend = graphics->GetBlendMode();
  261. Zone* fogColorZone = (blend == BLEND_ADD || blend == BLEND_ADDALPHA) ? renderer->GetDefaultZone() : zone_;
  262. unsigned zoneHash = (unsigned)zone_ + (unsigned)fogColorZone;
  263. if (zone_ && graphics->NeedParameterUpdate(SP_ZONE, (void*)zoneHash))
  264. {
  265. graphics->SetShaderParameter(VSP_AMBIENTSTARTCOLOR, zone_->GetAmbientStartColor());
  266. graphics->SetShaderParameter(VSP_AMBIENTENDCOLOR, zone_->GetAmbientEndColor().ToVector4() - zone_->GetAmbientStartColor().ToVector4());
  267. const BoundingBox& box = zone_->GetBoundingBox();
  268. Vector3 boxSize = box.Size();
  269. Matrix3x4 adjust(Matrix3x4::IDENTITY);
  270. adjust.SetScale(Vector3(1.0f / boxSize.x_, 1.0f / boxSize.y_, 1.0f / boxSize.z_));
  271. adjust.SetTranslation(Vector3(0.5f, 0.5f, 0.5f));
  272. Matrix3x4 zoneTransform = adjust * zone_->GetInverseWorldTransform();
  273. graphics->SetShaderParameter(VSP_ZONE, zoneTransform);
  274. graphics->SetShaderParameter(PSP_AMBIENTCOLOR, zone_->GetAmbientColor());
  275. // If the pass is additive, override fog color to black so that shaders do not need a separate additive path
  276. graphics->SetShaderParameter(PSP_FOGCOLOR, fogColorZone->GetFogColor());
  277. float farClip = camera_->GetFarClip();
  278. float fogStart = Min(zone_->GetFogStart(), farClip);
  279. float fogEnd = Min(zone_->GetFogEnd(), farClip);
  280. if (fogStart >= fogEnd * (1.0f - M_LARGE_EPSILON))
  281. fogStart = fogEnd * (1.0f - M_LARGE_EPSILON);
  282. float fogRange = Max(fogEnd - fogStart, M_EPSILON);
  283. Vector4 fogParams(fogEnd / farClip, farClip / fogRange, 0.0f, 0.0f);
  284. graphics->SetShaderParameter(PSP_FOGPARAMS, fogParams);
  285. }
  286. // Set light-related shader parameters
  287. Light* light = 0;
  288. Texture2D* shadowMap = 0;
  289. if (lightQueue_)
  290. {
  291. light = lightQueue_->light_;
  292. shadowMap = lightQueue_->shadowMap_;
  293. if (graphics->NeedParameterUpdate(SP_VERTEXLIGHTS, lightQueue_) && graphics->HasShaderParameter(VS, VSP_VERTEXLIGHTS))
  294. {
  295. Vector4 vertexLights[MAX_VERTEX_LIGHTS * 3];
  296. const PODVector<Light*>& lights = lightQueue_->vertexLights_;
  297. for (unsigned i = 0; i < lights.Size(); ++i)
  298. {
  299. Light* vertexLight = lights[i];
  300. Node* vertexLightNode = vertexLight->GetNode();
  301. LightType type = vertexLight->GetLightType();
  302. // Attenuation
  303. float invRange, cutoff, invCutoff;
  304. if (type == LIGHT_DIRECTIONAL)
  305. invRange = 0.0f;
  306. else
  307. invRange = 1.0f / Max(vertexLight->GetRange(), M_EPSILON);
  308. if (type == LIGHT_SPOT)
  309. {
  310. cutoff = cosf(vertexLight->GetFov() * 0.5f * M_DEGTORAD);
  311. invCutoff = 1.0f / (1.0f - cutoff);
  312. }
  313. else
  314. {
  315. cutoff = -1.0f;
  316. invCutoff = 1.0f;
  317. }
  318. // Color
  319. float fade = 1.0f;
  320. float fadeEnd = vertexLight->GetDrawDistance();
  321. float fadeStart = vertexLight->GetFadeDistance();
  322. // Do fade calculation for light if both fade & draw distance defined
  323. if (vertexLight->GetLightType() != LIGHT_DIRECTIONAL && fadeEnd > 0.0f && fadeStart > 0.0f && fadeStart < fadeEnd)
  324. fade = Min(1.0f - (vertexLight->GetDistance() - fadeStart) / (fadeEnd - fadeStart), 1.0f);
  325. Color color = vertexLight->GetColor() * fade;
  326. vertexLights[i * 3] = Vector4(color.r_, color.g_, color.b_, invRange);
  327. // Direction
  328. vertexLights[i * 3 + 1] = Vector4(-(vertexLightNode->GetWorldDirection()), cutoff);
  329. // Position
  330. vertexLights[i * 3 + 2] = Vector4(vertexLightNode->GetWorldPosition(), invCutoff);
  331. }
  332. if (lights.Size())
  333. graphics->SetShaderParameter(VSP_VERTEXLIGHTS, vertexLights[0].Data(), lights.Size() * 3 * 4);
  334. }
  335. }
  336. if (light && graphics->NeedParameterUpdate(SP_LIGHT, light))
  337. {
  338. Node* lightNode = light->GetNode();
  339. Matrix3 lightWorldRotation = lightNode->GetWorldTransform().RotationMatrix();
  340. graphics->SetShaderParameter(VSP_LIGHTDIR, lightWorldRotation * Vector3::BACK);
  341. float atten = 1.0f / Max(light->GetRange(), M_EPSILON);
  342. graphics->SetShaderParameter(VSP_LIGHTPOS, Vector4(lightNode->GetWorldPosition(), atten));
  343. if (graphics->HasShaderParameter(VS, VSP_LIGHTMATRICES))
  344. {
  345. switch (light->GetLightType())
  346. {
  347. case LIGHT_DIRECTIONAL:
  348. {
  349. Matrix4 shadowMatrices[MAX_CASCADE_SPLITS];
  350. unsigned numSplits = lightQueue_->shadowSplits_.Size();
  351. for (unsigned i = 0; i < numSplits; ++i)
  352. CalculateShadowMatrix(shadowMatrices[i], lightQueue_, i, renderer, Vector3::ZERO);
  353. graphics->SetShaderParameter(VSP_LIGHTMATRICES, shadowMatrices[0].Data(), 16 * numSplits);
  354. }
  355. break;
  356. case LIGHT_SPOT:
  357. {
  358. Matrix4 shadowMatrices[2];
  359. CalculateSpotMatrix(shadowMatrices[0], light, Vector3::ZERO);
  360. bool isShadowed = shadowMap && graphics->HasTextureUnit(TU_SHADOWMAP);
  361. if (isShadowed)
  362. CalculateShadowMatrix(shadowMatrices[1], lightQueue_, 0, renderer, Vector3::ZERO);
  363. graphics->SetShaderParameter(VSP_LIGHTMATRICES, shadowMatrices[0].Data(), isShadowed ? 32 : 16);
  364. }
  365. break;
  366. case LIGHT_POINT:
  367. {
  368. Matrix4 lightVecRot(lightNode->GetWorldTransform().RotationMatrix());
  369. // HLSL compiler will pack the parameters as if the matrix is only 3x4, so must be careful to not overwrite
  370. // the next parameter
  371. #ifdef USE_OPENGL
  372. graphics->SetShaderParameter(VSP_LIGHTMATRICES, lightVecRot.Data(), 16);
  373. #else
  374. graphics->SetShaderParameter(VSP_LIGHTMATRICES, lightVecRot.Data(), 12);
  375. #endif
  376. }
  377. break;
  378. }
  379. }
  380. float fade = 1.0f;
  381. float fadeEnd = light->GetDrawDistance();
  382. float fadeStart = light->GetFadeDistance();
  383. // Do fade calculation for light if both fade & draw distance defined
  384. if (light->GetLightType() != LIGHT_DIRECTIONAL && fadeEnd > 0.0f && fadeStart > 0.0f && fadeStart < fadeEnd)
  385. fade = Min(1.0f - (light->GetDistance() - fadeStart) / (fadeEnd - fadeStart), 1.0f);
  386. graphics->SetShaderParameter(PSP_LIGHTCOLOR, Vector4(light->GetColor().RGBValues(), light->GetSpecularIntensity()) * fade);
  387. graphics->SetShaderParameter(PSP_LIGHTDIR, lightWorldRotation * Vector3::BACK);
  388. graphics->SetShaderParameter(PSP_LIGHTPOS, Vector4(lightNode->GetWorldPosition() - cameraNode->GetWorldPosition(), atten));
  389. if (graphics->HasShaderParameter(PS, PSP_LIGHTMATRICES))
  390. {
  391. switch (light->GetLightType())
  392. {
  393. case LIGHT_DIRECTIONAL:
  394. {
  395. Matrix4 shadowMatrices[MAX_CASCADE_SPLITS];
  396. unsigned numSplits = lightQueue_->shadowSplits_.Size();
  397. for (unsigned i = 0; i < numSplits; ++i)
  398. CalculateShadowMatrix(shadowMatrices[i], lightQueue_, i, renderer, cameraNode->GetWorldPosition());
  399. graphics->SetShaderParameter(PSP_LIGHTMATRICES, shadowMatrices[0].Data(), 16 * numSplits);
  400. }
  401. break;
  402. case LIGHT_SPOT:
  403. {
  404. Matrix4 shadowMatrices[2];
  405. CalculateSpotMatrix(shadowMatrices[0], light, cameraNode->GetWorldPosition());
  406. bool isShadowed = lightQueue_->shadowMap_ != 0;
  407. if (isShadowed)
  408. CalculateShadowMatrix(shadowMatrices[1], lightQueue_, 0, renderer, cameraNode->GetWorldPosition());
  409. graphics->SetShaderParameter(PSP_LIGHTMATRICES, shadowMatrices[0].Data(), isShadowed ? 32 : 16);
  410. }
  411. break;
  412. case LIGHT_POINT:
  413. {
  414. Matrix4 lightVecRot(lightNode->GetWorldTransform().RotationMatrix());
  415. // HLSL compiler will pack the parameters as if the matrix is only 3x4, so must be careful to not overwrite
  416. // the next parameter
  417. #ifdef USE_OPENGL
  418. graphics->SetShaderParameter(PSP_LIGHTMATRICES, lightVecRot.Data(), 16);
  419. #else
  420. graphics->SetShaderParameter(PSP_LIGHTMATRICES, lightVecRot.Data(), 12);
  421. #endif
  422. }
  423. break;
  424. }
  425. }
  426. // Set shadow mapping shader parameters
  427. if (shadowMap)
  428. {
  429. {
  430. unsigned faceWidth = shadowMap->GetWidth() / 2;
  431. unsigned faceHeight = shadowMap->GetHeight() / 3;
  432. float width = (float)shadowMap->GetWidth();
  433. float height = (float)shadowMap->GetHeight();
  434. #ifdef USE_OPENGL
  435. float mulX = (float)(faceWidth - 3) / width;
  436. float mulY = (float)(faceHeight - 3) / height;
  437. float addX = 1.5f / width;
  438. float addY = 1.5f / height;
  439. #else
  440. float mulX = (float)(faceWidth - 4) / width;
  441. float mulY = (float)(faceHeight - 4) / height;
  442. float addX = 2.5f / width;
  443. float addY = 2.5f / height;
  444. #endif
  445. // If using 4 shadow samples, offset the position diagonally by half pixel
  446. if (renderer->GetShadowQuality() & SHADOWQUALITY_HIGH_16BIT)
  447. {
  448. addX -= 0.5f / width;
  449. addY -= 0.5f / height;
  450. }
  451. graphics->SetShaderParameter(PSP_SHADOWCUBEADJUST, Vector4(mulX, mulY, addX, addY));
  452. }
  453. {
  454. Camera* shadowCamera = lightQueue_->shadowSplits_[0].shadowCamera_;
  455. float nearClip = shadowCamera->GetNearClip();
  456. float farClip = shadowCamera->GetFarClip();
  457. float q = farClip / (farClip - nearClip);
  458. float r = -q * nearClip;
  459. const CascadeParameters& parameters = light->GetShadowCascade();
  460. float viewFarClip = camera_->GetFarClip();
  461. float shadowRange = parameters.GetShadowRange();
  462. float fadeStart = parameters.fadeStart_ * shadowRange / viewFarClip;
  463. float fadeEnd = shadowRange / viewFarClip;
  464. float fadeRange = fadeEnd - fadeStart;
  465. graphics->SetShaderParameter(PSP_SHADOWDEPTHFADE, Vector4(q, r, fadeStart, 1.0f / fadeRange));
  466. }
  467. {
  468. float intensity = light->GetShadowIntensity();
  469. float fadeStart = light->GetShadowFadeDistance();
  470. float fadeEnd = light->GetShadowDistance();
  471. if (fadeStart > 0.0f && fadeEnd > 0.0f && fadeEnd > fadeStart)
  472. intensity = Lerp(intensity, 1.0f, Clamp((light->GetDistance() - fadeStart) / (fadeEnd - fadeStart), 0.0f, 1.0f));
  473. float pcfValues = (1.0f - intensity);
  474. float samples = renderer->GetShadowQuality() >= SHADOWQUALITY_HIGH_16BIT ? 4.0f : 1.0f;
  475. graphics->SetShaderParameter(PSP_SHADOWINTENSITY, Vector4(pcfValues / samples, intensity, 0.0f, 0.0f));
  476. }
  477. float sizeX = 1.0f / (float)shadowMap->GetWidth();
  478. float sizeY = 1.0f / (float)shadowMap->GetHeight();
  479. graphics->SetShaderParameter(PSP_SHADOWMAPINVSIZE, Vector4(sizeX, sizeY, 0.0f, 0.0f));
  480. Vector4 lightSplits(M_LARGE_VALUE, M_LARGE_VALUE, M_LARGE_VALUE, M_LARGE_VALUE);
  481. if (lightQueue_->shadowSplits_.Size() > 1)
  482. lightSplits.x_ = lightQueue_->shadowSplits_[0].farSplit_ / camera_->GetFarClip();
  483. if (lightQueue_->shadowSplits_.Size() > 2)
  484. lightSplits.y_ = lightQueue_->shadowSplits_[1].farSplit_ / camera_->GetFarClip();
  485. if (lightQueue_->shadowSplits_.Size() > 3)
  486. lightSplits.z_ = lightQueue_->shadowSplits_[2].farSplit_ / camera_->GetFarClip();
  487. graphics->SetShaderParameter(PSP_SHADOWSPLITS, lightSplits);
  488. }
  489. }
  490. // Set material-specific shader parameters and textures
  491. if (material_)
  492. {
  493. if (graphics->NeedParameterUpdate(SP_MATERIAL, material_))
  494. {
  495. const HashMap<StringHash, MaterialShaderParameter>& parameters = material_->GetShaderParameters();
  496. for (HashMap<StringHash, MaterialShaderParameter>::ConstIterator i = parameters.Begin(); i != parameters.End(); ++i)
  497. graphics->SetShaderParameter(i->first_, i->second_.value_);
  498. }
  499. const SharedPtr<Texture>* textures = material_->GetTextures();
  500. for (unsigned i = 0; i < MAX_MATERIAL_TEXTURE_UNITS; ++i)
  501. {
  502. TextureUnit unit = (TextureUnit)i;
  503. if (graphics->HasTextureUnit(unit))
  504. graphics->SetTexture(i, textures[i]);
  505. }
  506. }
  507. // Set light-related textures
  508. if (light)
  509. {
  510. if (shadowMap && graphics->HasTextureUnit(TU_SHADOWMAP))
  511. graphics->SetTexture(TU_SHADOWMAP, shadowMap);
  512. if (graphics->HasTextureUnit(TU_LIGHTRAMP))
  513. {
  514. Texture* rampTexture = light->GetRampTexture();
  515. if (!rampTexture)
  516. rampTexture = renderer->GetDefaultLightRamp();
  517. graphics->SetTexture(TU_LIGHTRAMP, rampTexture);
  518. }
  519. if (graphics->HasTextureUnit(TU_LIGHTSHAPE))
  520. {
  521. Texture* shapeTexture = light->GetShapeTexture();
  522. if (!shapeTexture && light->GetLightType() == LIGHT_SPOT)
  523. shapeTexture = renderer->GetDefaultLightSpot();
  524. graphics->SetTexture(TU_LIGHTSHAPE, shapeTexture);
  525. }
  526. }
  527. }
  528. void Batch::Draw(View* view) const
  529. {
  530. if (!geometry_->IsEmpty())
  531. {
  532. Prepare(view);
  533. geometry_->Draw(view->GetGraphics());
  534. }
  535. }
  536. void BatchGroup::SetTransforms(View* view, void* lockedData, unsigned& freeIndex)
  537. {
  538. // Do not use up buffer space if not going to draw as instanced
  539. if (geometry_->GetIndexCount() > (unsigned)view->GetRenderer()->GetMaxInstanceTriangles() * 3)
  540. return;
  541. startIndex_ = freeIndex;
  542. Matrix3x4* dest = (Matrix3x4*)lockedData;
  543. dest += freeIndex;
  544. for (unsigned i = 0; i < instances_.Size(); ++i)
  545. *dest++ = *instances_[i].worldTransform_;
  546. freeIndex += instances_.Size();
  547. }
  548. void BatchGroup::Draw(View* view) const
  549. {
  550. Graphics* graphics = view->GetGraphics();
  551. Renderer* renderer = view->GetRenderer();
  552. if (instances_.Size() && !geometry_->IsEmpty())
  553. {
  554. // Draw as individual objects if instancing not supported
  555. VertexBuffer* instanceBuffer = renderer->GetInstancingBuffer();
  556. if (!instanceBuffer || geometry_->GetIndexCount() > (unsigned)renderer->GetMaxInstanceTriangles() * 3)
  557. {
  558. Batch::Prepare(view, false);
  559. graphics->SetIndexBuffer(geometry_->GetIndexBuffer());
  560. graphics->SetVertexBuffers(geometry_->GetVertexBuffers(), geometry_->GetVertexElementMasks());
  561. for (unsigned i = 0; i < instances_.Size(); ++i)
  562. {
  563. if (graphics->NeedParameterUpdate(SP_OBJECTTRANSFORM, instances_[i].worldTransform_))
  564. graphics->SetShaderParameter(VSP_MODEL, *instances_[i].worldTransform_);
  565. graphics->Draw(geometry_->GetPrimitiveType(), geometry_->GetIndexStart(), geometry_->GetIndexCount(),
  566. geometry_->GetVertexStart(), geometry_->GetVertexCount());
  567. }
  568. }
  569. else
  570. {
  571. Batch::Prepare(view, false);
  572. // Get the geometry vertex buffers, then add the instancing stream buffer
  573. // Hack: use a const_cast to avoid dynamic allocation of new temp vectors
  574. Vector<SharedPtr<VertexBuffer> >& vertexBuffers = const_cast<Vector<SharedPtr<VertexBuffer> >&>
  575. (geometry_->GetVertexBuffers());
  576. PODVector<unsigned>& elementMasks = const_cast<PODVector<unsigned>&>(geometry_->GetVertexElementMasks());
  577. vertexBuffers.Push(SharedPtr<VertexBuffer>(instanceBuffer));
  578. elementMasks.Push(instanceBuffer->GetElementMask());
  579. // No stream offset support, instancing buffer not pre-filled with transforms: have to fill now
  580. if (startIndex_ == M_MAX_UNSIGNED)
  581. {
  582. unsigned startIndex = 0;
  583. while (startIndex < instances_.Size())
  584. {
  585. unsigned instances = instances_.Size() - startIndex;
  586. if (instances > instanceBuffer->GetVertexCount())
  587. instances = instanceBuffer->GetVertexCount();
  588. // Copy the transforms
  589. Matrix3x4* dest = (Matrix3x4*)instanceBuffer->Lock(0, instances, true);
  590. if (dest)
  591. {
  592. for (unsigned i = 0; i < instances; ++i)
  593. dest[i] = *instances_[i + startIndex].worldTransform_;
  594. instanceBuffer->Unlock();
  595. graphics->SetIndexBuffer(geometry_->GetIndexBuffer());
  596. graphics->SetVertexBuffers(vertexBuffers, elementMasks);
  597. graphics->DrawInstanced(geometry_->GetPrimitiveType(), geometry_->GetIndexStart(),
  598. geometry_->GetIndexCount(), geometry_->GetVertexStart(), geometry_->GetVertexCount(), instances);
  599. }
  600. startIndex += instances;
  601. }
  602. }
  603. // Stream offset supported, and instancing buffer has been already filled, so just draw
  604. else
  605. {
  606. graphics->SetIndexBuffer(geometry_->GetIndexBuffer());
  607. graphics->SetVertexBuffers(vertexBuffers, elementMasks, startIndex_);
  608. graphics->DrawInstanced(geometry_->GetPrimitiveType(), geometry_->GetIndexStart(), geometry_->GetIndexCount(),
  609. geometry_->GetVertexStart(), geometry_->GetVertexCount(), instances_.Size());
  610. }
  611. // Remove the instancing buffer & element mask now
  612. vertexBuffers.Pop();
  613. elementMasks.Pop();
  614. }
  615. }
  616. }
  617. unsigned BatchGroupKey::ToHash() const
  618. {
  619. return ((unsigned)zone_) / sizeof(Zone) + ((unsigned)lightQueue_) / sizeof(LightBatchQueue) + ((unsigned)pass_) / sizeof(Pass)
  620. + ((unsigned)material_) / sizeof(Material) + ((unsigned)geometry_) / sizeof(Geometry);
  621. }
  622. void BatchQueue::Clear(int maxSortedInstances)
  623. {
  624. batches_.Clear();
  625. sortedBaseBatches_.Clear();
  626. sortedBatches_.Clear();
  627. baseBatchGroups_.Clear();
  628. batchGroups_.Clear();
  629. maxSortedInstances_ = maxSortedInstances;
  630. }
  631. void BatchQueue::SortBackToFront()
  632. {
  633. sortedBaseBatches_.Clear();
  634. sortedBatches_.Resize(batches_.Size());
  635. for (unsigned i = 0; i < batches_.Size(); ++i)
  636. sortedBatches_[i] = &batches_[i];
  637. Sort(sortedBatches_.Begin(), sortedBatches_.End(), CompareBatchesBackToFront);
  638. // Do not actually sort batch groups, just list them
  639. sortedBaseBatchGroups_.Resize(baseBatchGroups_.Size());
  640. sortedBatchGroups_.Resize(batchGroups_.Size());
  641. unsigned index = 0;
  642. for (HashMap<BatchGroupKey, BatchGroup>::Iterator i = baseBatchGroups_.Begin(); i != baseBatchGroups_.End(); ++i)
  643. sortedBaseBatchGroups_[index++] = &i->second_;
  644. index = 0;
  645. for (HashMap<BatchGroupKey, BatchGroup>::Iterator i = batchGroups_.Begin(); i != batchGroups_.End(); ++i)
  646. sortedBatchGroups_[index++] = &i->second_;
  647. }
  648. void BatchQueue::SortFrontToBack()
  649. {
  650. sortedBaseBatches_.Clear();
  651. sortedBatches_.Clear();
  652. // Need to divide into base and non-base batches here to ensure proper order in relation to grouped batches
  653. for (unsigned i = 0; i < batches_.Size(); ++i)
  654. {
  655. if (batches_[i].isBase_)
  656. sortedBaseBatches_.Push(&batches_[i]);
  657. else
  658. sortedBatches_.Push(&batches_[i]);
  659. }
  660. SortFrontToBack2Pass(sortedBaseBatches_);
  661. SortFrontToBack2Pass(sortedBatches_);
  662. // Sort each group front to back
  663. for (HashMap<BatchGroupKey, BatchGroup>::Iterator i = baseBatchGroups_.Begin(); i != baseBatchGroups_.End(); ++i)
  664. {
  665. if (i->second_.instances_.Size() <= maxSortedInstances_)
  666. {
  667. Sort(i->second_.instances_.Begin(), i->second_.instances_.End(), CompareInstancesFrontToBack);
  668. if (i->second_.instances_.Size())
  669. i->second_.distance_ = i->second_.instances_[0].distance_;
  670. }
  671. else
  672. {
  673. float minDistance = M_INFINITY;
  674. for (PODVector<InstanceData>::ConstIterator j = i->second_.instances_.Begin(); j != i->second_.instances_.End(); ++j)
  675. minDistance = Min(minDistance, j->distance_);
  676. i->second_.distance_ = minDistance;
  677. }
  678. }
  679. for (HashMap<BatchGroupKey, BatchGroup>::Iterator i = batchGroups_.Begin(); i != batchGroups_.End(); ++i)
  680. {
  681. if (i->second_.instances_.Size() <= maxSortedInstances_)
  682. {
  683. Sort(i->second_.instances_.Begin(), i->second_.instances_.End(), CompareInstancesFrontToBack);
  684. if (i->second_.instances_.Size())
  685. i->second_.distance_ = i->second_.instances_[0].distance_;
  686. }
  687. else
  688. {
  689. float minDistance = M_INFINITY;
  690. for (PODVector<InstanceData>::ConstIterator j = i->second_.instances_.Begin(); j != i->second_.instances_.End(); ++j)
  691. minDistance = Min(minDistance, j->distance_);
  692. i->second_.distance_ = minDistance;
  693. }
  694. }
  695. sortedBaseBatchGroups_.Resize(baseBatchGroups_.Size());
  696. sortedBatchGroups_.Resize(batchGroups_.Size());
  697. unsigned index = 0;
  698. for (HashMap<BatchGroupKey, BatchGroup>::Iterator i = baseBatchGroups_.Begin(); i != baseBatchGroups_.End(); ++i)
  699. sortedBaseBatchGroups_[index++] = &i->second_;
  700. index = 0;
  701. for (HashMap<BatchGroupKey, BatchGroup>::Iterator i = batchGroups_.Begin(); i != batchGroups_.End(); ++i)
  702. sortedBatchGroups_[index++] = &i->second_;
  703. SortFrontToBack2Pass(reinterpret_cast<PODVector<Batch*>& >(sortedBaseBatchGroups_));
  704. SortFrontToBack2Pass(reinterpret_cast<PODVector<Batch*>& >(sortedBatchGroups_));
  705. }
  706. void BatchQueue::SortFrontToBack2Pass(PODVector<Batch*>& batches)
  707. {
  708. // Mobile devices likely use a tiled deferred approach, with which front-to-back sorting is irrelevant. The 2-pass
  709. // method is also time consuming, so just sort with state having priority
  710. #ifdef GL_ES_VERSION_2_0
  711. Sort(batches.Begin(), batches.End(), CompareBatchesState);
  712. #else
  713. // For desktop, first sort by distance and remap shader/material/geometry IDs in the sort key
  714. Sort(batches.Begin(), batches.End(), CompareBatchesFrontToBack);
  715. unsigned freeShaderID = 0;
  716. unsigned short freeMaterialID = 0;
  717. unsigned short freeGeometryID = 0;
  718. for (PODVector<Batch*>::Iterator i = batches.Begin(); i != batches.End(); ++i)
  719. {
  720. Batch* batch = *i;
  721. unsigned shaderID = (batch->sortKey_ >> 32);
  722. HashMap<unsigned, unsigned>::ConstIterator j = shaderRemapping_.Find(shaderID);
  723. if (j != shaderRemapping_.End())
  724. shaderID = j->second_;
  725. else
  726. {
  727. shaderID = shaderRemapping_[shaderID] = freeShaderID | (shaderID & 0xc0000000);
  728. ++freeShaderID;
  729. }
  730. unsigned short materialID = (unsigned short)(batch->sortKey_ & 0xffff0000);
  731. HashMap<unsigned short, unsigned short>::ConstIterator k = materialRemapping_.Find(materialID);
  732. if (k != materialRemapping_.End())
  733. materialID = k->second_;
  734. else
  735. {
  736. materialID = materialRemapping_[materialID] = freeMaterialID;
  737. ++freeMaterialID;
  738. }
  739. unsigned short geometryID = (unsigned short)(batch->sortKey_ & 0xffff);
  740. HashMap<unsigned short, unsigned short>::ConstIterator l = geometryRemapping_.Find(geometryID);
  741. if (l != geometryRemapping_.End())
  742. geometryID = l->second_;
  743. else
  744. {
  745. geometryID = geometryRemapping_[geometryID] = freeGeometryID;
  746. ++freeGeometryID;
  747. }
  748. batch->sortKey_ = (((unsigned long long)shaderID) << 32) || (((unsigned long long)materialID) << 16) | geometryID;
  749. }
  750. shaderRemapping_.Clear();
  751. materialRemapping_.Clear();
  752. geometryRemapping_.Clear();
  753. // Finally sort again with the rewritten ID's
  754. Sort(batches.Begin(), batches.End(), CompareBatchesState);
  755. #endif
  756. }
  757. void BatchQueue::SetTransforms(View* view, void* lockedData, unsigned& freeIndex)
  758. {
  759. for (HashMap<BatchGroupKey, BatchGroup>::Iterator i = baseBatchGroups_.Begin(); i != baseBatchGroups_.End(); ++i)
  760. i->second_.SetTransforms(view, lockedData, freeIndex);
  761. for (HashMap<BatchGroupKey, BatchGroup>::Iterator i = batchGroups_.Begin(); i != batchGroups_.End(); ++i)
  762. i->second_.SetTransforms(view, lockedData, freeIndex);
  763. }
  764. void BatchQueue::Draw(View* view, bool useScissor, bool markToStencil) const
  765. {
  766. Graphics* graphics = view->GetGraphics();
  767. Renderer* renderer = view->GetRenderer();
  768. graphics->SetScissorTest(false);
  769. // During G-buffer rendering, mark opaque pixels to stencil buffer
  770. if (!markToStencil)
  771. graphics->SetStencilTest(false);
  772. // Base instanced
  773. for (PODVector<BatchGroup*>::ConstIterator i = sortedBaseBatchGroups_.Begin(); i != sortedBaseBatchGroups_.End(); ++i)
  774. {
  775. BatchGroup* group = *i;
  776. if (markToStencil)
  777. graphics->SetStencilTest(true, CMP_ALWAYS, OP_REF, OP_KEEP, OP_KEEP, group->lightMask_);
  778. group->Draw(view);
  779. }
  780. // Base non-instanced
  781. for (PODVector<Batch*>::ConstIterator i = sortedBaseBatches_.Begin(); i != sortedBaseBatches_.End(); ++i)
  782. {
  783. Batch* batch = *i;
  784. if (markToStencil)
  785. graphics->SetStencilTest(true, CMP_ALWAYS, OP_REF, OP_KEEP, OP_KEEP, batch->lightMask_);
  786. batch->Draw(view);
  787. }
  788. // Non-base instanced
  789. for (PODVector<BatchGroup*>::ConstIterator i = sortedBatchGroups_.Begin(); i != sortedBatchGroups_.End(); ++i)
  790. {
  791. BatchGroup* group = *i;
  792. if (useScissor && group->lightQueue_)
  793. renderer->OptimizeLightByScissor(group->lightQueue_->light_, group->camera_);
  794. if (markToStencil)
  795. graphics->SetStencilTest(true, CMP_ALWAYS, OP_REF, OP_KEEP, OP_KEEP, group->lightMask_);
  796. group->Draw(view);
  797. }
  798. // Non-base non-instanced
  799. for (PODVector<Batch*>::ConstIterator i = sortedBatches_.Begin(); i != sortedBatches_.End(); ++i)
  800. {
  801. Batch* batch = *i;
  802. if (useScissor)
  803. {
  804. if (!batch->isBase_ && batch->lightQueue_)
  805. renderer->OptimizeLightByScissor(batch->lightQueue_->light_, batch->camera_);
  806. else
  807. graphics->SetScissorTest(false);
  808. }
  809. if (markToStencil)
  810. graphics->SetStencilTest(true, CMP_ALWAYS, OP_REF, OP_KEEP, OP_KEEP, batch->lightMask_);
  811. batch->Draw(view);
  812. }
  813. }
  814. void BatchQueue::Draw(Light* light, View* view) const
  815. {
  816. Graphics* graphics = view->GetGraphics();
  817. Renderer* renderer = view->GetRenderer();
  818. graphics->SetScissorTest(false);
  819. graphics->SetStencilTest(false);
  820. // Base instanced
  821. for (PODVector<BatchGroup*>::ConstIterator i = sortedBaseBatchGroups_.Begin(); i != sortedBaseBatchGroups_.End(); ++i)
  822. {
  823. BatchGroup* group = *i;
  824. group->Draw(view);
  825. }
  826. // Base non-instanced
  827. for (PODVector<Batch*>::ConstIterator i = sortedBaseBatches_.Begin(); i != sortedBaseBatches_.End(); ++i)
  828. {
  829. Batch* batch = *i;
  830. batch->Draw(view);
  831. }
  832. // All base passes have been drawn. Optimize at this point by both stencil volume and scissor
  833. bool optimized = false;
  834. // Non-base instanced
  835. for (PODVector<BatchGroup*>::ConstIterator i = sortedBatchGroups_.Begin(); i != sortedBatchGroups_.End(); ++i)
  836. {
  837. BatchGroup* group = *i;
  838. if (!optimized)
  839. {
  840. renderer->OptimizeLightByStencil(light, group->camera_);
  841. renderer->OptimizeLightByScissor(light, group->camera_);
  842. optimized = true;
  843. }
  844. group->Draw(view);
  845. }
  846. // Non-base non-instanced
  847. for (PODVector<Batch*>::ConstIterator i = sortedBatches_.Begin(); i != sortedBatches_.End(); ++i)
  848. {
  849. Batch* batch = *i;
  850. if (!optimized)
  851. {
  852. renderer->OptimizeLightByStencil(light, batch->camera_);
  853. renderer->OptimizeLightByScissor(light, batch->camera_);
  854. optimized = true;
  855. }
  856. batch->Draw(view);
  857. }
  858. }
  859. unsigned BatchQueue::GetNumInstances() const
  860. {
  861. unsigned total = 0;
  862. for (HashMap<BatchGroupKey, BatchGroup>::ConstIterator i = baseBatchGroups_.Begin(); i != baseBatchGroups_.End(); ++i)
  863. {
  864. if (i->second_.geometryType_ == GEOM_INSTANCED)
  865. total += i->second_.instances_.Size();
  866. }
  867. for (HashMap<BatchGroupKey, BatchGroup>::ConstIterator i = batchGroups_.Begin(); i != batchGroups_.End(); ++i)
  868. {
  869. if (i->second_.geometryType_ == GEOM_INSTANCED)
  870. total += i->second_.instances_.Size();
  871. }
  872. return total;
  873. }
  874. }