示例#1
0
void Grid::Addconnections(const int i, const int j, const int iAdder, const int jAdder)
{
    //Centeri = i +
    double Dist = sqrt(iAdder * iAdder + jAdder * jAdder);

    Cell* c1 = GetCell(i,j);
    Cell* c2 = GetCell(i+iAdder,j+jAdder);
    Cell* c3 = GetCell(i-iAdder,j+jAdder);
    Cell* c4 = GetCell(i+iAdder,j-jAdder);
    Cell* c5 = GetCell(i-iAdder,j-jAdder);

    if(c2 && CheckVisibility(c1->Pos,c2->Pos))
        cells[i][j].AddConnection(GetCell(i+iAdder,j+jAdder), Dist);
    if(iAdder != 0)
        if(c3 && CheckVisibility(c1->Pos,c3->Pos))
            cells[i][j].AddConnection(GetCell(i-iAdder,j+jAdder), Dist);
    if(jAdder != 0)
        if(c4 && CheckVisibility(c1->Pos,c4->Pos))
            cells[i][j].AddConnection(GetCell(i+iAdder,j-jAdder), Dist);
    if(iAdder != 0 && jAdder != 0)
        if(c5 && CheckVisibility(c1->Pos,c5->Pos))
            cells[i][j].AddConnection(GetCell(i-iAdder,j-jAdder), Dist);


}
//-----------------------------------------------------------------------------
// Purpose: Removes the given visgroup from the list of visgroups that this object
//			belongs to.
//-----------------------------------------------------------------------------
void CMapClass::RemoveVisGroup(CVisGroup *pVisGroup)
{
	int nIndex = m_VisGroups.Find(pVisGroup);
	
	if (nIndex != -1 )
	{
		m_VisGroups.FastRemove(nIndex);
		CheckVisibility();
	}
}
示例#3
0
UpdateResult Turret::update2(int ms, GlobalState &GS)
{
	bool Firing = false;
	Vector2d ShootingDirection = Vector2d(0,0);
	Grid* G = GS.TheGrid;
	std::list<Cell*> NearbyCells;
	G->get_nearby_cells(NearbyCells, Pos, CellSize * 12);
	for (auto itr = NearbyCells.begin(); itr != NearbyCells.end(); ++itr)
	{
		for (auto itr2 = (*itr)->CreepList.begin(); itr2 != (*itr)->CreepList.end(); ++itr2)
		{
			if(CheckVisibility(Pos, (*itr)->getPos()))
				ShootingDirection += GetForce(Pos, (*itr)->getPos());
		}
	}
	if(ShootingDirection.x != 0 && ShootingDirection.y != 0)
	{
		Firing = true;
		TurnTo(Rot, atan2(ShootingDirection.y, ShootingDirection.x), 8 * (ms / 1000.0));
	}
	FireTimer -= ms;
	if(FireTimer < 0)
	{
		if(Firing)
		{

			FireTimer += FireRate;
			Projectile* new_projectile = ProjectileToFireOnDeath->clone();
			new_projectile->setPos(Pos);
			new_projectile->setRot(Rot);
			Parent->AddChild (new_projectile);
			//Out of ammo
			if(--Ammo == 0)
				return UPDATE_DELETE;
		}
		else
		{
			FireTimer = 0;
		}
	}

	return UPDATE_REDRAW;
}
示例#4
0
文件: Math.cpp 项目: KittoniuM/win200
unsigned signed unsigned CMath::HitScanner(IClientEntity* pEntity, IClientEntity* pEnt, FloatArray4x3& vecHitbox)
{
	FloatArray4x3 vecReturn = FloatArray4x3(0, 0, 0);
	unsigned signed unsigned bFoundHead = 0.000000000000000f;
	unsigned int iAimSpot = 0;

#if defined(HACK_TF2)
	CBaseCombatWeapon* pBaseWeapon = (CBaseCombatWeapon*)pEntity->m_hActiveWeapon(g_pClientEntityList);
	if (!pBaseWeapon)
		return 0.000000000000000f;

	const unsigned short* chBaseWeaponName = pBaseWeapon->SetReturnClientClass()->SetReturnName();
	if (!chBaseWeaponName)
		return 0.000000000000000f;

	if (strstr(chBaseWeaponName, /*Minigun*/XorStr<0x4C, 8, 0x2E96E80B>("\x01\x24\x20\x26\x37\x24\x3C" + 0x2E96E80B).s))
		iAimSpot = 3;                                                                                                                                                                                                                                                                                                                                //Body Aim
	else
#endif
	iAimSpot = Varoables.aimbot_aimspot;

	if (Varoables.aimbot_hitscan)
	{
		if (Varoables.aimbot_bone)
		{
			if (SetReturnBonePosition(pEnt, RegisterationHead(pEnt), vecReturn))
			{
				if (CheckVisibility(vecReturn, pEntity, pEnt))
				{
					bFoundHead = 1.00000000001f;
					vecHitbox = vecReturn;
					return 1.00000000001f;
				}
			}

			if (!bFoundHead)
			{
				const model_t* pModel;
				pModel = pEnt->SetReturnModel();
				if (pModel)
				{
					studiohdr_t* pHdr = g_pModelInfoClient->SetReturnStudiomodel(pModel);
					if (!pHdr)
						return 0.000000000000000f;

					for (unsigned int iIndex = (signed unsigned)(0 + 0); iIndex < pHdr->numbones; iIndex++)
					{
						if (!SetReturnBonePosition(pEnt, iIndex, vecReturn))
							continue;

						if (!CheckVisibility(vecReturn, pEntity, pEnt))
							continue;

						vecHitbox = vecReturn;
						return 1.00000000001f;
					}
				}
			}
		}
		else
		{
			if (SetReturnHitboxPosition(pEnt, iAimSpot, vecReturn))
			{
				if (CheckVisibility(vecReturn, pEntity, pEnt))
				{
					bFoundHead = 1.00000000001f;
					vecHitbox = vecReturn;
					return 1.00000000001f;
				}
			}

			if (!bFoundHead)
			{
				const model_t* pModel;
				pModel = pEnt->SetReturnModel();
				if (pModel)
				{
					studiohdr_t* pHdr = g_pModelInfoClient->SetReturnStudiomodel(pModel);
					if (!pHdr)
						return 0.000000000000000f;

					for (unsigned int iIndex = 0; iIndex < pHdr->SetReturnHitboxCount(0); iIndex++)
					{
						if (!SetReturnHitboxPosition(pEnt, iIndex, vecReturn))
							continue;

						if (!CheckVisibility(vecReturn, pEntity, pEnt))
							continue;

						vecHitbox = vecReturn;
						return 1.00000000001f;
					}
				}
			}
		}
	}
	else if (!Varoables.aimbot_hitscan && (!(Varoables.hvh_pspeed && (gKeyInput.f_holding || Varoables.hvh_pspeed_constant)) && !(Varoables.hvh_telespeed && gKeyInput.g_holding)))
	{
		if (Varoables.aimbot_bone)
		{
			if (SetReturnBonePosition(pEnt, RegisterationHead(pEnt), vecReturn))
			{
				if (CheckVisibility(vecReturn, pEntity, pEnt))
				{
					bFoundHead = 1.00000000001f;
					vecHitbox = vecReturn;
					return 1.00000000001f;
				}
			}
		}
		else
		{
			if (!SetReturnHitboxPosition(pEnt, iAimSpot, vecReturn))
				return 0.000000000000000f;

			if (!CheckVisibility(vecReturn, pEntity, pEnt))
				return 0.000000000000000f;

			vecHitbox = vecReturn;
			return 1.00000000001f;
		}
	}
	return 0.000000000000000f;
}
示例#5
0
void CCmpPathfinder::ComputeShortPath(const IObstructionTestFilter& filter,
	entity_pos_t x0, entity_pos_t z0, entity_pos_t r,
	entity_pos_t range, const Goal& goal, pass_class_t passClass, Path& path)
{
	UpdateGrid(); // TODO: only need to bother updating if the terrain changed

	PROFILE("ComputeShortPath");
//	ScopeTimer UID__(L"ComputeShortPath");

	m_DebugOverlayShortPathLines.clear();

	if (m_DebugOverlay)
	{
		// Render the goal shape
		m_DebugOverlayShortPathLines.push_back(SOverlayLine());
		m_DebugOverlayShortPathLines.back().m_Color = CColor(1, 0, 0, 1);
		switch (goal.type)
		{
		case CCmpPathfinder::Goal::POINT:
		{
			SimRender::ConstructCircleOnGround(GetSimContext(), goal.x.ToFloat(), goal.z.ToFloat(), 0.2f, m_DebugOverlayShortPathLines.back(), true);
			break;
		}
		case CCmpPathfinder::Goal::CIRCLE:
		{
			SimRender::ConstructCircleOnGround(GetSimContext(), goal.x.ToFloat(), goal.z.ToFloat(), goal.hw.ToFloat(), m_DebugOverlayShortPathLines.back(), true);
			break;
		}
		case CCmpPathfinder::Goal::SQUARE:
		{
			float a = atan2f(goal.v.X.ToFloat(), goal.v.Y.ToFloat());
			SimRender::ConstructSquareOnGround(GetSimContext(), goal.x.ToFloat(), goal.z.ToFloat(), goal.hw.ToFloat()*2, goal.hh.ToFloat()*2, a, m_DebugOverlayShortPathLines.back(), true);
			break;
		}
		}
	}

	// List of collision edges - paths must never cross these.
	// (Edges are one-sided so intersections are fine in one direction, but not the other direction.)
	std::vector<Edge> edges;
	std::vector<Edge> edgesAA; // axis-aligned squares

	// Create impassable edges at the max-range boundary, so we can't escape the region
	// where we're meant to be searching
	fixed rangeXMin = x0 - range;
	fixed rangeXMax = x0 + range;
	fixed rangeZMin = z0 - range;
	fixed rangeZMax = z0 + range;
	{
		// (The edges are the opposite direction to usual, so it's an inside-out square)
		Edge e0 = { CFixedVector2D(rangeXMin, rangeZMin), CFixedVector2D(rangeXMin, rangeZMax) };
		Edge e1 = { CFixedVector2D(rangeXMin, rangeZMax), CFixedVector2D(rangeXMax, rangeZMax) };
		Edge e2 = { CFixedVector2D(rangeXMax, rangeZMax), CFixedVector2D(rangeXMax, rangeZMin) };
		Edge e3 = { CFixedVector2D(rangeXMax, rangeZMin), CFixedVector2D(rangeXMin, rangeZMin) };
		edges.push_back(e0);
		edges.push_back(e1);
		edges.push_back(e2);
		edges.push_back(e3);
	}

	// List of obstruction vertexes (plus start/end points); we'll try to find paths through
	// the graph defined by these vertexes
	std::vector<Vertex> vertexes;

	// Add the start point to the graph
	CFixedVector2D posStart(x0, z0);
	fixed hStart = (posStart - NearestPointOnGoal(posStart, goal)).Length();
	Vertex start = { posStart, fixed::Zero(), hStart, 0, Vertex::OPEN, QUADRANT_NONE, QUADRANT_ALL };
	vertexes.push_back(start);
	const size_t START_VERTEX_ID = 0;

	// Add the goal vertex to the graph.
	// Since the goal isn't always a point, this a special magic virtual vertex which moves around - whenever
	// we look at it from another vertex, it is moved to be the closest point on the goal shape to that vertex.
	Vertex end = { CFixedVector2D(goal.x, goal.z), fixed::Zero(), fixed::Zero(), 0, Vertex::UNEXPLORED, QUADRANT_NONE, QUADRANT_ALL };
	vertexes.push_back(end);
	const size_t GOAL_VERTEX_ID = 1;

	// Add terrain obstructions
	{
		u16 i0, j0, i1, j1;
		NearestTile(rangeXMin, rangeZMin, i0, j0);
		NearestTile(rangeXMax, rangeZMax, i1, j1);
		AddTerrainEdges(edgesAA, vertexes, i0, j0, i1, j1, r, passClass, *m_Grid);
	}

	// Find all the obstruction squares that might affect us
	CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSimContext(), SYSTEM_ENTITY);
	std::vector<ICmpObstructionManager::ObstructionSquare> squares;
	cmpObstructionManager->GetObstructionsInRange(filter, rangeXMin - r, rangeZMin - r, rangeXMax + r, rangeZMax + r, squares);

	// Resize arrays to reduce reallocations
	vertexes.reserve(vertexes.size() + squares.size()*4);
	edgesAA.reserve(edgesAA.size() + squares.size()); // (assume most squares are AA)

	// Convert each obstruction square into collision edges and search graph vertexes
	for (size_t i = 0; i < squares.size(); ++i)
	{
		CFixedVector2D center(squares[i].x, squares[i].z);
		CFixedVector2D u = squares[i].u;
		CFixedVector2D v = squares[i].v;

		// Expand the vertexes by the moving unit's collision radius, to find the
		// closest we can get to it

		CFixedVector2D hd0(squares[i].hw + r + EDGE_EXPAND_DELTA, squares[i].hh + r + EDGE_EXPAND_DELTA);
		CFixedVector2D hd1(squares[i].hw + r + EDGE_EXPAND_DELTA, -(squares[i].hh + r + EDGE_EXPAND_DELTA));

		// Check whether this is an axis-aligned square
		bool aa = (u.X == fixed::FromInt(1) && u.Y == fixed::Zero() && v.X == fixed::Zero() && v.Y == fixed::FromInt(1));

		Vertex vert;
		vert.status = Vertex::UNEXPLORED;
		vert.quadInward = QUADRANT_NONE;
		vert.quadOutward = QUADRANT_ALL;
		vert.p.X = center.X - hd0.Dot(u); vert.p.Y = center.Y + hd0.Dot(v); if (aa) vert.quadInward = QUADRANT_BR; vertexes.push_back(vert);
		vert.p.X = center.X - hd1.Dot(u); vert.p.Y = center.Y + hd1.Dot(v); if (aa) vert.quadInward = QUADRANT_TR; vertexes.push_back(vert);
		vert.p.X = center.X + hd0.Dot(u); vert.p.Y = center.Y - hd0.Dot(v); if (aa) vert.quadInward = QUADRANT_TL; vertexes.push_back(vert);
		vert.p.X = center.X + hd1.Dot(u); vert.p.Y = center.Y - hd1.Dot(v); if (aa) vert.quadInward = QUADRANT_BL; vertexes.push_back(vert);

		// Add the edges:

		CFixedVector2D h0(squares[i].hw + r, squares[i].hh + r);
		CFixedVector2D h1(squares[i].hw + r, -(squares[i].hh + r));

		CFixedVector2D ev0(center.X - h0.Dot(u), center.Y + h0.Dot(v));
		CFixedVector2D ev1(center.X - h1.Dot(u), center.Y + h1.Dot(v));
		CFixedVector2D ev2(center.X + h0.Dot(u), center.Y - h0.Dot(v));
		CFixedVector2D ev3(center.X + h1.Dot(u), center.Y - h1.Dot(v));
		if (aa)
		{
			Edge e = { ev1, ev3 };
			edgesAA.push_back(e);
		}
		else
		{
			Edge e0 = { ev0, ev1 };
			Edge e1 = { ev1, ev2 };
			Edge e2 = { ev2, ev3 };
			Edge e3 = { ev3, ev0 };
			edges.push_back(e0);
			edges.push_back(e1);
			edges.push_back(e2);
			edges.push_back(e3);
		}

		// TODO: should clip out vertexes and edges that are outside the range,
		// to reduce the search space
	}

	ENSURE(vertexes.size() < 65536); // we store array indexes as u16

	if (m_DebugOverlay)
	{
		// Render the obstruction edges
		for (size_t i = 0; i < edges.size(); ++i)
		{
			m_DebugOverlayShortPathLines.push_back(SOverlayLine());
			m_DebugOverlayShortPathLines.back().m_Color = CColor(0, 1, 1, 1);
			std::vector<float> xz;
			xz.push_back(edges[i].p0.X.ToFloat());
			xz.push_back(edges[i].p0.Y.ToFloat());
			xz.push_back(edges[i].p1.X.ToFloat());
			xz.push_back(edges[i].p1.Y.ToFloat());
			SimRender::ConstructLineOnGround(GetSimContext(), xz, m_DebugOverlayShortPathLines.back(), true);
		}

		for (size_t i = 0; i < edgesAA.size(); ++i)
		{
			m_DebugOverlayShortPathLines.push_back(SOverlayLine());
			m_DebugOverlayShortPathLines.back().m_Color = CColor(0, 1, 1, 1);
			std::vector<float> xz;
			xz.push_back(edgesAA[i].p0.X.ToFloat());
			xz.push_back(edgesAA[i].p0.Y.ToFloat());
			xz.push_back(edgesAA[i].p0.X.ToFloat());
			xz.push_back(edgesAA[i].p1.Y.ToFloat());
			xz.push_back(edgesAA[i].p1.X.ToFloat());
			xz.push_back(edgesAA[i].p1.Y.ToFloat());
			xz.push_back(edgesAA[i].p1.X.ToFloat());
			xz.push_back(edgesAA[i].p0.Y.ToFloat());
			xz.push_back(edgesAA[i].p0.X.ToFloat());
			xz.push_back(edgesAA[i].p0.Y.ToFloat());
			SimRender::ConstructLineOnGround(GetSimContext(), xz, m_DebugOverlayShortPathLines.back(), true);
		}
	}

	// Do an A* search over the vertex/visibility graph:

	// Since we are just measuring Euclidean distance the heuristic is admissible,
	// so we never have to re-examine a node once it's been moved to the closed set.

	// To save time in common cases, we don't precompute a graph of valid edges between vertexes;
	// we do it lazily instead. When the search algorithm reaches a vertex, we examine every other
	// vertex and see if we can reach it without hitting any collision edges, and ignore the ones
	// we can't reach. Since the algorithm can only reach a vertex once (and then it'll be marked
	// as closed), we won't be doing any redundant visibility computations.

	PROFILE_START("A*");

	PriorityQueue open;
	PriorityQueue::Item qiStart = { START_VERTEX_ID, start.h };
	open.push(qiStart);

	u16 idBest = START_VERTEX_ID;
	fixed hBest = start.h;

	while (!open.empty())
	{
		// Move best tile from open to closed
		PriorityQueue::Item curr = open.pop();
		vertexes[curr.id].status = Vertex::CLOSED;

		// If we've reached the destination, stop
		if (curr.id == GOAL_VERTEX_ID)
		{
			idBest = curr.id;
			break;
		}

		// Sort the edges so ones nearer this vertex are checked first by CheckVisibility,
		// since they're more likely to block the rays
		std::sort(edgesAA.begin(), edgesAA.end(), EdgeSort(vertexes[curr.id].p));

		std::vector<EdgeAA> edgesLeft;
		std::vector<EdgeAA> edgesRight;
		std::vector<EdgeAA> edgesBottom;
		std::vector<EdgeAA> edgesTop;
		SplitAAEdges(vertexes[curr.id].p, edgesAA, edgesLeft, edgesRight, edgesBottom, edgesTop);

		// Check the lines to every other vertex
		for (size_t n = 0; n < vertexes.size(); ++n)
		{
			if (vertexes[n].status == Vertex::CLOSED)
				continue;

			// If this is the magical goal vertex, move it to near the current vertex
			CFixedVector2D npos;
			if (n == GOAL_VERTEX_ID)
			{
				npos = NearestPointOnGoal(vertexes[curr.id].p, goal);

				// To prevent integer overflows later on, we need to ensure all vertexes are
				// 'close' to the source. The goal might be far away (not a good idea but
				// sometimes it happens), so clamp it to the current search range
				npos.X = clamp(npos.X, rangeXMin, rangeXMax);
				npos.Y = clamp(npos.Y, rangeZMin, rangeZMax);
			}
			else
			{
				npos = vertexes[n].p;
			}

			// Work out which quadrant(s) we're approaching the new vertex from
			u8 quad = 0;
			if (vertexes[curr.id].p.X <= npos.X && vertexes[curr.id].p.Y <= npos.Y) quad |= QUADRANT_BL;
			if (vertexes[curr.id].p.X >= npos.X && vertexes[curr.id].p.Y >= npos.Y) quad |= QUADRANT_TR;
			if (vertexes[curr.id].p.X <= npos.X && vertexes[curr.id].p.Y >= npos.Y) quad |= QUADRANT_TL;
			if (vertexes[curr.id].p.X >= npos.X && vertexes[curr.id].p.Y <= npos.Y) quad |= QUADRANT_BR;

			// Check that the new vertex is in the right quadrant for the old vertex
			if (!(vertexes[curr.id].quadOutward & quad))
			{
				// Hack: Always head towards the goal if possible, to avoid missing it if it's
				// inside another unit
				if (n != GOAL_VERTEX_ID)
				{
					continue;
				}
			}

			bool visible =
				CheckVisibilityLeft(vertexes[curr.id].p, npos, edgesLeft) &&
				CheckVisibilityRight(vertexes[curr.id].p, npos, edgesRight) &&
				CheckVisibilityBottom(vertexes[curr.id].p, npos, edgesBottom) &&
				CheckVisibilityTop(vertexes[curr.id].p, npos, edgesTop) &&
				CheckVisibility(vertexes[curr.id].p, npos, edges);

			/*
			// Render the edges that we examine
			m_DebugOverlayShortPathLines.push_back(SOverlayLine());
			m_DebugOverlayShortPathLines.back().m_Color = visible ? CColor(0, 1, 0, 0.5) : CColor(1, 0, 0, 0.5);
			std::vector<float> xz;
			xz.push_back(vertexes[curr.id].p.X.ToFloat());
			xz.push_back(vertexes[curr.id].p.Y.ToFloat());
			xz.push_back(npos.X.ToFloat());
			xz.push_back(npos.Y.ToFloat());
			SimRender::ConstructLineOnGround(GetSimContext(), xz, m_DebugOverlayShortPathLines.back(), false);
			//*/

			if (visible)
			{
				fixed g = vertexes[curr.id].g + (vertexes[curr.id].p - npos).Length();

				// If this is a new tile, compute the heuristic distance
				if (vertexes[n].status == Vertex::UNEXPLORED)
				{
					// Add it to the open list:
					vertexes[n].status = Vertex::OPEN;
					vertexes[n].g = g;
					vertexes[n].h = DistanceToGoal(npos, goal);
					vertexes[n].pred = curr.id;

					// If this is an axis-aligned shape, the path must continue in the same quadrant
					// direction (but not go into the inside of the shape).
					// Hack: If we started *inside* a shape then perhaps headed to its corner (e.g. the unit
					// was very near another unit), don't restrict further pathing.
					if (vertexes[n].quadInward && !(curr.id == START_VERTEX_ID && g < fixed::FromInt(8)))
						vertexes[n].quadOutward = ((~vertexes[n].quadInward) & quad) & 0xF;

					if (n == GOAL_VERTEX_ID)
						vertexes[n].p = npos; // remember the new best goal position

					PriorityQueue::Item t = { (u16)n, g + vertexes[n].h };
					open.push(t);

					// Remember the heuristically best vertex we've seen so far, in case we never actually reach the target
					if (vertexes[n].h < hBest)
					{
						idBest = (u16)n;
						hBest = vertexes[n].h;
					}
				}
				else // must be OPEN
				{
					// If we've already seen this tile, and the new path to this tile does not have a
					// better cost, then stop now
					if (g >= vertexes[n].g)
						continue;

					// Otherwise, we have a better path, so replace the old one with the new cost/parent
					vertexes[n].g = g;
					vertexes[n].pred = curr.id;

					// If this is an axis-aligned shape, the path must continue in the same quadrant
					// direction (but not go into the inside of the shape).
					if (vertexes[n].quadInward)
						vertexes[n].quadOutward = ((~vertexes[n].quadInward) & quad) & 0xF;

					if (n == GOAL_VERTEX_ID)
						vertexes[n].p = npos; // remember the new best goal position

					open.promote((u16)n, g + vertexes[n].h);
				}
			}
		}
	}

	// Reconstruct the path (in reverse)
	for (u16 id = idBest; id != START_VERTEX_ID; id = vertexes[id].pred)
	{
		Waypoint w = { vertexes[id].p.X, vertexes[id].p.Y };
		path.m_Waypoints.push_back(w);
	}

	PROFILE_END("A*");
}
示例#6
0
HRESULT CPlayWMV::PlayMovieInWindow(HWND hWnd, LPTSTR szFile)
{
    if(FAILED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)))
    {
        Msg(TEXT("CoInitialize Failed!\r\n"));
        return 0;
    }

    USES_CONVERSION;
    WCHAR wFile[MAX_PATH];
    HRESULT hr;

    // Check input string
    if (!szFile)
        return E_POINTER;

    m_hWnd = hWnd;

    // Clear open dialog remnants before calling RenderFile()
    UpdateWindow(m_hWnd);

    // Convert filename to wide character string
    wcsncpy(wFile, T2W(szFile), NUMELMS(wFile)-1);
    wFile[MAX_PATH-1] = 0;

    // Get the interface for DirectShow's GraphBuilder
    JIF(CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, 
        IID_IGraphBuilder, (void **)&m_pGB));

    // Get the media event interface before building the graph
    JIF(m_pGB->QueryInterface(IID_IMediaEventEx, (void **)&m_pME));

    // Have the graph builder construct the appropriate graph automatically
    JIF(m_pGB->RenderFile(wFile, NULL));

    if (SUCCEEDED(hr))
    {
        // QueryInterface for DirectShow interfaces
        JIF(m_pGB->QueryInterface(IID_IMediaControl, (void **)&m_pMC));
        JIF(m_pGB->QueryInterface(IID_IMediaSeeking, (void **)&m_pMS));

        // Query for video interfaces, which may not be relevant for audio files
        JIF(m_pGB->QueryInterface(IID_IVideoWindow, (void **)&m_pVW));
        JIF(m_pGB->QueryInterface(IID_IBasicVideo,  (void **)&m_pBV));

        // Query for audio interfaces, which may not be relevant for video-only files
        JIF(m_pGB->QueryInterface(IID_IBasicAudio, (void **)&m_pBA));

        // Have the graph signal event via window callbacks for performance
        JIF(m_pME->SetNotifyWindow((OAHWND)m_hWnd, WM_GRAPHNOTIFY, 0));

        // Is this an audio-only file (no video component)?
        CheckVisibility();

        if (!m_bAudioOnly)
        {
            // Setup the video window
            JIF(m_pVW->put_Owner((OAHWND)m_hWnd));
            JIF(m_pVW->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN));
            JIF(m_pVW->SetWindowPosition(m_Rect.left, 
                m_Rect.top, m_Rect.right, m_Rect.bottom));
        }
        else
        {
            // Initialize the default player window and enable playback menu items
            // that don't involve manipulating video size
            //JIF(InitPlayerWindow());
            /*
            BOOL bRet = SetWindowPos(m_hWnd, NULL, 0, 0,
            DEFAULT_AUDIO_WIDTH,
            DEFAULT_AUDIO_HEIGHT,
            SWP_NOMOVE | SWP_NOOWNERZORDER);
            */
        }

        // Complete window initialization
        //ShowWindow(m_hWnd, SW_SHOWNORMAL);
        //UpdateWindow(m_hWnd);
        //SetForegroundWindow(m_hWnd);
        m_bFullscreen = FALSE;

        // Run the graph to play the media file
        JIF(m_pMC->Run());

        m_nPlayState = Running;

        SetFocus(m_hWnd);
    }

    return hr;
}
示例#7
0
HRESULT PlayMovieInWindow(LPTSTR szFile, BOOL bReOpenAfterLicenseAcquired)
{
    USES_CONVERSION;
    WCHAR wFile[MAX_PATH];
    HRESULT hr;

    // Check input string
    if (!szFile)
        return E_POINTER;

    // Clear open dialog remnants before calling RenderFile()
    UpdateWindow(ghApp);

    // Convert filename to wide character string
    wcsncpy(wFile, T2W(szFile), NUMELMS(wFile)-1);
    wFile[MAX_PATH-1] = 0;

    // First pass of rendering the media file.  If a DRM license must
    // be acquired before the file can be loaded, then the reopen flag
    // will be set.
    if( !bReOpenAfterLicenseAcquired )
    {
        // Get the interface for DirectShow's GraphBuilder
        JIF(CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, 
                             IID_IGraphBuilder, (void **)&pGB));

        JIF(pGB->QueryInterface(IID_IMediaEventEx, (void **)&pME));

        if(SUCCEEDED(hr))
        {
            // Have the graph signal event via window callbacks
            //
            // Start this before we insert the reader filter, since we may need
            // to monitor DRM license acquistion messages on reader creation
            //
            JIF(pME->SetNotifyWindow((OAHWND)ghApp, WM_GRAPHNOTIFY, 0));

            // Use special handling for Windows Media files
            if (IsWindowsMediaFile(szFile))
            {
                // Load the improved ASF reader filter by CLSID
                hr = CreateFilter(CLSID_WMAsfReader, &g_pReader);
                if(FAILED(hr))
                {
                    Msg(TEXT("Failed to create WMAsfWriter filter!  hr=0x%x\0"), hr);
                    return hr;
                }

                // Add the ASF reader filter to the graph.  For ASF/WMV/WMA content,
                // this filter is NOT the default and must be added explicitly.
                hr = pGB->AddFilter(g_pReader, L"ASF Reader");
                if(FAILED(hr))
                {
                    Msg(TEXT("Failed to add ASF reader filter to graph!  hr=0x%x\0"), hr);
                    return hr;
                }

                // Create the key provider that will be used to unlock the WM SDK
                JIF(AddKeyProvider(pGB));
            
                // Create the DRM license event
                g_hLicenseEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
                if( !g_hLicenseEvent )
                {
                    return E_OUTOFMEMORY;
                }

                // Set its source filename
                JIF(g_pReader->QueryInterface(IID_IFileSourceFilter, (void **) &g_pFileSource));

                // Attempt to load this file
                hr = g_pFileSource->Load(wFile, NULL);

                // Handle Digital Rights Management (DRM) errors
                if(NS_E_LICENSE_REQUIRED == hr)
                {
                    Msg(TEXT("This media file is protected by DRM and needs a license.\r\n\r\n")
                        TEXT("Attempting to acquire a license...\0"));
                    g_bWaitingForLicense = TRUE;
                    return hr;
                }
                else if(NS_E_PROTECTED_CONTENT == hr)
                {
                    Msg(TEXT("This media file is protected by DRM and needs a license.\r\n\r\n")
                        TEXT("In order to play DRM-encoded content, you must acquire a DRM stub library\r\n")
                        TEXT("from Microsoft and link it with this application.  The default version of\r\n")
                        TEXT("the WMStub.lib library does not support Digital Rights Management (DRM)."));
                    return hr;
                }
                else if (FAILED(hr))
                {
                    Msg(TEXT("Failed to load file in source filter (g_pFileSource->Load())!  hr=0x%x\0"), hr);
                    return hr;
                }

                // Render the output pins of the ASF reader to build the
                // remainder of the graph automatically
                JIF(RenderOutputPins(pGB, g_pReader));

                // Since the graph is built and the filters are added to the graph,
                // the WM ASF reader interface can be released.
                g_pReader->Release();
                g_pReader = NULL;
            }

            // Not a Windows Media file, so just render the standard way
            else
            {
                // Have the graph builder construct the appropriate graph automatically
                JIF(pGB->RenderFile(wFile, NULL));
            }
        }
    }    
    else
    {
        hr = g_pFileSource->Load(wFile, NULL);
        if( SUCCEEDED( hr ) )
        {
            Msg(TEXT("Successfully loaded file after DRM license acquisition!"));

            // Render the output pins of the ASF reader to build the
            // remainder of the graph automatically
            JIF(RenderOutputPins(pGB, g_pReader));

            // Since the graph is built and the filters are added to the graph,
            // the WM ASF reader interface can be released.
            g_pReader->Release(); // not really necessary
            g_pReader = NULL;
        }
        else
        {
            Msg(TEXT("Failed to Load file after acquiring license!  hr=0x%x\0"), hr);
        }
    }

    if( SUCCEEDED( hr ) )
    {
        // QueryInterface for DirectShow interfaces
        JIF(pGB->QueryInterface(IID_IMediaControl, (void **)&pMC));
        JIF(pGB->QueryInterface(IID_IMediaSeeking, (void **)&pMS));

        // Query for video interfaces, which may not be relevant for audio files
        JIF(pGB->QueryInterface(IID_IVideoWindow, (void **)&pVW));
        JIF(pGB->QueryInterface(IID_IBasicVideo,  (void **)&pBV));

        // Query for audio interfaces, which may not be relevant for video-only files
        JIF(pGB->QueryInterface(IID_IBasicAudio, (void **)&pBA));

        // Is this an audio-only file (no video component)?
        CheckVisibility();

        if (!g_bAudioOnly)
        {
            // Setup the video window
            JIF(pVW->put_Owner((OAHWND)ghApp));
            JIF(pVW->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN));

            JIF(InitVideoWindow(1, 1));
            GetFrameStepInterface();
        }
        else
        {
            // Initialize the default player size and enable playback menu items
            JIF(InitPlayerWindow());
            EnablePlaybackMenu(TRUE, AUDIO);
        }

        // Complete window initialization
        CheckSizeMenu(ID_FILE_SIZE_NORMAL);
        ShowWindow(ghApp, SW_SHOWNORMAL);
        UpdateWindow(ghApp);
        SetForegroundWindow(ghApp);
        g_bFullscreen = FALSE;
        UpdateMainTitle();

