Example #1
0
// Calculates local north vector for this Camera
Vector3D Camera::getUp(){
	
	// If there are no over-own-center rotations
	if(xoangle == 0.0 && yoangle == 0.0)
		return getSingleUp();
	
	// Get new space coordinates refference
	Vector3D eye = getEye();
			// Not using getFocus ... we need original focus, not rotated one
	Vector3D dirAxis = eye.sub(focus).normalize(); // z' (eye - focus, not focus - eye)
	                                               // couse the z+ direction
	Vector3D up = getSingleUp(); // y'
	Vector3D right = dirAxis.crossProduct(up).normalize(); // x'
	
	// Rotate standar position focus
	Vector3D pivotalAxis(0.0, 1.0, 0.0);
	pivotalAxis = pivotalAxis.rotate(xoangle, 1, 0, 0);
	pivotalAxis = pivotalAxis.rotate(yoangle, 0, 1, 0);
	
	// Translate new standar position focus to the new space coordinates refference
	Vector3D nuPivotalAxis = right.byScalarProduct(pivotalAxis.getCoord(0));
	nuPivotalAxis = nuPivotalAxis.add(up.byScalarProduct(pivotalAxis.getCoord(1)));
	nuPivotalAxis = nuPivotalAxis.add(dirAxis.byScalarProduct(pivotalAxis.getCoord(2)));
	return nuPivotalAxis;
	
}
Example #2
0
Plane3D::Plane3D(Vector3D a, Vector3D b, Vector3D c)
{
	mPoint = a;

	Vector3D ab = b.sub(a);	//vector ab
	Vector3D ac = c.sub(a);	//vector ac
	mNormal = ab.crossProduct(ac);

	mD = mNormal.dotProduct(mPoint);
}
Example #3
0
// Return distance from ray origin to intersection
// See comments for variables and the equations in Plane.cpp's findIntersection
double Triangle::findIntersection(const Ray3D & ray) const {
    // See if the ray intersects the bounding box
    // *** With bounding box
    if (!Object::intersectsBBox(ray)) { return -1; }
    // First check if the ray intersects with the plane (use same calculations)
    Vector3D rayDirection = ray.getDirection();
    Vector3D rayOrigin = ray.getOrigin();
    double ldotn = rayDirection.dotProduct(normal);
    if (0 == ldotn) { // Ray is || to triangle
        return -1;
    } else {
        Vector3D p0 = normal * distance;
        double distanceToPlane = (p0 - rayOrigin).dotProduct(normal) / ldotn;
        // Then see if the point is inside the triangle (3 conditions)
        // Q is the point of intersection
        Vector3D Q = (rayDirection * distanceToPlane) + rayOrigin;
        Vector3D sideCA = epC - epA;
        Vector3D segQA = Q - epA;
        // 1. (CA x QA) * n >= 0
        if (sideCA.crossProduct(segQA).dotProduct(normal) < 0) {
            return -1;
        }
        Vector3D sideBC = epB - epC;
        Vector3D segQC = Q - epC;
        // 2. (BC x QC) * n >= 0
        if (sideBC.crossProduct(segQC).dotProduct(normal) < 0) {
            return -1;
        }
        Vector3D sideAB = epA - epB;
        Vector3D segQB = Q - epB;
        // 3. (AB x QB) * n >= 0
        if (sideAB.crossProduct(segQB).dotProduct(normal) < 0) {
            return -1;
        }
        return distanceToPlane;
    }
}
Example #4
0
void CamFree::VectorsFromAngles()
{
    static const Vector3D up(0,1,0);
	if (angle_horizontal > 30)
        angle_horizontal = 30;
    else if (angle_horizontal < -30)
        angle_horizontal = -30;
    double r_temp = cos(angle_vertical*M_PI/180);
    forward.Z = sin(angle_vertical*M_PI/180);
    forward.X = r_temp*cos(angle_horizontal*M_PI/180);
    forward.Y = r_temp*sin(angle_horizontal*M_PI/180);
    left = up.crossProduct(forward);
    left.normalize();

	UpdateChild();
}
Example #5
0
void FreeFlyCamera::VectorsFromAngles()
{
    static const Vector3D up(0,0,1);
    if (_phi > 89)
        _phi = 89;
    else if (_phi < -89)
        _phi = -89;
    double r_temp = cos(_phi*M_PI/180);
    _forward.Z = sin(_phi*M_PI/180);
    _forward.X = r_temp*cos(_theta*M_PI/180);
    _forward.Y = r_temp*sin(_theta*M_PI/180);

    _left = up.crossProduct(_forward);
    _left.normalize();

    _target = _position + _forward;
}
void GUICutRenderWindow::renderImage(wxImage* image) {

	//Speichern der Zeit für die Laufzeitmessung
	timeval tm1;
	gettimeofday(&tm1, NULL);

	int width = imgWidthEdit->GetValue();
	int height = imgHeightEdit->GetValue();

	//aktualisieren der Skala
	canvas->getScalePanel()->refresh(width, height);

	//die Anzahl der zur Berechnung zu verwendenden Kerne
	core_count = threadcountedit->GetValue();

	/*
	 * Versuchen, die Interpolation durch vorgezogenes Testen des zuletzt verwendeten Tetraeders zu beschleunigen.
	 * Diese Option ist verursacht Ungenauigkeiten und bietet zumeist wenig Performancegewinn.
	 * Sie ist deshalb standardmäßig deaktiviert und nicht über die Programmoberfläche aktivierbar.
	 */
	bool use_last_tet = false;

	//zurücksetzen der Grafik
	delete image;
	image = new wxImage(width, height, true);
	image->InitAlpha();

	//Punkt als Dezimaltrennzeichen
	setlocale(LC_NUMERIC, "C");

	//Eigenschaften der Temperaturverteilung
	CutRender_info* info = getCutRenderProperties();
	Triangle* tri = info->tri;
	//X-Achse der Ebene im 3D-Raum
	Vector3D* xvec = tri->getV2()->copy();
	xvec->sub(tri->getV1());
	//Normale der Ebene
	Vector3D* tri_nor = tri->getNormal();
	//Y-Achse der Ebene im 3D-Raum
	Vector3D* yvec = xvec->crossProduct(tri_nor);
	delete tri_nor;
	xvec->normalize();
	yvec->normalize();

	//Das aktive Objekt
	ObjectData* obj = wxGetApp().getActiveObject();

	//Erstellen möglichst einfacher Geometrien für die Materialien
	vector<tetgenio*> bases(obj->getMaterials()->size());
	for (unsigned int i = 0; i < obj->getMaterials()->size(); i++) {
		tetgenio* tri_io = new tetgenio();
		string args = "Q";
		tetrahedralize(const_cast<char*>(args.c_str()),
				obj->getMaterials()->at(i).tetgeninput, tri_io, NULL, NULL);
		bases.at(i) = tri_io;
	}

	//Zurücksetzen des Datenarrays für die Temperaturverteilung
	if (value_img != NULL) {
		delete[] value_img;
	}
	value_img = new float[width * height];

	//Verknüpfen der Ausgabe mit den Temperaturverteilungsdaten und der Grafik
	canvas->setImage(image);
	canvas->setValueImg(value_img);

	//Erstellen des Statusregisters für die Berechnungsthreads
	bool thread_running[core_count];
	for (int i = 0; i < core_count; i++) {
		thread_running[i] = 1;
	}

	//Array für die Thread-Objekts
	vector<thread*> threads = vector<thread*>(0);
	//Array für die Kopierten Sensordaten. Das Kopieren ist erforderlich, da die Threads die Daten verändern (sortieren).
	vector<vector<SensorPoint>> copied_sensor_data =
			vector<vector<SensorPoint>>(core_count);

	//Höhe für die Streifen, die die einzelnen Threads berechnen
	int delta_h = height / core_count;

	//Für alle Threads...
	for (int i = 0; i < core_count; i++) {
		//Die Starthöhe des Threads in der Temperaturverteilung
		int startheight = delta_h * i;

		//eventuelle Korrektur der Streifenhöhe für den letzten Thread
		if (i == core_count - 1) {
			if (startheight + delta_h < height) {
				delta_h = height - startheight;
			}
		}

		//Aktueller Sensordatensatz
		SensorData* dataset = &obj->getSensorDataList()->at(
				obj->getCurrentSensorIndex());
		vector<SensorPoint>* original_sd = &dataset->data.at(
				dataset->current_time_index);
		copied_sensor_data.at(i).resize(original_sd->size());

		//Kopieren der Sensordaten den Thread
		for (int p = 0; p < int(original_sd->size()); p++) {
			copySensorPoint(&original_sd->at(p),
					&copied_sensor_data.at(i).at(p));
		}

		//Starten des Threads
		threads.resize(threads.size() + 1,
				new thread(render_thread, &thread_running[i], value_img, image,
						width, height, startheight, delta_h, info, xvec, yvec,
						tri->getV1(), &bases, obj, &copied_sensor_data.at(i),
						use_last_tet));
	}

	//Pfüfzahl, ob noch Threads laufen
	unsigned int running = 0;

	//Solange threads laufen...
	do {
		//Ausgabe aktualisieren
		canvas->Update();
		canvas->Refresh();

		//ermitteln der Pfüfzahl, ob noch Threads laufen. Wenn diese 0 bleibt, sind alle Threads fertig.
		running = 0;
		for (int i = 0; i < core_count; i++) {
			running = running << 1;
			running += thread_running[i];
		};
	} while (running);

	//Threads zusammenführen
	for (int i = 0; i < core_count; i++) {
		threads.at(i)->join();
		delete threads.at(i);
	}

	//Freigeben des Ebenendreiecks
	for (int i = 0; i < 3; i++) {
		delete tri->getVert(i);
	}
	delete tri;

	//Zeitmessung beenden
	timeval tm2;
	gettimeofday(&tm2, NULL);

	unsigned long long t = 1000 * (tm2.tv_sec - tm1.tv_sec)
			+ (tm2.tv_usec - tm1.tv_usec) / 1000;

	//Ausgeben der Berechnungsdauer auf
	SetTitle(
			wxT(
					"Berechnung abgeschlossen. ( ") + floattowxstr(t / 1000.)+wxT( "s )"));

	//Freigeben der Ressourcen
	delete xvec;
	delete yvec;
	for (unsigned int i = 0; i < obj->getMaterials()->size(); i++) {
		delete bases.at(i);
	}
	delete info;
}
Example #7
0
/**
 * Visualisiert Informationen über eine 2D-Temperaturverteilung.
 * @param info Die zu visualisierenden Informationen.
 */
