| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- // Copyright (c) 2008-2023 the Urho3D project
- // License: MIT
- #include "SpriteBatchBase.h"
- #include "Graphics.h"
- #include "Camera.h"
- namespace Urho3D
- {
- void SpriteBatchBase::AddTriangle()
- {
- // Рендерили четырёхугольники, а теперь нужно рендерить треугольники
- if (qNumVertices_ > 0)
- Flush();
- memcpy(tVertices_ + tNumVertices_, &triangle_, sizeof(triangle_));
- tNumVertices_ += VERTICES_PER_TRIANGLE;
- // Если после добавления вершин мы заполнили массив до предела, то рендерим порцию
- if (tNumVertices_ == MAX_TRIANGLES_IN_PORTION * VERTICES_PER_TRIANGLE)
- Flush();
- }
- void SpriteBatchBase::SetShapeColor(u32 color)
- {
- triangle_.v0_.color_ = color;
- triangle_.v1_.color_ = color;
- triangle_.v2_.color_ = color;
- }
- void SpriteBatchBase::SetShapeColor(const Color& color)
- {
- SetShapeColor(color.ToU32());
- }
- void SpriteBatchBase::AddQuad()
- {
- // Рендерили треугольники, а теперь нужно рендерить четырехугольники
- if (tNumVertices_ > 0)
- Flush();
- if (quad_.texture_ != qCurrentTexture_ || quad_.vs_ != qCurrentVS_ || quad_.ps_ != qCurrentPS_)
- {
- Flush();
- qCurrentVS_ = quad_.vs_;
- qCurrentPS_ = quad_.ps_;
- qCurrentTexture_ = quad_.texture_;
- }
- memcpy(qVertices_ + qNumVertices_, &(quad_.v0_), sizeof(QVertex) * VERTICES_PER_QUAD);
- qNumVertices_ += VERTICES_PER_QUAD;
- // Если после добавления вершин мы заполнили массив до предела, то рендерим порцию
- if (qNumVertices_ == MAX_QUADS_IN_PORTION * VERTICES_PER_QUAD)
- Flush();
- }
- IntRect SpriteBatchBase::GetViewportRect()
- {
- if (!VirtualScreenUsed())
- return IntRect(0, 0, graphics_->GetWidth(), graphics_->GetHeight());
- float realAspect = (float)graphics_->GetWidth() / graphics_->GetHeight();
- float virtualAspect = (float)virtualScreenSize_.x_ / virtualScreenSize_.y_;
- float virtualScreenScale;
- if (realAspect > virtualAspect)
- {
- // Окно шире, чем надо. Будут пустые полосы по бокам
- virtualScreenScale = (float)graphics_->GetHeight() / virtualScreenSize_.y_;
- }
- else
- {
- // Высота окна больше, чем надо. Будут пустые полосы сверху и снизу
- virtualScreenScale = (float)graphics_->GetWidth() / virtualScreenSize_.x_;
- }
- i32 viewportWidth = (i32)(virtualScreenSize_.x_ * virtualScreenScale);
- i32 viewportHeight = (i32)(virtualScreenSize_.y_ * virtualScreenScale);
- // Центрируем вьюпорт
- i32 viewportX = (graphics_->GetWidth() - viewportWidth) / 2;
- i32 viewportY = (graphics_->GetHeight() - viewportHeight) / 2;
- return IntRect(viewportX, viewportY, viewportWidth + viewportX, viewportHeight + viewportY);
- }
- Vector2 SpriteBatchBase::GetVirtualPos(const Vector2& realPos)
- {
- if (!VirtualScreenUsed())
- return realPos;
- IntRect viewportRect = GetViewportRect();
- float factor = (float)virtualScreenSize_.x_ / viewportRect.Width();
- float virtualX = (realPos.x_ - viewportRect.left_) * factor;
- float virtualY = (realPos.y_ - viewportRect.top_) * factor;
- return Vector2(virtualX, virtualY);
- }
- void SpriteBatchBase::UpdateViewProjMatrix()
- {
- if (camera_)
- {
- Matrix4 matrix = camera_->GetGPUProjection() * camera_->GetView();
- graphics_->SetShaderParameter(VSP_VIEWPROJ, matrix);
- return;
- }
- i32 width;
- i32 height;
- if (VirtualScreenUsed())
- {
- width = virtualScreenSize_.x_;
- height = virtualScreenSize_.y_;
- }
- else
- {
- width = graphics_->GetWidth();
- height = graphics_->GetHeight();
- }
- float pixelWidth = 2.0f / width; // Двойка, так как длина отрезка [-1, 1] равна двум
- float pixelHeight = 2.0f / height;
- Matrix4 matrix = Matrix4(pixelWidth, 0.0f, 0.0f, -1.0f,
- 0.0f, -pixelHeight, 0.0f, 1.0f,
- 0.0f, 0.0f, 1.0f, 0.0f,
- 0.0f, 0.0f, 0.0f, 1.0f);
- graphics_->SetShaderParameter(VSP_VIEWPROJ, matrix);
- }
- using GpuIndex16 = u16;
- SpriteBatchBase::SpriteBatchBase(Context* context) : Object(context)
- {
- graphics_ = GetSubsystem<Graphics>();
- qIndexBuffer_ = new IndexBuffer(context_);
- qIndexBuffer_->SetShadowed(true);
- // Индексный буфер всегда содержит набор четырёхугольников, поэтому его можно сразу заполнить
- qIndexBuffer_->SetSize(MAX_QUADS_IN_PORTION * INDICES_PER_QUAD, false);
- GpuIndex16* buffer = (GpuIndex16*)qIndexBuffer_->Lock(0, qIndexBuffer_->GetIndexCount());
- for (i32 i = 0; i < MAX_QUADS_IN_PORTION; i++)
- {
- // Первый треугольник четырёхугольника
- buffer[i * INDICES_PER_QUAD + 0] = i * VERTICES_PER_QUAD + 0;
- buffer[i * INDICES_PER_QUAD + 1] = i * VERTICES_PER_QUAD + 1;
- buffer[i * INDICES_PER_QUAD + 2] = i * VERTICES_PER_QUAD + 2;
- // Второй треугольник
- buffer[i * INDICES_PER_QUAD + 3] = i * VERTICES_PER_QUAD + 2;
- buffer[i * INDICES_PER_QUAD + 4] = i * VERTICES_PER_QUAD + 3;
- buffer[i * INDICES_PER_QUAD + 5] = i * VERTICES_PER_QUAD + 0;
- }
- qIndexBuffer_->Unlock();
- qVertexBuffer_ = new VertexBuffer(context_);
- qVertexBuffer_->SetSize(MAX_QUADS_IN_PORTION * VERTICES_PER_QUAD, VertexElements::Position | VertexElements::Color | VertexElements::TexCoord1, true);
- tVertexBuffer_ = new VertexBuffer(context_);
- tVertexBuffer_->SetSize(MAX_TRIANGLES_IN_PORTION * VERTICES_PER_TRIANGLE, VertexElements::Position | VertexElements::Color, true);
- tVertexShader_ = graphics_->GetShader(VS, "TriangleBatch");
- tPixelShader_ = graphics_->GetShader(PS, "TriangleBatch");
- SetShapeColor(Color::WHITE);
- }
- void SpriteBatchBase::Flush()
- {
- if (tNumVertices_ > 0)
- {
- graphics_->ResetRenderTargets();
- graphics_->ClearParameterSources();
- graphics_->SetCullMode(CULL_NONE);
- graphics_->SetDepthWrite(false);
- graphics_->SetStencilTest(false);
- graphics_->SetScissorTest(false);
- graphics_->SetColorWrite(true);
- graphics_->SetDepthTest(compareMode_);
- graphics_->SetBlendMode(blendMode_);
- graphics_->SetViewport(GetViewportRect());
- graphics_->SetIndexBuffer(nullptr);
- graphics_->SetVertexBuffer(tVertexBuffer_);
- graphics_->SetTexture(0, nullptr);
- // Параметры шейдеров нужно задавать после указания шейдеров
- graphics_->SetShaders(tVertexShader_, tPixelShader_);
- graphics_->SetShaderParameter(VSP_MODEL, Matrix3x4::IDENTITY);
- UpdateViewProjMatrix();
- // Копируем накопленную геометрию в память видеокарты
- TVertex* buffer = (TVertex*)tVertexBuffer_->Lock(0, tNumVertices_, true);
- memcpy(buffer, tVertices_, tNumVertices_ * sizeof(TVertex));
- tVertexBuffer_->Unlock();
- // И отрисовываем её
- graphics_->Draw(TRIANGLE_LIST, 0, tNumVertices_);
- // Начинаем новую порцию
- tNumVertices_ = 0;
- }
- else if (qNumVertices_ > 0)
- {
- graphics_->ResetRenderTargets();
- graphics_->ClearParameterSources();
- graphics_->SetCullMode(CULL_NONE);
- graphics_->SetDepthWrite(false);
- graphics_->SetStencilTest(false);
- graphics_->SetScissorTest(false);
- graphics_->SetColorWrite(true);
- graphics_->SetDepthTest(compareMode_);
- graphics_->SetBlendMode(blendMode_);
- graphics_->SetViewport(GetViewportRect());
- graphics_->SetIndexBuffer(qIndexBuffer_);
- graphics_->SetVertexBuffer(qVertexBuffer_);
- graphics_->SetTexture(0, qCurrentTexture_);
- // Параметры шейдеров нужно задавать после указания шейдеров
- graphics_->SetShaders(qCurrentVS_, qCurrentPS_);
- graphics_->SetShaderParameter(VSP_MODEL, Matrix3x4::IDENTITY);
- UpdateViewProjMatrix();
- // Мы используем только цвета вершин. Но это значение требует шейдер Basic
- graphics_->SetShaderParameter(PSP_MATDIFFCOLOR, Color::WHITE);
- // Копируем накопленную геометрию в память видеокарты
- QVertex* buffer = (QVertex*)qVertexBuffer_->Lock(0, qNumVertices_, true);
- memcpy(buffer, qVertices_, qNumVertices_ * sizeof(QVertex));
- qVertexBuffer_->Unlock();
- // И отрисовываем её
- i32 numQuads = qNumVertices_ / VERTICES_PER_QUAD;
- graphics_->Draw(TRIANGLE_LIST, 0, numQuads * INDICES_PER_QUAD, 0, qNumVertices_);
- // Начинаем новую порцию
- qNumVertices_ = 0;
- }
- }
- }
|