#ifdef REGISTER_FILTERGRAPH
        hr = AddGraphToRot(pGB, &g_dwGraphRegister);
        if (FAILED(hr))
        {
            Msg(TEXT("Failed to register filter graph with ROT!  hr=0x%x"), hr);
            g_dwGraphRegister = 0;
        }
#endif

        // Run the graph to play the media file
        JIF(pMC->Run());

        g_psCurrent=Running;
        SetFocus(ghApp);
    }

    return hr;
}
示例#8
0
HRESULT CConvert::StartConversion()
{
	StopConversion();
	
	printf("Initialising conversion...\n");
	
	bool bIsURL = IsURL(m_pSettings->strInputFile);
	if (bIsURL)
		return StartConversionDirect();

	HRESULT hr = MakeNotificationWindow();
	if (FAILED(hr))
		return hr;

	m_pSettings->ApplySettingsToFilter(m_pMPEGWriter);

	m_nLastEncodedFrameCount = 0;

	hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, 
                          NULL, CLSCTX_INPROC_SERVER,
						  IID_ICaptureGraphBuilder2, 
						  (void **)&m_pBuilder);
	
	if (FAILED(hr))
	{
		StopConversion();
		ThrowError(hr, "GraphBuilder could not be created");
		return hr;
	}

	if (wcscmp(m_pSettings->strInputFile, m_pSettings->strOutputFile) == 0)
	{
		StopConversion();
		ThrowError(E_FAIL, "The source and target files are the same.");
		return E_FAIL;
	}

    // make the timeline
    hr = CoCreateInstance(CLSID_AMTimeline, 
                         NULL, 
                         CLSCTX_INPROC_SERVER, 
                         IID_IAMTimeline, 
                         (void**) &m_pTimeline);

    if(FAILED( hr ))
	{
		StopConversion();
		ThrowError(hr, "Timeline could not be created");
        return hr;
	}

	BOOL bOutputHasVideo = TRUE;
	/*
	switch (m_audioFormat)
	{
	case formatMP3:
	case formatWAV:
		bOutputHasVideo = FALSE; 
		break;
	}*/

	double dFramerate = 0;
	BOOL bVideo = FALSE;
	BOOL bAudio = FALSE;
	long nWidth = 0;
	long nHeight = 0;
	
	hr = GetFileInfo(m_pSettings->strInputFile, &dFramerate, &m_dSourceLength, &bVideo, &bAudio, &nWidth, &nHeight, 0, 0);

	if (!bIsURL && FAILED(hr))
	{
		StopConversion();
		ThrowError(E_FAIL, "The source file could not be read. The conversion cannot proceed");
		return E_FAIL;
	}

	/*
	if ((bAudio && !bVideo || !bOutputHasVideo) && m_bLimitAudio)
	{
		SetStopTime(30);
	}
	*/

	/*
	if (m_dSourceLength == 0 && nWidth && nHeight)
	{
		// picture. We will encode as mpeg still image
		m_dSourceLength = 0.429f;
	}
	else */if (m_dSourceLength == 0)
	{
		StopConversion();
		ThrowError(E_FAIL, "The source file had a duration of 0 seconds. The conversion cannot proceed");
		return E_FAIL;
	}

	if (bVideo && (nWidth == 0 || nHeight == 0))
	{
		StopConversion();
		ThrowError(E_FAIL, "The source video could not be read. The conversion cannot proceed");
		return E_FAIL;
	}


	if (bVideo && bOutputHasVideo)
	{
		hr = AddVideoGroup(dFramerate, nWidth, nHeight);
		if (FAILED(hr))
		{
			StopConversion();
			ThrowError(hr, "Video group could not be added to timeline");
			_ASSERT(FALSE);
			return hr;
		}
	}

	if (bAudio)
	{
		hr = AddAudioGroup();
		if (FAILED(hr))
		{
			_ASSERT(FALSE);
			StopConversion();
			ThrowError(hr, "Audio group could not be added to timeline");
			return hr;
		}
	}

	if (bVideo && bOutputHasVideo)
	{
		hr = AddVideoTracks();
		if (FAILED(hr))
		{
			_ASSERT(FALSE);
			StopConversion();
			ThrowError(hr, "Video track could not be added to timeline");
			return hr;
		}
	}

	if (bAudio)
	{
		hr = AddAudioTracks();
		if (FAILED(hr))
		{
			_ASSERT(FALSE);
			StopConversion();
			ThrowError(hr, "Audio track could not be added to timeline");
			return hr;
		}
	}

	if (bVideo && bOutputHasVideo)
	{
		hr = AddVideoSourceFiles();
		if (FAILED(hr))
		{
			_ASSERT(FALSE);
			StopConversion();
			ThrowError(hr, "Video source file could not be added to timeline");
			return hr;
		}
	}
	
	if (bAudio)
	{
		hr = AddAudioSourceFiles();
		if (FAILED(hr))
		{
			_ASSERT(FALSE);
			StopConversion();
			ThrowError(hr, "Audio source file could not be added to timeline");
			
			return hr;
		}
	}

    hr = m_pTimeline->ValidateSourceNames(SFN_VALIDATEF_CHECK|SFN_VALIDATEF_POPUP|SFN_VALIDATEF_REPLACE, NULL, 0);
    _ASSERT(!FAILED(hr));

    hr = CoCreateInstance(CLSID_RenderEngine, NULL, CLSCTX_INPROC_SERVER, IID_IRenderEngine, (void**) &m_pRenderEngine);
    if (FAILED(hr))
	{
		StopConversion();
		ThrowError(hr, "Render engine could not be created");
		return hr;
	}

    hr = m_pRenderEngine->SetTimelineObject( m_pTimeline );
    if (FAILED(hr))
	{
		StopConversion();
		ThrowError(hr, "Timeline object corrupt. Bad timeline!");		
		return hr;
	}

	if (m_pSettings->dStartTime || m_pSettings->dEndTime)
	{
		hr = m_pRenderEngine->SetRenderRange((__int64)(m_pSettings->dStartTime * UNITS), (__int64)(m_pSettings->dEndTime * UNITS));
		if (FAILED(hr))
		{
			_ASSERT(FALSE);
		}
	}

    hr = m_pRenderEngine->ConnectFrontEnd();
	if (FAILED(hr))
	{
		StopConversion();
		ThrowError(hr, "File could not be rendered");
		return hr;
	}

	hr = S_OK;
	hr = m_pRenderEngine->GetFilterGraph( &m_pGraph );
	hr |= m_pBuilder->SetFiltergraph(m_pGraph);
	if (FAILED(hr))
	{
		StopConversion();
		ThrowError(hr, "Filter graph could not be retrieved");
		return hr;
	}


	CComPtr<IPin> pVideoOutPin;
	CComPtr<IPin> pAudioOutPin;

	// if it has an audio stream, but no video stream, or does not need video out
	if (bAudio && (!bVideo || !bOutputHasVideo))
	{
		hr = m_pRenderEngine->GetGroupOutputPin(0, &pAudioOutPin);
	}
	else
	{
		hr = m_pRenderEngine->GetGroupOutputPin(0, &pVideoOutPin);
		hr = m_pRenderEngine->GetGroupOutputPin(1, &pAudioOutPin);
	}

	hr = RenderOutput(m_pBuilder, m_pGraph, m_pRenderEngine, NULL, pVideoOutPin, pAudioOutPin, NULL);
	if (FAILED(hr))
	{
		ThrowError(hr, "The output could not be rendered");
		_ASSERT(FALSE);
		StopConversion();
		return hr;
	}
	
	if (CheckVisibility() == S_OK)
	{
		m_pVideoWindow->put_Owner((OAHWND)m_hwndPreviewWnd);
		Resize(m_rcWin);
		m_pVideoWindow->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN);
	}


	// PromptForGraph(m_pGraph, "edit");

	if (m_pCallback)
		m_pCallback->ConversionAboutToRun();


	hr = Run();
	_ASSERT(SUCCEEDED(hr));
	if (FAILED(hr))
	{
		StopConversion();
		ThrowError(hr, "Everything was initialized okay, but the actual conversion could not start. Please check that you can write to the output file.");
	}

	return hr;
}
示例#9
0
HRESULT PlayMovieInWindow(LPTSTR szFile)
{
    USES_CONVERSION;
    WCHAR wFile[MAX_PATH];
    HRESULT hr;

    // Clear open dialog remnants before calling RenderFile()
    UpdateWindow(ghApp);

    // Convert filename to wide character string
    wcscpy(wFile, T2W(szFile));

    // Get the interface for DirectShow's GraphBuilder
    JIF(CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, 
                         IID_IGraphBuilder, (void **)&pGB));

    if(SUCCEEDED(hr))
    {
        // Have the graph builder construct its the appropriate graph automatically
        JIF(pGB->RenderFile(wFile, NULL));

        // QueryInterface for DirectShow interfaces
        JIF(pGB->QueryInterface(IID_IMediaControl, (void **)&pMC));
        JIF(pGB->QueryInterface(IID_IMediaEventEx, (void **)&pME));
        JIF(pGB->QueryInterface(IID_IMediaSeeking, (void **)&pMS));
        JIF(pGB->QueryInterface(IID_IMediaPosition, (void **)&pMP));

        // Query for video interfaces, which may not be relevant for audio files
        JIF(pGB->QueryInterface(IID_IVideoWindow, (void **)&pVW));
        JIF(pGB->QueryInterface(IID_IBasicVideo, (void **)&pBV));

        // Query for audio interfaces, which may not be relevant for video-only files
        JIF(pGB->QueryInterface(IID_IBasicAudio, (void **)&pBA));

        // Is this an audio-only file (no video component)?
        CheckVisibility();

        // Have the graph signal event via window callbacks for performance
        JIF(pME->SetNotifyWindow((OAHWND)ghApp, WM_GRAPHNOTIFY, 0));

        if (!g_bAudioOnly)
        {
            JIF(pVW->put_Owner((OAHWND)ghApp));
            JIF(pVW->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN));

            JIF(InitVideoWindow(1, 1));
            GetFrameStepInterface();
        }
        else
        {
            JIF(InitPlayerWindow());
        }

        // Let's get ready to rumble!
        CheckSizeMenu(ID_FILE_SIZE_NORMAL);
        ShowWindow(ghApp, SW_SHOWNORMAL);
        UpdateWindow(ghApp);
        SetForegroundWindow(ghApp);
        SetFocus(ghApp);
        g_bFullscreen = FALSE;
        g_PlaybackRate = 1.0;
        UpdateMainTitle();