void drawCutRenderInfo(CutRender_info* info) {

	//Die Visualisierung soll nicht verdeckt weden
	glDepthFunc(GL_ALWAYS);
	//Blending für das die Ebene definierende Dreieck
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glEnable(GL_BLEND);

	glDisable(GL_LIGHTING);

	//zeichnen des die Ebene definierenden Dreiecks
	Triangle* tri = info->tri;
	glColor4f(.5, 0, 0, .5);
	glBegin(GL_TRIANGLES);
	Vector3D* nor = tri->getNormal();
	glNormal3dv(nor->getXYZ());
	glVertex3dv(tri->getV1()->getXYZ());
	glVertex3dv(tri->getV2()->getXYZ());
	glVertex3dv(tri->getV3()->getXYZ());
	glEnd();
	glDisable(GL_BLEND);

	//Vektoren zur Visualisierung der X- und Y-Achse der Ebene
	Vector3D* xvec = tri->getV2()->copy();
	xvec->sub(tri->getV1());
	Vector3D* yvec = nor->crossProduct(xvec);
	xvec->normalize();
	yvec->normalize();
	xvec->mult(info->img_width / 2 * info->mmperpixel / 1000.);
	yvec->mult(info->img_height / 2 * info->mmperpixel / 1000.);

	//Zeichnen der Achsen als Pfeile
	glColor3f(1, 0, 0);
	drawVector(tri->getV1(), xvec);
	glColor3f(0, 1, 0);
	drawVector(tri->getV1(), yvec);

	//Zeichnen des Rahmens für den dargestellten Bereich
	glColor4f(0, 0, 0, 1);
	Vector3D* tl = tri->getV1()->copy();
	tl->sub(xvec);
	tl->add(yvec);
	Vector3D* tr = tri->getV1()->copy();
	tr->add(xvec);
	tr->add(yvec);
	Vector3D* bl = tri->getV1()->copy();
	bl->sub(xvec);
	bl->sub(yvec);
	Vector3D* br = tri->getV1()->copy();
	br->add(xvec);
	br->sub(yvec);

	glBegin(GL_LINES);
	glVertex3dv(tl->getXYZ());
	glVertex3dv(tr->getXYZ());

	glVertex3dv(tl->getXYZ());
	glVertex3dv(bl->getXYZ());

	glVertex3dv(br->getXYZ());
	glVertex3dv(tr->getXYZ());

	glVertex3dv(br->getXYZ());
	glVertex3dv(bl->getXYZ());
	glEnd();

	delete tl;
	delete tr;
	delete bl;
	delete br;
	delete nor;
	delete xvec;
	delete yvec;

	//normaler Tiefentest
	glDepthFunc(GL_LESS);
}
Example #8
0
/**
 * Zeichnet einen Vektor als Pfeil.
 * @param pos Startpunkt des Pfeils.
 * @param dir Richtung und Länge des Pfeils.
 */
