SpriteBatchBase.cpp 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. // Copyright (c) 2008-2023 the Urho3D project
  2. // License: MIT
  3. #include "SpriteBatchBase.h"
  4. #include "Graphics.h"
  5. #include "Camera.h"
  6. namespace Urho3D
  7. {
  8. void SpriteBatchBase::AddTriangle()
  9. {
  10. // Рендерили четырёхугольники, а теперь нужно рендерить треугольники
  11. if (qNumVertices_ > 0)
  12. Flush();
  13. memcpy(tVertices_ + tNumVertices_, &triangle_, sizeof(triangle_));
  14. tNumVertices_ += VERTICES_PER_TRIANGLE;
  15. // Если после добавления вершин мы заполнили массив до предела, то рендерим порцию
  16. if (tNumVertices_ == MAX_TRIANGLES_IN_PORTION * VERTICES_PER_TRIANGLE)
  17. Flush();
  18. }
  19. void SpriteBatchBase::SetShapeColor(u32 color)
  20. {
  21. triangle_.v0_.color_ = color;
  22. triangle_.v1_.color_ = color;
  23. triangle_.v2_.color_ = color;
  24. }
  25. void SpriteBatchBase::SetShapeColor(const Color& color)
  26. {
  27. SetShapeColor(color.ToU32());
  28. }
  29. void SpriteBatchBase::AddQuad()
  30. {
  31. // Рендерили треугольники, а теперь нужно рендерить четырехугольники
  32. if (tNumVertices_ > 0)
  33. Flush();
  34. if (quad_.texture_ != qCurrentTexture_ || quad_.vs_ != qCurrentVS_ || quad_.ps_ != qCurrentPS_)
  35. {
  36. Flush();
  37. qCurrentVS_ = quad_.vs_;
  38. qCurrentPS_ = quad_.ps_;
  39. qCurrentTexture_ = quad_.texture_;
  40. }
  41. memcpy(qVertices_ + qNumVertices_, &(quad_.v0_), sizeof(QVertex) * VERTICES_PER_QUAD);
  42. qNumVertices_ += VERTICES_PER_QUAD;
  43. // Если после добавления вершин мы заполнили массив до предела, то рендерим порцию
  44. if (qNumVertices_ == MAX_QUADS_IN_PORTION * VERTICES_PER_QUAD)
  45. Flush();
  46. }
  47. IntRect SpriteBatchBase::GetViewportRect()
  48. {
  49. if (!VirtualScreenUsed())
  50. return IntRect(0, 0, graphics_->GetWidth(), graphics_->GetHeight());
  51. float realAspect = (float)graphics_->GetWidth() / graphics_->GetHeight();
  52. float virtualAspect = (float)virtualScreenSize_.x_ / virtualScreenSize_.y_;
  53. float virtualScreenScale;
  54. if (realAspect > virtualAspect)
  55. {
  56. // Окно шире, чем надо. Будут пустые полосы по бокам
  57. virtualScreenScale = (float)graphics_->GetHeight() / virtualScreenSize_.y_;
  58. }
  59. else
  60. {
  61. // Высота окна больше, чем надо. Будут пустые полосы сверху и снизу
  62. virtualScreenScale = (float)graphics_->GetWidth() / virtualScreenSize_.x_;
  63. }
  64. i32 viewportWidth = (i32)(virtualScreenSize_.x_ * virtualScreenScale);
  65. i32 viewportHeight = (i32)(virtualScreenSize_.y_ * virtualScreenScale);
  66. // Центрируем вьюпорт
  67. i32 viewportX = (graphics_->GetWidth() - viewportWidth) / 2;
  68. i32 viewportY = (graphics_->GetHeight() - viewportHeight) / 2;
  69. return IntRect(viewportX, viewportY, viewportWidth + viewportX, viewportHeight + viewportY);
  70. }
  71. Vector2 SpriteBatchBase::GetVirtualPos(const Vector2& realPos)
  72. {
  73. if (!VirtualScreenUsed())
  74. return realPos;
  75. IntRect viewportRect = GetViewportRect();
  76. float factor = (float)virtualScreenSize_.x_ / viewportRect.Width();
  77. float virtualX = (realPos.x_ - viewportRect.left_) * factor;
  78. float virtualY = (realPos.y_ - viewportRect.top_) * factor;
  79. return Vector2(virtualX, virtualY);
  80. }
  81. void SpriteBatchBase::UpdateViewProjMatrix()
  82. {
  83. if (camera_)
  84. {
  85. Matrix4 matrix = camera_->GetGPUProjection() * camera_->GetView();
  86. graphics_->SetShaderParameter(VSP_VIEWPROJ, matrix);
  87. return;
  88. }
  89. i32 width;
  90. i32 height;
  91. if (VirtualScreenUsed())
  92. {
  93. width = virtualScreenSize_.x_;
  94. height = virtualScreenSize_.y_;
  95. }
  96. else
  97. {
  98. width = graphics_->GetWidth();
  99. height = graphics_->GetHeight();
  100. }
  101. float pixelWidth = 2.0f / width; // Двойка, так как длина отрезка [-1, 1] равна двум
  102. float pixelHeight = 2.0f / height;
  103. Matrix4 matrix = Matrix4(pixelWidth, 0.0f, 0.0f, -1.0f,
  104. 0.0f, -pixelHeight, 0.0f, 1.0f,
  105. 0.0f, 0.0f, 1.0f, 0.0f,
  106. 0.0f, 0.0f, 0.0f, 1.0f);
  107. graphics_->SetShaderParameter(VSP_VIEWPROJ, matrix);
  108. }
  109. using GpuIndex16 = u16;
  110. SpriteBatchBase::SpriteBatchBase(Context* context) : Object(context)
  111. {
  112. graphics_ = GetSubsystem<Graphics>();
  113. qIndexBuffer_ = new IndexBuffer(context_);
  114. qIndexBuffer_->SetShadowed(true);
  115. // Индексный буфер всегда содержит набор четырёхугольников, поэтому его можно сразу заполнить
  116. qIndexBuffer_->SetSize(MAX_QUADS_IN_PORTION * INDICES_PER_QUAD, false);
  117. GpuIndex16* buffer = (GpuIndex16*)qIndexBuffer_->Lock(0, qIndexBuffer_->GetIndexCount());
  118. for (i32 i = 0; i < MAX_QUADS_IN_PORTION; i++)
  119. {
  120. // Первый треугольник четырёхугольника
  121. buffer[i * INDICES_PER_QUAD + 0] = i * VERTICES_PER_QUAD + 0;
  122. buffer[i * INDICES_PER_QUAD + 1] = i * VERTICES_PER_QUAD + 1;
  123. buffer[i * INDICES_PER_QUAD + 2] = i * VERTICES_PER_QUAD + 2;
  124. // Второй треугольник
  125. buffer[i * INDICES_PER_QUAD + 3] = i * VERTICES_PER_QUAD + 2;
  126. buffer[i * INDICES_PER_QUAD + 4] = i * VERTICES_PER_QUAD + 3;
  127. buffer[i * INDICES_PER_QUAD + 5] = i * VERTICES_PER_QUAD + 0;
  128. }
  129. qIndexBuffer_->Unlock();
  130. qVertexBuffer_ = new VertexBuffer(context_);
  131. qVertexBuffer_->SetSize(MAX_QUADS_IN_PORTION * VERTICES_PER_QUAD, VertexElements::Position | VertexElements::Color | VertexElements::TexCoord1, true);
  132. tVertexBuffer_ = new VertexBuffer(context_);
  133. tVertexBuffer_->SetSize(MAX_TRIANGLES_IN_PORTION * VERTICES_PER_TRIANGLE, VertexElements::Position | VertexElements::Color, true);
  134. tVertexShader_ = graphics_->GetShader(VS, "TriangleBatch");
  135. tPixelShader_ = graphics_->GetShader(PS, "TriangleBatch");
  136. SetShapeColor(Color::WHITE);
  137. }
  138. void SpriteBatchBase::Flush()
  139. {
  140. if (tNumVertices_ > 0)
  141. {
  142. graphics_->ResetRenderTargets();
  143. graphics_->ClearParameterSources();
  144. graphics_->SetCullMode(CULL_NONE);
  145. graphics_->SetDepthWrite(false);
  146. graphics_->SetStencilTest(false);
  147. graphics_->SetScissorTest(false);
  148. graphics_->SetColorWrite(true);
  149. graphics_->SetDepthTest(compareMode_);
  150. graphics_->SetBlendMode(blendMode_);
  151. graphics_->SetViewport(GetViewportRect());
  152. graphics_->SetIndexBuffer(nullptr);
  153. graphics_->SetVertexBuffer(tVertexBuffer_);
  154. graphics_->SetTexture(0, nullptr);
  155. // Параметры шейдеров нужно задавать после указания шейдеров
  156. graphics_->SetShaders(tVertexShader_, tPixelShader_);
  157. graphics_->SetShaderParameter(VSP_MODEL, Matrix3x4::IDENTITY);
  158. UpdateViewProjMatrix();
  159. // Копируем накопленную геометрию в память видеокарты
  160. TVertex* buffer = (TVertex*)tVertexBuffer_->Lock(0, tNumVertices_, true);
  161. memcpy(buffer, tVertices_, tNumVertices_ * sizeof(TVertex));
  162. tVertexBuffer_->Unlock();
  163. // И отрисовываем её
  164. graphics_->Draw(TRIANGLE_LIST, 0, tNumVertices_);
  165. // Начинаем новую порцию
  166. tNumVertices_ = 0;
  167. }
  168. else if (qNumVertices_ > 0)
  169. {
  170. graphics_->ResetRenderTargets();
  171. graphics_->ClearParameterSources();
  172. graphics_->SetCullMode(CULL_NONE);
  173. graphics_->SetDepthWrite(false);
  174. graphics_->SetStencilTest(false);
  175. graphics_->SetScissorTest(false);
  176. graphics_->SetColorWrite(true);
  177. graphics_->SetDepthTest(compareMode_);
  178. graphics_->SetBlendMode(blendMode_);
  179. graphics_->SetViewport(GetViewportRect());
  180. graphics_->SetIndexBuffer(qIndexBuffer_);
  181. graphics_->SetVertexBuffer(qVertexBuffer_);
  182. graphics_->SetTexture(0, qCurrentTexture_);
  183. // Параметры шейдеров нужно задавать после указания шейдеров
  184. graphics_->SetShaders(qCurrentVS_, qCurrentPS_);
  185. graphics_->SetShaderParameter(VSP_MODEL, Matrix3x4::IDENTITY);
  186. UpdateViewProjMatrix();
  187. // Мы используем только цвета вершин. Но это значение требует шейдер Basic
  188. graphics_->SetShaderParameter(PSP_MATDIFFCOLOR, Color::WHITE);
  189. // Копируем накопленную геометрию в память видеокарты
  190. QVertex* buffer = (QVertex*)qVertexBuffer_->Lock(0, qNumVertices_, true);
  191. memcpy(buffer, qVertices_, qNumVertices_ * sizeof(QVertex));
  192. qVertexBuffer_->Unlock();
  193. // И отрисовываем её
  194. i32 numQuads = qNumVertices_ / VERTICES_PER_QUAD;
  195. graphics_->Draw(TRIANGLE_LIST, 0, numQuads * INDICES_PER_QUAD, 0, qNumVertices_);
  196. // Начинаем новую порцию
  197. qNumVertices_ = 0;
  198. }
  199. }
  200. }