From 8349b4c9d8725ec6b6aa2009bad9a6ec263218e8 Mon Sep 17 00:00:00 2001 From: yvt Date: Mon, 9 Jan 2017 05:00:05 +0900 Subject: [PATCH] Tracers on MapView Fixes #139. --- Sources/Client/Client_Update.cpp | 5 +- Sources/Client/MapView.cpp | 160 +++++++++++++++++++++++++++++-- Sources/Client/MapView.h | 25 ++++- 3 files changed, 179 insertions(+), 11 deletions(-) diff --git a/Sources/Client/Client_Update.cpp b/Sources/Client/Client_Update.cpp index 17ae0469b..b54e77ac9 100644 --- a/Sources/Client/Client_Update.cpp +++ b/Sources/Client/Client_Update.cpp @@ -1012,15 +1012,14 @@ namespace spades { spades::Vector3 hitPos) { SPADES_MARK_FUNCTION(); - Tracer *t; float vel; switch (player->GetWeapon()->GetWeaponType()) { case RIFLE_WEAPON: vel = 700.f; break; case SMG_WEAPON: vel = 360.f; break; case SHOTGUN_WEAPON: return; } - t = new Tracer(this, muzzlePos, hitPos, vel); - AddLocalEntity(t); + AddLocalEntity(new Tracer(this, muzzlePos, hitPos, vel)); + AddLocalEntity(new MapViewTracer(muzzlePos, hitPos, vel)); } void Client::BlocksFell(std::vector blocks) { diff --git a/Sources/Client/MapView.cpp b/Sources/Client/MapView.cpp index 9714235c7..7f0b48cbf 100644 --- a/Sources/Client/MapView.cpp +++ b/Sources/Client/MapView.cpp @@ -18,24 +18,71 @@ */ -#include "MapView.h" +#include + #include "CTFGameMode.h" #include "Client.h" #include "GameMap.h" #include "IImage.h" #include "IRenderer.h" +#include "MapView.h" #include "Player.h" #include "TCGameMode.h" #include "Weapon.h" #include "World.h" #include +#include DEFINE_SPADES_SETTING(cg_minimapSize, "128"); DEFINE_SPADES_SETTING(cg_minimapPlayerColor, "1"); DEFINE_SPADES_SETTING(cg_minimapPlayerIcon, "1"); +using std::pair; +using stmp::optional; + namespace spades { namespace client { + namespace { + optional> ClipLineSegment(const pair &inLine, + const Plane2 &plane) { + const float distance1 = plane.GetDistanceTo(inLine.first); + const float distance2 = plane.GetDistanceTo(inLine.second); + int bits = distance1 > 0 ? 1 : 0; + bits |= distance2 > 0 ? 2 : 0; + switch (bits) { + case 0: return {}; + case 3: return inLine; + } + + const float fraction = distance1 / (distance1 - distance2); + Vector2 intersection = Mix(inLine.first, inLine.second, fraction); + if (bits == 1) { + return std::make_pair(inLine.first, intersection); + } else { + return std::make_pair(intersection, inLine.second); + } + } + + optional> ClipLineSegment(const pair &inLine, + const AABB2 &rect) { + optional> line = + ClipLineSegment(inLine, Plane2{1.0f, 0.0f, -rect.GetMinX()}); + if (!line) { + return line; + } + line = ClipLineSegment(*line, Plane2{-1.0f, 0.0f, rect.GetMaxX()}); + if (!line) { + return line; + } + line = ClipLineSegment(*line, Plane2{0.0f, 1.0f, -rect.GetMinY()}); + if (!line) { + return line; + } + line = ClipLineSegment(*line, Plane2{0.0f, -1.0f, rect.GetMaxY()}); + return line; + } + } + MapView::MapView(Client *c, bool largeMap) : client(c), renderer(c->GetRenderer()), largeMap(largeMap) { scaleMode = 2; @@ -90,16 +137,21 @@ namespace spades { } } - void MapView::DrawIcon(spades::Vector3 pos, spades::client::IImage *img, float rotation) { - if (pos.x < inRect.GetMinX() || pos.x > inRect.GetMaxX() || pos.y < inRect.GetMinY() || - pos.y > inRect.GetMaxY()) - return; - + Vector2 MapView::Project(const Vector2 &pos) const { Vector2 scrPos; scrPos.x = (pos.x - inRect.GetMinX()) / inRect.GetWidth(); scrPos.x = (scrPos.x * outRect.GetWidth()) + outRect.GetMinX(); scrPos.y = (pos.y - inRect.GetMinY()) / inRect.GetHeight(); scrPos.y = (scrPos.y * outRect.GetHeight()) + outRect.GetMinY(); + return scrPos; + } + + void MapView::DrawIcon(spades::Vector3 pos, spades::client::IImage *img, float rotation) { + if (pos.x < inRect.GetMinX() || pos.x > inRect.GetMaxX() || pos.y < inRect.GetMinY() || + pos.y > inRect.GetMaxY()) + return; + + Vector2 scrPos = Project(Vector2{pos.x, pos.y}); scrPos.x = floorf(scrPos.x); scrPos.y = floorf(scrPos.y); @@ -438,7 +490,7 @@ namespace spades { for (int i = 0; i < world->GetNumPlayerSlots(); i++) { Player *p = world->GetPlayer(i); if (p == nullptr || - (p->GetTeamId() != world->GetLocalPlayer()->GetTeamId() && !isSpectating) || + (p->GetTeamId() != world->GetLocalPlayer()->GetTeamId() && !isSpectating) || !p->IsAlive()) continue; @@ -541,6 +593,100 @@ namespace spades { DrawIcon(t->pos, icon, 0.f); } } + + // draw tracers + Handle tracerImage = renderer->RegisterImage("Gfx/Ball.png"); + const float tracerWidth = 2.0f; + const AABB2 tracerInRect{0.0f, 0.0f, tracerImage->GetWidth(), tracerImage->GetHeight()}; + + for (const auto &localEntity : client->localEntities) { + auto *const tracer = dynamic_cast(localEntity.get()); + if (!tracer) { + continue; + } + + const auto line1 = tracer->GetLineSegment(); + if (!line1) { + continue; + } + + auto line2 = + ClipLineSegment(std::make_pair(Vector2{(*line1).first.x, (*line1).first.y}, + Vector2{(*line1).second.x, (*line1).second.y}), + inRect); + if (!line2) { + continue; + } + + auto &line3 = *line2; + line3.first = Project(line3.first); + line3.second = Project(line3.second); + + if (line3.first == line3.second) { + continue; + } + + Vector2 normal = (line3.second - line3.first).Normalize(); + normal = {-normal.y, normal.x}; + + { + const Vector2 vertices[] = {line3.first - normal * tracerWidth, + line3.first + normal * tracerWidth, + line3.second - normal * tracerWidth}; + + renderer->SetColorAlphaPremultiplied(Vector4{1.0f, 0.8f, 0.6f, 1.0f} * alpha); + renderer->DrawImage(tracerImage, vertices[0], vertices[1], vertices[2], + tracerInRect); + } + } } + + MapViewTracer::MapViewTracer(Vector3 p1, Vector3 p2, float bulletVel) + : startPos(p1), velocity(bulletVel) { + // Z coordinate doesn't matter in MapView + p1.z = 0.0f; + p2.z = 0.0f; + + dir = (p2 - p1).Normalize(); + length = (p2 - p1).GetLength(); + + // in MapView it looks slower than it is actually, so compensate for that + bulletVel *= 4.0f; + + const float maxTimeSpread = 1.0f / 10.f; + const float shutterTime = 1.0f / 10.f; + + visibleLength = shutterTime * velocity; + curDistance = -visibleLength; + curDistance += maxTimeSpread * GetRandom(); + + firstUpdate = true; + } + + bool MapViewTracer::Update(float dt) { + if (!firstUpdate) { + curDistance += dt * velocity; + if (curDistance > length) { + return false; + } + } + firstUpdate = false; + return true; + } + + stmp::optional> MapViewTracer::GetLineSegment() { + float startDist = curDistance; + float endDist = curDistance + visibleLength; + startDist = std::max(startDist, 0.f); + endDist = std::min(endDist, length); + if (startDist >= endDist) { + return {}; + } + Vector3 pos1 = startPos + dir * startDist; + Vector3 pos2 = startPos + dir * endDist; + return std::make_pair(pos1, pos2); + } + + MapViewTracer::~MapViewTracer() {} } } diff --git a/Sources/Client/MapView.h b/Sources/Client/MapView.h index 377befd99..359c93982 100644 --- a/Sources/Client/MapView.h +++ b/Sources/Client/MapView.h @@ -20,7 +20,11 @@ #pragma once +#include + +#include "ILocalEntity.h" #include +#include namespace spades { namespace client { @@ -44,6 +48,8 @@ namespace spades { AABB2 inRect; AABB2 outRect; + Vector2 Project(const Vector2 &) const; + void DrawIcon(Vector3 pos, IImage *img, float rotation); public: @@ -56,5 +62,22 @@ namespace spades { void Draw(); }; + + class MapViewTracer : public ILocalEntity { + Vector3 startPos, dir; + float length; + float curDistance; + float visibleLength; + float velocity; + bool firstUpdate; + + public: + MapViewTracer(Vector3 p1, Vector3 p2, float bulletVel); + ~MapViewTracer(); + + bool Update(float dt) override; + + stmp::optional> GetLineSegment(); + }; } -} \ No newline at end of file +}