void drawVector(Vector3D* pos, Vector3D* dir) {

	//spitze des Vektors
	Vector3D tip = Vector3D(pos);
	//normalisierte Richtung des Vektors
	Vector3D ndir = Vector3D(dir);
	ndir.normalize();

	//Referenzvektor, darf dem Vektor nicht entsprechen
	Vector3D refdir = Vector3D(1, 0, 0);
	if (refdir.equals(&ndir)) {
		refdir.getXYZ()[0] = 0;
		refdir.getXYZ()[1] = 1;
	}

	//Radius der Vektorspitze
	float radius = .012 * dir->getLength();
	//Länge der Vektorspitze
	float length = .024 * dir->getLength();

	//Zwei Referenzvektoren, die zum Ausgansvektor und zueinander einen Winkel von 90° einschließen
	Vector3D* refvec = ndir.crossProduct(&refdir);
	Vector3D* refvec2 = ndir.crossProduct(refvec);
	refvec->normalize();
	refvec2->normalize();

	//zeichnen der Linie von Ausganspunkt bis Spitze des Pfeils
	tip.add(dir);
	Vector3D behind = Vector3D(pos);
	glLineWidth(2.0);
	glBegin(GL_LINES);
	glVertex3dv(behind.getXYZ());
	glVertex3dv(tip.getXYZ());
	glEnd();

	//Hilfsvektoren zum zusammentragen der Spitze des Vektors
	Vector3D v1 = Vector3D(refvec);
	Vector3D v2 = Vector3D(refvec2);
	Vector3D v3 = Vector3D(v1);
	v3.mult(-1);
	Vector3D v4 = Vector3D(refvec2);
	v4.mult(-1);

	//zeichen der Pfeilspitze
	v1.mult(radius);
	v2.mult(radius);
	v3.mult(radius);
	v4.mult(radius);
	ndir.mult(dir->getLength() - length);
	v1.add(&ndir);
	v2.add(&ndir);
	v3.add(&ndir);
	v4.add(&ndir);
	v1.add(pos);
	v2.add(pos);
	v3.add(pos);
	v4.add(pos);
	glBegin(GL_TRIANGLES);
	glVertex3dv(v1.getXYZ());
	glVertex3dv(v2.getXYZ());
	glVertex3dv(tip.getXYZ());

	glVertex3dv(v2.getXYZ());
	glVertex3dv(v3.getXYZ());
	glVertex3dv(tip.getXYZ());

	glVertex3dv(v3.getXYZ());
	glVertex3dv(v4.getXYZ());
	glVertex3dv(tip.getXYZ());

	glVertex3dv(v4.getXYZ());
	glVertex3dv(v1.getXYZ());
	glVertex3dv(tip.getXYZ());
	glEnd();
	glLineWidth(1.0);
	delete refvec;
	delete refvec2;
}
Vector3D crossProduct(const Vector3D & a, const Vector3D & b){
	return a.crossProduct(b);
}
Example #10
0
void Simulation::renderHud()
{
  if ((_hudMode == Hud_None) || (_viewMode == View_Outside))
    return;

  float margin = Decorator::instance()->getDefaultMargin();
  glColor3f(0.0f, 1.0f, 0.0f);

  // Crosshair at the center
  glPushMatrix();
  {
    glTranslatef(0.5f * geometry().w, 0.5f * geometry().h, 0.0f);

    glBegin(GL_LINES);
    {
      glVertex2f(-4.5f * margin, 0.0f);
      glVertex2f(-3.0f * margin, 0.0f);

      glVertex2f(-3.0f * margin, 3.0f * margin);
      glVertex2f(0.0f, 0.0f);

      glVertex2f(0.0f, 0.0f);
      glVertex2f(3.0f * margin, 3.0f * margin);

      glVertex2f(3.0f * margin, 0.0f);
      glVertex2f(4.5f * margin, 0.0f);
    }
    glEnd();

    // Labels around the center

    FontMetrics bigMetrics(_bigHudFont);

    int w = bigMetrics.width(_heightString);
    _bigHudFont->renderText(_heightString, Point(-12.5f * margin - w, -1.5f * bigMetrics.height()));

    w = bigMetrics.width(_altitudeString);
    _bigHudFont->renderText(_altitudeString, Point(-12.5f * margin - w, 0.5f * bigMetrics.height()));

    _bigHudFont->renderText(_velocityString, Point(12.5f * margin, -1.5f * bigMetrics.height()));

    if (_simulationType == Simulation_Game)
      _bigHudFont->renderText(_ammoString, Point(12.5f * margin, 0.5f * bigMetrics.height()));
  }
  glPopMatrix();

  if (_hudMode == Hud_Minimal)
    return;

  FontMetrics metrics(_hudFont);
  int fontHeight = metrics.height();

  // Direction markers

  glPushMatrix();
  {
    glTranslatef(0.0f, 2.0f * margin, 0.0f);
    float fovX =  geometry().w * _fov / geometry().h;
    int minH = (int)floor(_player->rotation().heading() - 0.25f * fovX);
    int maxH = (int)ceil(_player->rotation().heading() + 0.25f * fovX);
    float dx = geometry().w / fovX;
    float x = 0.5f * geometry().w - (_player->rotation().heading() - minH) * dx;

    glBegin(GL_LINES);
    {
      for (int h = minH; h <= maxH; ++h)
      {
        glVertex2f(x, 0.0f);
        if (h % 10 == 0)
          glVertex2f(x, 2.0f * margin);
        else
          glVertex2f(x, margin);

       x += dx;
      }

      glVertex2f(0.5f * geometry().w - 0.5f * margin, -margin);
      glVertex2f(0.5f * geometry().w, -0.5f * margin);

      glVertex2f(0.5f * geometry().w, -0.5f * margin);
      glVertex2f(0.5f * geometry().w + 0.5f * margin, -margin);
    }
    glEnd();

    x = geometry().w / 2.0f - (_player->rotation().heading() - minH) * dx;
    for (int h = minH; h <= maxH; ++h)
    {
      if (h % 10 == 0)
      {
        int hValue = h;
        if (h >= 360)
          hValue = h - 360;
        else if (h < 0)
          hValue = 360 + h;

        stringstream s;
        s.fill('0');
        s.width(2);
        s << hValue / 10;
        int w = metrics.width(s.str());
        _hudFont->renderText(s.str(), Point(x - w / 2.0f, 2.5f * margin));
      }
      x += dx;
    }
  }
  glPopMatrix();

  // Pitch markers

  glPushMatrix();
  {
    glTranslatef(0.5f * geometry().w, 0.5f * geometry().h, 0.0f);
    glRotatef(-_player->rotation().roll(), 0.0f, 0.0f, 1.0f);

    int minP = (int)floor(_player->rotation().pitch() - 0.4f * _fov);
    int maxP = (int)ceil(_player->rotation().pitch() + 0.4f * _fov);
    float dy = geometry().h / _fov;
    float y = (_player->rotation().pitch() - minP) * dy;

    glBegin(GL_LINES);
    {
      for (int p = minP; p <= maxP; ++p)
      {
        if (p % 10 == 0)
        {
          glVertex2f(-12.0f * margin, y);
          glVertex2f(-2.0f * margin, y);

          glVertex2f( 2.0f * margin, y);
          glVertex2f( 12.0f * margin, y);

          if (p > 0)
          {
            glVertex2f(-12.0f * margin, y);
            glVertex2f(-12.0f * margin, y + margin);

            glVertex2f( 12.0f * margin, y);
            glVertex2f( 12.0f * margin, y + margin);
          }
          else if (p < 0)
          {
            glVertex2f(-12.0f * margin, y);
            glVertex2f(-12.0f * margin, y - margin);

            glVertex2f( 12.0f * margin, y);
            glVertex2f( 12.0f * margin, y - margin);
          }
        }

        y -= dy;
      }
    }
    glEnd();

    y = (_player->rotation().pitch() - minP) * dy;
    for (int p = minP; p <= maxP; ++p)
    {
      if (p % 10 == 0)
      {
        int pValue = p;
        if (p > 90)
          pValue = 90 - p;
        else if (p < -90)
          pValue = -90 - p;

        stringstream s;
        s.fill('0');
        s.width(2);
        s.setf(ios_base::showpos);
        s << pValue;
        int w = metrics.width(s.str());
        _hudFont->renderText(s.str(), Point(-0.5f * w, y - 0.5f * fontHeight));
      }
      y -= dy;
    }
  }
  glPopMatrix();

  if (_simulationType == Simulation_Normal)
    return;

  // Radar
  glPushMatrix();
  {
    float radarSize = min(0.1f * geometry().w, 0.1f * geometry().h);

    float margin = Decorator::instance()->getDefaultMargin();

    glScissor((int)(geometry().w - 2.0f * radarSize - margin),
              (int)(margin), (int)(2.0f * radarSize), (int)(2.0f * radarSize));

    glEnable(GL_SCISSOR_TEST);

    glTranslatef(geometry().w - 2.0f * radarSize - margin,
                 geometry().h - 2.0f * radarSize - margin,
                 0.0f);

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    glColor4f(0.0f, 1.0f, 0.0f, 0.5f);

    glBegin(GL_QUADS);
    {
      glVertex2f(0.0f, 0.0f);
      glVertex2f(2.0f * radarSize, 0.0f);
      glVertex2f(2.0f * radarSize, 2.0f * radarSize);
      glVertex2f(0.0f, 2.0f * radarSize);
    }
    glEnd();

    glTranslatef(radarSize, radarSize, 0.0f);

    glPushMatrix();
    {
      Vector3D qs = _map->quadSize();

      Vector3D offset = _player->positionOffset() * (radarSize / RADAR_RANGE);

      // Grid
      glPushMatrix();
      {
        glRotatef(-_player->rotation().heading(), 0.0f, 0.0f, 1.0f);
        glTranslatef(-offset.x, offset.z, 0.0f);

        int nGrids = 1 + (int)max(1.5f * ceil(RADAR_RANGE / qs.x),
                                  1.5f * ceil(RADAR_RANGE / qs.z));
        if (nGrids % 2 == 1)
          ++nGrids;

        glColor3f(0.5f, 0.5f, 0.5f);

        glBegin(GL_LINES);
        {
          for (int grid = -nGrids / 2; grid <= nGrids / 2; ++grid)
          {
            glVertex2f(grid * qs.x * (radarSize / RADAR_RANGE), -2.0f * radarSize);
            glVertex2f(grid * qs.x * (radarSize / RADAR_RANGE),  2.0f * radarSize);

            glVertex2f(-2.0f * radarSize, grid * qs.z * (radarSize / RADAR_RANGE));
            glVertex2f( 2.0f * radarSize, grid * qs.z * (radarSize / RADAR_RANGE));
          }
        }
        glEnd();
      }
      glPopMatrix();

      // Player's dot
      glColor3fv(_player->color());

      glPointSize(4.0f);

      glBegin(GL_POINTS);
      {
        glVertex2d(0.0f, 0.0f);
      }
      glEnd();

      // Enemy positions
      glPushMatrix();
      {
        for (list<Player*>::iterator it = _enemyPlayers.begin();
           it != _enemyPlayers.end(); ++it)
        {
          Vector3D deltaPos = (*it)->actualPosition() - _player->actualPosition();
          deltaPos.y = 0.0f;
          deltaPos.rotate(-_player->rotation().heading(), Vector3D(0.0f, 1.0f, 0.0f));

          glColor3fv((*it)->color());

          deltaPos *= (radarSize / RADAR_RANGE);

          if ((fabs(deltaPos.x) < radarSize) && (fabs(deltaPos.z) < radarSize))
          {
            glBegin(GL_POINTS);
            {
              glVertex2f(deltaPos.x, -deltaPos.z);
            }
            glEnd();
          }
          else
          {
            deltaPos.clamp(Vector3D(-radarSize, 0.0f, -radarSize),
                           Vector3D(radarSize, 0.0f, radarSize));
            Vector3D dir = Vector3D::normalize(deltaPos);
            Vector3D side = dir.crossProduct(Vector3D(0.0f, 1.0f, 0.0f));
            side.normalize();

            Vector3D v1 = deltaPos - 2.5f * side;
            Vector3D v2 = deltaPos + 2.5f * side;
            Vector3D v3 = deltaPos + 5.0f * dir;

            glDisable(GL_SCISSOR_TEST);

            glBegin(GL_TRIANGLES);
            {
              glVertex2f(v1.x, -v1.z);
              glVertex2f(v2.x, -v2.z);
              glVertex2f(v3.x, -v3.z);
            }
            glEnd();

            glEnable(GL_SCISSOR_TEST);
          }
        }
      }
      glPopMatrix();

      glPointSize(1.0f);
    }
    glPopMatrix();

    glDisable(GL_SCISSOR_TEST);
  }
  glPopMatrix();
}