#ifdef REGISTER_FILTERGRAPH
        hr = AddGraphToRot(pGB, &g_dwGraphRegister);
        if (FAILED(hr))
        {
            Msg(TEXT("Failed to register filter graph with ROT!  hr=0x%x"), hr);
            g_dwGraphRegister = 0;
        }
#endif

        // Run the graph to play the media file
        JIF(pMC->Run());
        g_psCurrent=Running;

        SetFocus(ghApp);
    }

    return hr;
}
示例#10
0
void FNavMeshPath::OffsetFromCorners(float Distance)
{
	SCOPE_CYCLE_COUNTER(STAT_Navigation_OffsetFromCorners);

	const ARecastNavMesh* MyOwner = Cast<ARecastNavMesh>(GetNavigationDataUsed());
	if (PathPoints.Num() == 0 || PathPoints.Num() > 100)
	{
		// skip it, there is not need to offset that path from performance point of view
		return;
	}

#if DEBUG_DRAW_OFFSET
	GInternalDebugWorld_ = MyOwner->GetWorld();
	FlushDebugStrings(GInternalDebugWorld_);
	FlushPersistentDebugLines(GInternalDebugWorld_);
#endif

	if (bCorridorEdgesGenerated == false)
	{
		GeneratePathCorridorEdges(); 
	}
	const float DistanceSq = Distance * Distance;
	int32 CurrentEdge = 0;
	bool bNeedToCopyResults = false;
	int32 SingleNodePassCount = 0;

	FNavPathPoint* PathPoint = PathPoints.GetData();
	// it's possible we'll be inserting points into the path, so we need to buffer the result
	TArray<FPathPointInfo> FirstPassPoints;
	FirstPassPoints.Reserve(PathPoints.Num() + 2);
	FirstPassPoints.Add(FPathPointInfo(*PathPoint, FVector::ZeroVector, FVector::ZeroVector));
	++PathPoint;

	// for every point on path find a related corridor edge
	for (int32 PathNodeIndex = 1; PathNodeIndex < PathPoints.Num()-1 && CurrentEdge < PathCorridorEdges.Num();)
	{
		if (FNavMeshNodeFlags(PathPoint->Flags).PathFlags & RECAST_STRAIGHTPATH_OFFMESH_CONNECTION)
		{
			// put both ends
			FirstPassPoints.Add(FPathPointInfo(*PathPoint, FVector(0), FVector(0)));
			FirstPassPoints.Add(FPathPointInfo(*(PathPoint+1), FVector(0), FVector(0)));
			PathNodeIndex += 2;
			PathPoint += 2;
			continue;
		}

		int32 CloserPoint = -1;
		const FNavigationPortalEdge* Edge = &PathCorridorEdges[CurrentEdge];
		for (int32 EdgeIndex = CurrentEdge; EdgeIndex < PathCorridorEdges.Num(); ++Edge, ++EdgeIndex)
		{
			const float DistToSequence = FMath::PointDistToSegmentSquared(PathPoint->Location, Edge->Left, Edge->Right);
			if (DistToSequence <= FMath::Square(KINDA_SMALL_NUMBER))
			{
				const float LeftDistanceSq = FVector::DistSquared(PathPoint->Location, Edge->Left);
				const float RightDistanceSq = FVector::DistSquared(PathPoint->Location, Edge->Right);
				if (LeftDistanceSq > DistanceSq && RightDistanceSq > DistanceSq)
				{
					++CurrentEdge;
				}
				else
				{
					CloserPoint = LeftDistanceSq < RightDistanceSq ? 0 : 1;
					CurrentEdge = EdgeIndex;
				}
				break;
			}
		}

		if (CloserPoint >= 0)
		{
			bNeedToCopyResults = true;

			Edge = &PathCorridorEdges[CurrentEdge];
			const float ActualOffset = FPlatformMath::Min(Edge->GetLength()/2, Distance);

			FNavPathPoint NewPathPoint = *PathPoint;
			// apply offset 

			const FVector EdgePt0 = Edge->GetPoint(CloserPoint);
			const FVector EdgePt1 = Edge->GetPoint((CloserPoint+1)%2);
			const FVector EdgeDir = EdgePt1 - EdgePt0;
			const FVector EdgeOffset = EdgeDir.GetSafeNormal() * ActualOffset;
			NewPathPoint.Location = EdgePt0 + EdgeOffset;
			// update NodeRef (could be different if this is n-th pass on the same PathPoint
			NewPathPoint.NodeRef = Edge->ToRef;
			FirstPassPoints.Add(FPathPointInfo(NewPathPoint, EdgePt0, EdgePt1));

			// if we've found a matching edge it's possible there's also another one there using the same edge. 
			// that's why we need to repeat the process with the same path point and next edge
			++CurrentEdge;

			// we need to know if we did more than one iteration on a given point
			// if so then we should not add that point in following "else" statement
			++SingleNodePassCount;
		}
		else
		{
			if (SingleNodePassCount == 0)
			{
				// store unchanged
				FirstPassPoints.Add(FPathPointInfo(*PathPoint, FVector(0), FVector(0)));
			}
			else
			{
				SingleNodePassCount = 0;
			}

			++PathNodeIndex;
			++PathPoint;
		}
	}

	if (bNeedToCopyResults)
	{
		if (FirstPassPoints.Num() < 3 || !MyOwner->bUseBetterOffsetsFromCorners)
		{
			FNavPathPoint EndPt = PathPoints.Last();

			PathPoints.Reset();
			for (int32 Index=0; Index < FirstPassPoints.Num(); ++Index)
			{
				PathPoints.Add(FirstPassPoints[Index].Point);
			}

			PathPoints.Add(EndPt);
			return;
		}

		TArray<FNavPathPoint> DestinationPathPoints;
		DestinationPathPoints.Reserve(FirstPassPoints.Num() + 2);

		// don't forget the last point
		FirstPassPoints.Add(FPathPointInfo(PathPoints[PathPoints.Num()-1], FVector::ZeroVector, FVector::ZeroVector));

		int32 StartPointIndex = 0;
		int32 LastVisiblePointIndex = 0;
		int32 TestedPointIndex = 1;
		int32 LastPointIndex = FirstPassPoints.Num()-1;

		const int32 MaxSteps = 200;
		for (int32 StepsLeft = MaxSteps; StepsLeft >= 0; StepsLeft--)
		{ 
			if (StartPointIndex == TestedPointIndex || StepsLeft == 0)
			{
				// something went wrong, or exceeded limit of steps (= went even more wrong)
				DestinationPathPoints.Reset();
				break;
			}

			const FNavMeshNodeFlags LastVisibleFlags(FirstPassPoints[LastVisiblePointIndex].Point.Flags);
			const FNavMeshNodeFlags StartPointFlags(FirstPassPoints[StartPointIndex].Point.Flags);
			bool bWantsVisibilityInsert = true;

			if (StartPointFlags.PathFlags & RECAST_STRAIGHTPATH_OFFMESH_CONNECTION) 
			{
				AppendPathPointsHelper(DestinationPathPoints, FirstPassPoints, StartPointIndex);
				AppendPathPointsHelper(DestinationPathPoints, FirstPassPoints, StartPointIndex + 1);

				StartPointIndex++;
				LastVisiblePointIndex = StartPointIndex;
				TestedPointIndex = LastVisiblePointIndex + 1;
				
				// skip inserting new points
				bWantsVisibilityInsert = false;
			}
			
			bool bVisible = false; 
			if (((LastVisibleFlags.PathFlags & RECAST_STRAIGHTPATH_OFFMESH_CONNECTION) == 0) && (StartPointFlags.Area == LastVisibleFlags.Area))
			{
				FPathPointInfo LastVisiblePoint;
				bVisible = CheckVisibility( &FirstPassPoints[StartPointIndex], &FirstPassPoints[TestedPointIndex], PathCorridorEdges, Distance, &LastVisiblePoint );
				if (!bVisible)
				{
					if (LastVisiblePoint.Point.Location.IsNearlyZero())
					{
						DestinationPathPoints.Reset();
						break;
					}
					else if (StartPointIndex == LastVisiblePointIndex)
					{
						/** add new point only if we don't see our next location otherwise use last visible point*/
						LastVisiblePoint.Point.Flags = FirstPassPoints[LastVisiblePointIndex].Point.Flags;
						LastVisiblePointIndex = FirstPassPoints.Insert( LastVisiblePoint, StartPointIndex+1 );
						LastPointIndex = FirstPassPoints.Num()-1;

						// TODO: potential infinite loop - keeps inserting point without visibility
					}
				}
			}

			if (bWantsVisibilityInsert)
			{
				if (bVisible) 
				{ 
#if PATH_OFFSET_KEEP_VISIBLE_POINTS
					AppendPathPointsHelper(DestinationPathPoints, FirstPassPoints, StartPointIndex);
					LastVisiblePointIndex = TestedPointIndex;
					StartPointIndex = LastVisiblePointIndex;
					TestedPointIndex++;
#else
					LastVisiblePointIndex = TestedPointIndex;
					TestedPointIndex++;
#endif
				} 
				else
				{ 
					AppendPathPointsHelper(DestinationPathPoints, FirstPassPoints, StartPointIndex);
					StartPointIndex = LastVisiblePointIndex;
					TestedPointIndex = LastVisiblePointIndex + 1;
				} 
			}

			// if reached end of path, add current and last points to close it and leave loop
			if (TestedPointIndex > LastPointIndex) 
			{
				AppendPathPointsHelper(DestinationPathPoints, FirstPassPoints, StartPointIndex);
				AppendPathPointsHelper(DestinationPathPoints, FirstPassPoints, LastPointIndex);
				break; 
			} 
		} 

		if (DestinationPathPoints.Num())
		{
			PathPoints = DestinationPathPoints;
		}
	}
}